1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/basictypes.h"
7 #include "base/command_line.h"
8 #include "base/files/file_util.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/stl_util.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "base/time/time.h"
14 #include "content/browser/frame_host/cross_site_transferring_request.h"
15 #include "content/browser/frame_host/frame_navigation_entry.h"
16 #include "content/browser/frame_host/navigation_controller_impl.h"
17 #include "content/browser/frame_host/navigation_entry_impl.h"
18 #include "content/browser/frame_host/navigation_entry_screenshot_manager.h"
19 #include "content/browser/frame_host/navigation_request.h"
20 #include "content/browser/frame_host/navigator.h"
21 #include "content/browser/frame_host/navigator_impl.h"
22 #include "content/browser/site_instance_impl.h"
23 #include "content/browser/web_contents/web_contents_impl.h"
24 #include "content/common/frame_messages.h"
25 #include "content/common/view_messages.h"
26 #include "content/public/browser/navigation_details.h"
27 #include "content/public/browser/notification_registrar.h"
28 #include "content/public/browser/notification_types.h"
29 #include "content/public/browser/render_view_host.h"
30 #include "content/public/browser/web_contents_delegate.h"
31 #include "content/public/browser/web_contents_observer.h"
32 #include "content/public/common/content_switches.h"
33 #include "content/public/common/page_state.h"
34 #include "content/public/common/page_type.h"
35 #include "content/public/common/url_constants.h"
36 #include "content/public/test/mock_render_process_host.h"
37 #include "content/public/test/test_notification_tracker.h"
38 #include "content/public/test/test_utils.h"
39 #include "content/test/test_render_frame_host.h"
40 #include "content/test/test_render_view_host.h"
41 #include "content/test/test_web_contents.h"
42 #include "net/base/net_util.h"
43 #include "skia/ext/platform_canvas.h"
44 #include "testing/gtest/include/gtest/gtest.h"
45 #include "third_party/WebKit/public/web/WebSandboxFlags.h"
51 // Creates an image with a 1x1 SkBitmap of the specified |color|.
52 gfx::Image
CreateImage(SkColor color
) {
54 bitmap
.allocN32Pixels(1, 1);
55 bitmap
.eraseColor(color
);
56 return gfx::Image::CreateFrom1xBitmap(bitmap
);
59 // Returns true if images |a| and |b| have the same pixel data.
60 bool DoImagesMatch(const gfx::Image
& a
, const gfx::Image
& b
) {
61 // Assume that if the 1x bitmaps match, the images match.
62 SkBitmap a_bitmap
= a
.AsBitmap();
63 SkBitmap b_bitmap
= b
.AsBitmap();
65 if (a_bitmap
.width() != b_bitmap
.width() ||
66 a_bitmap
.height() != b_bitmap
.height()) {
69 SkAutoLockPixels
a_bitmap_lock(a_bitmap
);
70 SkAutoLockPixels
b_bitmap_lock(b_bitmap
);
71 return memcmp(a_bitmap
.getPixels(),
73 a_bitmap
.getSize()) == 0;
76 class MockScreenshotManager
: public content::NavigationEntryScreenshotManager
{
78 explicit MockScreenshotManager(content::NavigationControllerImpl
* owner
)
79 : content::NavigationEntryScreenshotManager(owner
),
80 encoding_screenshot_in_progress_(false) {
83 ~MockScreenshotManager() override
{}
85 void TakeScreenshotFor(content::NavigationEntryImpl
* entry
) {
87 bitmap
.allocPixels(SkImageInfo::Make(
88 1, 1, kAlpha_8_SkColorType
, kPremul_SkAlphaType
));
89 bitmap
.eraseARGB(0, 0, 0, 0);
90 encoding_screenshot_in_progress_
= true;
91 OnScreenshotTaken(entry
->GetUniqueID(), bitmap
, content::READBACK_SUCCESS
);
92 WaitUntilScreenshotIsReady();
95 int GetScreenshotCount() {
96 return content::NavigationEntryScreenshotManager::GetScreenshotCount();
99 void WaitUntilScreenshotIsReady() {
100 if (!encoding_screenshot_in_progress_
)
102 message_loop_runner_
= new content::MessageLoopRunner
;
103 message_loop_runner_
->Run();
107 // Overridden from content::NavigationEntryScreenshotManager:
108 void TakeScreenshotImpl(content::RenderViewHost
* host
,
109 content::NavigationEntryImpl
* entry
) override
{}
111 void OnScreenshotSet(content::NavigationEntryImpl
* entry
) override
{
112 encoding_screenshot_in_progress_
= false;
113 NavigationEntryScreenshotManager::OnScreenshotSet(entry
);
114 if (message_loop_runner_
.get())
115 message_loop_runner_
->Quit();
118 scoped_refptr
<content::MessageLoopRunner
> message_loop_runner_
;
119 bool encoding_screenshot_in_progress_
;
121 DISALLOW_COPY_AND_ASSIGN(MockScreenshotManager
);
128 // TimeSmoother tests ----------------------------------------------------------
130 // With no duplicates, GetSmoothedTime should be the identity
132 TEST(TimeSmoother
, Basic
) {
133 NavigationControllerImpl::TimeSmoother smoother
;
134 for (int64 i
= 1; i
< 1000; ++i
) {
135 base::Time t
= base::Time::FromInternalValue(i
);
136 EXPECT_EQ(t
, smoother
.GetSmoothedTime(t
));
140 // With a single duplicate and timestamps thereafter increasing by one
141 // microsecond, the smoothed time should always be one behind.
142 TEST(TimeSmoother
, SingleDuplicate
) {
143 NavigationControllerImpl::TimeSmoother smoother
;
144 base::Time t
= base::Time::FromInternalValue(1);
145 EXPECT_EQ(t
, smoother
.GetSmoothedTime(t
));
146 for (int64 i
= 1; i
< 1000; ++i
) {
147 base::Time expected_t
= base::Time::FromInternalValue(i
+ 1);
148 t
= base::Time::FromInternalValue(i
);
149 EXPECT_EQ(expected_t
, smoother
.GetSmoothedTime(t
));
153 // With k duplicates and timestamps thereafter increasing by one
154 // microsecond, the smoothed time should always be k behind.
155 TEST(TimeSmoother
, ManyDuplicates
) {
156 const int64 kNumDuplicates
= 100;
157 NavigationControllerImpl::TimeSmoother smoother
;
158 base::Time t
= base::Time::FromInternalValue(1);
159 for (int64 i
= 0; i
< kNumDuplicates
; ++i
) {
160 base::Time expected_t
= base::Time::FromInternalValue(i
+ 1);
161 EXPECT_EQ(expected_t
, smoother
.GetSmoothedTime(t
));
163 for (int64 i
= 1; i
< 1000; ++i
) {
164 base::Time expected_t
=
165 base::Time::FromInternalValue(i
+ kNumDuplicates
);
166 t
= base::Time::FromInternalValue(i
);
167 EXPECT_EQ(expected_t
, smoother
.GetSmoothedTime(t
));
171 // If the clock jumps far back enough after a run of duplicates, it
172 // should immediately jump to that value.
173 TEST(TimeSmoother
, ClockBackwardsJump
) {
174 const int64 kNumDuplicates
= 100;
175 NavigationControllerImpl::TimeSmoother smoother
;
176 base::Time t
= base::Time::FromInternalValue(1000);
177 for (int64 i
= 0; i
< kNumDuplicates
; ++i
) {
178 base::Time expected_t
= base::Time::FromInternalValue(i
+ 1000);
179 EXPECT_EQ(expected_t
, smoother
.GetSmoothedTime(t
));
181 t
= base::Time::FromInternalValue(500);
182 EXPECT_EQ(t
, smoother
.GetSmoothedTime(t
));
185 // NavigationControllerTest ----------------------------------------------------
187 class NavigationControllerTest
188 : public RenderViewHostImplTestHarness
,
189 public WebContentsObserver
{
191 NavigationControllerTest() : navigation_entry_committed_counter_(0) {
194 void SetUp() override
{
195 RenderViewHostImplTestHarness::SetUp();
196 WebContents
* web_contents
= RenderViewHostImplTestHarness::web_contents();
197 ASSERT_TRUE(web_contents
); // The WebContents should be created by now.
198 WebContentsObserver::Observe(web_contents
);
201 // WebContentsObserver:
202 void DidStartNavigationToPendingEntry(
204 NavigationController::ReloadType reload_type
) override
{
205 navigated_url_
= url
;
208 void NavigationEntryCommitted(
209 const LoadCommittedDetails
& load_details
) override
{
210 navigation_entry_committed_counter_
++;
213 const GURL
& navigated_url() const {
214 return navigated_url_
;
217 NavigationControllerImpl
& controller_impl() {
218 return static_cast<NavigationControllerImpl
&>(controller());
221 bool HasNavigationRequest() {
222 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
223 switches::kEnableBrowserSideNavigation
)) {
224 return contents()->GetFrameTree()->root()->navigation_request() !=
227 return process()->sink().GetFirstMessageMatching(FrameMsg_Navigate::ID
)
231 const GURL
GetLastNavigationURL() {
232 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
233 switches::kEnableBrowserSideNavigation
)) {
234 NavigationRequest
* navigation_request
=
235 contents()->GetFrameTree()->root()->navigation_request();
236 CHECK(navigation_request
);
237 return navigation_request
->common_params().url
;
239 const IPC::Message
* message
=
240 process()->sink().GetFirstMessageMatching(FrameMsg_Navigate::ID
);
242 Tuple
<CommonNavigationParams
, StartNavigationParams
,
243 RequestNavigationParams
> nav_params
;
244 FrameMsg_Navigate::Read(message
, &nav_params
);
245 return get
<0>(nav_params
).url
;
250 size_t navigation_entry_committed_counter_
;
253 void RegisterForAllNavNotifications(TestNotificationTracker
* tracker
,
254 NavigationController
* controller
) {
255 tracker
->ListenFor(NOTIFICATION_NAV_LIST_PRUNED
,
256 Source
<NavigationController
>(controller
));
257 tracker
->ListenFor(NOTIFICATION_NAV_ENTRY_CHANGED
,
258 Source
<NavigationController
>(controller
));
261 class TestWebContentsDelegate
: public WebContentsDelegate
{
263 explicit TestWebContentsDelegate() :
264 navigation_state_change_count_(0),
265 repost_form_warning_count_(0) {}
267 int navigation_state_change_count() {
268 return navigation_state_change_count_
;
271 int repost_form_warning_count() {
272 return repost_form_warning_count_
;
275 // Keep track of whether the tab has notified us of a navigation state change.
276 void NavigationStateChanged(WebContents
* source
,
277 InvalidateTypes changed_flags
) override
{
278 navigation_state_change_count_
++;
281 void ShowRepostFormWarningDialog(WebContents
* source
) override
{
282 repost_form_warning_count_
++;
286 // The number of times NavigationStateChanged has been called.
287 int navigation_state_change_count_
;
289 // The number of times ShowRepostFormWarningDialog() was called.
290 int repost_form_warning_count_
;
293 // -----------------------------------------------------------------------------
295 TEST_F(NavigationControllerTest
, Defaults
) {
296 NavigationControllerImpl
& controller
= controller_impl();
298 EXPECT_FALSE(controller
.GetPendingEntry());
299 EXPECT_FALSE(controller
.GetVisibleEntry());
300 EXPECT_FALSE(controller
.GetLastCommittedEntry());
301 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
302 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), -1);
303 EXPECT_EQ(controller
.GetEntryCount(), 0);
304 EXPECT_FALSE(controller
.CanGoBack());
305 EXPECT_FALSE(controller
.CanGoForward());
308 TEST_F(NavigationControllerTest
, GoToOffset
) {
309 NavigationControllerImpl
& controller
= controller_impl();
310 TestNotificationTracker notifications
;
311 RegisterForAllNavNotifications(¬ifications
, &controller
);
313 const int kNumUrls
= 5;
314 std::vector
<GURL
> urls(kNumUrls
);
315 for (int i
= 0; i
< kNumUrls
; ++i
) {
316 urls
[i
] = GURL(base::StringPrintf("http://www.a.com/%d", i
));
319 main_test_rfh()->SendRendererInitiatedNavigationRequest(urls
[0], true);
320 main_test_rfh()->PrepareForCommit();
321 main_test_rfh()->SendNavigate(0, 0, true, urls
[0]);
322 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
323 navigation_entry_committed_counter_
= 0;
324 EXPECT_EQ(urls
[0], controller
.GetVisibleEntry()->GetVirtualURL());
325 EXPECT_FALSE(controller
.CanGoBack());
326 EXPECT_FALSE(controller
.CanGoForward());
327 EXPECT_FALSE(controller
.CanGoToOffset(1));
329 for (int i
= 1; i
<= 4; ++i
) {
330 main_test_rfh()->SendRendererInitiatedNavigationRequest(urls
[i
], true);
331 main_test_rfh()->PrepareForCommit();
332 main_test_rfh()->SendNavigate(i
, 0, true, urls
[i
]);
333 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
334 navigation_entry_committed_counter_
= 0;
335 EXPECT_EQ(urls
[i
], controller
.GetVisibleEntry()->GetVirtualURL());
336 EXPECT_TRUE(controller
.CanGoToOffset(-i
));
337 EXPECT_FALSE(controller
.CanGoToOffset(-(i
+ 1)));
338 EXPECT_FALSE(controller
.CanGoToOffset(1));
341 // We have loaded 5 pages, and are currently at the last-loaded page.
345 GO_TO_MIDDLE_PAGE
= -2,
348 GO_TO_BEGINNING
= -2,
353 const int test_offsets
[NUM_TESTS
] = {
361 for (int test
= 0; test
< NUM_TESTS
; ++test
) {
362 int offset
= test_offsets
[test
];
363 controller
.GoToOffset(offset
);
364 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
366 // Check that the GoToOffset will land on the expected page.
367 EXPECT_EQ(urls
[url_index
], controller
.GetPendingEntry()->GetVirtualURL());
368 main_test_rfh()->PrepareForCommit();
369 main_test_rfh()->SendNavigate(url_index
, entry_id
, false, urls
[url_index
]);
370 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
371 navigation_entry_committed_counter_
= 0;
372 // Check that we can go to any valid offset into the history.
373 for (size_t j
= 0; j
< urls
.size(); ++j
)
374 EXPECT_TRUE(controller
.CanGoToOffset(j
- url_index
));
375 // Check that we can't go beyond the beginning or end of the history.
376 EXPECT_FALSE(controller
.CanGoToOffset(-(url_index
+ 1)));
377 EXPECT_FALSE(controller
.CanGoToOffset(urls
.size() - url_index
));
381 TEST_F(NavigationControllerTest
, LoadURL
) {
382 NavigationControllerImpl
& controller
= controller_impl();
383 TestNotificationTracker notifications
;
384 RegisterForAllNavNotifications(¬ifications
, &controller
);
386 const GURL
url1("http://foo1");
387 const GURL
url2("http://foo2");
390 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
391 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
392 // Creating a pending notification should not have issued any of the
393 // notifications we're listening for.
394 EXPECT_EQ(0U, notifications
.size());
396 // The load should now be pending.
397 EXPECT_EQ(controller
.GetEntryCount(), 0);
398 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), -1);
399 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
400 EXPECT_FALSE(controller
.GetLastCommittedEntry());
401 ASSERT_TRUE(controller
.GetPendingEntry());
402 EXPECT_EQ(controller
.GetPendingEntry(), controller
.GetVisibleEntry());
403 EXPECT_FALSE(controller
.CanGoBack());
404 EXPECT_FALSE(controller
.CanGoForward());
405 EXPECT_EQ(contents()->GetMaxPageID(), -1);
407 // Neither the timestamp nor the status code should have been set yet.
408 EXPECT_TRUE(controller
.GetPendingEntry()->GetTimestamp().is_null());
409 EXPECT_EQ(0, controller
.GetPendingEntry()->GetHttpStatusCode());
411 // We should have gotten no notifications from the preceeding checks.
412 EXPECT_EQ(0U, notifications
.size());
414 main_test_rfh()->PrepareForCommit();
415 main_test_rfh()->SendNavigate(0, entry_id
, true, url1
);
416 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
417 navigation_entry_committed_counter_
= 0;
419 // The load should now be committed.
420 EXPECT_EQ(controller
.GetEntryCount(), 1);
421 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
422 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
423 EXPECT_TRUE(controller
.GetLastCommittedEntry());
424 EXPECT_FALSE(controller
.GetPendingEntry());
425 ASSERT_TRUE(controller
.GetVisibleEntry());
426 EXPECT_FALSE(controller
.CanGoBack());
427 EXPECT_FALSE(controller
.CanGoForward());
428 EXPECT_EQ(contents()->GetMaxPageID(), 0);
429 EXPECT_EQ(0, controller
.GetLastCommittedEntry()->bindings());
431 // The timestamp should have been set.
432 EXPECT_FALSE(controller
.GetVisibleEntry()->GetTimestamp().is_null());
436 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
437 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
439 // The load should now be pending.
440 EXPECT_EQ(controller
.GetEntryCount(), 1);
441 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
442 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
443 EXPECT_TRUE(controller
.GetLastCommittedEntry());
444 ASSERT_TRUE(controller
.GetPendingEntry());
445 EXPECT_EQ(controller
.GetPendingEntry(), controller
.GetVisibleEntry());
446 // TODO(darin): maybe this should really be true?
447 EXPECT_FALSE(controller
.CanGoBack());
448 EXPECT_FALSE(controller
.CanGoForward());
449 EXPECT_EQ(contents()->GetMaxPageID(), 0);
451 EXPECT_TRUE(controller
.GetPendingEntry()->GetTimestamp().is_null());
453 // Simulate the beforeunload ack for the cross-site transition, and then the
455 main_test_rfh()->PrepareForCommit();
456 contents()->GetPendingMainFrame()->SendNavigate(1, entry_id
, true, url2
);
457 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
458 navigation_entry_committed_counter_
= 0;
460 // The load should now be committed.
461 EXPECT_EQ(controller
.GetEntryCount(), 2);
462 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 1);
463 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
464 EXPECT_TRUE(controller
.GetLastCommittedEntry());
465 EXPECT_FALSE(controller
.GetPendingEntry());
466 ASSERT_TRUE(controller
.GetVisibleEntry());
467 EXPECT_TRUE(controller
.CanGoBack());
468 EXPECT_FALSE(controller
.CanGoForward());
469 EXPECT_EQ(contents()->GetMaxPageID(), 1);
471 EXPECT_FALSE(controller
.GetVisibleEntry()->GetTimestamp().is_null());
476 base::Time
GetFixedTime(base::Time time
) {
482 TEST_F(NavigationControllerTest
, LoadURLSameTime
) {
483 NavigationControllerImpl
& controller
= controller_impl();
484 TestNotificationTracker notifications
;
485 RegisterForAllNavNotifications(¬ifications
, &controller
);
487 // Set the clock to always return a timestamp of 1.
488 controller
.SetGetTimestampCallbackForTest(
489 base::Bind(&GetFixedTime
, base::Time::FromInternalValue(1)));
491 const GURL
url1("http://foo1");
492 const GURL
url2("http://foo2");
495 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
496 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
498 main_test_rfh()->PrepareForCommit();
499 main_test_rfh()->SendNavigate(0, entry_id
, true, url1
);
500 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
501 navigation_entry_committed_counter_
= 0;
505 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
506 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
508 // Simulate the beforeunload ack for the cross-site transition, and then the
510 main_test_rfh()->PrepareForCommit();
511 contents()->GetPendingMainFrame()->SendNavigate(1, entry_id
, true, url2
);
512 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
513 navigation_entry_committed_counter_
= 0;
515 // The two loads should now be committed.
516 ASSERT_EQ(controller
.GetEntryCount(), 2);
518 // Timestamps should be distinct despite the clock returning the
521 controller
.GetEntryAtIndex(0)->GetTimestamp().ToInternalValue());
523 controller
.GetEntryAtIndex(1)->GetTimestamp().ToInternalValue());
526 void CheckNavigationEntryMatchLoadParams(
527 NavigationController::LoadURLParams
& load_params
,
528 NavigationEntryImpl
* entry
) {
529 EXPECT_EQ(load_params
.url
, entry
->GetURL());
530 EXPECT_EQ(load_params
.referrer
.url
, entry
->GetReferrer().url
);
531 EXPECT_EQ(load_params
.referrer
.policy
, entry
->GetReferrer().policy
);
532 EXPECT_EQ(load_params
.transition_type
, entry
->GetTransitionType());
533 EXPECT_EQ(load_params
.extra_headers
, entry
->extra_headers());
535 EXPECT_EQ(load_params
.is_renderer_initiated
, entry
->is_renderer_initiated());
536 EXPECT_EQ(load_params
.base_url_for_data_url
, entry
->GetBaseURLForDataURL());
537 if (!load_params
.virtual_url_for_data_url
.is_empty()) {
538 EXPECT_EQ(load_params
.virtual_url_for_data_url
, entry
->GetVirtualURL());
540 if (NavigationController::UA_OVERRIDE_INHERIT
!=
541 load_params
.override_user_agent
) {
542 bool should_override
= (NavigationController::UA_OVERRIDE_TRUE
==
543 load_params
.override_user_agent
);
544 EXPECT_EQ(should_override
, entry
->GetIsOverridingUserAgent());
546 EXPECT_EQ(load_params
.browser_initiated_post_data
.get(),
547 entry
->GetBrowserInitiatedPostData());
548 EXPECT_EQ(load_params
.transferred_global_request_id
,
549 entry
->transferred_global_request_id());
552 TEST_F(NavigationControllerTest
, LoadURLWithParams
) {
553 NavigationControllerImpl
& controller
= controller_impl();
555 NavigationController::LoadURLParams
load_params(GURL("http://foo"));
556 load_params
.referrer
=
557 Referrer(GURL("http://referrer"), blink::WebReferrerPolicyDefault
);
558 load_params
.transition_type
= ui::PAGE_TRANSITION_GENERATED
;
559 load_params
.extra_headers
= "content-type: text/plain";
560 load_params
.load_type
= NavigationController::LOAD_TYPE_DEFAULT
;
561 load_params
.is_renderer_initiated
= true;
562 load_params
.override_user_agent
= NavigationController::UA_OVERRIDE_TRUE
;
563 load_params
.transferred_global_request_id
= GlobalRequestID(2, 3);
565 controller
.LoadURLWithParams(load_params
);
566 NavigationEntryImpl
* entry
= controller
.GetPendingEntry();
568 // The timestamp should not have been set yet.
570 EXPECT_TRUE(entry
->GetTimestamp().is_null());
572 CheckNavigationEntryMatchLoadParams(load_params
, entry
);
575 TEST_F(NavigationControllerTest
, LoadURLWithExtraParams_Data
) {
576 NavigationControllerImpl
& controller
= controller_impl();
578 NavigationController::LoadURLParams
load_params(
579 GURL("data:text/html,dataurl"));
580 load_params
.load_type
= NavigationController::LOAD_TYPE_DATA
;
581 load_params
.base_url_for_data_url
= GURL("http://foo");
582 load_params
.virtual_url_for_data_url
= GURL(url::kAboutBlankURL
);
583 load_params
.override_user_agent
= NavigationController::UA_OVERRIDE_FALSE
;
585 controller
.LoadURLWithParams(load_params
);
586 NavigationEntryImpl
* entry
= controller
.GetPendingEntry();
588 CheckNavigationEntryMatchLoadParams(load_params
, entry
);
591 TEST_F(NavigationControllerTest
, LoadURLWithExtraParams_HttpPost
) {
592 NavigationControllerImpl
& controller
= controller_impl();
594 NavigationController::LoadURLParams
load_params(GURL("https://posturl"));
595 load_params
.transition_type
= ui::PAGE_TRANSITION_TYPED
;
596 load_params
.load_type
=
597 NavigationController::LOAD_TYPE_BROWSER_INITIATED_HTTP_POST
;
598 load_params
.override_user_agent
= NavigationController::UA_OVERRIDE_TRUE
;
601 const unsigned char* raw_data
=
602 reinterpret_cast<const unsigned char*>("d\n\0a2");
603 const int length
= 5;
604 std::vector
<unsigned char> post_data_vector(raw_data
, raw_data
+length
);
605 scoped_refptr
<base::RefCountedBytes
> data
=
606 base::RefCountedBytes::TakeVector(&post_data_vector
);
607 load_params
.browser_initiated_post_data
= data
.get();
609 controller
.LoadURLWithParams(load_params
);
610 NavigationEntryImpl
* entry
= controller
.GetPendingEntry();
612 CheckNavigationEntryMatchLoadParams(load_params
, entry
);
615 // Tests what happens when the same page is loaded again. Should not create a
616 // new session history entry. This is what happens when you press enter in the
617 // URL bar to reload: a pending entry is created and then it is discarded when
618 // the load commits (because WebCore didn't actually make a new entry).
619 TEST_F(NavigationControllerTest
, LoadURL_SamePage
) {
620 NavigationControllerImpl
& controller
= controller_impl();
621 TestNotificationTracker notifications
;
622 RegisterForAllNavNotifications(¬ifications
, &controller
);
624 const GURL
url1("http://foo1");
627 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
628 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
629 EXPECT_EQ(0U, notifications
.size());
630 main_test_rfh()->PrepareForCommit();
631 main_test_rfh()->SendNavigateWithTransition(
632 0, entry_id
, true, url1
, ui::PAGE_TRANSITION_TYPED
);
633 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
634 navigation_entry_committed_counter_
= 0;
636 ASSERT_TRUE(controller
.GetVisibleEntry());
637 const base::Time timestamp
= controller
.GetVisibleEntry()->GetTimestamp();
638 EXPECT_FALSE(timestamp
.is_null());
641 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
642 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
643 EXPECT_EQ(0U, notifications
.size());
644 main_test_rfh()->PrepareForCommit();
645 main_test_rfh()->SendNavigateWithTransition(
646 0, entry_id
, false, url1
, ui::PAGE_TRANSITION_TYPED
);
647 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
648 navigation_entry_committed_counter_
= 0;
650 // We should not have produced a new session history entry.
651 EXPECT_EQ(controller
.GetEntryCount(), 1);
652 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
653 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
654 EXPECT_TRUE(controller
.GetLastCommittedEntry());
655 EXPECT_FALSE(controller
.GetPendingEntry());
656 ASSERT_TRUE(controller
.GetVisibleEntry());
657 EXPECT_FALSE(controller
.CanGoBack());
658 EXPECT_FALSE(controller
.CanGoForward());
660 // The timestamp should have been updated.
662 // TODO(akalin): Change this EXPECT_GE (and other similar ones) to
663 // EXPECT_GT once we guarantee that timestamps are unique.
664 EXPECT_GE(controller
.GetVisibleEntry()->GetTimestamp(), timestamp
);
667 // Load the same page twice, once as a GET and once as a POST.
668 // We should update the post state on the NavigationEntry.
669 TEST_F(NavigationControllerTest
, LoadURL_SamePage_DifferentMethod
) {
670 NavigationControllerImpl
& controller
= controller_impl();
671 TestNotificationTracker notifications
;
672 RegisterForAllNavNotifications(¬ifications
, &controller
);
674 const GURL
url1("http://foo1");
677 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
678 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
680 params
.nav_entry_id
= controller
.GetPendingEntry()->GetUniqueID();
681 params
.did_create_new_entry
= true;
683 params
.transition
= ui::PAGE_TRANSITION_TYPED
;
684 params
.is_post
= true;
685 params
.post_id
= 123;
686 params
.page_state
= PageState::CreateForTesting(url1
, false, 0, 0);
687 main_test_rfh()->PrepareForCommit();
688 main_test_rfh()->SendNavigateWithParams(¶ms
);
690 // The post data should be visible.
691 NavigationEntry
* entry
= controller
.GetVisibleEntry();
693 EXPECT_TRUE(entry
->GetHasPostData());
694 EXPECT_EQ(entry
->GetPostID(), 123);
697 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
698 main_test_rfh()->PrepareForCommit();
699 main_test_rfh()->SendNavigateWithTransition(
700 0, controller
.GetPendingEntry()->GetUniqueID(),
701 false, url1
, ui::PAGE_TRANSITION_TYPED
);
703 // We should not have produced a new session history entry.
704 ASSERT_EQ(controller
.GetVisibleEntry(), entry
);
706 // The post data should have been cleared due to the GET.
707 EXPECT_FALSE(entry
->GetHasPostData());
708 EXPECT_EQ(entry
->GetPostID(), 0);
711 // Tests loading a URL but discarding it before the load commits.
712 TEST_F(NavigationControllerTest
, LoadURL_Discarded
) {
713 NavigationControllerImpl
& controller
= controller_impl();
714 TestNotificationTracker notifications
;
715 RegisterForAllNavNotifications(¬ifications
, &controller
);
717 const GURL
url1("http://foo1");
718 const GURL
url2("http://foo2");
721 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
722 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
723 EXPECT_EQ(0U, notifications
.size());
724 main_test_rfh()->PrepareForCommit();
725 main_test_rfh()->SendNavigate(0, entry_id
, true, url1
);
726 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
727 navigation_entry_committed_counter_
= 0;
729 ASSERT_TRUE(controller
.GetVisibleEntry());
730 const base::Time timestamp
= controller
.GetVisibleEntry()->GetTimestamp();
731 EXPECT_FALSE(timestamp
.is_null());
734 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
735 controller
.DiscardNonCommittedEntries();
736 EXPECT_EQ(0U, notifications
.size());
738 // Should not have produced a new session history entry.
739 EXPECT_EQ(controller
.GetEntryCount(), 1);
740 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
741 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
742 EXPECT_TRUE(controller
.GetLastCommittedEntry());
743 EXPECT_FALSE(controller
.GetPendingEntry());
744 ASSERT_TRUE(controller
.GetVisibleEntry());
745 EXPECT_FALSE(controller
.CanGoBack());
746 EXPECT_FALSE(controller
.CanGoForward());
748 // Timestamp should not have changed.
749 EXPECT_EQ(timestamp
, controller
.GetVisibleEntry()->GetTimestamp());
752 // Tests navigations that come in unrequested. This happens when the user
753 // navigates from the web page, and here we test that there is no pending entry.
754 TEST_F(NavigationControllerTest
, LoadURL_NoPending
) {
755 NavigationControllerImpl
& controller
= controller_impl();
756 TestNotificationTracker notifications
;
757 RegisterForAllNavNotifications(¬ifications
, &controller
);
759 // First make an existing committed entry.
760 const GURL
kExistingURL1("http://eh");
762 kExistingURL1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
763 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
764 main_test_rfh()->PrepareForCommit();
765 main_test_rfh()->SendNavigate(0, entry_id
, true, kExistingURL1
);
766 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
767 navigation_entry_committed_counter_
= 0;
769 // Do a new navigation without making a pending one.
770 const GURL
kNewURL("http://see");
771 main_test_rfh()->NavigateAndCommitRendererInitiated(99, true, kNewURL
);
773 // There should no longer be any pending entry, and the second navigation we
774 // just made should be committed.
775 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
776 navigation_entry_committed_counter_
= 0;
777 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
778 EXPECT_EQ(1, controller
.GetLastCommittedEntryIndex());
779 EXPECT_EQ(kNewURL
, controller
.GetVisibleEntry()->GetURL());
782 // Tests navigating to a new URL when there is a new pending navigation that is
783 // not the one that just loaded. This will happen if the user types in a URL to
784 // somewhere slow, and then navigates the current page before the typed URL
786 TEST_F(NavigationControllerTest
, LoadURL_NewPending
) {
787 NavigationControllerImpl
& controller
= controller_impl();
788 TestNotificationTracker notifications
;
789 RegisterForAllNavNotifications(¬ifications
, &controller
);
791 // First make an existing committed entry.
792 const GURL
kExistingURL1("http://eh");
794 kExistingURL1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
795 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
796 main_test_rfh()->PrepareForCommit();
797 main_test_rfh()->SendNavigate(0, entry_id
, true, kExistingURL1
);
798 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
799 navigation_entry_committed_counter_
= 0;
801 // Make a pending entry to somewhere new.
802 const GURL
kExistingURL2("http://bee");
804 kExistingURL2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
805 EXPECT_EQ(0U, notifications
.size());
807 // After the beforeunload but before it commits...
808 main_test_rfh()->PrepareForCommit();
810 // ... Do a new navigation.
811 const GURL
kNewURL("http://see");
812 main_test_rfh()->SendRendererInitiatedNavigationRequest(kNewURL
, true);
813 main_test_rfh()->PrepareForCommit();
814 contents()->GetMainFrame()->SendNavigate(3, 0, true, kNewURL
);
816 // There should no longer be any pending entry, and the third navigation we
817 // just made should be committed.
818 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
819 navigation_entry_committed_counter_
= 0;
820 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
821 EXPECT_EQ(1, controller
.GetLastCommittedEntryIndex());
822 EXPECT_EQ(kNewURL
, controller
.GetVisibleEntry()->GetURL());
825 // Tests navigating to a new URL when there is a pending back/forward
826 // navigation. This will happen if the user hits back, but before that commits,
827 // they navigate somewhere new.
828 TEST_F(NavigationControllerTest
, LoadURL_ExistingPending
) {
829 NavigationControllerImpl
& controller
= controller_impl();
830 TestNotificationTracker notifications
;
831 RegisterForAllNavNotifications(¬ifications
, &controller
);
833 // First make some history.
834 const GURL
kExistingURL1("http://foo/eh");
836 kExistingURL1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
837 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
838 main_test_rfh()->PrepareForCommit();
839 main_test_rfh()->SendNavigate(0, entry_id
, true, kExistingURL1
);
840 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
841 navigation_entry_committed_counter_
= 0;
843 const GURL
kExistingURL2("http://foo/bee");
845 kExistingURL2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
846 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
847 main_test_rfh()->PrepareForCommit();
848 main_test_rfh()->SendNavigate(1, entry_id
, true, kExistingURL2
);
849 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
850 navigation_entry_committed_counter_
= 0;
852 // Now make a pending back/forward navigation. The zeroth entry should be
855 EXPECT_EQ(0U, notifications
.size());
856 EXPECT_EQ(0, controller
.GetPendingEntryIndex());
857 EXPECT_EQ(1, controller
.GetLastCommittedEntryIndex());
859 // Before that commits, do a new navigation.
860 const GURL
kNewURL("http://foo/see");
861 main_test_rfh()->SendRendererInitiatedNavigationRequest(kNewURL
, true);
862 main_test_rfh()->PrepareForCommit();
863 main_test_rfh()->SendNavigate(3, 0, true, kNewURL
);
865 // There should no longer be any pending entry, and the new navigation we
866 // just made should be committed.
867 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
868 navigation_entry_committed_counter_
= 0;
869 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
870 EXPECT_EQ(2, controller
.GetLastCommittedEntryIndex());
871 EXPECT_EQ(kNewURL
, controller
.GetVisibleEntry()->GetURL());
874 // Tests navigating to a new URL when there is a pending back/forward
875 // navigation to a cross-process, privileged URL. This will happen if the user
876 // hits back, but before that commits, they navigate somewhere new.
877 TEST_F(NavigationControllerTest
, LoadURL_PrivilegedPending
) {
878 NavigationControllerImpl
& controller
= controller_impl();
879 TestNotificationTracker notifications
;
880 RegisterForAllNavNotifications(¬ifications
, &controller
);
882 // First make some history, starting with a privileged URL.
883 const GURL
kExistingURL1("http://privileged");
885 kExistingURL1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
886 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
887 // Pretend it has bindings so we can tell if we incorrectly copy it.
888 main_test_rfh()->GetRenderViewHost()->AllowBindings(2);
889 main_test_rfh()->PrepareForCommit();
890 main_test_rfh()->SendNavigate(0, entry_id
, true, kExistingURL1
);
891 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
892 navigation_entry_committed_counter_
= 0;
894 // Navigate cross-process to a second URL.
895 const GURL
kExistingURL2("http://foo/eh");
897 kExistingURL2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
898 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
899 main_test_rfh()->PrepareForCommit();
900 TestRenderFrameHost
* foo_rfh
= contents()->GetPendingMainFrame();
901 foo_rfh
->SendNavigate(1, entry_id
, true, kExistingURL2
);
902 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
903 navigation_entry_committed_counter_
= 0;
905 // Now make a pending back/forward navigation to a privileged entry.
906 // The zeroth entry should be pending.
908 foo_rfh
->SendBeforeUnloadACK(true);
909 EXPECT_EQ(0U, notifications
.size());
910 EXPECT_EQ(0, controller
.GetPendingEntryIndex());
911 EXPECT_EQ(1, controller
.GetLastCommittedEntryIndex());
912 EXPECT_EQ(2, controller
.GetPendingEntry()->bindings());
914 // Before that commits, do a new navigation.
915 const GURL
kNewURL("http://foo/bee");
916 foo_rfh
->SendRendererInitiatedNavigationRequest(kNewURL
, true);
917 foo_rfh
->PrepareForCommit();
918 foo_rfh
->SendNavigate(3, 0, true, kNewURL
);
920 // There should no longer be any pending entry, and the new navigation we
921 // just made should be committed.
922 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
923 navigation_entry_committed_counter_
= 0;
924 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
925 EXPECT_EQ(2, controller
.GetLastCommittedEntryIndex());
926 EXPECT_EQ(kNewURL
, controller
.GetVisibleEntry()->GetURL());
927 EXPECT_EQ(0, controller
.GetLastCommittedEntry()->bindings());
930 // Tests navigating to an existing URL when there is a pending new navigation.
931 // This will happen if the user enters a URL, but before that commits, the
932 // current page fires history.back().
933 TEST_F(NavigationControllerTest
, LoadURL_BackPreemptsPending
) {
934 NavigationControllerImpl
& controller
= controller_impl();
935 TestNotificationTracker notifications
;
936 RegisterForAllNavNotifications(¬ifications
, &controller
);
938 // First make some history.
939 const GURL
kExistingURL1("http://foo/eh");
941 kExistingURL1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
942 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
943 main_test_rfh()->PrepareForCommit();
944 main_test_rfh()->SendNavigate(0, entry_id
, true, kExistingURL1
);
945 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
946 navigation_entry_committed_counter_
= 0;
948 const GURL
kExistingURL2("http://foo/bee");
950 kExistingURL2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
951 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
952 main_test_rfh()->PrepareForCommit();
953 main_test_rfh()->SendNavigate(1, entry_id
, true, kExistingURL2
);
954 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
955 navigation_entry_committed_counter_
= 0;
957 // A back navigation comes in from the renderer...
958 controller
.GoToOffset(-1);
959 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
961 // ...while the user tries to navigate to a new page...
962 const GURL
kNewURL("http://foo/see");
964 kNewURL
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
965 EXPECT_EQ(0U, notifications
.size());
966 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
967 EXPECT_EQ(1, controller
.GetLastCommittedEntryIndex());
969 // ...and the back navigation commits.
970 main_test_rfh()->PrepareForCommit();
971 main_test_rfh()->SendNavigate(0, entry_id
, false, kExistingURL1
);
973 // There should no longer be any pending entry, and the back navigation should
975 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
976 navigation_entry_committed_counter_
= 0;
977 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
978 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
979 EXPECT_EQ(kExistingURL1
, controller
.GetVisibleEntry()->GetURL());
982 // Tests an ignored navigation when there is a pending new navigation.
983 // This will happen if the user enters a URL, but before that commits, the
984 // current blank page reloads. See http://crbug.com/77507.
985 TEST_F(NavigationControllerTest
, LoadURL_IgnorePreemptsPending
) {
986 NavigationControllerImpl
& controller
= controller_impl();
987 TestNotificationTracker notifications
;
988 RegisterForAllNavNotifications(¬ifications
, &controller
);
990 // Set a WebContentsDelegate to listen for state changes.
991 scoped_ptr
<TestWebContentsDelegate
> delegate(new TestWebContentsDelegate());
992 EXPECT_FALSE(contents()->GetDelegate());
993 contents()->SetDelegate(delegate
.get());
995 // Without any navigations, the renderer starts at about:blank.
996 const GURL
kExistingURL(url::kAboutBlankURL
);
998 // Now make a pending new navigation.
999 const GURL
kNewURL("http://eh");
1001 kNewURL
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1002 EXPECT_EQ(0U, notifications
.size());
1003 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
1004 EXPECT_TRUE(controller
.GetPendingEntry());
1005 EXPECT_EQ(-1, controller
.GetLastCommittedEntryIndex());
1006 EXPECT_EQ(1, delegate
->navigation_state_change_count());
1008 // Before that commits, a document.write and location.reload can cause the
1009 // renderer to send a FrameNavigate with page_id -1 and nav_entry_id 0.
1010 // PlzNavigate: this will stop the old navigation and start a new one.
1011 main_test_rfh()->SendRendererInitiatedNavigationRequest(kExistingURL
, true);
1012 main_test_rfh()->PrepareForCommit();
1013 main_test_rfh()->SendNavigate(-1, 0, false, kExistingURL
);
1015 // This should clear the pending entry and notify of a navigation state
1016 // change, so that we do not keep displaying kNewURL.
1017 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
1018 EXPECT_FALSE(controller
.GetPendingEntry());
1019 EXPECT_EQ(-1, controller
.GetLastCommittedEntryIndex());
1020 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1021 switches::kEnableBrowserSideNavigation
))
1022 EXPECT_EQ(4, delegate
->navigation_state_change_count());
1024 EXPECT_EQ(2, delegate
->navigation_state_change_count());
1026 contents()->SetDelegate(NULL
);
1029 // Tests that the pending entry state is correct after an abort.
1030 // We do not want to clear the pending entry, so that the user doesn't
1031 // lose a typed URL. (See http://crbug.com/9682.)
1032 TEST_F(NavigationControllerTest
, LoadURL_AbortDoesntCancelPending
) {
1033 NavigationControllerImpl
& controller
= controller_impl();
1034 TestNotificationTracker notifications
;
1035 RegisterForAllNavNotifications(¬ifications
, &controller
);
1037 // Set a WebContentsDelegate to listen for state changes.
1038 scoped_ptr
<TestWebContentsDelegate
> delegate(new TestWebContentsDelegate());
1039 EXPECT_FALSE(contents()->GetDelegate());
1040 contents()->SetDelegate(delegate
.get());
1042 // Start with a pending new navigation.
1043 const GURL
kNewURL("http://eh");
1045 kNewURL
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1046 main_test_rfh()->PrepareForCommit();
1047 EXPECT_EQ(0U, notifications
.size());
1048 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
1049 EXPECT_TRUE(controller
.GetPendingEntry());
1050 EXPECT_EQ(-1, controller
.GetLastCommittedEntryIndex());
1051 EXPECT_EQ(1, delegate
->navigation_state_change_count());
1053 // It may abort before committing, if it's a download or due to a stop or
1054 // a new navigation from the user.
1055 FrameHostMsg_DidFailProvisionalLoadWithError_Params params
;
1056 params
.error_code
= net::ERR_ABORTED
;
1057 params
.error_description
= base::string16();
1058 params
.url
= kNewURL
;
1059 params
.showing_repost_interstitial
= false;
1060 main_test_rfh()->OnMessageReceived(
1061 FrameHostMsg_DidFailProvisionalLoadWithError(0, // routing_id
1064 // This should not clear the pending entry or notify of a navigation state
1065 // change, so that we keep displaying kNewURL (until the user clears it).
1066 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
1067 EXPECT_TRUE(controller
.GetPendingEntry());
1068 EXPECT_EQ(-1, controller
.GetLastCommittedEntryIndex());
1069 EXPECT_EQ(1, delegate
->navigation_state_change_count());
1070 NavigationEntry
* pending_entry
= controller
.GetPendingEntry();
1072 // Ensure that a reload keeps the same pending entry.
1073 controller
.Reload(true);
1074 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
1075 EXPECT_TRUE(controller
.GetPendingEntry());
1076 EXPECT_EQ(pending_entry
, controller
.GetPendingEntry());
1077 EXPECT_EQ(-1, controller
.GetLastCommittedEntryIndex());
1079 contents()->SetDelegate(NULL
);
1082 // Tests that the pending URL is not visible during a renderer-initiated
1083 // redirect and abort. See http://crbug.com/83031.
1084 TEST_F(NavigationControllerTest
, LoadURL_RedirectAbortDoesntShowPendingURL
) {
1085 NavigationControllerImpl
& controller
= controller_impl();
1086 TestNotificationTracker notifications
;
1087 RegisterForAllNavNotifications(¬ifications
, &controller
);
1089 // First make an existing committed entry.
1090 const GURL
kExistingURL("http://foo/eh");
1091 controller
.LoadURL(kExistingURL
, content::Referrer(),
1092 ui::PAGE_TRANSITION_TYPED
, std::string());
1093 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1094 main_test_rfh()->PrepareForCommit();
1095 main_test_rfh()->SendNavigate(1, entry_id
, true, kExistingURL
);
1096 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1097 navigation_entry_committed_counter_
= 0;
1099 // Set a WebContentsDelegate to listen for state changes.
1100 scoped_ptr
<TestWebContentsDelegate
> delegate(new TestWebContentsDelegate());
1101 EXPECT_FALSE(contents()->GetDelegate());
1102 contents()->SetDelegate(delegate
.get());
1104 // Now make a pending new navigation, initiated by the renderer.
1105 const GURL
kNewURL("http://foo/bee");
1106 NavigationController::LoadURLParams
load_url_params(kNewURL
);
1107 load_url_params
.transition_type
= ui::PAGE_TRANSITION_TYPED
;
1108 load_url_params
.is_renderer_initiated
= true;
1109 controller
.LoadURLWithParams(load_url_params
);
1110 EXPECT_EQ(0U, notifications
.size());
1111 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
1112 EXPECT_TRUE(controller
.GetPendingEntry());
1113 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
1114 EXPECT_EQ(0, delegate
->navigation_state_change_count());
1116 // The visible entry should be the last committed URL, not the pending one.
1117 EXPECT_EQ(kExistingURL
, controller
.GetVisibleEntry()->GetURL());
1119 // Now the navigation redirects. (There is no corresponding message here.)
1120 const GURL
kRedirectURL("http://foo/see");
1122 // We don't want to change the NavigationEntry's url, in case it cancels.
1123 // Prevents regression of http://crbug.com/77786.
1124 EXPECT_EQ(kNewURL
, controller
.GetPendingEntry()->GetURL());
1126 // It may abort before committing, if it's a download or due to a stop or
1127 // a new navigation from the user.
1128 FrameHostMsg_DidFailProvisionalLoadWithError_Params params
;
1129 params
.error_code
= net::ERR_ABORTED
;
1130 params
.error_description
= base::string16();
1131 params
.url
= kRedirectURL
;
1132 params
.showing_repost_interstitial
= false;
1133 main_test_rfh()->OnMessageReceived(
1134 FrameHostMsg_DidFailProvisionalLoadWithError(0, // routing_id
1137 // Because the pending entry is renderer initiated and not visible, we
1138 // clear it when it fails.
1139 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
1140 EXPECT_FALSE(controller
.GetPendingEntry());
1141 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
1142 EXPECT_EQ(1, delegate
->navigation_state_change_count());
1144 // The visible entry should be the last committed URL, not the pending one,
1145 // so that no spoof is possible.
1146 EXPECT_EQ(kExistingURL
, controller
.GetVisibleEntry()->GetURL());
1148 contents()->SetDelegate(NULL
);
1151 // Ensure that NavigationEntries track which bindings their RenderViewHost had
1152 // at the time they committed. http://crbug.com/173672.
1153 TEST_F(NavigationControllerTest
, LoadURL_WithBindings
) {
1154 NavigationControllerImpl
& controller
= controller_impl();
1155 TestNotificationTracker notifications
;
1156 RegisterForAllNavNotifications(¬ifications
, &controller
);
1157 std::vector
<GURL
> url_chain
;
1159 const GURL
url1("http://foo1");
1160 const GURL
url2("http://foo2");
1162 // Navigate to a first, unprivileged URL.
1164 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1165 EXPECT_EQ(NavigationEntryImpl::kInvalidBindings
,
1166 controller
.GetPendingEntry()->bindings());
1167 int entry1_id
= controller
.GetPendingEntry()->GetUniqueID();
1170 TestRenderFrameHost
* orig_rfh
= contents()->GetMainFrame();
1171 orig_rfh
->PrepareForCommit();
1172 orig_rfh
->SendNavigate(0, entry1_id
, true, url1
);
1173 EXPECT_EQ(controller
.GetEntryCount(), 1);
1174 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
1175 EXPECT_EQ(0, controller
.GetLastCommittedEntry()->bindings());
1176 entry1_id
= controller
.GetLastCommittedEntry()->GetUniqueID();
1178 // Manually increase the number of active frames in the SiteInstance
1179 // that orig_rfh belongs to, to prevent it from being destroyed when
1180 // it gets swapped out, so that we can reuse orig_rfh when the
1181 // controller goes back.
1182 orig_rfh
->GetSiteInstance()->increment_active_frame_count();
1184 // Navigate to a second URL, simulate the beforeunload ack for the cross-site
1185 // transition, and set bindings on the pending RenderViewHost to simulate a
1188 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1189 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1190 orig_rfh
->PrepareForCommit();
1191 TestRenderFrameHost
* new_rfh
= contents()->GetPendingMainFrame();
1192 new_rfh
->GetRenderViewHost()->AllowBindings(1);
1193 new_rfh
->SendNavigate(1, entry_id
, true, url2
);
1195 // The second load should be committed, and bindings should be remembered.
1196 EXPECT_EQ(controller
.GetEntryCount(), 2);
1197 EXPECT_EQ(1, controller
.GetLastCommittedEntryIndex());
1198 EXPECT_TRUE(controller
.CanGoBack());
1199 EXPECT_EQ(1, controller
.GetLastCommittedEntry()->bindings());
1201 // Going back, the first entry should still appear unprivileged.
1202 controller
.GoBack();
1203 new_rfh
->PrepareForCommit();
1204 orig_rfh
->SendNavigate(0, entry1_id
, false, url1
);
1205 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
1206 EXPECT_EQ(0, controller
.GetLastCommittedEntry()->bindings());
1209 TEST_F(NavigationControllerTest
, Reload
) {
1210 NavigationControllerImpl
& controller
= controller_impl();
1211 TestNotificationTracker notifications
;
1212 RegisterForAllNavNotifications(¬ifications
, &controller
);
1214 const GURL
url1("http://foo1");
1217 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1218 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1219 EXPECT_EQ(0U, notifications
.size());
1220 main_test_rfh()->PrepareForCommit();
1221 main_test_rfh()->SendNavigate(0, entry_id
, true, url1
);
1222 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1223 navigation_entry_committed_counter_
= 0;
1224 ASSERT_TRUE(controller
.GetVisibleEntry());
1225 controller
.GetVisibleEntry()->SetTitle(base::ASCIIToUTF16("Title"));
1226 entry_id
= controller
.GetLastCommittedEntry()->GetUniqueID();
1228 controller
.Reload(true);
1229 EXPECT_EQ(0U, notifications
.size());
1231 const base::Time timestamp
= controller
.GetVisibleEntry()->GetTimestamp();
1232 EXPECT_FALSE(timestamp
.is_null());
1234 // The reload is pending.
1235 EXPECT_EQ(controller
.GetEntryCount(), 1);
1236 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1237 EXPECT_EQ(controller
.GetPendingEntryIndex(), 0);
1238 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1239 EXPECT_TRUE(controller
.GetPendingEntry());
1240 EXPECT_FALSE(controller
.CanGoBack());
1241 EXPECT_FALSE(controller
.CanGoForward());
1242 // Make sure the title has been cleared (will be redrawn just after reload).
1243 // Avoids a stale cached title when the new page being reloaded has no title.
1244 // See http://crbug.com/96041.
1245 EXPECT_TRUE(controller
.GetVisibleEntry()->GetTitle().empty());
1247 main_test_rfh()->PrepareForCommit();
1248 main_test_rfh()->SendNavigate(0, entry_id
, false, url1
);
1249 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1250 navigation_entry_committed_counter_
= 0;
1252 // Now the reload is committed.
1253 EXPECT_EQ(controller
.GetEntryCount(), 1);
1254 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1255 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1256 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1257 EXPECT_FALSE(controller
.GetPendingEntry());
1258 EXPECT_FALSE(controller
.CanGoBack());
1259 EXPECT_FALSE(controller
.CanGoForward());
1261 // The timestamp should have been updated.
1262 ASSERT_TRUE(controller
.GetVisibleEntry());
1263 EXPECT_GE(controller
.GetVisibleEntry()->GetTimestamp(), timestamp
);
1266 // Tests what happens when a reload navigation produces a new page.
1267 TEST_F(NavigationControllerTest
, Reload_GeneratesNewPage
) {
1268 NavigationControllerImpl
& controller
= controller_impl();
1269 TestNotificationTracker notifications
;
1270 RegisterForAllNavNotifications(¬ifications
, &controller
);
1272 const GURL
url1("http://foo1");
1273 const GURL
url2("http://foo2");
1276 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1277 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1278 main_test_rfh()->PrepareForCommit();
1279 main_test_rfh()->SendNavigate(0, entry_id
, true, url1
);
1280 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1281 navigation_entry_committed_counter_
= 0;
1282 entry_id
= controller
.GetLastCommittedEntry()->GetUniqueID();
1284 controller
.Reload(true);
1285 EXPECT_EQ(0U, notifications
.size());
1287 main_test_rfh()->PrepareForCommitWithServerRedirect(url2
);
1288 main_test_rfh()->SendNavigate(1, entry_id
, true, url2
);
1289 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1290 navigation_entry_committed_counter_
= 0;
1292 // Now the reload is committed.
1293 EXPECT_EQ(controller
.GetEntryCount(), 2);
1294 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 1);
1295 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1296 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1297 EXPECT_FALSE(controller
.GetPendingEntry());
1298 EXPECT_TRUE(controller
.CanGoBack());
1299 EXPECT_FALSE(controller
.CanGoForward());
1302 // This test ensures that when a guest renderer reloads, the reload goes through
1303 // without ending up in the "we have a wrong process for the URL" branch in
1304 // NavigationControllerImpl::ReloadInternal.
1305 TEST_F(NavigationControllerTest
, ReloadWithGuest
) {
1306 NavigationControllerImpl
& controller
= controller_impl();
1308 const GURL
url1("http://foo1");
1310 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1311 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1312 main_test_rfh()->PrepareForCommit();
1313 main_test_rfh()->SendNavigate(0, entry_id
, true, url1
);
1314 ASSERT_TRUE(controller
.GetVisibleEntry());
1316 // Make the entry believe its RenderProcessHost is a guest.
1317 NavigationEntryImpl
* entry1
= controller
.GetVisibleEntry();
1318 reinterpret_cast<MockRenderProcessHost
*>(
1319 entry1
->site_instance()->GetProcess())->set_is_isolated_guest(true);
1322 controller
.Reload(true);
1324 // The reload is pending. Check that the NavigationEntry didn't get replaced
1325 // because of having the wrong process.
1326 EXPECT_EQ(controller
.GetEntryCount(), 1);
1327 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1328 EXPECT_EQ(controller
.GetPendingEntryIndex(), 0);
1330 NavigationEntryImpl
* entry2
= controller
.GetPendingEntry();
1331 EXPECT_EQ(entry1
, entry2
);
1334 #if !defined(OS_ANDROID) // http://crbug.com/157428
1335 TEST_F(NavigationControllerTest
, ReloadOriginalRequestURL
) {
1336 NavigationControllerImpl
& controller
= controller_impl();
1337 TestNotificationTracker notifications
;
1338 RegisterForAllNavNotifications(¬ifications
, &controller
);
1340 const GURL
original_url("http://foo1");
1341 const GURL
final_url("http://foo2");
1343 // Load up the original URL, but get redirected.
1345 original_url
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1346 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1347 EXPECT_EQ(0U, notifications
.size());
1348 main_test_rfh()->PrepareForCommitWithServerRedirect(final_url
);
1349 main_test_rfh()->SendNavigateWithOriginalRequestURL(0, entry_id
, true,
1350 final_url
, original_url
);
1351 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1352 navigation_entry_committed_counter_
= 0;
1353 entry_id
= controller
.GetLastCommittedEntry()->GetUniqueID();
1355 // The NavigationEntry should save both the original URL and the final
1358 original_url
, controller
.GetVisibleEntry()->GetOriginalRequestURL());
1359 EXPECT_EQ(final_url
, controller
.GetVisibleEntry()->GetURL());
1361 // Reload using the original URL.
1362 controller
.GetVisibleEntry()->SetTitle(base::ASCIIToUTF16("Title"));
1363 controller
.ReloadOriginalRequestURL(false);
1364 EXPECT_EQ(0U, notifications
.size());
1366 // The reload is pending. The request should point to the original URL.
1367 EXPECT_EQ(original_url
, navigated_url());
1368 EXPECT_EQ(controller
.GetEntryCount(), 1);
1369 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1370 EXPECT_EQ(controller
.GetPendingEntryIndex(), 0);
1371 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1372 EXPECT_TRUE(controller
.GetPendingEntry());
1373 EXPECT_FALSE(controller
.CanGoBack());
1374 EXPECT_FALSE(controller
.CanGoForward());
1376 // Make sure the title has been cleared (will be redrawn just after reload).
1377 // Avoids a stale cached title when the new page being reloaded has no title.
1378 // See http://crbug.com/96041.
1379 EXPECT_TRUE(controller
.GetVisibleEntry()->GetTitle().empty());
1381 // Send that the navigation has proceeded; say it got redirected again.
1382 main_test_rfh()->PrepareForCommitWithServerRedirect(final_url
);
1383 main_test_rfh()->SendNavigate(0, entry_id
, false, final_url
);
1384 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1385 navigation_entry_committed_counter_
= 0;
1387 // Now the reload is committed.
1388 EXPECT_EQ(controller
.GetEntryCount(), 1);
1389 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1390 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1391 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1392 EXPECT_FALSE(controller
.GetPendingEntry());
1393 EXPECT_FALSE(controller
.CanGoBack());
1394 EXPECT_FALSE(controller
.CanGoForward());
1397 #endif // !defined(OS_ANDROID)
1399 // Test that certain non-persisted NavigationEntryImpl values get reset after
1401 TEST_F(NavigationControllerTest
, ResetEntryValuesAfterCommit
) {
1402 NavigationControllerImpl
& controller
= controller_impl();
1404 // The value of "should replace entry" will be tested, but it's an error to
1405 // specify it when there are no entries. Create a simple entry to be replaced.
1406 const GURL
url0("http://foo/0");
1408 url0
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1409 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1410 main_test_rfh()->PrepareForCommit();
1411 main_test_rfh()->SendNavigate(0, entry_id
, true, url0
);
1413 // Set up the pending entry.
1414 const GURL
url1("http://foo/1");
1416 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1417 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1419 // Set up some sample values.
1420 const unsigned char* raw_data
=
1421 reinterpret_cast<const unsigned char*>("post\n\n\0data");
1422 const int length
= 11;
1423 std::vector
<unsigned char> post_data_vector(raw_data
, raw_data
+length
);
1424 scoped_refptr
<base::RefCountedBytes
> post_data
=
1425 base::RefCountedBytes::TakeVector(&post_data_vector
);
1426 GlobalRequestID
transfer_id(3, 4);
1428 // Set non-persisted values on the pending entry.
1429 NavigationEntryImpl
* pending_entry
= controller
.GetPendingEntry();
1430 pending_entry
->SetBrowserInitiatedPostData(post_data
.get());
1431 pending_entry
->set_is_renderer_initiated(true);
1432 pending_entry
->set_transferred_global_request_id(transfer_id
);
1433 pending_entry
->set_should_replace_entry(true);
1434 pending_entry
->set_should_clear_history_list(true);
1435 EXPECT_EQ(post_data
.get(), pending_entry
->GetBrowserInitiatedPostData());
1436 EXPECT_TRUE(pending_entry
->is_renderer_initiated());
1437 EXPECT_EQ(transfer_id
, pending_entry
->transferred_global_request_id());
1438 EXPECT_TRUE(pending_entry
->should_replace_entry());
1439 EXPECT_TRUE(pending_entry
->should_clear_history_list());
1441 // Fake a commit response.
1442 main_test_rfh()->PrepareForCommit();
1443 main_test_rfh()->SendNavigate(1, entry_id
, true, url1
);
1445 // Certain values that are only used for pending entries get reset after
1447 NavigationEntryImpl
* committed_entry
= controller
.GetLastCommittedEntry();
1448 EXPECT_FALSE(committed_entry
->GetBrowserInitiatedPostData());
1449 EXPECT_FALSE(committed_entry
->is_renderer_initiated());
1450 EXPECT_EQ(GlobalRequestID(-1, -1),
1451 committed_entry
->transferred_global_request_id());
1452 EXPECT_FALSE(committed_entry
->should_replace_entry());
1453 EXPECT_FALSE(committed_entry
->should_clear_history_list());
1456 // Test that Redirects are preserved after a commit.
1457 TEST_F(NavigationControllerTest
, RedirectsAreNotResetByCommit
) {
1458 NavigationControllerImpl
& controller
= controller_impl();
1459 const GURL
url1("http://foo1");
1460 const GURL
url2("http://foo2");
1462 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1463 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1465 // Set up some redirect values.
1466 std::vector
<GURL
> redirects
;
1467 redirects
.push_back(url2
);
1469 // Set redirects on the pending entry.
1470 NavigationEntryImpl
* pending_entry
= controller
.GetPendingEntry();
1471 pending_entry
->SetRedirectChain(redirects
);
1472 EXPECT_EQ(1U, pending_entry
->GetRedirectChain().size());
1473 EXPECT_EQ(url2
, pending_entry
->GetRedirectChain()[0]);
1475 // Normal navigation will preserve redirects in the committed entry.
1476 main_test_rfh()->PrepareForCommitWithServerRedirect(url2
);
1477 main_test_rfh()->SendNavigateWithRedirects(0, entry_id
, true, url1
,
1479 NavigationEntryImpl
* committed_entry
= controller
.GetLastCommittedEntry();
1480 ASSERT_EQ(1U, committed_entry
->GetRedirectChain().size());
1481 EXPECT_EQ(url2
, committed_entry
->GetRedirectChain()[0]);
1484 // Tests what happens when we navigate back successfully
1485 TEST_F(NavigationControllerTest
, Back
) {
1486 NavigationControllerImpl
& controller
= controller_impl();
1487 TestNotificationTracker notifications
;
1488 RegisterForAllNavNotifications(¬ifications
, &controller
);
1490 const GURL
url1("http://foo1");
1491 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url1
);
1492 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1493 navigation_entry_committed_counter_
= 0;
1495 const GURL
url2("http://foo2");
1496 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url2
);
1497 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1498 navigation_entry_committed_counter_
= 0;
1500 controller
.GoBack();
1501 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1502 EXPECT_EQ(0U, notifications
.size());
1504 // We should now have a pending navigation to go back.
1505 EXPECT_EQ(controller
.GetEntryCount(), 2);
1506 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 1);
1507 EXPECT_EQ(controller
.GetPendingEntryIndex(), 0);
1508 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1509 EXPECT_TRUE(controller
.GetPendingEntry());
1510 EXPECT_FALSE(controller
.CanGoBack());
1511 EXPECT_FALSE(controller
.CanGoToOffset(-1));
1512 EXPECT_TRUE(controller
.CanGoForward());
1513 EXPECT_TRUE(controller
.CanGoToOffset(1));
1514 EXPECT_FALSE(controller
.CanGoToOffset(2)); // Cannot go forward 2 steps.
1516 // Timestamp for entry 1 should be on or after that of entry 0.
1517 EXPECT_FALSE(controller
.GetEntryAtIndex(0)->GetTimestamp().is_null());
1518 EXPECT_GE(controller
.GetEntryAtIndex(1)->GetTimestamp(),
1519 controller
.GetEntryAtIndex(0)->GetTimestamp());
1521 main_test_rfh()->PrepareForCommit();
1522 main_test_rfh()->SendNavigate(0, entry_id
, false, url2
);
1523 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1524 navigation_entry_committed_counter_
= 0;
1526 // The back navigation completed successfully.
1527 EXPECT_EQ(controller
.GetEntryCount(), 2);
1528 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1529 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1530 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1531 EXPECT_FALSE(controller
.GetPendingEntry());
1532 EXPECT_FALSE(controller
.CanGoBack());
1533 EXPECT_FALSE(controller
.CanGoToOffset(-1));
1534 EXPECT_TRUE(controller
.CanGoForward());
1535 EXPECT_TRUE(controller
.CanGoToOffset(1));
1536 EXPECT_FALSE(controller
.CanGoToOffset(2)); // Cannot go foward 2 steps.
1538 // Timestamp for entry 0 should be on or after that of entry 1
1539 // (since we went back to it).
1540 EXPECT_GE(controller
.GetEntryAtIndex(0)->GetTimestamp(),
1541 controller
.GetEntryAtIndex(1)->GetTimestamp());
1544 // Tests what happens when a back navigation produces a new page.
1545 TEST_F(NavigationControllerTest
, Back_GeneratesNewPage
) {
1546 NavigationControllerImpl
& controller
= controller_impl();
1547 TestNotificationTracker notifications
;
1548 RegisterForAllNavNotifications(¬ifications
, &controller
);
1550 const GURL
url1("http://foo/1");
1551 const GURL
url2("http://foo/2");
1552 const GURL
url3("http://foo/3");
1555 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1556 int entry1_id
= controller
.GetPendingEntry()->GetUniqueID();
1557 main_test_rfh()->PrepareForCommit();
1558 main_test_rfh()->SendNavigate(0, entry1_id
, true, url1
);
1559 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1560 navigation_entry_committed_counter_
= 0;
1561 entry1_id
= controller
.GetLastCommittedEntry()->GetUniqueID();
1564 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1565 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1566 main_test_rfh()->PrepareForCommit();
1567 main_test_rfh()->SendNavigate(1, entry_id
, true, url2
);
1568 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1569 navigation_entry_committed_counter_
= 0;
1571 controller
.GoBack();
1572 EXPECT_EQ(0U, notifications
.size());
1574 // We should now have a pending navigation to go back.
1575 EXPECT_EQ(controller
.GetEntryCount(), 2);
1576 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 1);
1577 EXPECT_EQ(controller
.GetPendingEntryIndex(), 0);
1578 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1579 EXPECT_TRUE(controller
.GetPendingEntry());
1580 EXPECT_FALSE(controller
.CanGoBack());
1581 EXPECT_TRUE(controller
.CanGoForward());
1583 main_test_rfh()->PrepareForCommitWithServerRedirect(url3
);
1584 main_test_rfh()->SendNavigate(2, entry1_id
, true, url3
);
1585 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1586 navigation_entry_committed_counter_
= 0;
1588 // The back navigation resulted in a completely new navigation.
1589 // TODO(darin): perhaps this behavior will be confusing to users?
1590 EXPECT_EQ(controller
.GetEntryCount(), 3);
1591 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 2);
1592 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1593 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1594 EXPECT_FALSE(controller
.GetPendingEntry());
1595 EXPECT_TRUE(controller
.CanGoBack());
1596 EXPECT_FALSE(controller
.CanGoForward());
1599 // Receives a back message when there is a new pending navigation entry.
1600 TEST_F(NavigationControllerTest
, Back_NewPending
) {
1601 NavigationControllerImpl
& controller
= controller_impl();
1602 TestNotificationTracker notifications
;
1603 RegisterForAllNavNotifications(¬ifications
, &controller
);
1605 const GURL
kUrl1("http://foo1");
1606 const GURL
kUrl2("http://foo2");
1607 const GURL
kUrl3("http://foo3");
1609 // First navigate two places so we have some back history.
1610 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, kUrl1
);
1611 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1612 navigation_entry_committed_counter_
= 0;
1614 // controller.LoadURL(kUrl2, ui::PAGE_TRANSITION_TYPED);
1615 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, kUrl2
);
1616 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1617 navigation_entry_committed_counter_
= 0;
1619 // Now start a new pending navigation and go back before it commits.
1621 kUrl3
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1622 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
1623 EXPECT_EQ(kUrl3
, controller
.GetPendingEntry()->GetURL());
1624 controller
.GoBack();
1626 // The pending navigation should now be the "back" item and the new one
1628 EXPECT_EQ(0, controller
.GetPendingEntryIndex());
1629 EXPECT_EQ(kUrl1
, controller
.GetPendingEntry()->GetURL());
1632 // Receives a back message when there is a different renavigation already
1634 TEST_F(NavigationControllerTest
, Back_OtherBackPending
) {
1635 NavigationControllerImpl
& controller
= controller_impl();
1636 const GURL
kUrl1("http://foo/1");
1637 const GURL
kUrl2("http://foo/2");
1638 const GURL
kUrl3("http://foo/3");
1640 // First navigate three places so we have some back history.
1641 main_test_rfh()->SendRendererInitiatedNavigationRequest(kUrl1
, true);
1642 main_test_rfh()->PrepareForCommit();
1643 main_test_rfh()->SendNavigate(0, 0, true, kUrl1
);
1644 main_test_rfh()->SendRendererInitiatedNavigationRequest(kUrl2
, true);
1645 main_test_rfh()->PrepareForCommit();
1646 main_test_rfh()->SendNavigate(1, 0, true, kUrl2
);
1647 main_test_rfh()->SendRendererInitiatedNavigationRequest(kUrl3
, true);
1648 main_test_rfh()->PrepareForCommit();
1649 main_test_rfh()->SendNavigate(2, 0, true, kUrl3
);
1651 // With nothing pending, say we get a renderer back navigation request to the
1653 controller
.GoToOffset(-1);
1654 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1655 main_test_rfh()->PrepareForCommit();
1656 main_test_rfh()->SendNavigate(1, entry_id
, false, kUrl2
);
1658 // We know all the entries have the same site instance, so we can just grab
1659 // a random one for looking up other entries.
1660 SiteInstance
* site_instance
=
1661 controller
.GetLastCommittedEntry()->site_instance();
1663 // That second URL should be the last committed and it should have gotten the
1665 EXPECT_EQ(kUrl2
, controller
.GetEntryWithPageID(site_instance
, 1)->GetURL());
1666 EXPECT_EQ(1, controller
.GetLastCommittedEntryIndex());
1667 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
1669 // Now go forward to the last item again and say it was committed.
1670 controller
.GoForward();
1671 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1672 main_test_rfh()->PrepareForCommit();
1673 main_test_rfh()->SendNavigate(2, entry_id
, false, kUrl3
);
1675 // Now start going back one to the second page. It will be pending.
1676 controller
.GoBack();
1677 EXPECT_EQ(1, controller
.GetPendingEntryIndex());
1678 EXPECT_EQ(2, controller
.GetLastCommittedEntryIndex());
1680 // Now have the renderer request a navigation back to the first page. This
1681 // will not match the pending one.
1682 controller
.GoToOffset(-2);
1683 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1684 main_test_rfh()->PrepareForCommit();
1685 main_test_rfh()->SendNavigate(0, entry_id
, false, kUrl1
);
1687 // The committed navigation should clear the pending entry.
1688 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
1690 // But the navigated entry should be the last committed.
1691 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
1692 EXPECT_EQ(kUrl1
, controller
.GetLastCommittedEntry()->GetURL());
1695 // Tests what happens when we navigate forward successfully.
1696 TEST_F(NavigationControllerTest
, Forward
) {
1697 NavigationControllerImpl
& controller
= controller_impl();
1698 TestNotificationTracker notifications
;
1699 RegisterForAllNavNotifications(¬ifications
, &controller
);
1701 const GURL
url1("http://foo1");
1702 const GURL
url2("http://foo2");
1704 main_test_rfh()->SendRendererInitiatedNavigationRequest(url1
, true);
1705 main_test_rfh()->PrepareForCommit();
1706 main_test_rfh()->SendNavigate(0, 0, true, url1
);
1707 NavigationEntry
* entry1
= controller
.GetLastCommittedEntry();
1708 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1709 navigation_entry_committed_counter_
= 0;
1711 main_test_rfh()->SendRendererInitiatedNavigationRequest(url2
, true);
1712 main_test_rfh()->PrepareForCommit();
1713 main_test_rfh()->SendNavigate(1, 0, true, url2
);
1714 NavigationEntry
* entry2
= controller
.GetLastCommittedEntry();
1715 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1716 navigation_entry_committed_counter_
= 0;
1718 controller
.GoBack();
1719 main_test_rfh()->PrepareForCommit();
1720 main_test_rfh()->SendNavigate(0, entry1
->GetUniqueID(), false, url1
);
1721 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1722 navigation_entry_committed_counter_
= 0;
1724 controller
.GoForward();
1726 // We should now have a pending navigation to go forward.
1727 EXPECT_EQ(controller
.GetEntryCount(), 2);
1728 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1729 EXPECT_EQ(controller
.GetPendingEntryIndex(), 1);
1730 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1731 EXPECT_TRUE(controller
.GetPendingEntry());
1732 EXPECT_TRUE(controller
.CanGoBack());
1733 EXPECT_TRUE(controller
.CanGoToOffset(-1));
1734 EXPECT_FALSE(controller
.CanGoToOffset(-2)); // Cannot go back 2 steps.
1735 EXPECT_FALSE(controller
.CanGoForward());
1736 EXPECT_FALSE(controller
.CanGoToOffset(1));
1738 // Timestamp for entry 0 should be on or after that of entry 1
1739 // (since we went back to it).
1740 EXPECT_FALSE(controller
.GetEntryAtIndex(0)->GetTimestamp().is_null());
1741 EXPECT_GE(controller
.GetEntryAtIndex(0)->GetTimestamp(),
1742 controller
.GetEntryAtIndex(1)->GetTimestamp());
1744 main_test_rfh()->PrepareForCommit();
1745 main_test_rfh()->SendNavigate(1, entry2
->GetUniqueID(), false, url2
);
1746 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1747 navigation_entry_committed_counter_
= 0;
1749 // The forward navigation completed successfully.
1750 EXPECT_EQ(controller
.GetEntryCount(), 2);
1751 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 1);
1752 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1753 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1754 EXPECT_FALSE(controller
.GetPendingEntry());
1755 EXPECT_TRUE(controller
.CanGoBack());
1756 EXPECT_TRUE(controller
.CanGoToOffset(-1));
1757 EXPECT_FALSE(controller
.CanGoToOffset(-2)); // Cannot go back 2 steps.
1758 EXPECT_FALSE(controller
.CanGoForward());
1759 EXPECT_FALSE(controller
.CanGoToOffset(1));
1761 // Timestamp for entry 1 should be on or after that of entry 0
1762 // (since we went forward to it).
1763 EXPECT_GE(controller
.GetEntryAtIndex(1)->GetTimestamp(),
1764 controller
.GetEntryAtIndex(0)->GetTimestamp());
1767 // Tests what happens when a forward navigation produces a new page.
1768 TEST_F(NavigationControllerTest
, Forward_GeneratesNewPage
) {
1769 NavigationControllerImpl
& controller
= controller_impl();
1770 TestNotificationTracker notifications
;
1771 RegisterForAllNavNotifications(¬ifications
, &controller
);
1773 const GURL
url1("http://foo1");
1774 const GURL
url2("http://foo2");
1775 const GURL
url3("http://foo3");
1777 main_test_rfh()->SendRendererInitiatedNavigationRequest(url1
, true);
1778 main_test_rfh()->PrepareForCommit();
1779 main_test_rfh()->SendNavigate(0, 0, true, url1
);
1780 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1781 NavigationEntry
* entry1
= controller
.GetLastCommittedEntry();
1782 navigation_entry_committed_counter_
= 0;
1783 main_test_rfh()->SendRendererInitiatedNavigationRequest(url2
, true);
1784 main_test_rfh()->PrepareForCommit();
1785 main_test_rfh()->SendNavigate(1, 0, true, url2
);
1786 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1787 NavigationEntry
* entry2
= controller
.GetLastCommittedEntry();
1788 navigation_entry_committed_counter_
= 0;
1790 controller
.GoBack();
1791 main_test_rfh()->PrepareForCommit();
1792 main_test_rfh()->SendNavigate(0, entry1
->GetUniqueID(), false, url1
);
1793 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1794 navigation_entry_committed_counter_
= 0;
1796 controller
.GoForward();
1797 EXPECT_EQ(0U, notifications
.size());
1799 // Should now have a pending navigation to go forward.
1800 EXPECT_EQ(controller
.GetEntryCount(), 2);
1801 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1802 EXPECT_EQ(controller
.GetPendingEntryIndex(), 1);
1803 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1804 EXPECT_TRUE(controller
.GetPendingEntry());
1805 EXPECT_TRUE(controller
.CanGoBack());
1806 EXPECT_FALSE(controller
.CanGoForward());
1808 main_test_rfh()->PrepareForCommit();
1809 main_test_rfh()->SendNavigate(2, entry2
->GetUniqueID(), true, url3
);
1810 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1811 navigation_entry_committed_counter_
= 0;
1812 EXPECT_TRUE(notifications
.Check1AndReset(NOTIFICATION_NAV_LIST_PRUNED
));
1814 EXPECT_EQ(controller
.GetEntryCount(), 2);
1815 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 1);
1816 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1817 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1818 EXPECT_FALSE(controller
.GetPendingEntry());
1819 EXPECT_TRUE(controller
.CanGoBack());
1820 EXPECT_FALSE(controller
.CanGoForward());
1823 // Two consecutive navigations for the same URL entered in should be considered
1824 // as SAME_PAGE navigation even when we are redirected to some other page.
1825 TEST_F(NavigationControllerTest
, Redirect
) {
1826 NavigationControllerImpl
& controller
= controller_impl();
1827 TestNotificationTracker notifications
;
1828 RegisterForAllNavNotifications(¬ifications
, &controller
);
1830 const GURL
url1("http://foo1");
1831 const GURL
url2("http://foo2"); // Redirection target
1835 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1836 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1838 EXPECT_EQ(0U, notifications
.size());
1840 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
1842 params
.nav_entry_id
= entry_id
;
1843 params
.did_create_new_entry
= true;
1845 params
.transition
= ui::PAGE_TRANSITION_SERVER_REDIRECT
;
1846 params
.redirects
.push_back(GURL("http://foo1"));
1847 params
.redirects
.push_back(GURL("http://foo2"));
1848 params
.should_update_history
= false;
1849 params
.gesture
= NavigationGestureAuto
;
1850 params
.is_post
= false;
1851 params
.page_state
= PageState::CreateFromURL(url2
);
1853 LoadCommittedDetails details
;
1855 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
1857 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1858 navigation_entry_committed_counter_
= 0;
1862 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1863 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1865 EXPECT_TRUE(controller
.GetPendingEntry());
1866 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1867 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
1869 params
.nav_entry_id
= entry_id
;
1870 params
.did_create_new_entry
= false;
1872 EXPECT_EQ(0U, notifications
.size());
1873 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
1875 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1876 navigation_entry_committed_counter_
= 0;
1878 EXPECT_TRUE(details
.type
== NAVIGATION_TYPE_SAME_PAGE
);
1879 EXPECT_EQ(controller
.GetEntryCount(), 1);
1880 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1881 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1882 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1883 EXPECT_FALSE(controller
.GetPendingEntry());
1884 EXPECT_EQ(url2
, controller
.GetVisibleEntry()->GetURL());
1886 EXPECT_FALSE(controller
.CanGoBack());
1887 EXPECT_FALSE(controller
.CanGoForward());
1890 // Similar to Redirect above, but the first URL is requested by POST,
1891 // the second URL is requested by GET. NavigationEntry::has_post_data_
1892 // must be cleared. http://crbug.com/21245
1893 TEST_F(NavigationControllerTest
, PostThenRedirect
) {
1894 NavigationControllerImpl
& controller
= controller_impl();
1895 TestNotificationTracker notifications
;
1896 RegisterForAllNavNotifications(¬ifications
, &controller
);
1898 const GURL
url1("http://foo1");
1899 const GURL
url2("http://foo2"); // Redirection target
1901 // First request as POST.
1903 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1904 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1905 controller
.GetVisibleEntry()->SetHasPostData(true);
1907 EXPECT_EQ(0U, notifications
.size());
1909 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
1911 params
.nav_entry_id
= entry_id
;
1912 params
.did_create_new_entry
= true;
1914 params
.transition
= ui::PAGE_TRANSITION_SERVER_REDIRECT
;
1915 params
.redirects
.push_back(GURL("http://foo1"));
1916 params
.redirects
.push_back(GURL("http://foo2"));
1917 params
.should_update_history
= false;
1918 params
.gesture
= NavigationGestureAuto
;
1919 params
.is_post
= true;
1920 params
.page_state
= PageState::CreateFromURL(url2
);
1922 LoadCommittedDetails details
;
1924 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
1926 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1927 navigation_entry_committed_counter_
= 0;
1931 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1932 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1934 EXPECT_TRUE(controller
.GetPendingEntry());
1935 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1936 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
1938 params
.nav_entry_id
= entry_id
;
1939 params
.did_create_new_entry
= false;
1940 params
.is_post
= false;
1942 EXPECT_EQ(0U, notifications
.size());
1943 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
1945 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1946 navigation_entry_committed_counter_
= 0;
1948 EXPECT_TRUE(details
.type
== NAVIGATION_TYPE_SAME_PAGE
);
1949 EXPECT_EQ(controller
.GetEntryCount(), 1);
1950 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1951 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1952 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1953 EXPECT_FALSE(controller
.GetPendingEntry());
1954 EXPECT_EQ(url2
, controller
.GetVisibleEntry()->GetURL());
1955 EXPECT_FALSE(controller
.GetVisibleEntry()->GetHasPostData());
1957 EXPECT_FALSE(controller
.CanGoBack());
1958 EXPECT_FALSE(controller
.CanGoForward());
1961 // A redirect right off the bat should be a NEW_PAGE.
1962 TEST_F(NavigationControllerTest
, ImmediateRedirect
) {
1963 NavigationControllerImpl
& controller
= controller_impl();
1964 TestNotificationTracker notifications
;
1965 RegisterForAllNavNotifications(¬ifications
, &controller
);
1967 const GURL
url1("http://foo1");
1968 const GURL
url2("http://foo2"); // Redirection target
1972 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1973 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1975 EXPECT_TRUE(controller
.GetPendingEntry());
1976 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1977 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
1979 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
1981 params
.nav_entry_id
= entry_id
;
1982 params
.did_create_new_entry
= true;
1984 params
.transition
= ui::PAGE_TRANSITION_SERVER_REDIRECT
;
1985 params
.redirects
.push_back(GURL("http://foo1"));
1986 params
.redirects
.push_back(GURL("http://foo2"));
1987 params
.should_update_history
= false;
1988 params
.gesture
= NavigationGestureAuto
;
1989 params
.is_post
= false;
1990 params
.page_state
= PageState::CreateFromURL(url2
);
1992 LoadCommittedDetails details
;
1994 EXPECT_EQ(0U, notifications
.size());
1995 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
1997 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1998 navigation_entry_committed_counter_
= 0;
2000 EXPECT_TRUE(details
.type
== NAVIGATION_TYPE_NEW_PAGE
);
2001 EXPECT_EQ(controller
.GetEntryCount(), 1);
2002 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
2003 EXPECT_TRUE(controller
.GetLastCommittedEntry());
2004 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
2005 EXPECT_FALSE(controller
.GetPendingEntry());
2006 EXPECT_EQ(url2
, controller
.GetVisibleEntry()->GetURL());
2008 EXPECT_FALSE(controller
.CanGoBack());
2009 EXPECT_FALSE(controller
.CanGoForward());
2012 // If something is pumping the event loop in the browser process and is loading
2013 // pages rapidly one after the other, there can be a race with two closely-
2014 // spaced load requests. Once the first load request is sent, will the renderer
2015 // be fast enough to get the load committed, send a DidCommitProvisionalLoad
2016 // IPC, and have the browser process handle that IPC before the caller makes
2017 // another load request, replacing the pending entry of the first request?
2019 // This test is about what happens in such a race when that pending entry
2020 // replacement happens. If it happens, and the first load had the same URL as
2021 // the page before it, we must make sure that the replacement of the pending
2022 // entry correctly turns a SAME_PAGE classification into an EXISTING_PAGE one.
2024 // (This is a unit test rather than a browser test because it's not currently
2025 // possible to force this sequence of events with a browser test.)
2026 TEST_F(NavigationControllerTest
,
2027 NavigationTypeClassification_ExistingPageRace
) {
2028 NavigationControllerImpl
& controller
= controller_impl();
2029 const GURL
url1("http://foo1");
2030 const GURL
url2("http://foo2");
2032 // Start with a loaded page.
2033 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url1
);
2034 EXPECT_EQ(nullptr, controller_impl().GetPendingEntry());
2036 // Start a load of the same page again.
2038 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2039 int entry_id1
= controller
.GetPendingEntry()->GetUniqueID();
2041 // Immediately start loading a different page...
2043 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2044 int entry_id2
= controller
.GetPendingEntry()->GetUniqueID();
2045 EXPECT_NE(entry_id1
, entry_id2
);
2047 // ... and now the renderer sends a commit for the first navigation.
2048 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2050 params
.nav_entry_id
= entry_id1
;
2051 params
.intended_as_new_entry
= true;
2052 params
.did_create_new_entry
= false;
2054 params
.transition
= ui::PAGE_TRANSITION_TYPED
;
2055 params
.page_state
= PageState::CreateFromURL(url1
);
2057 LoadCommittedDetails details
;
2059 main_test_rfh()->PrepareForCommit();
2060 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
2062 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE
, details
.type
);
2065 // Tests navigation via link click within a subframe. A new navigation entry
2066 // should be created.
2067 TEST_F(NavigationControllerTest
, NewSubframe
) {
2068 NavigationControllerImpl
& controller
= controller_impl();
2069 TestNotificationTracker notifications
;
2070 RegisterForAllNavNotifications(¬ifications
, &controller
);
2072 const GURL
url1("http://foo1");
2073 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url1
);
2074 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2075 navigation_entry_committed_counter_
= 0;
2077 const GURL
url2("http://foo2");
2078 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2080 params
.nav_entry_id
= 0;
2081 params
.did_create_new_entry
= true;
2083 params
.transition
= ui::PAGE_TRANSITION_MANUAL_SUBFRAME
;
2084 params
.should_update_history
= false;
2085 params
.gesture
= NavigationGestureUser
;
2086 params
.is_post
= false;
2087 params
.page_state
= PageState::CreateFromURL(url2
);
2089 LoadCommittedDetails details
;
2090 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
2092 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2093 navigation_entry_committed_counter_
= 0;
2094 EXPECT_EQ(url1
, details
.previous_url
);
2095 EXPECT_FALSE(details
.is_in_page
);
2096 EXPECT_FALSE(details
.is_main_frame
);
2098 // The new entry should be appended.
2099 EXPECT_EQ(2, controller
.GetEntryCount());
2101 // New entry should refer to the new page, but the old URL (entries only
2102 // reflect the toplevel URL).
2103 EXPECT_EQ(url1
, details
.entry
->GetURL());
2104 EXPECT_EQ(params
.page_id
, details
.entry
->GetPageID());
2107 // Auto subframes are ones the page loads automatically like ads. They should
2108 // not create new navigation entries.
2109 // TODO(creis): Test updating entries for history auto subframe navigations.
2110 TEST_F(NavigationControllerTest
, AutoSubframe
) {
2111 NavigationControllerImpl
& controller
= controller_impl();
2112 TestNotificationTracker notifications
;
2113 RegisterForAllNavNotifications(¬ifications
, &controller
);
2115 const GURL
url1("http://foo/1");
2116 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url1
);
2117 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2118 navigation_entry_committed_counter_
= 0;
2120 // Add a subframe and navigate it.
2121 main_test_rfh()->OnCreateChildFrame(
2122 MSG_ROUTING_NONE
, blink::WebTreeScopeType::Document
, std::string(),
2123 blink::WebSandboxFlags::None
);
2124 RenderFrameHostImpl
* subframe
=
2125 contents()->GetFrameTree()->root()->child_at(0)->current_frame_host();
2126 const GURL
url2("http://foo/2");
2128 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2130 params
.nav_entry_id
= 0;
2131 params
.did_create_new_entry
= false;
2133 params
.transition
= ui::PAGE_TRANSITION_AUTO_SUBFRAME
;
2134 params
.should_update_history
= false;
2135 params
.gesture
= NavigationGestureUser
;
2136 params
.is_post
= false;
2137 params
.page_state
= PageState::CreateFromURL(url2
);
2139 // Navigating should do nothing.
2140 LoadCommittedDetails details
;
2141 EXPECT_FALSE(controller
.RendererDidNavigate(subframe
, params
, &details
));
2142 EXPECT_EQ(0U, notifications
.size());
2145 // There should still be only one entry.
2146 EXPECT_EQ(1, controller
.GetEntryCount());
2147 NavigationEntryImpl
* entry
= controller
.GetLastCommittedEntry();
2148 EXPECT_EQ(url1
, entry
->GetURL());
2149 EXPECT_EQ(1, entry
->GetPageID());
2150 FrameNavigationEntry
* root_entry
= entry
->root_node()->frame_entry
.get();
2151 EXPECT_EQ(url1
, root_entry
->url());
2153 // Verify subframe entries if we're in --site-per-process mode.
2154 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
2155 switches::kSitePerProcess
)) {
2156 // The entry should now have a subframe FrameNavigationEntry.
2157 ASSERT_EQ(1U, entry
->root_node()->children
.size());
2158 FrameNavigationEntry
* frame_entry
=
2159 entry
->root_node()->children
[0]->frame_entry
.get();
2160 EXPECT_EQ(url2
, frame_entry
->url());
2162 // There are no subframe FrameNavigationEntries by default.
2163 EXPECT_EQ(0U, entry
->root_node()->children
.size());
2166 // Add a second subframe and navigate.
2167 main_test_rfh()->OnCreateChildFrame(
2168 MSG_ROUTING_NONE
, blink::WebTreeScopeType::Document
, std::string(),
2169 blink::WebSandboxFlags::None
);
2170 RenderFrameHostImpl
* subframe2
=
2171 contents()->GetFrameTree()->root()->child_at(1)->current_frame_host();
2172 const GURL
url3("http://foo/3");
2174 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2176 params
.nav_entry_id
= 0;
2177 params
.did_create_new_entry
= false;
2179 params
.transition
= ui::PAGE_TRANSITION_AUTO_SUBFRAME
;
2180 params
.should_update_history
= false;
2181 params
.gesture
= NavigationGestureUser
;
2182 params
.is_post
= false;
2183 params
.page_state
= PageState::CreateFromURL(url3
);
2185 // Navigating should do nothing.
2186 LoadCommittedDetails details
;
2187 EXPECT_FALSE(controller
.RendererDidNavigate(subframe2
, params
, &details
));
2188 EXPECT_EQ(0U, notifications
.size());
2191 // There should still be only one entry, mostly unchanged.
2192 EXPECT_EQ(1, controller
.GetEntryCount());
2193 EXPECT_EQ(entry
, controller
.GetLastCommittedEntry());
2194 EXPECT_EQ(url1
, entry
->GetURL());
2195 EXPECT_EQ(1, entry
->GetPageID());
2196 EXPECT_EQ(root_entry
, entry
->root_node()->frame_entry
.get());
2197 EXPECT_EQ(url1
, root_entry
->url());
2199 // Verify subframe entries if we're in --site-per-process mode.
2200 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
2201 switches::kSitePerProcess
)) {
2202 // The entry should now have 2 subframe FrameNavigationEntries.
2203 ASSERT_EQ(2U, entry
->root_node()->children
.size());
2204 FrameNavigationEntry
* new_frame_entry
=
2205 entry
->root_node()->children
[1]->frame_entry
.get();
2206 EXPECT_EQ(url3
, new_frame_entry
->url());
2208 // There are no subframe FrameNavigationEntries by default.
2209 EXPECT_EQ(0U, entry
->root_node()->children
.size());
2212 // Add a nested subframe and navigate.
2213 subframe
->OnCreateChildFrame(MSG_ROUTING_NONE
,
2214 blink::WebTreeScopeType::Document
, std::string(),
2215 blink::WebSandboxFlags::None
);
2216 RenderFrameHostImpl
* subframe3
= contents()
2221 ->current_frame_host();
2222 const GURL
url4("http://foo/4");
2224 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2226 params
.nav_entry_id
= 0;
2227 params
.did_create_new_entry
= false;
2229 params
.transition
= ui::PAGE_TRANSITION_AUTO_SUBFRAME
;
2230 params
.should_update_history
= false;
2231 params
.gesture
= NavigationGestureUser
;
2232 params
.is_post
= false;
2233 params
.page_state
= PageState::CreateFromURL(url4
);
2235 // Navigating should do nothing.
2236 LoadCommittedDetails details
;
2237 EXPECT_FALSE(controller
.RendererDidNavigate(subframe3
, params
, &details
));
2238 EXPECT_EQ(0U, notifications
.size());
2241 // There should still be only one entry, mostly unchanged.
2242 EXPECT_EQ(1, controller
.GetEntryCount());
2243 EXPECT_EQ(entry
, controller
.GetLastCommittedEntry());
2244 EXPECT_EQ(url1
, entry
->GetURL());
2245 EXPECT_EQ(1, entry
->GetPageID());
2246 EXPECT_EQ(root_entry
, entry
->root_node()->frame_entry
.get());
2247 EXPECT_EQ(url1
, root_entry
->url());
2249 // Verify subframe entries if we're in --site-per-process mode.
2250 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
2251 switches::kSitePerProcess
)) {
2252 // The entry should now have a nested FrameNavigationEntry.
2253 EXPECT_EQ(2U, entry
->root_node()->children
.size());
2254 ASSERT_EQ(1U, entry
->root_node()->children
[0]->children
.size());
2255 FrameNavigationEntry
* new_frame_entry
=
2256 entry
->root_node()->children
[0]->children
[0]->frame_entry
.get();
2257 EXPECT_EQ(url4
, new_frame_entry
->url());
2259 // There are no subframe FrameNavigationEntries by default.
2260 EXPECT_EQ(0U, entry
->root_node()->children
.size());
2264 // Tests navigation and then going back to a subframe navigation.
2265 TEST_F(NavigationControllerTest
, BackSubframe
) {
2266 NavigationControllerImpl
& controller
= controller_impl();
2267 TestNotificationTracker notifications
;
2268 RegisterForAllNavNotifications(¬ifications
, &controller
);
2271 const GURL
url1("http://foo1");
2272 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url1
);
2273 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2274 NavigationEntry
* entry1
= controller
.GetLastCommittedEntry();
2275 navigation_entry_committed_counter_
= 0;
2277 // First manual subframe navigation.
2278 const GURL
url2("http://foo2");
2279 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2281 params
.nav_entry_id
= 0;
2282 params
.did_create_new_entry
= true;
2284 params
.transition
= ui::PAGE_TRANSITION_MANUAL_SUBFRAME
;
2285 params
.should_update_history
= false;
2286 params
.gesture
= NavigationGestureUser
;
2287 params
.is_post
= false;
2288 params
.page_state
= PageState::CreateFromURL(url2
);
2290 // This should generate a new entry.
2291 LoadCommittedDetails details
;
2292 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
2294 NavigationEntry
* entry2
= controller
.GetLastCommittedEntry();
2295 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2296 navigation_entry_committed_counter_
= 0;
2297 EXPECT_EQ(2, controller
.GetEntryCount());
2299 // Second manual subframe navigation should also make a new entry.
2300 const GURL
url3("http://foo3");
2302 params
.nav_entry_id
= 0;
2303 params
.did_create_new_entry
= true;
2305 params
.transition
= ui::PAGE_TRANSITION_MANUAL_SUBFRAME
;
2306 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
2308 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2309 navigation_entry_committed_counter_
= 0;
2310 EXPECT_EQ(3, controller
.GetEntryCount());
2311 EXPECT_EQ(2, controller
.GetCurrentEntryIndex());
2314 controller
.GoBack();
2316 params
.nav_entry_id
= entry2
->GetUniqueID();
2317 params
.did_create_new_entry
= false;
2319 params
.transition
= ui::PAGE_TRANSITION_AUTO_SUBFRAME
;
2320 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
2322 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2323 navigation_entry_committed_counter_
= 0;
2324 EXPECT_EQ(3, controller
.GetEntryCount());
2325 EXPECT_EQ(1, controller
.GetCurrentEntryIndex());
2326 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
2327 EXPECT_FALSE(controller
.GetPendingEntry());
2329 // Go back one more.
2330 controller
.GoBack();
2332 params
.nav_entry_id
= entry1
->GetUniqueID();
2333 params
.did_create_new_entry
= false;
2335 params
.transition
= ui::PAGE_TRANSITION_AUTO_SUBFRAME
;
2336 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
2338 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2339 navigation_entry_committed_counter_
= 0;
2340 EXPECT_EQ(3, controller
.GetEntryCount());
2341 EXPECT_EQ(0, controller
.GetCurrentEntryIndex());
2342 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
2343 EXPECT_FALSE(controller
.GetPendingEntry());
2346 TEST_F(NavigationControllerTest
, LinkClick
) {
2347 NavigationControllerImpl
& controller
= controller_impl();
2348 TestNotificationTracker notifications
;
2349 RegisterForAllNavNotifications(¬ifications
, &controller
);
2351 const GURL
url1("http://foo1");
2352 const GURL
url2("http://foo2");
2354 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url1
);
2355 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2356 navigation_entry_committed_counter_
= 0;
2358 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url2
);
2359 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2360 navigation_entry_committed_counter_
= 0;
2362 // Should have produced a new session history entry.
2363 EXPECT_EQ(controller
.GetEntryCount(), 2);
2364 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 1);
2365 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
2366 EXPECT_TRUE(controller
.GetLastCommittedEntry());
2367 EXPECT_FALSE(controller
.GetPendingEntry());
2368 EXPECT_TRUE(controller
.CanGoBack());
2369 EXPECT_FALSE(controller
.CanGoForward());
2372 TEST_F(NavigationControllerTest
, InPage
) {
2373 NavigationControllerImpl
& controller
= controller_impl();
2374 TestNotificationTracker notifications
;
2375 RegisterForAllNavNotifications(¬ifications
, &controller
);
2378 const GURL
url1("http://foo");
2379 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url1
);
2380 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2381 navigation_entry_committed_counter_
= 0;
2383 // Ensure main page navigation to same url respects the was_within_same_page
2384 // hint provided in the params.
2385 FrameHostMsg_DidCommitProvisionalLoad_Params self_params
;
2386 self_params
.page_id
= 0;
2387 self_params
.nav_entry_id
= 0;
2388 self_params
.did_create_new_entry
= false;
2389 self_params
.url
= url1
;
2390 self_params
.transition
= ui::PAGE_TRANSITION_LINK
;
2391 self_params
.should_update_history
= false;
2392 self_params
.gesture
= NavigationGestureUser
;
2393 self_params
.is_post
= false;
2394 self_params
.page_state
= PageState::CreateFromURL(url1
);
2395 self_params
.was_within_same_page
= true;
2397 LoadCommittedDetails details
;
2398 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), self_params
,
2400 NavigationEntry
* entry1
= controller
.GetLastCommittedEntry();
2401 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2402 navigation_entry_committed_counter_
= 0;
2403 EXPECT_TRUE(details
.is_in_page
);
2404 EXPECT_TRUE(details
.did_replace_entry
);
2405 EXPECT_EQ(1, controller
.GetEntryCount());
2407 // Fragment navigation to a new page_id.
2408 const GURL
url2("http://foo#a");
2409 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2411 params
.nav_entry_id
= 0;
2412 params
.did_create_new_entry
= true;
2414 params
.transition
= ui::PAGE_TRANSITION_LINK
;
2415 params
.should_update_history
= false;
2416 params
.gesture
= NavigationGestureUser
;
2417 params
.is_post
= false;
2418 params
.page_state
= PageState::CreateFromURL(url2
);
2419 params
.was_within_same_page
= true;
2421 // This should generate a new entry.
2422 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
2424 NavigationEntry
* entry2
= controller
.GetLastCommittedEntry();
2425 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2426 navigation_entry_committed_counter_
= 0;
2427 EXPECT_TRUE(details
.is_in_page
);
2428 EXPECT_FALSE(details
.did_replace_entry
);
2429 EXPECT_EQ(2, controller
.GetEntryCount());
2432 FrameHostMsg_DidCommitProvisionalLoad_Params
back_params(params
);
2433 controller
.GoBack();
2434 back_params
.url
= url1
;
2435 back_params
.page_id
= 0;
2436 back_params
.nav_entry_id
= entry1
->GetUniqueID();
2437 back_params
.did_create_new_entry
= false;
2438 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), back_params
,
2440 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2441 navigation_entry_committed_counter_
= 0;
2442 EXPECT_TRUE(details
.is_in_page
);
2443 EXPECT_EQ(2, controller
.GetEntryCount());
2444 EXPECT_EQ(0, controller
.GetCurrentEntryIndex());
2445 EXPECT_EQ(back_params
.url
, controller
.GetVisibleEntry()->GetURL());
2448 FrameHostMsg_DidCommitProvisionalLoad_Params
forward_params(params
);
2449 controller
.GoForward();
2450 forward_params
.url
= url2
;
2451 forward_params
.page_id
= 1;
2452 forward_params
.nav_entry_id
= entry2
->GetUniqueID();
2453 forward_params
.did_create_new_entry
= false;
2454 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), forward_params
,
2456 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2457 navigation_entry_committed_counter_
= 0;
2458 EXPECT_TRUE(details
.is_in_page
);
2459 EXPECT_EQ(2, controller
.GetEntryCount());
2460 EXPECT_EQ(1, controller
.GetCurrentEntryIndex());
2461 EXPECT_EQ(forward_params
.url
,
2462 controller
.GetVisibleEntry()->GetURL());
2464 // Now go back and forward again. This is to work around a bug where we would
2465 // compare the incoming URL with the last committed entry rather than the
2466 // one identified by an existing page ID. This would result in the second URL
2467 // losing the reference fragment when you navigate away from it and then back.
2468 controller
.GoBack();
2469 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), back_params
,
2471 controller
.GoForward();
2472 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), forward_params
,
2474 EXPECT_EQ(forward_params
.url
,
2475 controller
.GetVisibleEntry()->GetURL());
2477 // Finally, navigate to an unrelated URL to make sure in_page is not sticky.
2478 const GURL
url3("http://bar");
2480 params
.nav_entry_id
= 0;
2481 params
.did_create_new_entry
= true;
2483 navigation_entry_committed_counter_
= 0;
2484 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
2486 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2487 navigation_entry_committed_counter_
= 0;
2488 EXPECT_FALSE(details
.is_in_page
);
2489 EXPECT_EQ(3, controller
.GetEntryCount());
2490 EXPECT_EQ(2, controller
.GetCurrentEntryIndex());
2493 TEST_F(NavigationControllerTest
, InPage_Replace
) {
2494 NavigationControllerImpl
& controller
= controller_impl();
2495 TestNotificationTracker notifications
;
2496 RegisterForAllNavNotifications(¬ifications
, &controller
);
2499 const GURL
url1("http://foo");
2500 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url1
);
2501 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2502 navigation_entry_committed_counter_
= 0;
2504 // First navigation.
2505 const GURL
url2("http://foo#a");
2506 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2507 params
.page_id
= 0; // Same page_id
2508 params
.nav_entry_id
= 0;
2509 params
.did_create_new_entry
= false;
2511 params
.transition
= ui::PAGE_TRANSITION_LINK
;
2512 params
.should_update_history
= false;
2513 params
.gesture
= NavigationGestureUser
;
2514 params
.is_post
= false;
2515 params
.page_state
= PageState::CreateFromURL(url2
);
2516 params
.was_within_same_page
= true;
2518 // This should NOT generate a new entry, nor prune the list.
2519 LoadCommittedDetails details
;
2520 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
2522 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2523 navigation_entry_committed_counter_
= 0;
2524 EXPECT_TRUE(details
.is_in_page
);
2525 EXPECT_TRUE(details
.did_replace_entry
);
2526 EXPECT_EQ(1, controller
.GetEntryCount());
2529 // Tests for http://crbug.com/40395
2532 // window.location.replace("#a");
2533 // window.location='http://foo3/';
2535 TEST_F(NavigationControllerTest
, ClientRedirectAfterInPageNavigation
) {
2536 NavigationControllerImpl
& controller
= controller_impl();
2537 TestNotificationTracker notifications
;
2538 RegisterForAllNavNotifications(¬ifications
, &controller
);
2540 // Load an initial page.
2542 const GURL
url("http://foo/");
2543 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url
);
2544 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2545 navigation_entry_committed_counter_
= 0;
2548 // Navigate to a new page.
2550 const GURL
url("http://foo2/");
2551 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url
);
2552 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2553 navigation_entry_committed_counter_
= 0;
2556 // Navigate within the page.
2558 const GURL
url("http://foo2/#a");
2559 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2560 params
.page_id
= 1; // Same page_id
2561 params
.nav_entry_id
= 0;
2562 params
.did_create_new_entry
= false;
2564 params
.transition
= ui::PAGE_TRANSITION_LINK
;
2565 params
.redirects
.push_back(url
);
2566 params
.should_update_history
= true;
2567 params
.gesture
= NavigationGestureUnknown
;
2568 params
.is_post
= false;
2569 params
.page_state
= PageState::CreateFromURL(url
);
2570 params
.was_within_same_page
= true;
2572 // This should NOT generate a new entry, nor prune the list.
2573 LoadCommittedDetails details
;
2574 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
2576 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2577 navigation_entry_committed_counter_
= 0;
2578 EXPECT_TRUE(details
.is_in_page
);
2579 EXPECT_TRUE(details
.did_replace_entry
);
2580 EXPECT_EQ(2, controller
.GetEntryCount());
2583 // Perform a client redirect to a new page.
2585 const GURL
url("http://foo3/");
2586 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2587 params
.page_id
= 2; // New page_id
2588 params
.nav_entry_id
= 0;
2589 params
.did_create_new_entry
= true;
2591 params
.transition
= ui::PAGE_TRANSITION_CLIENT_REDIRECT
;
2592 params
.redirects
.push_back(GURL("http://foo2/#a"));
2593 params
.redirects
.push_back(url
);
2594 params
.should_update_history
= true;
2595 params
.gesture
= NavigationGestureUnknown
;
2596 params
.is_post
= false;
2597 params
.page_state
= PageState::CreateFromURL(url
);
2599 // This SHOULD generate a new entry.
2600 LoadCommittedDetails details
;
2601 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
2603 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2604 navigation_entry_committed_counter_
= 0;
2605 EXPECT_FALSE(details
.is_in_page
);
2606 EXPECT_EQ(3, controller
.GetEntryCount());
2609 // Verify that BACK brings us back to http://foo2/.
2611 const GURL
url("http://foo2/");
2612 controller
.GoBack();
2613 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2614 main_test_rfh()->PrepareForCommit();
2615 main_test_rfh()->SendNavigate(1, entry_id
, false, url
);
2616 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2617 navigation_entry_committed_counter_
= 0;
2618 EXPECT_EQ(url
, controller
.GetVisibleEntry()->GetURL());
2622 TEST_F(NavigationControllerTest
, PushStateWithoutPreviousEntry
)
2624 ASSERT_FALSE(controller_impl().GetLastCommittedEntry());
2625 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2626 GURL
url("http://foo");
2628 params
.nav_entry_id
= 0;
2629 params
.did_create_new_entry
= true;
2631 params
.page_state
= PageState::CreateFromURL(url
);
2632 params
.was_within_same_page
= true;
2633 main_test_rfh()->SendRendererInitiatedNavigationRequest(url
, false);
2634 main_test_rfh()->PrepareForCommit();
2635 contents()->GetMainFrame()->SendNavigateWithParams(¶ms
);
2636 // We pass if we don't crash.
2639 // NotificationObserver implementation used in verifying we've received the
2640 // NOTIFICATION_NAV_LIST_PRUNED method.
2641 class PrunedListener
: public NotificationObserver
{
2643 explicit PrunedListener(NavigationControllerImpl
* controller
)
2644 : notification_count_(0) {
2645 registrar_
.Add(this, NOTIFICATION_NAV_LIST_PRUNED
,
2646 Source
<NavigationController
>(controller
));
2649 void Observe(int type
,
2650 const NotificationSource
& source
,
2651 const NotificationDetails
& details
) override
{
2652 if (type
== NOTIFICATION_NAV_LIST_PRUNED
) {
2653 notification_count_
++;
2654 details_
= *(Details
<PrunedDetails
>(details
).ptr());
2658 // Number of times NAV_LIST_PRUNED has been observed.
2659 int notification_count_
;
2661 // Details from the last NAV_LIST_PRUNED.
2662 PrunedDetails details_
;
2665 NotificationRegistrar registrar_
;
2667 DISALLOW_COPY_AND_ASSIGN(PrunedListener
);
2670 // Tests that we limit the number of navigation entries created correctly.
2671 TEST_F(NavigationControllerTest
, EnforceMaxNavigationCount
) {
2672 NavigationControllerImpl
& controller
= controller_impl();
2673 size_t original_count
= NavigationControllerImpl::max_entry_count();
2674 const int kMaxEntryCount
= 5;
2676 NavigationControllerImpl::set_max_entry_count_for_testing(kMaxEntryCount
);
2679 // Load up to the max count, all entries should be there.
2680 for (url_index
= 0; url_index
< kMaxEntryCount
; url_index
++) {
2681 GURL
url(base::StringPrintf("http://www.a.com/%d", url_index
));
2683 url
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2684 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2685 main_test_rfh()->PrepareForCommit();
2686 main_test_rfh()->SendNavigate(url_index
, entry_id
, true, url
);
2689 EXPECT_EQ(controller
.GetEntryCount(), kMaxEntryCount
);
2691 // Created a PrunedListener to observe prune notifications.
2692 PrunedListener
listener(&controller
);
2694 // Navigate some more.
2695 GURL
url(base::StringPrintf("http://www.a.com/%d", url_index
));
2697 url
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2698 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2699 main_test_rfh()->PrepareForCommit();
2700 main_test_rfh()->SendNavigate(url_index
, entry_id
, true, url
);
2703 // We should have got a pruned navigation.
2704 EXPECT_EQ(1, listener
.notification_count_
);
2705 EXPECT_TRUE(listener
.details_
.from_front
);
2706 EXPECT_EQ(1, listener
.details_
.count
);
2708 // We expect http://www.a.com/0 to be gone.
2709 EXPECT_EQ(controller
.GetEntryCount(), kMaxEntryCount
);
2710 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(),
2711 GURL("http://www.a.com/1"));
2713 // More navigations.
2714 for (int i
= 0; i
< 3; i
++) {
2715 url
= GURL(base::StringPrintf("http://www.a.com/%d", url_index
));
2717 url
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2718 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2719 main_test_rfh()->PrepareForCommit();
2720 main_test_rfh()->SendNavigate(url_index
, entry_id
, true, url
);
2723 EXPECT_EQ(controller
.GetEntryCount(), kMaxEntryCount
);
2724 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(),
2725 GURL("http://www.a.com/4"));
2727 NavigationControllerImpl::set_max_entry_count_for_testing(original_count
);
2730 // Tests that we can do a restore and navigate to the restored entries and
2731 // everything is updated properly. This can be tricky since there is no
2732 // SiteInstance for the entries created initially.
2733 TEST_F(NavigationControllerTest
, RestoreNavigate
) {
2734 // Create a NavigationController with a restored set of tabs.
2735 GURL
url("http://foo");
2736 std::vector
<NavigationEntry
*> entries
;
2737 NavigationEntry
* entry
= NavigationControllerImpl::CreateNavigationEntry(
2738 url
, Referrer(), ui::PAGE_TRANSITION_RELOAD
, false, std::string(),
2740 entry
->SetPageID(0);
2741 entry
->SetTitle(base::ASCIIToUTF16("Title"));
2742 entry
->SetPageState(PageState::CreateFromEncodedData("state"));
2743 const base::Time timestamp
= base::Time::Now();
2744 entry
->SetTimestamp(timestamp
);
2745 entries
.push_back(entry
);
2746 scoped_ptr
<WebContentsImpl
> our_contents(static_cast<WebContentsImpl
*>(
2747 WebContents::Create(WebContents::CreateParams(browser_context()))));
2748 NavigationControllerImpl
& our_controller
= our_contents
->GetController();
2749 our_controller
.Restore(
2751 NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY
,
2753 ASSERT_EQ(0u, entries
.size());
2755 // Before navigating to the restored entry, it should have a restore_type
2756 // and no SiteInstance.
2757 ASSERT_EQ(1, our_controller
.GetEntryCount());
2758 EXPECT_EQ(NavigationEntryImpl::RESTORE_LAST_SESSION_EXITED_CLEANLY
,
2759 our_controller
.GetEntryAtIndex(0)->restore_type());
2760 EXPECT_FALSE(our_controller
.GetEntryAtIndex(0)->site_instance());
2762 // After navigating, we should have one entry, and it should be "pending".
2763 // It should now have a SiteInstance and no restore_type.
2764 our_controller
.GoToIndex(0);
2765 EXPECT_EQ(1, our_controller
.GetEntryCount());
2766 EXPECT_EQ(our_controller
.GetEntryAtIndex(0),
2767 our_controller
.GetPendingEntry());
2768 EXPECT_EQ(0, our_controller
.GetEntryAtIndex(0)->GetPageID());
2769 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE
,
2770 our_controller
.GetEntryAtIndex(0)->restore_type());
2771 EXPECT_TRUE(our_controller
.GetEntryAtIndex(0)->site_instance());
2773 // Timestamp should remain the same before the navigation finishes.
2774 EXPECT_EQ(timestamp
, our_controller
.GetEntryAtIndex(0)->GetTimestamp());
2776 // Say we navigated to that entry.
2777 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2779 params
.nav_entry_id
= our_controller
.GetPendingEntry()->GetUniqueID();
2780 params
.did_create_new_entry
= false;
2782 params
.transition
= ui::PAGE_TRANSITION_LINK
;
2783 params
.should_update_history
= false;
2784 params
.gesture
= NavigationGestureUser
;
2785 params
.is_post
= false;
2786 params
.page_state
= PageState::CreateFromURL(url
);
2787 LoadCommittedDetails details
;
2788 our_controller
.RendererDidNavigate(our_contents
->GetMainFrame(), params
,
2791 // There should be no longer any pending entry and one committed one. This
2792 // means that we were able to locate the entry, assign its site instance, and
2793 // commit it properly.
2794 EXPECT_EQ(1, our_controller
.GetEntryCount());
2795 EXPECT_EQ(0, our_controller
.GetLastCommittedEntryIndex());
2796 EXPECT_FALSE(our_controller
.GetPendingEntry());
2799 our_controller
.GetLastCommittedEntry()->site_instance()->GetSiteURL());
2800 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE
,
2801 our_controller
.GetEntryAtIndex(0)->restore_type());
2803 // Timestamp should have been updated.
2804 EXPECT_GE(our_controller
.GetEntryAtIndex(0)->GetTimestamp(), timestamp
);
2807 // Tests that we can still navigate to a restored entry after a different
2808 // navigation fails and clears the pending entry. http://crbug.com/90085
2809 TEST_F(NavigationControllerTest
, RestoreNavigateAfterFailure
) {
2810 // Create a NavigationController with a restored set of tabs.
2811 GURL
url("http://foo");
2812 std::vector
<NavigationEntry
*> entries
;
2813 NavigationEntry
* entry
= NavigationControllerImpl::CreateNavigationEntry(
2814 url
, Referrer(), ui::PAGE_TRANSITION_RELOAD
, false, std::string(),
2816 entry
->SetPageID(0);
2817 entry
->SetTitle(base::ASCIIToUTF16("Title"));
2818 entry
->SetPageState(PageState::CreateFromEncodedData("state"));
2819 entries
.push_back(entry
);
2820 scoped_ptr
<WebContentsImpl
> our_contents(static_cast<WebContentsImpl
*>(
2821 WebContents::Create(WebContents::CreateParams(browser_context()))));
2822 NavigationControllerImpl
& our_controller
= our_contents
->GetController();
2823 our_controller
.Restore(
2824 0, NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY
, &entries
);
2825 ASSERT_EQ(0u, entries
.size());
2827 // Before navigating to the restored entry, it should have a restore_type
2828 // and no SiteInstance.
2829 entry
= our_controller
.GetEntryAtIndex(0);
2830 EXPECT_EQ(NavigationEntryImpl::RESTORE_LAST_SESSION_EXITED_CLEANLY
,
2831 our_controller
.GetEntryAtIndex(0)->restore_type());
2832 EXPECT_FALSE(our_controller
.GetEntryAtIndex(0)->site_instance());
2834 // After navigating, we should have one entry, and it should be "pending".
2835 // It should now have a SiteInstance and no restore_type.
2836 our_controller
.GoToIndex(0);
2837 EXPECT_EQ(1, our_controller
.GetEntryCount());
2838 EXPECT_EQ(our_controller
.GetEntryAtIndex(0),
2839 our_controller
.GetPendingEntry());
2840 EXPECT_EQ(0, our_controller
.GetEntryAtIndex(0)->GetPageID());
2841 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE
,
2842 our_controller
.GetEntryAtIndex(0)->restore_type());
2843 EXPECT_TRUE(our_controller
.GetEntryAtIndex(0)->site_instance());
2845 // This pending navigation may have caused a different navigation to fail,
2846 // which causes the pending entry to be cleared.
2847 FrameHostMsg_DidFailProvisionalLoadWithError_Params fail_load_params
;
2848 fail_load_params
.error_code
= net::ERR_ABORTED
;
2849 fail_load_params
.error_description
= base::string16();
2850 fail_load_params
.url
= url
;
2851 fail_load_params
.showing_repost_interstitial
= false;
2852 main_test_rfh()->OnMessageReceived(
2853 FrameHostMsg_DidFailProvisionalLoadWithError(0, // routing_id
2856 // Now the pending restored entry commits.
2857 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2859 params
.nav_entry_id
= entry
->GetUniqueID();
2860 params
.did_create_new_entry
= false;
2862 params
.transition
= ui::PAGE_TRANSITION_LINK
;
2863 params
.should_update_history
= false;
2864 params
.gesture
= NavigationGestureUser
;
2865 params
.is_post
= false;
2866 params
.page_state
= PageState::CreateFromURL(url
);
2867 LoadCommittedDetails details
;
2868 our_controller
.RendererDidNavigate(our_contents
->GetMainFrame(), params
,
2871 // There should be no pending entry and one committed one.
2872 EXPECT_EQ(1, our_controller
.GetEntryCount());
2873 EXPECT_EQ(0, our_controller
.GetLastCommittedEntryIndex());
2874 EXPECT_FALSE(our_controller
.GetPendingEntry());
2877 our_controller
.GetLastCommittedEntry()->site_instance()->GetSiteURL());
2878 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE
,
2879 our_controller
.GetEntryAtIndex(0)->restore_type());
2882 // Make sure that the page type and stuff is correct after an interstitial.
2883 TEST_F(NavigationControllerTest
, Interstitial
) {
2884 NavigationControllerImpl
& controller
= controller_impl();
2885 // First navigate somewhere normal.
2886 const GURL
url1("http://foo");
2888 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2889 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2890 main_test_rfh()->PrepareForCommit();
2891 main_test_rfh()->SendNavigate(0, entry_id
, true, url1
);
2893 // Now navigate somewhere with an interstitial.
2894 const GURL
url2("http://bar");
2895 controller
.LoadURL(url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
,
2897 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2898 controller
.GetPendingEntry()->set_page_type(PAGE_TYPE_INTERSTITIAL
);
2900 // At this point the interstitial will be displayed and the load will still
2901 // be pending. If the user continues, the load will commit.
2902 main_test_rfh()->PrepareForCommit();
2903 main_test_rfh()->SendNavigate(1, entry_id
, true, url2
);
2905 // The page should be a normal page again.
2906 EXPECT_EQ(url2
, controller
.GetLastCommittedEntry()->GetURL());
2907 EXPECT_EQ(PAGE_TYPE_NORMAL
,
2908 controller
.GetLastCommittedEntry()->GetPageType());
2911 TEST_F(NavigationControllerTest
, RemoveEntry
) {
2912 NavigationControllerImpl
& controller
= controller_impl();
2913 const GURL
url1("http://foo/1");
2914 const GURL
url2("http://foo/2");
2915 const GURL
url3("http://foo/3");
2916 const GURL
url4("http://foo/4");
2917 const GURL
url5("http://foo/5");
2918 const GURL
pending_url("http://foo/pending");
2919 const GURL
default_url("http://foo/default");
2922 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2923 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2924 main_test_rfh()->PrepareForCommit();
2925 main_test_rfh()->SendNavigate(0, entry_id
, true, url1
);
2927 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2928 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2929 main_test_rfh()->PrepareForCommit();
2930 main_test_rfh()->SendNavigate(1, entry_id
, true, url2
);
2932 url3
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2933 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2934 main_test_rfh()->PrepareForCommit();
2935 main_test_rfh()->SendNavigate(2, entry_id
, true, url3
);
2937 url4
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2938 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2939 main_test_rfh()->PrepareForCommit();
2940 main_test_rfh()->SendNavigate(3, entry_id
, true, url4
);
2942 url5
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2943 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2944 main_test_rfh()->PrepareForCommit();
2945 main_test_rfh()->SendNavigate(4, entry_id
, true, url5
);
2947 // Try to remove the last entry. Will fail because it is the current entry.
2948 EXPECT_FALSE(controller
.RemoveEntryAtIndex(controller
.GetEntryCount() - 1));
2949 EXPECT_EQ(5, controller
.GetEntryCount());
2950 EXPECT_EQ(4, controller
.GetLastCommittedEntryIndex());
2952 // Go back, but don't commit yet. Check that we can't delete the current
2953 // and pending entries.
2954 controller
.GoBack();
2955 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2956 EXPECT_FALSE(controller
.RemoveEntryAtIndex(controller
.GetEntryCount() - 1));
2957 EXPECT_FALSE(controller
.RemoveEntryAtIndex(controller
.GetEntryCount() - 2));
2959 // Now commit and delete the last entry.
2960 main_test_rfh()->PrepareForCommit();
2961 main_test_rfh()->SendNavigate(3, entry_id
, false, url4
);
2962 EXPECT_TRUE(controller
.RemoveEntryAtIndex(controller
.GetEntryCount() - 1));
2963 EXPECT_EQ(4, controller
.GetEntryCount());
2964 EXPECT_EQ(3, controller
.GetLastCommittedEntryIndex());
2965 EXPECT_FALSE(controller
.GetPendingEntry());
2967 // Remove an entry which is not the last committed one.
2968 EXPECT_TRUE(controller
.RemoveEntryAtIndex(0));
2969 EXPECT_EQ(3, controller
.GetEntryCount());
2970 EXPECT_EQ(2, controller
.GetLastCommittedEntryIndex());
2971 EXPECT_FALSE(controller
.GetPendingEntry());
2973 // Remove the 2 remaining entries.
2974 controller
.RemoveEntryAtIndex(1);
2975 controller
.RemoveEntryAtIndex(0);
2977 // This should leave us with only the last committed entry.
2978 EXPECT_EQ(1, controller
.GetEntryCount());
2979 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
2982 TEST_F(NavigationControllerTest
, RemoveEntryWithPending
) {
2983 NavigationControllerImpl
& controller
= controller_impl();
2984 const GURL
url1("http://foo/1");
2985 const GURL
url2("http://foo/2");
2986 const GURL
url3("http://foo/3");
2987 const GURL
default_url("http://foo/default");
2990 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2991 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2992 main_test_rfh()->PrepareForCommit();
2993 main_test_rfh()->SendNavigate(0, entry_id
, true, url1
);
2995 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2996 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2997 main_test_rfh()->PrepareForCommit();
2998 main_test_rfh()->SendNavigate(1, entry_id
, true, url2
);
3000 url3
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3001 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3002 main_test_rfh()->PrepareForCommit();
3003 main_test_rfh()->SendNavigate(2, entry_id
, true, url3
);
3005 // Go back, but don't commit yet. Check that we can't delete the current
3006 // and pending entries.
3007 controller
.GoBack();
3008 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3009 EXPECT_FALSE(controller
.RemoveEntryAtIndex(2));
3010 EXPECT_FALSE(controller
.RemoveEntryAtIndex(1));
3012 // Remove the first entry, while there is a pending entry. This is expected
3013 // to discard the pending entry.
3014 EXPECT_TRUE(controller
.RemoveEntryAtIndex(0));
3015 EXPECT_FALSE(controller
.GetPendingEntry());
3016 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
3018 // We should update the last committed entry index.
3019 EXPECT_EQ(1, controller
.GetLastCommittedEntryIndex());
3021 // Now commit and ensure we land on the right entry.
3022 main_test_rfh()->PrepareForCommit();
3023 main_test_rfh()->SendNavigate(1, entry_id
, false, url2
);
3024 EXPECT_EQ(2, controller
.GetEntryCount());
3025 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
3026 EXPECT_FALSE(controller
.GetPendingEntry());
3029 // Tests the transient entry, making sure it goes away with all navigations.
3030 TEST_F(NavigationControllerTest
, TransientEntry
) {
3031 NavigationControllerImpl
& controller
= controller_impl();
3032 TestNotificationTracker notifications
;
3033 RegisterForAllNavNotifications(¬ifications
, &controller
);
3035 const GURL
url0("http://foo/0");
3036 const GURL
url1("http://foo/1");
3037 const GURL
url2("http://foo/2");
3038 const GURL
url3("http://foo/3");
3039 const GURL
url3_ref("http://foo/3#bar");
3040 const GURL
url4("http://foo/4");
3041 const GURL
transient_url("http://foo/transient");
3044 url0
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3045 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3046 main_test_rfh()->PrepareForCommit();
3047 main_test_rfh()->SendNavigate(0, entry_id
, true, url0
);
3049 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3050 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3051 main_test_rfh()->PrepareForCommit();
3052 main_test_rfh()->SendNavigate(1, entry_id
, true, url1
);
3054 notifications
.Reset();
3056 // Adding a transient with no pending entry.
3057 NavigationEntryImpl
* transient_entry
= new NavigationEntryImpl
;
3058 transient_entry
->SetURL(transient_url
);
3059 controller
.SetTransientEntry(transient_entry
);
3061 // We should not have received any notifications.
3062 EXPECT_EQ(0U, notifications
.size());
3065 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3066 EXPECT_EQ(controller
.GetEntryCount(), 3);
3067 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 1);
3068 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
3069 EXPECT_TRUE(controller
.GetLastCommittedEntry());
3070 EXPECT_FALSE(controller
.GetPendingEntry());
3071 EXPECT_TRUE(controller
.CanGoBack());
3072 EXPECT_FALSE(controller
.CanGoForward());
3073 EXPECT_EQ(contents()->GetMaxPageID(), 1);
3077 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3078 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3079 main_test_rfh()->PrepareForCommit();
3080 main_test_rfh()->SendNavigate(2, entry_id
, true, url2
);
3082 // We should have navigated, transient entry should be gone.
3083 EXPECT_EQ(url2
, controller
.GetVisibleEntry()->GetURL());
3084 EXPECT_EQ(controller
.GetEntryCount(), 3);
3086 // Add a transient again, then navigate with no pending entry this time.
3087 transient_entry
= new NavigationEntryImpl
;
3088 transient_entry
->SetURL(transient_url
);
3089 controller
.SetTransientEntry(transient_entry
);
3090 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3091 main_test_rfh()->SendRendererInitiatedNavigationRequest(url3
, true);
3092 main_test_rfh()->PrepareForCommit();
3093 main_test_rfh()->SendNavigate(3, 0, true, url3
);
3094 // Transient entry should be gone.
3095 EXPECT_EQ(url3
, controller
.GetVisibleEntry()->GetURL());
3096 EXPECT_EQ(controller
.GetEntryCount(), 4);
3098 // Initiate a navigation, add a transient then commit navigation.
3100 url4
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3101 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3102 transient_entry
= new NavigationEntryImpl
;
3103 transient_entry
->SetURL(transient_url
);
3104 controller
.SetTransientEntry(transient_entry
);
3105 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3106 main_test_rfh()->PrepareForCommit();
3107 main_test_rfh()->SendNavigate(4, entry_id
, true, url4
);
3108 EXPECT_EQ(url4
, controller
.GetVisibleEntry()->GetURL());
3109 EXPECT_EQ(controller
.GetEntryCount(), 5);
3111 // Add a transient and go back. This should simply remove the transient.
3112 transient_entry
= new NavigationEntryImpl
;
3113 transient_entry
->SetURL(transient_url
);
3114 controller
.SetTransientEntry(transient_entry
);
3115 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3116 EXPECT_TRUE(controller
.CanGoBack());
3117 EXPECT_FALSE(controller
.CanGoForward());
3118 controller
.GoBack();
3119 // Transient entry should be gone.
3120 EXPECT_EQ(url4
, controller
.GetVisibleEntry()->GetURL());
3121 EXPECT_EQ(controller
.GetEntryCount(), 5);
3123 // Suppose the page requested a history navigation backward.
3124 controller
.GoToOffset(-1);
3125 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3126 main_test_rfh()->PrepareForCommit();
3127 main_test_rfh()->SendNavigate(3, entry_id
, false, url3
);
3129 // Add a transient and go to an entry before the current one.
3130 transient_entry
= new NavigationEntryImpl
;
3131 transient_entry
->SetURL(transient_url
);
3132 controller
.SetTransientEntry(transient_entry
);
3133 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3134 controller
.GoToIndex(1);
3135 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3136 // The navigation should have been initiated, transient entry should be gone.
3137 EXPECT_FALSE(controller
.GetTransientEntry());
3138 EXPECT_EQ(url1
, controller
.GetPendingEntry()->GetURL());
3139 // Visible entry does not update for history navigations until commit.
3140 EXPECT_EQ(url3
, controller
.GetVisibleEntry()->GetURL());
3141 main_test_rfh()->PrepareForCommit();
3142 main_test_rfh()->SendNavigate(1, entry_id
, false, url1
);
3143 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
3145 // Add a transient and go to an entry after the current one.
3146 transient_entry
= new NavigationEntryImpl
;
3147 transient_entry
->SetURL(transient_url
);
3148 controller
.SetTransientEntry(transient_entry
);
3149 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3150 controller
.GoToIndex(3);
3151 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3152 // The navigation should have been initiated, transient entry should be gone.
3153 // Because of the transient entry that is removed, going to index 3 makes us
3154 // land on url2 (which is visible after the commit).
3155 EXPECT_EQ(url2
, controller
.GetPendingEntry()->GetURL());
3156 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
3157 main_test_rfh()->PrepareForCommit();
3158 main_test_rfh()->SendNavigate(2, entry_id
, false, url2
);
3159 EXPECT_EQ(url2
, controller
.GetVisibleEntry()->GetURL());
3161 // Add a transient and go forward.
3162 transient_entry
= new NavigationEntryImpl
;
3163 transient_entry
->SetURL(transient_url
);
3164 controller
.SetTransientEntry(transient_entry
);
3165 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3166 EXPECT_TRUE(controller
.CanGoForward());
3167 controller
.GoForward();
3168 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3169 // We should have navigated, transient entry should be gone.
3170 EXPECT_FALSE(controller
.GetTransientEntry());
3171 EXPECT_EQ(url3
, controller
.GetPendingEntry()->GetURL());
3172 EXPECT_EQ(url2
, controller
.GetVisibleEntry()->GetURL());
3173 main_test_rfh()->PrepareForCommit();
3174 main_test_rfh()->SendNavigate(3, entry_id
, false, url3
);
3175 EXPECT_EQ(url3
, controller
.GetVisibleEntry()->GetURL());
3177 // Add a transient and do an in-page navigation, replacing the current entry.
3178 transient_entry
= new NavigationEntryImpl
;
3179 transient_entry
->SetURL(transient_url
);
3180 controller
.SetTransientEntry(transient_entry
);
3181 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3183 main_test_rfh()->SendRendererInitiatedNavigationRequest(url3_ref
, false);
3184 main_test_rfh()->PrepareForCommit();
3185 main_test_rfh()->SendNavigate(3, 0, false, url3_ref
);
3186 // Transient entry should be gone.
3187 EXPECT_FALSE(controller
.GetTransientEntry());
3188 EXPECT_EQ(url3_ref
, controller
.GetVisibleEntry()->GetURL());
3190 // Ensure the URLs are correct.
3191 EXPECT_EQ(controller
.GetEntryCount(), 5);
3192 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(), url0
);
3193 EXPECT_EQ(controller
.GetEntryAtIndex(1)->GetURL(), url1
);
3194 EXPECT_EQ(controller
.GetEntryAtIndex(2)->GetURL(), url2
);
3195 EXPECT_EQ(controller
.GetEntryAtIndex(3)->GetURL(), url3_ref
);
3196 EXPECT_EQ(controller
.GetEntryAtIndex(4)->GetURL(), url4
);
3199 // Test that Reload initiates a new navigation to a transient entry's URL.
3200 TEST_F(NavigationControllerTest
, ReloadTransient
) {
3201 NavigationControllerImpl
& controller
= controller_impl();
3202 const GURL
url0("http://foo/0");
3203 const GURL
url1("http://foo/1");
3204 const GURL
transient_url("http://foo/transient");
3206 // Load |url0|, and start a pending navigation to |url1|.
3208 url0
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3209 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3210 main_test_rfh()->PrepareForCommit();
3211 main_test_rfh()->SendNavigate(0, entry_id
, true, url0
);
3213 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3215 // A transient entry is added, interrupting the navigation.
3216 NavigationEntryImpl
* transient_entry
= new NavigationEntryImpl
;
3217 transient_entry
->SetURL(transient_url
);
3218 controller
.SetTransientEntry(transient_entry
);
3219 EXPECT_TRUE(controller
.GetTransientEntry());
3220 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3222 // The page is reloaded, which should remove the pending entry for |url1| and
3223 // the transient entry for |transient_url|, and start a navigation to
3225 controller
.Reload(true);
3226 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3227 EXPECT_FALSE(controller
.GetTransientEntry());
3228 EXPECT_TRUE(controller
.GetPendingEntry());
3229 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3230 ASSERT_EQ(controller
.GetEntryCount(), 1);
3231 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(), url0
);
3233 // Load of |transient_url| completes.
3234 main_test_rfh()->PrepareForCommit();
3235 main_test_rfh()->SendNavigate(1, entry_id
, true, transient_url
);
3236 ASSERT_EQ(controller
.GetEntryCount(), 2);
3237 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(), url0
);
3238 EXPECT_EQ(controller
.GetEntryAtIndex(1)->GetURL(), transient_url
);
3241 // Ensure that renderer initiated pending entries get replaced, so that we
3242 // don't show a stale virtual URL when a navigation commits.
3243 // See http://crbug.com/266922.
3244 TEST_F(NavigationControllerTest
, RendererInitiatedPendingEntries
) {
3245 NavigationControllerImpl
& controller
= controller_impl();
3246 Navigator
* navigator
=
3247 contents()->GetFrameTree()->root()->navigator();
3249 const GURL
url1("nonexistent:12121");
3250 const GURL
url1_fixed("http://nonexistent:12121/");
3251 const GURL
url2("http://foo");
3253 // We create pending entries for renderer-initiated navigations so that we
3254 // can show them in new tabs when it is safe.
3255 navigator
->DidStartProvisionalLoad(main_test_rfh(), url1
);
3257 // Simulate what happens if a BrowserURLHandler rewrites the URL, causing
3258 // the virtual URL to differ from the URL.
3259 controller
.GetPendingEntry()->SetURL(url1_fixed
);
3260 controller
.GetPendingEntry()->SetVirtualURL(url1
);
3262 EXPECT_EQ(url1_fixed
, controller
.GetPendingEntry()->GetURL());
3263 EXPECT_EQ(url1
, controller
.GetPendingEntry()->GetVirtualURL());
3264 EXPECT_TRUE(controller
.GetPendingEntry()->is_renderer_initiated());
3266 // If the user clicks another link, we should replace the pending entry.
3267 main_test_rfh()->SendRendererInitiatedNavigationRequest(url2
, false);
3268 main_test_rfh()->PrepareForCommit();
3269 navigator
->DidStartProvisionalLoad(main_test_rfh(), url2
);
3270 EXPECT_EQ(url2
, controller
.GetPendingEntry()->GetURL());
3271 EXPECT_EQ(url2
, controller
.GetPendingEntry()->GetVirtualURL());
3273 // Once it commits, the URL and virtual URL should reflect the actual page.
3274 main_test_rfh()->SendNavigate(0, 0, true, url2
);
3275 EXPECT_EQ(url2
, controller
.GetLastCommittedEntry()->GetURL());
3276 EXPECT_EQ(url2
, controller
.GetLastCommittedEntry()->GetVirtualURL());
3278 // We should not replace the pending entry for an error URL.
3279 navigator
->DidStartProvisionalLoad(main_test_rfh(), url1
);
3280 EXPECT_EQ(url1
, controller
.GetPendingEntry()->GetURL());
3281 navigator
->DidStartProvisionalLoad(main_test_rfh(),
3282 GURL(kUnreachableWebDataURL
));
3283 EXPECT_EQ(url1
, controller
.GetPendingEntry()->GetURL());
3285 // We should remember if the pending entry will replace the current one.
3286 // http://crbug.com/308444.
3287 navigator
->DidStartProvisionalLoad(main_test_rfh(), url1
);
3288 controller
.GetPendingEntry()->set_should_replace_entry(true);
3290 main_test_rfh()->SendRendererInitiatedNavigationRequest(url2
, false);
3291 main_test_rfh()->PrepareForCommit();
3292 navigator
->DidStartProvisionalLoad(main_test_rfh(), url2
);
3293 EXPECT_TRUE(controller
.GetPendingEntry()->should_replace_entry());
3294 main_test_rfh()->SendNavigate(0, 0, false, url2
);
3295 EXPECT_EQ(url2
, controller
.GetLastCommittedEntry()->GetURL());
3298 // Tests that the URLs for renderer-initiated navigations are not displayed to
3299 // the user until the navigation commits, to prevent URL spoof attacks.
3300 // See http://crbug.com/99016.
3301 TEST_F(NavigationControllerTest
, DontShowRendererURLUntilCommit
) {
3302 NavigationControllerImpl
& controller
= controller_impl();
3303 TestNotificationTracker notifications
;
3304 RegisterForAllNavNotifications(¬ifications
, &controller
);
3306 const GURL
url0("http://foo/0");
3307 const GURL
url1("http://foo/1");
3309 // For typed navigations (browser-initiated), both pending and visible entries
3310 // should update before commit.
3312 url0
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3313 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3314 EXPECT_EQ(url0
, controller
.GetPendingEntry()->GetURL());
3315 EXPECT_EQ(url0
, controller
.GetVisibleEntry()->GetURL());
3316 main_test_rfh()->PrepareForCommit();
3317 main_test_rfh()->SendNavigate(0, entry_id
, true, url0
);
3319 // For link clicks (renderer-initiated navigations), the pending entry should
3320 // update before commit but the visible should not.
3321 NavigationController::LoadURLParams
load_url_params(url1
);
3322 load_url_params
.is_renderer_initiated
= true;
3323 controller
.LoadURLWithParams(load_url_params
);
3324 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3325 EXPECT_EQ(url0
, controller
.GetVisibleEntry()->GetURL());
3326 EXPECT_EQ(url1
, controller
.GetPendingEntry()->GetURL());
3327 EXPECT_TRUE(controller
.GetPendingEntry()->is_renderer_initiated());
3329 // After commit, both visible should be updated, there should be no pending
3330 // entry, and we should no longer treat the entry as renderer-initiated.
3331 main_test_rfh()->PrepareForCommit();
3332 main_test_rfh()->SendNavigate(1, entry_id
, true, url1
);
3333 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
3334 EXPECT_FALSE(controller
.GetPendingEntry());
3335 EXPECT_FALSE(controller
.GetLastCommittedEntry()->is_renderer_initiated());
3337 notifications
.Reset();
3340 // Tests that the URLs for renderer-initiated navigations in new tabs are
3341 // displayed to the user before commit, as long as the initial about:blank
3342 // page has not been modified. If so, we must revert to showing about:blank.
3343 // See http://crbug.com/9682.
3344 TEST_F(NavigationControllerTest
, ShowRendererURLInNewTabUntilModified
) {
3345 NavigationControllerImpl
& controller
= controller_impl();
3346 TestNotificationTracker notifications
;
3347 RegisterForAllNavNotifications(¬ifications
, &controller
);
3349 const GURL
url("http://foo");
3351 // For renderer-initiated navigations in new tabs (with no committed entries),
3352 // we show the pending entry's URL as long as the about:blank page is not
3354 NavigationController::LoadURLParams
load_url_params(url
);
3355 load_url_params
.transition_type
= ui::PAGE_TRANSITION_LINK
;
3356 load_url_params
.is_renderer_initiated
= true;
3357 controller
.LoadURLWithParams(load_url_params
);
3358 EXPECT_EQ(url
, controller
.GetVisibleEntry()->GetURL());
3359 EXPECT_EQ(url
, controller
.GetPendingEntry()->GetURL());
3360 EXPECT_TRUE(controller
.GetPendingEntry()->is_renderer_initiated());
3361 EXPECT_TRUE(controller
.IsInitialNavigation());
3362 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3364 // There should be no title yet.
3365 EXPECT_TRUE(contents()->GetTitle().empty());
3367 // If something else modifies the contents of the about:blank page, then
3368 // we must revert to showing about:blank to avoid a URL spoof.
3369 main_test_rfh()->OnMessageReceived(FrameHostMsg_DidAccessInitialDocument(0));
3370 EXPECT_TRUE(contents()->HasAccessedInitialDocument());
3371 EXPECT_FALSE(controller
.GetVisibleEntry());
3372 EXPECT_EQ(url
, controller
.GetPendingEntry()->GetURL());
3374 notifications
.Reset();
3377 // Tests that the URLs for browser-initiated navigations in new tabs are
3378 // displayed to the user even after they fail, as long as the initial
3379 // about:blank page has not been modified. If so, we must revert to showing
3380 // about:blank. See http://crbug.com/355537.
3381 TEST_F(NavigationControllerTest
, ShowBrowserURLAfterFailUntilModified
) {
3382 NavigationControllerImpl
& controller
= controller_impl();
3383 TestNotificationTracker notifications
;
3384 RegisterForAllNavNotifications(¬ifications
, &controller
);
3386 const GURL
url("http://foo");
3388 // For browser-initiated navigations in new tabs (with no committed entries),
3389 // we show the pending entry's URL as long as the about:blank page is not
3390 // modified. This is possible in cases that the user types a URL into a popup
3391 // tab created with a slow URL.
3392 NavigationController::LoadURLParams
load_url_params(url
);
3393 load_url_params
.transition_type
= ui::PAGE_TRANSITION_TYPED
;
3394 load_url_params
.is_renderer_initiated
= false;
3395 controller
.LoadURLWithParams(load_url_params
);
3396 EXPECT_EQ(url
, controller
.GetVisibleEntry()->GetURL());
3397 EXPECT_EQ(url
, controller
.GetPendingEntry()->GetURL());
3398 EXPECT_FALSE(controller
.GetPendingEntry()->is_renderer_initiated());
3399 EXPECT_TRUE(controller
.IsInitialNavigation());
3400 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3402 // There should be no title yet.
3403 EXPECT_TRUE(contents()->GetTitle().empty());
3405 // Suppose it aborts before committing, if it's a 204 or download or due to a
3406 // stop or a new navigation from the user. The URL should remain visible.
3407 FrameHostMsg_DidFailProvisionalLoadWithError_Params params
;
3408 params
.error_code
= net::ERR_ABORTED
;
3409 params
.error_description
= base::string16();
3411 params
.showing_repost_interstitial
= false;
3412 main_test_rfh()->OnMessageReceived(
3413 FrameHostMsg_DidFailProvisionalLoadWithError(0, params
));
3414 contents()->SetIsLoading(false, true, NULL
);
3415 EXPECT_EQ(url
, controller
.GetVisibleEntry()->GetURL());
3417 // If something else later modifies the contents of the about:blank page, then
3418 // we must revert to showing about:blank to avoid a URL spoof.
3419 main_test_rfh()->OnMessageReceived(FrameHostMsg_DidAccessInitialDocument(0));
3420 EXPECT_TRUE(contents()->HasAccessedInitialDocument());
3421 EXPECT_FALSE(controller
.GetVisibleEntry());
3422 EXPECT_FALSE(controller
.GetPendingEntry());
3424 notifications
.Reset();
3427 // Tests that the URLs for renderer-initiated navigations in new tabs are
3428 // displayed to the user even after they fail, as long as the initial
3429 // about:blank page has not been modified. If so, we must revert to showing
3430 // about:blank. See http://crbug.com/355537.
3431 TEST_F(NavigationControllerTest
, ShowRendererURLAfterFailUntilModified
) {
3432 NavigationControllerImpl
& controller
= controller_impl();
3433 TestNotificationTracker notifications
;
3434 RegisterForAllNavNotifications(¬ifications
, &controller
);
3436 const GURL
url("http://foo");
3438 // For renderer-initiated navigations in new tabs (with no committed entries),
3439 // we show the pending entry's URL as long as the about:blank page is not
3441 NavigationController::LoadURLParams
load_url_params(url
);
3442 load_url_params
.transition_type
= ui::PAGE_TRANSITION_LINK
;
3443 load_url_params
.is_renderer_initiated
= true;
3444 controller
.LoadURLWithParams(load_url_params
);
3445 EXPECT_EQ(url
, controller
.GetVisibleEntry()->GetURL());
3446 EXPECT_EQ(url
, controller
.GetPendingEntry()->GetURL());
3447 EXPECT_TRUE(controller
.GetPendingEntry()->is_renderer_initiated());
3448 EXPECT_TRUE(controller
.IsInitialNavigation());
3449 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3451 // There should be no title yet.
3452 EXPECT_TRUE(contents()->GetTitle().empty());
3454 // Suppose it aborts before committing, if it's a 204 or download or due to a
3455 // stop or a new navigation from the user. The URL should remain visible.
3456 FrameHostMsg_DidFailProvisionalLoadWithError_Params params
;
3457 params
.error_code
= net::ERR_ABORTED
;
3458 params
.error_description
= base::string16();
3460 params
.showing_repost_interstitial
= false;
3461 main_test_rfh()->OnMessageReceived(
3462 FrameHostMsg_DidFailProvisionalLoadWithError(0, params
));
3463 EXPECT_EQ(url
, controller
.GetVisibleEntry()->GetURL());
3465 // If something else later modifies the contents of the about:blank page, then
3466 // we must revert to showing about:blank to avoid a URL spoof.
3467 main_test_rfh()->OnMessageReceived(FrameHostMsg_DidAccessInitialDocument(0));
3468 EXPECT_TRUE(contents()->HasAccessedInitialDocument());
3469 EXPECT_FALSE(controller
.GetVisibleEntry());
3470 EXPECT_EQ(url
, controller
.GetPendingEntry()->GetURL());
3472 notifications
.Reset();
3475 TEST_F(NavigationControllerTest
, DontShowRendererURLInNewTabAfterCommit
) {
3476 NavigationControllerImpl
& controller
= controller_impl();
3477 TestNotificationTracker notifications
;
3478 RegisterForAllNavNotifications(¬ifications
, &controller
);
3480 const GURL
url1("http://foo/eh");
3481 const GURL
url2("http://foo/bee");
3483 // For renderer-initiated navigations in new tabs (with no committed entries),
3484 // we show the pending entry's URL as long as the about:blank page is not
3486 NavigationController::LoadURLParams
load_url_params(url1
);
3487 load_url_params
.transition_type
= ui::PAGE_TRANSITION_LINK
;
3488 load_url_params
.is_renderer_initiated
= true;
3489 controller
.LoadURLWithParams(load_url_params
);
3490 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3491 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
3492 EXPECT_TRUE(controller
.GetPendingEntry()->is_renderer_initiated());
3493 EXPECT_TRUE(controller
.IsInitialNavigation());
3494 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3496 // Simulate a commit and then starting a new pending navigation.
3497 main_test_rfh()->PrepareForCommit();
3498 main_test_rfh()->SendNavigate(0, entry_id
, true, url1
);
3499 NavigationController::LoadURLParams
load_url2_params(url2
);
3500 load_url2_params
.transition_type
= ui::PAGE_TRANSITION_LINK
;
3501 load_url2_params
.is_renderer_initiated
= true;
3502 controller
.LoadURLWithParams(load_url2_params
);
3504 // We should not consider this an initial navigation, and thus should
3505 // not show the pending URL.
3506 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3507 EXPECT_FALSE(controller
.IsInitialNavigation());
3508 EXPECT_TRUE(controller
.GetVisibleEntry());
3509 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
3511 notifications
.Reset();
3514 // Tests that IsInPageNavigation returns appropriate results. Prevents
3515 // regression for bug 1126349.
3516 TEST_F(NavigationControllerTest
, IsInPageNavigation
) {
3517 NavigationControllerImpl
& controller
= controller_impl();
3518 const GURL
url("http://www.google.com/home.html");
3520 // If the renderer claims it performed an in-page navigation from
3521 // about:blank, trust the renderer.
3522 // This can happen when an iframe is created and populated via
3523 // document.write(), then tries to perform a fragment navigation.
3524 // TODO(japhet): We should only trust the renderer if the about:blank
3525 // was the first document in the given frame, but we don't have enough
3526 // information to identify that case currently.
3527 const GURL
blank_url(url::kAboutBlankURL
);
3528 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, blank_url
);
3529 EXPECT_TRUE(controller
.IsURLInPageNavigation(url
, true,
3532 // Navigate to URL with no refs.
3533 main_test_rfh()->NavigateAndCommitRendererInitiated(0, false, url
);
3535 // Reloading the page is not an in-page navigation.
3536 EXPECT_FALSE(controller
.IsURLInPageNavigation(url
, false, main_test_rfh()));
3537 const GURL
other_url("http://www.google.com/add.html");
3538 EXPECT_FALSE(controller
.IsURLInPageNavigation(other_url
, false,
3540 const GURL
url_with_ref("http://www.google.com/home.html#my_ref");
3541 EXPECT_TRUE(controller
.IsURLInPageNavigation(url_with_ref
, true,
3544 // Navigate to URL with refs.
3545 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url_with_ref
);
3547 // Reloading the page is not an in-page navigation.
3548 EXPECT_FALSE(controller
.IsURLInPageNavigation(url_with_ref
, false,
3550 EXPECT_FALSE(controller
.IsURLInPageNavigation(url
, false,
3552 EXPECT_FALSE(controller
.IsURLInPageNavigation(other_url
, false,
3554 const GURL
other_url_with_ref("http://www.google.com/home.html#my_other_ref");
3555 EXPECT_TRUE(controller
.IsURLInPageNavigation(other_url_with_ref
, true,
3558 // Going to the same url again will be considered in-page
3559 // if the renderer says it is even if the navigation type isn't IN_PAGE.
3560 EXPECT_TRUE(controller
.IsURLInPageNavigation(url_with_ref
, true,
3563 // Going back to the non ref url will be considered in-page if the navigation
3565 EXPECT_TRUE(controller
.IsURLInPageNavigation(url
, true,
3568 // If the renderer says this is a same-origin in-page navigation, believe it.
3569 // This is the pushState/replaceState case.
3570 EXPECT_TRUE(controller
.IsURLInPageNavigation(other_url
, true,
3573 // Test allow_universal_access_from_file_urls flag.
3574 const GURL
different_origin_url("http://www.example.com");
3575 MockRenderProcessHost
* rph
= main_test_rfh()->GetProcess();
3576 WebPreferences prefs
= test_rvh()->GetWebkitPreferences();
3577 prefs
.allow_universal_access_from_file_urls
= true;
3578 test_rvh()->UpdateWebkitPreferences(prefs
);
3579 prefs
= test_rvh()->GetWebkitPreferences();
3580 EXPECT_TRUE(prefs
.allow_universal_access_from_file_urls
);
3581 // Allow in page navigation if existing URL is file scheme.
3582 const GURL
file_url("file:///foo/index.html");
3583 main_test_rfh()->NavigateAndCommitRendererInitiated(0, false, file_url
);
3584 EXPECT_EQ(0, rph
->bad_msg_count());
3585 EXPECT_TRUE(controller
.IsURLInPageNavigation(different_origin_url
, true,
3587 EXPECT_EQ(0, rph
->bad_msg_count());
3588 // Don't honor allow_universal_access_from_file_urls if existing URL is
3590 main_test_rfh()->NavigateAndCommitRendererInitiated(0, false, url
);
3591 EXPECT_FALSE(controller
.IsURLInPageNavigation(different_origin_url
, true,
3593 EXPECT_EQ(1, rph
->bad_msg_count());
3595 // Remove allow_universal_access_from_file_urls flag.
3596 prefs
.allow_universal_access_from_file_urls
= false;
3597 test_rvh()->UpdateWebkitPreferences(prefs
);
3598 prefs
= test_rvh()->GetWebkitPreferences();
3599 EXPECT_FALSE(prefs
.allow_universal_access_from_file_urls
);
3601 // Don't believe the renderer if it claims a cross-origin navigation is
3603 EXPECT_EQ(1, rph
->bad_msg_count());
3604 EXPECT_FALSE(controller
.IsURLInPageNavigation(different_origin_url
, true,
3606 EXPECT_EQ(2, rph
->bad_msg_count());
3609 // Some pages can have subframes with the same base URL (minus the reference) as
3610 // the main page. Even though this is hard, it can happen, and we don't want
3611 // these subframe navigations to affect the toplevel document. They should
3612 // instead be ignored. http://crbug.com/5585
3613 TEST_F(NavigationControllerTest
, SameSubframe
) {
3614 NavigationControllerImpl
& controller
= controller_impl();
3615 // Navigate the main frame.
3616 const GURL
url("http://www.google.com/");
3617 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url
);
3619 // We should be at the first navigation entry.
3620 EXPECT_EQ(controller
.GetEntryCount(), 1);
3621 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
3623 // Add and navigate a subframe that would normally count as in-page.
3624 main_test_rfh()->OnCreateChildFrame(
3625 MSG_ROUTING_NONE
, blink::WebTreeScopeType::Document
, std::string(),
3626 blink::WebSandboxFlags::None
);
3627 RenderFrameHostImpl
* subframe
=
3628 contents()->GetFrameTree()->root()->child_at(0)->current_frame_host();
3629 const GURL
subframe_url("http://www.google.com/#");
3630 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
3632 params
.nav_entry_id
= 0;
3633 params
.did_create_new_entry
= false;
3634 params
.url
= subframe_url
;
3635 params
.transition
= ui::PAGE_TRANSITION_AUTO_SUBFRAME
;
3636 params
.should_update_history
= false;
3637 params
.gesture
= NavigationGestureAuto
;
3638 params
.is_post
= false;
3639 params
.page_state
= PageState::CreateFromURL(subframe_url
);
3640 LoadCommittedDetails details
;
3641 EXPECT_FALSE(controller
.RendererDidNavigate(subframe
, params
, &details
));
3643 // Nothing should have changed.
3644 EXPECT_EQ(controller
.GetEntryCount(), 1);
3645 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
3648 // Make sure that on cloning a WebContentsImpl and going back needs_reload is
3650 TEST_F(NavigationControllerTest
, CloneAndGoBack
) {
3651 NavigationControllerImpl
& controller
= controller_impl();
3652 const GURL
url1("http://foo1");
3653 const GURL
url2("http://foo2");
3654 const base::string16
title(base::ASCIIToUTF16("Title"));
3656 NavigateAndCommit(url1
);
3657 controller
.GetVisibleEntry()->SetTitle(title
);
3658 NavigateAndCommit(url2
);
3660 scoped_ptr
<WebContents
> clone(controller
.GetWebContents()->Clone());
3662 ASSERT_EQ(2, clone
->GetController().GetEntryCount());
3663 EXPECT_TRUE(clone
->GetController().NeedsReload());
3664 clone
->GetController().GoBack();
3665 // Navigating back should have triggered needs_reload_ to go false.
3666 EXPECT_FALSE(clone
->GetController().NeedsReload());
3668 // Ensure that the pending URL and its title are visible.
3669 EXPECT_EQ(url1
, clone
->GetController().GetVisibleEntry()->GetURL());
3670 EXPECT_EQ(title
, clone
->GetTitle());
3673 // Make sure that reloading a cloned tab doesn't change its pending entry index.
3674 // See http://crbug.com/234491.
3675 TEST_F(NavigationControllerTest
, CloneAndReload
) {
3676 NavigationControllerImpl
& controller
= controller_impl();
3677 const GURL
url1("http://foo1");
3678 const GURL
url2("http://foo2");
3679 const base::string16
title(base::ASCIIToUTF16("Title"));
3681 NavigateAndCommit(url1
);
3682 controller
.GetVisibleEntry()->SetTitle(title
);
3683 NavigateAndCommit(url2
);
3685 scoped_ptr
<WebContents
> clone(controller
.GetWebContents()->Clone());
3686 clone
->GetController().LoadIfNecessary();
3688 ASSERT_EQ(2, clone
->GetController().GetEntryCount());
3689 EXPECT_EQ(1, clone
->GetController().GetPendingEntryIndex());
3691 clone
->GetController().Reload(true);
3692 EXPECT_EQ(1, clone
->GetController().GetPendingEntryIndex());
3695 // Make sure that cloning a WebContentsImpl doesn't copy interstitials.
3696 TEST_F(NavigationControllerTest
, CloneOmitsInterstitials
) {
3697 NavigationControllerImpl
& controller
= controller_impl();
3698 const GURL
url1("http://foo1");
3699 const GURL
url2("http://foo2");
3701 NavigateAndCommit(url1
);
3702 NavigateAndCommit(url2
);
3704 // Add an interstitial entry. Should be deleted with controller.
3705 NavigationEntryImpl
* interstitial_entry
= new NavigationEntryImpl();
3706 interstitial_entry
->set_page_type(PAGE_TYPE_INTERSTITIAL
);
3707 controller
.SetTransientEntry(interstitial_entry
);
3709 scoped_ptr
<WebContents
> clone(controller
.GetWebContents()->Clone());
3711 ASSERT_EQ(2, clone
->GetController().GetEntryCount());
3714 // Test requesting and triggering a lazy reload.
3715 TEST_F(NavigationControllerTest
, LazyReload
) {
3716 NavigationControllerImpl
& controller
= controller_impl();
3717 const GURL
url("http://foo");
3718 NavigateAndCommit(url
);
3719 ASSERT_FALSE(controller
.NeedsReload());
3720 EXPECT_NE(ui::PAGE_TRANSITION_RELOAD
,
3721 controller
.GetLastCommittedEntry()->GetTransitionType());
3723 // Request a reload to happen when the controller becomes active (e.g. after
3724 // the renderer gets killed in background on Android).
3725 controller
.SetNeedsReload();
3726 ASSERT_TRUE(controller
.NeedsReload());
3727 EXPECT_EQ(ui::PAGE_TRANSITION_RELOAD
,
3728 controller
.GetLastCommittedEntry()->GetTransitionType());
3730 // Set the controller as active, triggering the requested reload.
3731 controller
.SetActive(true);
3732 ASSERT_FALSE(controller
.NeedsReload());
3733 EXPECT_EQ(ui::PAGE_TRANSITION_RELOAD
,
3734 controller
.GetPendingEntry()->GetTransitionType());
3737 // Test requesting and triggering a lazy reload without any committed entry.
3738 TEST_F(NavigationControllerTest
, LazyReloadWithoutCommittedEntry
) {
3739 NavigationControllerImpl
& controller
= controller_impl();
3740 const GURL
url("http://foo");
3741 controller
.LoadURL(url
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3742 ASSERT_FALSE(controller
.NeedsReload());
3743 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED
,
3744 controller
.GetPendingEntry()->GetTransitionType());
3746 // Request a reload to happen when the controller becomes active (e.g. after
3747 // the renderer gets killed in background on Android).
3748 controller
.SetNeedsReload();
3749 ASSERT_TRUE(controller
.NeedsReload());
3750 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED
,
3751 controller
.GetPendingEntry()->GetTransitionType());
3753 // Set the controller as active, triggering the requested reload.
3754 controller
.SetActive(true);
3755 ASSERT_FALSE(controller
.NeedsReload());
3756 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED
,
3757 controller
.GetPendingEntry()->GetTransitionType());
3760 // Tests a subframe navigation while a toplevel navigation is pending.
3761 // http://crbug.com/43967
3762 TEST_F(NavigationControllerTest
, SubframeWhilePending
) {
3763 NavigationControllerImpl
& controller
= controller_impl();
3764 // Load the first page.
3765 const GURL
url1("http://foo/");
3766 NavigateAndCommit(url1
);
3768 // Now start a pending load to a totally different page, but don't commit it.
3769 const GURL
url2("http://bar/");
3771 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3773 // Send a subframe update from the first page, as if one had just
3774 // automatically loaded. Auto subframes don't increment the page ID.
3775 main_test_rfh()->OnCreateChildFrame(
3776 MSG_ROUTING_NONE
, blink::WebTreeScopeType::Document
, std::string(),
3777 blink::WebSandboxFlags::None
);
3778 RenderFrameHostImpl
* subframe
=
3779 contents()->GetFrameTree()->root()->child_at(0)->current_frame_host();
3780 const GURL
url1_sub("http://foo/subframe");
3781 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
3782 params
.page_id
= controller
.GetLastCommittedEntry()->GetPageID();
3783 params
.nav_entry_id
= 0;
3784 params
.did_create_new_entry
= false;
3785 params
.url
= url1_sub
;
3786 params
.transition
= ui::PAGE_TRANSITION_AUTO_SUBFRAME
;
3787 params
.should_update_history
= false;
3788 params
.gesture
= NavigationGestureAuto
;
3789 params
.is_post
= false;
3790 params
.page_state
= PageState::CreateFromURL(url1_sub
);
3791 LoadCommittedDetails details
;
3793 // This should return false meaning that nothing was actually updated.
3794 EXPECT_FALSE(controller
.RendererDidNavigate(subframe
, params
, &details
));
3796 // The notification should have updated the last committed one, and not
3797 // the pending load.
3798 EXPECT_EQ(url1
, controller
.GetLastCommittedEntry()->GetURL());
3800 // The active entry should be unchanged by the subframe load.
3801 EXPECT_EQ(url2
, controller
.GetVisibleEntry()->GetURL());
3804 // Test CopyStateFrom with 2 urls, the first selected and nothing in the target.
3805 TEST_F(NavigationControllerTest
, CopyStateFrom
) {
3806 NavigationControllerImpl
& controller
= controller_impl();
3807 const GURL
url1("http://foo1");
3808 const GURL
url2("http://foo2");
3810 NavigateAndCommit(url1
);
3811 NavigateAndCommit(url2
);
3812 controller
.GoBack();
3813 contents()->CommitPendingNavigation();
3815 scoped_ptr
<TestWebContents
> other_contents(
3816 static_cast<TestWebContents
*>(CreateTestWebContents()));
3817 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
3818 other_controller
.CopyStateFrom(controller
);
3820 // other_controller should now contain 2 urls.
3821 ASSERT_EQ(2, other_controller
.GetEntryCount());
3822 // We should be looking at the first one.
3823 ASSERT_EQ(0, other_controller
.GetCurrentEntryIndex());
3825 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
3826 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(0)->GetPageID());
3827 EXPECT_EQ(url2
, other_controller
.GetEntryAtIndex(1)->GetURL());
3828 // This is a different site than url1, so the IDs start again at 0.
3829 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(1)->GetPageID());
3831 // The max page ID map should be copied over and updated with the max page ID
3832 // from the current tab.
3833 SiteInstance
* instance1
=
3834 other_controller
.GetEntryAtIndex(0)->site_instance();
3835 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
3837 // Ensure the SessionStorageNamespaceMaps are the same size and have
3838 // the same partitons loaded.
3840 // TODO(ajwong): We should load a url from a different partition earlier
3841 // to make sure this map has more than one entry.
3842 const SessionStorageNamespaceMap
& session_storage_namespace_map
=
3843 controller
.GetSessionStorageNamespaceMap();
3844 const SessionStorageNamespaceMap
& other_session_storage_namespace_map
=
3845 other_controller
.GetSessionStorageNamespaceMap();
3846 EXPECT_EQ(session_storage_namespace_map
.size(),
3847 other_session_storage_namespace_map
.size());
3848 for (SessionStorageNamespaceMap::const_iterator it
=
3849 session_storage_namespace_map
.begin();
3850 it
!= session_storage_namespace_map
.end();
3852 SessionStorageNamespaceMap::const_iterator other
=
3853 other_session_storage_namespace_map
.find(it
->first
);
3854 EXPECT_TRUE(other
!= other_session_storage_namespace_map
.end());
3858 // Tests CopyStateFromAndPrune with 2 urls in source, 1 in dest.
3859 TEST_F(NavigationControllerTest
, CopyStateFromAndPrune
) {
3860 NavigationControllerImpl
& controller
= controller_impl();
3861 const GURL
url1("http://foo/1");
3862 const GURL
url2("http://foo/2");
3863 const GURL
url3("http://foo/3");
3865 NavigateAndCommit(url1
);
3866 NavigateAndCommit(url2
);
3868 // First two entries should have the same SiteInstance.
3869 SiteInstance
* instance1
= controller
.GetEntryAtIndex(0)->site_instance();
3870 SiteInstance
* instance2
= controller
.GetEntryAtIndex(1)->site_instance();
3871 EXPECT_EQ(instance1
, instance2
);
3872 EXPECT_EQ(0, controller
.GetEntryAtIndex(0)->GetPageID());
3873 EXPECT_EQ(1, controller
.GetEntryAtIndex(1)->GetPageID());
3874 EXPECT_EQ(1, contents()->GetMaxPageIDForSiteInstance(instance1
));
3876 scoped_ptr
<TestWebContents
> other_contents(
3877 static_cast<TestWebContents
*>(CreateTestWebContents()));
3878 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
3879 other_contents
->NavigateAndCommit(url3
);
3880 other_contents
->ExpectSetHistoryOffsetAndLength(2, 3);
3881 other_controller
.CopyStateFromAndPrune(&controller
, false);
3883 // other_controller should now contain the 3 urls: url1, url2 and url3.
3885 ASSERT_EQ(3, other_controller
.GetEntryCount());
3887 ASSERT_EQ(2, other_controller
.GetCurrentEntryIndex());
3889 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
3890 EXPECT_EQ(url2
, other_controller
.GetEntryAtIndex(1)->GetURL());
3891 EXPECT_EQ(url3
, other_controller
.GetEntryAtIndex(2)->GetURL());
3892 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(0)->GetPageID());
3893 EXPECT_EQ(1, other_controller
.GetEntryAtIndex(1)->GetPageID());
3894 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(2)->GetPageID());
3896 // A new SiteInstance in a different BrowsingInstance should be used for the
3898 SiteInstance
* instance3
=
3899 other_controller
.GetEntryAtIndex(2)->site_instance();
3900 EXPECT_NE(instance3
, instance1
);
3901 EXPECT_FALSE(instance3
->IsRelatedSiteInstance(instance1
));
3903 // The max page ID map should be copied over and updated with the max page ID
3904 // from the current tab.
3905 EXPECT_EQ(1, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
3906 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance3
));
3909 // Test CopyStateFromAndPrune with 2 urls, the first selected and 1 entry in
3911 TEST_F(NavigationControllerTest
, CopyStateFromAndPrune2
) {
3912 NavigationControllerImpl
& controller
= controller_impl();
3913 const GURL
url1("http://foo1");
3914 const GURL
url2("http://foo2");
3915 const GURL
url3("http://foo3");
3917 NavigateAndCommit(url1
);
3918 NavigateAndCommit(url2
);
3919 controller
.GoBack();
3920 contents()->CommitPendingNavigation();
3922 scoped_ptr
<TestWebContents
> other_contents(
3923 static_cast<TestWebContents
*>(CreateTestWebContents()));
3924 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
3925 other_contents
->NavigateAndCommit(url3
);
3926 other_contents
->ExpectSetHistoryOffsetAndLength(1, 2);
3927 other_controller
.CopyStateFromAndPrune(&controller
, false);
3929 // other_controller should now contain: url1, url3
3931 ASSERT_EQ(2, other_controller
.GetEntryCount());
3932 ASSERT_EQ(1, other_controller
.GetCurrentEntryIndex());
3934 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
3935 EXPECT_EQ(url3
, other_controller
.GetEntryAtIndex(1)->GetURL());
3936 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(1)->GetPageID());
3938 // The max page ID map should be copied over and updated with the max page ID
3939 // from the current tab.
3940 SiteInstance
* instance1
=
3941 other_controller
.GetEntryAtIndex(1)->site_instance();
3942 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
3945 // Test CopyStateFromAndPrune with 2 urls, the last selected and 2 entries in
3947 TEST_F(NavigationControllerTest
, CopyStateFromAndPrune3
) {
3948 NavigationControllerImpl
& controller
= controller_impl();
3949 const GURL
url1("http://foo1");
3950 const GURL
url2("http://foo2");
3951 const GURL
url3("http://foo3");
3952 const GURL
url4("http://foo4");
3954 NavigateAndCommit(url1
);
3955 NavigateAndCommit(url2
);
3957 scoped_ptr
<TestWebContents
> other_contents(
3958 static_cast<TestWebContents
*>(CreateTestWebContents()));
3959 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
3960 other_contents
->NavigateAndCommit(url3
);
3961 other_contents
->NavigateAndCommit(url4
);
3962 other_contents
->ExpectSetHistoryOffsetAndLength(2, 3);
3963 other_controller
.CopyStateFromAndPrune(&controller
, false);
3965 // other_controller should now contain: url1, url2, url4
3967 ASSERT_EQ(3, other_controller
.GetEntryCount());
3968 ASSERT_EQ(2, other_controller
.GetCurrentEntryIndex());
3970 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
3971 EXPECT_EQ(url2
, other_controller
.GetEntryAtIndex(1)->GetURL());
3972 EXPECT_EQ(url4
, other_controller
.GetEntryAtIndex(2)->GetURL());
3974 // The max page ID map should be copied over and updated with the max page ID
3975 // from the current tab.
3976 SiteInstance
* instance1
=
3977 other_controller
.GetEntryAtIndex(2)->site_instance();
3978 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
3981 // Test CopyStateFromAndPrune with 2 urls, 2 entries in the target, with
3982 // not the last entry selected in the target.
3983 TEST_F(NavigationControllerTest
, CopyStateFromAndPruneNotLast
) {
3984 NavigationControllerImpl
& controller
= controller_impl();
3985 const GURL
url1("http://foo1");
3986 const GURL
url2("http://foo2");
3987 const GURL
url3("http://foo3");
3988 const GURL
url4("http://foo4");
3990 NavigateAndCommit(url1
);
3991 NavigateAndCommit(url2
);
3993 scoped_ptr
<TestWebContents
> other_contents(
3994 static_cast<TestWebContents
*>(CreateTestWebContents()));
3995 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
3996 other_contents
->NavigateAndCommit(url3
);
3997 other_contents
->NavigateAndCommit(url4
);
3998 other_controller
.GoBack();
3999 other_contents
->CommitPendingNavigation();
4000 other_contents
->ExpectSetHistoryOffsetAndLength(2, 3);
4001 other_controller
.CopyStateFromAndPrune(&controller
, false);
4003 // other_controller should now contain: url1, url2, url3
4005 ASSERT_EQ(3, other_controller
.GetEntryCount());
4006 ASSERT_EQ(2, other_controller
.GetCurrentEntryIndex());
4008 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
4009 EXPECT_EQ(url2
, other_controller
.GetEntryAtIndex(1)->GetURL());
4010 EXPECT_EQ(url3
, other_controller
.GetEntryAtIndex(2)->GetURL());
4012 // The max page ID map should be copied over and updated with the max page ID
4013 // from the current tab.
4014 SiteInstance
* instance1
=
4015 other_controller
.GetEntryAtIndex(2)->site_instance();
4016 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
4019 // Test CopyStateFromAndPrune with 2 urls, the first selected and 1 entry plus
4020 // a pending entry in the target.
4021 TEST_F(NavigationControllerTest
, CopyStateFromAndPruneTargetPending
) {
4022 NavigationControllerImpl
& controller
= controller_impl();
4023 const GURL
url1("http://foo1");
4024 const GURL
url2("http://foo2");
4025 const GURL
url3("http://foo3");
4026 const GURL
url4("http://foo4");
4028 NavigateAndCommit(url1
);
4029 NavigateAndCommit(url2
);
4030 controller
.GoBack();
4031 contents()->CommitPendingNavigation();
4033 scoped_ptr
<TestWebContents
> other_contents(
4034 static_cast<TestWebContents
*>(CreateTestWebContents()));
4035 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
4036 other_contents
->NavigateAndCommit(url3
);
4037 other_controller
.LoadURL(
4038 url4
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
4039 other_contents
->ExpectSetHistoryOffsetAndLength(1, 2);
4040 other_controller
.CopyStateFromAndPrune(&controller
, false);
4042 // other_controller should now contain url1, url3, and a pending entry
4045 ASSERT_EQ(2, other_controller
.GetEntryCount());
4046 EXPECT_EQ(1, other_controller
.GetCurrentEntryIndex());
4048 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
4049 EXPECT_EQ(url3
, other_controller
.GetEntryAtIndex(1)->GetURL());
4051 // And there should be a pending entry for url4.
4052 ASSERT_TRUE(other_controller
.GetPendingEntry());
4053 EXPECT_EQ(url4
, other_controller
.GetPendingEntry()->GetURL());
4055 // The max page ID map should be copied over and updated with the max page ID
4056 // from the current tab.
4057 SiteInstance
* instance1
=
4058 other_controller
.GetEntryAtIndex(0)->site_instance();
4059 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
4062 // Test CopyStateFromAndPrune with 1 url in the source, 1 entry and a pending
4063 // client redirect entry (with the same page ID) in the target. This used to
4064 // crash because the last committed entry would be pruned but max_page_id
4065 // remembered the page ID (http://crbug.com/234809).
4066 TEST_F(NavigationControllerTest
, CopyStateFromAndPruneTargetPending2
) {
4067 NavigationControllerImpl
& controller
= controller_impl();
4068 const GURL
url1("http://foo1");
4069 const GURL
url2a("http://foo2/a");
4070 const GURL
url2b("http://foo2/b");
4072 NavigateAndCommit(url1
);
4074 scoped_ptr
<TestWebContents
> other_contents(
4075 static_cast<TestWebContents
*>(CreateTestWebContents()));
4076 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
4077 other_contents
->NavigateAndCommit(url2a
);
4078 // Simulate a client redirect, which has the same page ID as entry 2a.
4079 other_controller
.LoadURL(
4080 url2b
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
4081 NavigationEntry
* entry
= other_controller
.GetPendingEntry();
4082 entry
->SetPageID(other_controller
.GetLastCommittedEntry()->GetPageID());
4084 other_contents
->ExpectSetHistoryOffsetAndLength(1, 2);
4085 other_controller
.CopyStateFromAndPrune(&controller
, false);
4087 // other_controller should now contain url1, url2a, and a pending entry
4090 ASSERT_EQ(2, other_controller
.GetEntryCount());
4091 EXPECT_EQ(1, other_controller
.GetCurrentEntryIndex());
4093 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
4094 EXPECT_EQ(url2a
, other_controller
.GetEntryAtIndex(1)->GetURL());
4096 // And there should be a pending entry for url4.
4097 ASSERT_TRUE(other_controller
.GetPendingEntry());
4098 EXPECT_EQ(url2b
, other_controller
.GetPendingEntry()->GetURL());
4100 // Let the pending entry commit.
4101 other_contents
->TestDidNavigate(other_contents
->GetMainFrame(),
4102 entry
->GetPageID(), 0, false, url2b
,
4103 ui::PAGE_TRANSITION_LINK
);
4105 // The max page ID map should be copied over and updated with the max page ID
4106 // from the current tab.
4107 SiteInstance
* instance1
=
4108 other_controller
.GetEntryAtIndex(1)->site_instance();
4109 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
4112 // Test CopyStateFromAndPrune with 2 urls, a back navigation pending in the
4113 // source, and 1 entry in the target. The back pending entry should be ignored.
4114 TEST_F(NavigationControllerTest
, CopyStateFromAndPruneSourcePending
) {
4115 NavigationControllerImpl
& controller
= controller_impl();
4116 const GURL
url1("http://foo1");
4117 const GURL
url2("http://foo2");
4118 const GURL
url3("http://foo3");
4120 NavigateAndCommit(url1
);
4121 NavigateAndCommit(url2
);
4122 controller
.GoBack();
4124 scoped_ptr
<TestWebContents
> other_contents(
4125 static_cast<TestWebContents
*>(CreateTestWebContents()));
4126 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
4127 other_contents
->NavigateAndCommit(url3
);
4128 other_contents
->ExpectSetHistoryOffsetAndLength(2, 3);
4129 other_controller
.CopyStateFromAndPrune(&controller
, false);
4131 // other_controller should now contain: url1, url2, url3
4133 ASSERT_EQ(3, other_controller
.GetEntryCount());
4134 ASSERT_EQ(2, other_controller
.GetCurrentEntryIndex());
4136 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
4137 EXPECT_EQ(url2
, other_controller
.GetEntryAtIndex(1)->GetURL());
4138 EXPECT_EQ(url3
, other_controller
.GetEntryAtIndex(2)->GetURL());
4139 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(2)->GetPageID());
4141 // The max page ID map should be copied over and updated with the max page ID
4142 // from the current tab.
4143 SiteInstance
* instance1
=
4144 other_controller
.GetEntryAtIndex(2)->site_instance();
4145 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
4148 // Tests CopyStateFromAndPrune with 3 urls in source, 1 in dest,
4149 // when the max entry count is 3. We should prune one entry.
4150 TEST_F(NavigationControllerTest
, CopyStateFromAndPruneMaxEntries
) {
4151 NavigationControllerImpl
& controller
= controller_impl();
4152 size_t original_count
= NavigationControllerImpl::max_entry_count();
4153 const int kMaxEntryCount
= 3;
4155 NavigationControllerImpl::set_max_entry_count_for_testing(kMaxEntryCount
);
4157 const GURL
url1("http://foo/1");
4158 const GURL
url2("http://foo/2");
4159 const GURL
url3("http://foo/3");
4160 const GURL
url4("http://foo/4");
4162 // Create a PrunedListener to observe prune notifications.
4163 PrunedListener
listener(&controller
);
4165 NavigateAndCommit(url1
);
4166 NavigateAndCommit(url2
);
4167 NavigateAndCommit(url3
);
4169 scoped_ptr
<TestWebContents
> other_contents(
4170 static_cast<TestWebContents
*>(CreateTestWebContents()));
4171 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
4172 other_contents
->NavigateAndCommit(url4
);
4173 other_contents
->ExpectSetHistoryOffsetAndLength(2, 3);
4174 other_controller
.CopyStateFromAndPrune(&controller
, false);
4176 // We should have received a pruned notification.
4177 EXPECT_EQ(1, listener
.notification_count_
);
4178 EXPECT_TRUE(listener
.details_
.from_front
);
4179 EXPECT_EQ(1, listener
.details_
.count
);
4181 // other_controller should now contain only 3 urls: url2, url3 and url4.
4183 ASSERT_EQ(3, other_controller
.GetEntryCount());
4185 ASSERT_EQ(2, other_controller
.GetCurrentEntryIndex());
4187 EXPECT_EQ(url2
, other_controller
.GetEntryAtIndex(0)->GetURL());
4188 EXPECT_EQ(url3
, other_controller
.GetEntryAtIndex(1)->GetURL());
4189 EXPECT_EQ(url4
, other_controller
.GetEntryAtIndex(2)->GetURL());
4190 EXPECT_EQ(1, other_controller
.GetEntryAtIndex(0)->GetPageID());
4191 EXPECT_EQ(2, other_controller
.GetEntryAtIndex(1)->GetPageID());
4192 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(2)->GetPageID());
4194 NavigationControllerImpl::set_max_entry_count_for_testing(original_count
);
4197 // Tests CopyStateFromAndPrune with 2 urls in source, 1 in dest, with
4198 // replace_entry set to true.
4199 TEST_F(NavigationControllerTest
, CopyStateFromAndPruneReplaceEntry
) {
4200 NavigationControllerImpl
& controller
= controller_impl();
4201 const GURL
url1("http://foo/1");
4202 const GURL
url2("http://foo/2");
4203 const GURL
url3("http://foo/3");
4205 NavigateAndCommit(url1
);
4206 NavigateAndCommit(url2
);
4208 // First two entries should have the same SiteInstance.
4209 SiteInstance
* instance1
= controller
.GetEntryAtIndex(0)->site_instance();
4210 SiteInstance
* instance2
= controller
.GetEntryAtIndex(1)->site_instance();
4211 EXPECT_EQ(instance1
, instance2
);
4212 EXPECT_EQ(0, controller
.GetEntryAtIndex(0)->GetPageID());
4213 EXPECT_EQ(1, controller
.GetEntryAtIndex(1)->GetPageID());
4214 EXPECT_EQ(1, contents()->GetMaxPageIDForSiteInstance(instance1
));
4216 scoped_ptr
<TestWebContents
> other_contents(
4217 static_cast<TestWebContents
*>(CreateTestWebContents()));
4218 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
4219 other_contents
->NavigateAndCommit(url3
);
4220 other_contents
->ExpectSetHistoryOffsetAndLength(1, 2);
4221 other_controller
.CopyStateFromAndPrune(&controller
, true);
4223 // other_controller should now contain the 2 urls: url1 and url3.
4225 ASSERT_EQ(2, other_controller
.GetEntryCount());
4227 ASSERT_EQ(1, other_controller
.GetCurrentEntryIndex());
4229 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
4230 EXPECT_EQ(url3
, other_controller
.GetEntryAtIndex(1)->GetURL());
4231 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(0)->GetPageID());
4232 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(1)->GetPageID());
4234 // A new SiteInstance in a different BrowsingInstance should be used for the
4236 SiteInstance
* instance3
=
4237 other_controller
.GetEntryAtIndex(1)->site_instance();
4238 EXPECT_NE(instance3
, instance1
);
4239 EXPECT_FALSE(instance3
->IsRelatedSiteInstance(instance1
));
4241 // The max page ID map should be copied over and updated with the max page ID
4242 // from the current tab.
4243 EXPECT_EQ(1, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
4244 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance3
));
4247 // Tests CopyStateFromAndPrune with 3 urls in source, 1 in dest, when the max
4248 // entry count is 3 and replace_entry is true. We should not prune entries.
4249 TEST_F(NavigationControllerTest
, CopyStateFromAndPruneMaxEntriesReplaceEntry
) {
4250 NavigationControllerImpl
& controller
= controller_impl();
4251 size_t original_count
= NavigationControllerImpl::max_entry_count();
4252 const int kMaxEntryCount
= 3;
4254 NavigationControllerImpl::set_max_entry_count_for_testing(kMaxEntryCount
);
4256 const GURL
url1("http://foo/1");
4257 const GURL
url2("http://foo/2");
4258 const GURL
url3("http://foo/3");
4259 const GURL
url4("http://foo/4");
4261 // Create a PrunedListener to observe prune notifications.
4262 PrunedListener
listener(&controller
);
4264 NavigateAndCommit(url1
);
4265 NavigateAndCommit(url2
);
4266 NavigateAndCommit(url3
);
4268 scoped_ptr
<TestWebContents
> other_contents(
4269 static_cast<TestWebContents
*>(CreateTestWebContents()));
4270 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
4271 other_contents
->NavigateAndCommit(url4
);
4272 other_contents
->ExpectSetHistoryOffsetAndLength(2, 3);
4273 other_controller
.CopyStateFromAndPrune(&controller
, true);
4275 // We should have received no pruned notification.
4276 EXPECT_EQ(0, listener
.notification_count_
);
4278 // other_controller should now contain only 3 urls: url1, url2 and url4.
4280 ASSERT_EQ(3, other_controller
.GetEntryCount());
4282 ASSERT_EQ(2, other_controller
.GetCurrentEntryIndex());
4284 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
4285 EXPECT_EQ(url2
, other_controller
.GetEntryAtIndex(1)->GetURL());
4286 EXPECT_EQ(url4
, other_controller
.GetEntryAtIndex(2)->GetURL());
4287 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(0)->GetPageID());
4288 EXPECT_EQ(1, other_controller
.GetEntryAtIndex(1)->GetPageID());
4289 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(2)->GetPageID());
4291 NavigationControllerImpl::set_max_entry_count_for_testing(original_count
);
4294 // Tests that we can navigate to the restored entries
4295 // imported by CopyStateFromAndPrune.
4296 TEST_F(NavigationControllerTest
, CopyRestoredStateAndNavigate
) {
4297 const GURL kRestoredUrls
[] = {
4298 GURL("http://site1.com"),
4299 GURL("http://site2.com"),
4301 const GURL
kInitialUrl("http://site3.com");
4303 std::vector
<NavigationEntry
*> entries
;
4304 for (size_t i
= 0; i
< arraysize(kRestoredUrls
); ++i
) {
4305 NavigationEntry
* entry
= NavigationControllerImpl::CreateNavigationEntry(
4306 kRestoredUrls
[i
], Referrer(), ui::PAGE_TRANSITION_RELOAD
, false,
4307 std::string(), browser_context());
4308 entry
->SetPageID(static_cast<int>(i
));
4309 entries
.push_back(entry
);
4312 // Create a WebContents with restored entries.
4313 scoped_ptr
<TestWebContents
> source_contents(
4314 static_cast<TestWebContents
*>(CreateTestWebContents()));
4315 NavigationControllerImpl
& source_controller
=
4316 source_contents
->GetController();
4317 source_controller
.Restore(
4319 NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY
,
4321 ASSERT_EQ(0u, entries
.size());
4322 source_controller
.LoadIfNecessary();
4323 source_contents
->CommitPendingNavigation();
4325 // Load a page, then copy state from |source_contents|.
4326 NavigateAndCommit(kInitialUrl
);
4327 contents()->ExpectSetHistoryOffsetAndLength(2, 3);
4328 controller_impl().CopyStateFromAndPrune(&source_controller
, false);
4329 ASSERT_EQ(3, controller_impl().GetEntryCount());
4331 // Go back to the first entry one at a time and
4332 // verify that it works as expected.
4333 EXPECT_EQ(2, controller_impl().GetCurrentEntryIndex());
4334 EXPECT_EQ(kInitialUrl
, controller_impl().GetActiveEntry()->GetURL());
4336 controller_impl().GoBack();
4337 contents()->CommitPendingNavigation();
4338 EXPECT_EQ(1, controller_impl().GetCurrentEntryIndex());
4339 EXPECT_EQ(kRestoredUrls
[1], controller_impl().GetActiveEntry()->GetURL());
4341 controller_impl().GoBack();
4342 contents()->CommitPendingNavigation();
4343 EXPECT_EQ(0, controller_impl().GetCurrentEntryIndex());
4344 EXPECT_EQ(kRestoredUrls
[0], controller_impl().GetActiveEntry()->GetURL());
4347 // Tests that navigations initiated from the page (with the history object)
4348 // work as expected, creating pending entries.
4349 TEST_F(NavigationControllerTest
, HistoryNavigate
) {
4350 NavigationControllerImpl
& controller
= controller_impl();
4351 const GURL
url1("http://foo/1");
4352 const GURL
url2("http://foo/2");
4353 const GURL
url3("http://foo/3");
4355 NavigateAndCommit(url1
);
4356 NavigateAndCommit(url2
);
4357 NavigateAndCommit(url3
);
4358 controller
.GoBack();
4359 contents()->CommitPendingNavigation();
4360 process()->sink().ClearMessages();
4362 // Simulate the page calling history.back(). It should create a pending entry.
4363 contents()->OnGoToEntryAtOffset(-1);
4364 EXPECT_EQ(0, controller
.GetPendingEntryIndex());
4365 // The actual cross-navigation is suspended until the current RVH tells us
4366 // it unloaded, simulate that.
4367 contents()->ProceedWithCrossSiteNavigation();
4368 // Also make sure we told the page to navigate.
4369 GURL nav_url
= GetLastNavigationURL();
4370 EXPECT_EQ(url1
, nav_url
);
4371 contents()->CommitPendingNavigation();
4372 process()->sink().ClearMessages();
4374 // Now test history.forward()
4375 contents()->OnGoToEntryAtOffset(2);
4376 EXPECT_EQ(2, controller
.GetPendingEntryIndex());
4377 // The actual cross-navigation is suspended until the current RVH tells us
4378 // it unloaded, simulate that.
4379 contents()->ProceedWithCrossSiteNavigation();
4380 nav_url
= GetLastNavigationURL();
4381 EXPECT_EQ(url3
, nav_url
);
4382 contents()->CommitPendingNavigation();
4383 process()->sink().ClearMessages();
4385 controller
.DiscardNonCommittedEntries();
4387 // Make sure an extravagant history.go() doesn't break.
4388 contents()->OnGoToEntryAtOffset(120); // Out of bounds.
4389 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4390 EXPECT_FALSE(HasNavigationRequest());
4393 // Test call to PruneAllButLastCommitted for the only entry.
4394 TEST_F(NavigationControllerTest
, PruneAllButLastCommittedForSingle
) {
4395 NavigationControllerImpl
& controller
= controller_impl();
4396 const GURL
url1("http://foo1");
4397 NavigateAndCommit(url1
);
4399 contents()->ExpectSetHistoryOffsetAndLength(0, 1);
4401 controller
.PruneAllButLastCommitted();
4403 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4404 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(), url1
);
4407 // Test call to PruneAllButLastCommitted for first entry.
4408 TEST_F(NavigationControllerTest
, PruneAllButLastCommittedForFirst
) {
4409 NavigationControllerImpl
& controller
= controller_impl();
4410 const GURL
url1("http://foo/1");
4411 const GURL
url2("http://foo/2");
4412 const GURL
url3("http://foo/3");
4414 NavigateAndCommit(url1
);
4415 NavigateAndCommit(url2
);
4416 NavigateAndCommit(url3
);
4417 controller
.GoBack();
4418 controller
.GoBack();
4419 contents()->CommitPendingNavigation();
4421 contents()->ExpectSetHistoryOffsetAndLength(0, 1);
4423 controller
.PruneAllButLastCommitted();
4425 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4426 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(), url1
);
4429 // Test call to PruneAllButLastCommitted for intermediate entry.
4430 TEST_F(NavigationControllerTest
, PruneAllButLastCommittedForIntermediate
) {
4431 NavigationControllerImpl
& controller
= controller_impl();
4432 const GURL
url1("http://foo/1");
4433 const GURL
url2("http://foo/2");
4434 const GURL
url3("http://foo/3");
4436 NavigateAndCommit(url1
);
4437 NavigateAndCommit(url2
);
4438 NavigateAndCommit(url3
);
4439 controller
.GoBack();
4440 contents()->CommitPendingNavigation();
4442 contents()->ExpectSetHistoryOffsetAndLength(0, 1);
4444 controller
.PruneAllButLastCommitted();
4446 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4447 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(), url2
);
4450 // Test call to PruneAllButLastCommitted for a pending entry that is not yet in
4451 // the list of entries.
4452 TEST_F(NavigationControllerTest
, PruneAllButLastCommittedForPendingNotInList
) {
4453 NavigationControllerImpl
& controller
= controller_impl();
4454 const GURL
url1("http://foo/1");
4455 const GURL
url2("http://foo/2");
4456 const GURL
url3("http://foo/3");
4458 NavigateAndCommit(url1
);
4459 NavigateAndCommit(url2
);
4461 // Create a pending entry that is not in the entry list.
4463 url3
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
4464 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
4465 EXPECT_TRUE(controller
.GetPendingEntry());
4466 EXPECT_EQ(2, controller
.GetEntryCount());
4468 contents()->ExpectSetHistoryOffsetAndLength(0, 1);
4469 controller
.PruneAllButLastCommitted();
4471 // We should only have the last committed and pending entries at this point,
4472 // and the pending entry should still not be in the entry list.
4473 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
4474 EXPECT_EQ(url2
, controller
.GetEntryAtIndex(0)->GetURL());
4475 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4476 EXPECT_TRUE(controller
.GetPendingEntry());
4477 EXPECT_EQ(1, controller
.GetEntryCount());
4479 // Try to commit the pending entry.
4480 main_test_rfh()->PrepareForCommit();
4481 main_test_rfh()->SendNavigate(2, entry_id
, true, url3
);
4482 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4483 EXPECT_FALSE(controller
.GetPendingEntry());
4484 EXPECT_EQ(2, controller
.GetEntryCount());
4485 EXPECT_EQ(url3
, controller
.GetEntryAtIndex(1)->GetURL());
4488 // Test to ensure that when we do a history navigation back to the current
4489 // committed page (e.g., going forward to a slow-loading page, then pressing
4490 // the back button), we just stop the navigation to prevent the throbber from
4491 // running continuously. Otherwise, the RenderViewHost forces the throbber to
4492 // start, but WebKit essentially ignores the navigation and never sends a
4493 // message to stop the throbber.
4494 TEST_F(NavigationControllerTest
, StopOnHistoryNavigationToCurrentPage
) {
4495 NavigationControllerImpl
& controller
= controller_impl();
4496 const GURL
url0("http://foo/0");
4497 const GURL
url1("http://foo/1");
4499 NavigateAndCommit(url0
);
4500 NavigateAndCommit(url1
);
4502 // Go back to the original page, then forward to the slow page, then back
4503 controller
.GoBack();
4504 contents()->CommitPendingNavigation();
4506 controller
.GoForward();
4507 EXPECT_EQ(1, controller
.GetPendingEntryIndex());
4509 controller
.GoBack();
4510 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4513 TEST_F(NavigationControllerTest
, IsInitialNavigation
) {
4514 NavigationControllerImpl
& controller
= controller_impl();
4515 TestNotificationTracker notifications
;
4516 RegisterForAllNavNotifications(¬ifications
, &controller
);
4519 EXPECT_TRUE(controller
.IsInitialNavigation());
4521 // After commit, it stays false.
4522 const GURL
url1("http://foo1");
4523 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url1
);
4524 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4525 navigation_entry_committed_counter_
= 0;
4526 EXPECT_FALSE(controller
.IsInitialNavigation());
4528 // After starting a new navigation, it stays false.
4529 const GURL
url2("http://foo2");
4531 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
4534 // Check that the favicon is not reused across a client redirect.
4535 // (crbug.com/28515)
4536 TEST_F(NavigationControllerTest
, ClearFaviconOnRedirect
) {
4537 const GURL
kPageWithFavicon("http://withfavicon.html");
4538 const GURL
kPageWithoutFavicon("http://withoutfavicon.html");
4539 const GURL
kIconURL("http://withfavicon.ico");
4540 const gfx::Image kDefaultFavicon
= FaviconStatus().image
;
4542 NavigationControllerImpl
& controller
= controller_impl();
4543 TestNotificationTracker notifications
;
4544 RegisterForAllNavNotifications(¬ifications
, &controller
);
4546 main_test_rfh()->NavigateAndCommitRendererInitiated(
4547 0, true, kPageWithFavicon
);
4548 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4549 navigation_entry_committed_counter_
= 0;
4551 NavigationEntry
* entry
= controller
.GetLastCommittedEntry();
4553 EXPECT_EQ(kPageWithFavicon
, entry
->GetURL());
4555 // Simulate Chromium having set the favicon for |kPageWithFavicon|.
4556 content::FaviconStatus
& favicon_status
= entry
->GetFavicon();
4557 favicon_status
.image
= CreateImage(SK_ColorWHITE
);
4558 favicon_status
.url
= kIconURL
;
4559 favicon_status
.valid
= true;
4560 EXPECT_FALSE(DoImagesMatch(kDefaultFavicon
, entry
->GetFavicon().image
));
4562 main_test_rfh()->SendRendererInitiatedNavigationRequest(kPageWithoutFavicon
,
4564 main_test_rfh()->PrepareForCommit();
4565 main_test_rfh()->SendNavigateWithTransition(
4568 false, // no new entry
4569 kPageWithoutFavicon
, ui::PAGE_TRANSITION_CLIENT_REDIRECT
);
4570 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4571 navigation_entry_committed_counter_
= 0;
4573 entry
= controller
.GetLastCommittedEntry();
4575 EXPECT_EQ(kPageWithoutFavicon
, entry
->GetURL());
4577 EXPECT_TRUE(DoImagesMatch(kDefaultFavicon
, entry
->GetFavicon().image
));
4580 // Check that the favicon is not cleared for NavigationEntries which were
4581 // previously navigated to.
4582 TEST_F(NavigationControllerTest
, BackNavigationDoesNotClearFavicon
) {
4583 const GURL
kUrl1("http://www.a.com/1");
4584 const GURL
kUrl2("http://www.a.com/2");
4585 const GURL
kIconURL("http://www.a.com/1/favicon.ico");
4587 NavigationControllerImpl
& controller
= controller_impl();
4588 TestNotificationTracker notifications
;
4589 RegisterForAllNavNotifications(¬ifications
, &controller
);
4591 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, kUrl1
);
4592 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4593 navigation_entry_committed_counter_
= 0;
4595 // Simulate Chromium having set the favicon for |kUrl1|.
4596 gfx::Image favicon_image
= CreateImage(SK_ColorWHITE
);
4597 content::NavigationEntry
* entry
= controller
.GetLastCommittedEntry();
4599 content::FaviconStatus
& favicon_status
= entry
->GetFavicon();
4600 favicon_status
.image
= favicon_image
;
4601 favicon_status
.url
= kIconURL
;
4602 favicon_status
.valid
= true;
4604 // Navigate to another page and go back to the original page.
4605 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, kUrl2
);
4606 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4607 navigation_entry_committed_counter_
= 0;
4608 main_test_rfh()->SendRendererInitiatedNavigationRequest(kUrl1
, false);
4609 main_test_rfh()->PrepareForCommit();
4610 main_test_rfh()->SendNavigateWithTransition(
4611 0, controller
.GetEntryAtIndex(0)->GetUniqueID(), false, kUrl1
,
4612 ui::PAGE_TRANSITION_FORWARD_BACK
);
4613 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4614 navigation_entry_committed_counter_
= 0;
4616 // Verify that the favicon for the page at |kUrl1| was not cleared.
4617 entry
= controller
.GetEntryAtIndex(0);
4619 EXPECT_EQ(kUrl1
, entry
->GetURL());
4620 EXPECT_TRUE(DoImagesMatch(favicon_image
, entry
->GetFavicon().image
));
4623 // The test crashes on android: http://crbug.com/170449
4624 #if defined(OS_ANDROID)
4625 #define MAYBE_PurgeScreenshot DISABLED_PurgeScreenshot
4627 #define MAYBE_PurgeScreenshot PurgeScreenshot
4629 // Tests that screenshot are purged correctly.
4630 TEST_F(NavigationControllerTest
, MAYBE_PurgeScreenshot
) {
4631 NavigationControllerImpl
& controller
= controller_impl();
4633 NavigationEntryImpl
* entry
;
4635 // Navigate enough times to make sure that some screenshots are purged.
4636 for (int i
= 0; i
< 12; ++i
) {
4637 const GURL
url(base::StringPrintf("http://foo%d/", i
));
4638 NavigateAndCommit(url
);
4639 EXPECT_EQ(i
, controller
.GetCurrentEntryIndex());
4642 MockScreenshotManager
* screenshot_manager
=
4643 new MockScreenshotManager(&controller
);
4644 controller
.SetScreenshotManager(screenshot_manager
);
4645 for (int i
= 0; i
< controller
.GetEntryCount(); ++i
) {
4646 entry
= controller
.GetEntryAtIndex(i
);
4647 screenshot_manager
->TakeScreenshotFor(entry
);
4648 EXPECT_TRUE(entry
->screenshot().get());
4651 NavigateAndCommit(GURL("https://foo/"));
4652 EXPECT_EQ(13, controller
.GetEntryCount());
4653 entry
= controller
.GetEntryAtIndex(11);
4654 screenshot_manager
->TakeScreenshotFor(entry
);
4656 for (int i
= 0; i
< 2; ++i
) {
4657 entry
= controller
.GetEntryAtIndex(i
);
4658 EXPECT_FALSE(entry
->screenshot().get()) << "Screenshot " << i
4662 for (int i
= 2; i
< controller
.GetEntryCount() - 1; ++i
) {
4663 entry
= controller
.GetEntryAtIndex(i
);
4664 EXPECT_TRUE(entry
->screenshot().get()) << "Screenshot not found for " << i
;
4667 // Navigate to index 5 and then try to assign screenshot to all entries.
4668 controller
.GoToIndex(5);
4669 contents()->CommitPendingNavigation();
4670 EXPECT_EQ(5, controller
.GetCurrentEntryIndex());
4671 for (int i
= 0; i
< controller
.GetEntryCount() - 1; ++i
) {
4672 entry
= controller
.GetEntryAtIndex(i
);
4673 screenshot_manager
->TakeScreenshotFor(entry
);
4676 for (int i
= 10; i
<= 12; ++i
) {
4677 entry
= controller
.GetEntryAtIndex(i
);
4678 EXPECT_FALSE(entry
->screenshot().get()) << "Screenshot " << i
4680 screenshot_manager
->TakeScreenshotFor(entry
);
4683 // Navigate to index 7 and assign screenshot to all entries.
4684 controller
.GoToIndex(7);
4685 contents()->CommitPendingNavigation();
4686 EXPECT_EQ(7, controller
.GetCurrentEntryIndex());
4687 for (int i
= 0; i
< controller
.GetEntryCount() - 1; ++i
) {
4688 entry
= controller
.GetEntryAtIndex(i
);
4689 screenshot_manager
->TakeScreenshotFor(entry
);
4692 for (int i
= 0; i
< 2; ++i
) {
4693 entry
= controller
.GetEntryAtIndex(i
);
4694 EXPECT_FALSE(entry
->screenshot().get()) << "Screenshot " << i
4698 // Clear all screenshots.
4699 EXPECT_EQ(13, controller
.GetEntryCount());
4700 EXPECT_EQ(10, screenshot_manager
->GetScreenshotCount());
4701 controller
.ClearAllScreenshots();
4702 EXPECT_EQ(0, screenshot_manager
->GetScreenshotCount());
4703 for (int i
= 0; i
< controller
.GetEntryCount(); ++i
) {
4704 entry
= controller
.GetEntryAtIndex(i
);
4705 EXPECT_FALSE(entry
->screenshot().get()) << "Screenshot " << i
4710 TEST_F(NavigationControllerTest
, PushStateUpdatesTitleAndFavicon
) {
4712 main_test_rfh()->NavigateAndCommitRendererInitiated(
4713 1, true, GURL("http://foo"));
4715 // Set title and favicon.
4716 base::string16
title(base::ASCIIToUTF16("Title"));
4717 FaviconStatus favicon
;
4718 favicon
.valid
= true;
4719 favicon
.url
= GURL("http://foo/favicon.ico");
4720 controller().GetLastCommittedEntry()->SetTitle(title
);
4721 controller().GetLastCommittedEntry()->GetFavicon() = favicon
;
4723 // history.pushState() is called.
4724 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
4725 GURL
kUrl2("http://foo#foo");
4727 params
.nav_entry_id
= 0;
4728 params
.did_create_new_entry
= true;
4730 params
.page_state
= PageState::CreateFromURL(kUrl2
);
4731 params
.was_within_same_page
= true;
4732 main_test_rfh()->SendRendererInitiatedNavigationRequest(kUrl2
, false);
4733 main_test_rfh()->PrepareForCommit();
4734 main_test_rfh()->SendNavigateWithParams(¶ms
);
4736 // The title should immediately be visible on the new NavigationEntry.
4737 base::string16 new_title
=
4738 controller().GetLastCommittedEntry()->GetTitleForDisplay(std::string());
4739 EXPECT_EQ(title
, new_title
);
4740 FaviconStatus new_favicon
=
4741 controller().GetLastCommittedEntry()->GetFavicon();
4742 EXPECT_EQ(favicon
.valid
, new_favicon
.valid
);
4743 EXPECT_EQ(favicon
.url
, new_favicon
.url
);
4746 // Test that the navigation controller clears its session history when a
4747 // navigation commits with the clear history list flag set.
4748 TEST_F(NavigationControllerTest
, ClearHistoryList
) {
4749 const GURL
url1("http://foo1");
4750 const GURL
url2("http://foo2");
4751 const GURL
url3("http://foo3");
4752 const GURL
url4("http://foo4");
4754 NavigationControllerImpl
& controller
= controller_impl();
4756 // Create a session history with three entries, second entry is active.
4757 NavigateAndCommit(url1
);
4758 NavigateAndCommit(url2
);
4759 NavigateAndCommit(url3
);
4760 controller
.GoBack();
4761 contents()->CommitPendingNavigation();
4762 EXPECT_EQ(3, controller
.GetEntryCount());
4763 EXPECT_EQ(1, controller
.GetCurrentEntryIndex());
4765 // Create a new pending navigation, and indicate that the session history
4766 // should be cleared.
4767 NavigationController::LoadURLParams
params(url4
);
4768 params
.should_clear_history_list
= true;
4769 controller
.LoadURLWithParams(params
);
4771 // Verify that the pending entry correctly indicates that the session history
4772 // should be cleared.
4773 NavigationEntryImpl
* entry
= controller
.GetPendingEntry();
4775 EXPECT_TRUE(entry
->should_clear_history_list());
4777 // Assume that the RenderFrame correctly cleared its history and commit the
4779 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
4780 switches::kEnableBrowserSideNavigation
)) {
4781 contents()->GetMainFrame()->SendBeforeUnloadACK(true);
4783 contents()->GetPendingMainFrame()->
4784 set_simulate_history_list_was_cleared(true);
4785 contents()->CommitPendingNavigation();
4787 // Verify that the NavigationController's session history was correctly
4789 EXPECT_EQ(1, controller
.GetEntryCount());
4790 EXPECT_EQ(0, controller
.GetCurrentEntryIndex());
4791 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
4792 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4793 EXPECT_FALSE(controller
.CanGoBack());
4794 EXPECT_FALSE(controller
.CanGoForward());
4795 EXPECT_EQ(url4
, controller
.GetVisibleEntry()->GetURL());
4798 TEST_F(NavigationControllerTest
, PostThenReplaceStateThenReload
) {
4799 scoped_ptr
<TestWebContentsDelegate
> delegate(new TestWebContentsDelegate());
4800 EXPECT_FALSE(contents()->GetDelegate());
4801 contents()->SetDelegate(delegate
.get());
4804 GURL
url("http://foo");
4805 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
4807 params
.nav_entry_id
= 0;
4808 params
.did_create_new_entry
= true;
4810 params
.transition
= ui::PAGE_TRANSITION_FORM_SUBMIT
;
4811 params
.gesture
= NavigationGestureUser
;
4812 params
.page_state
= PageState::CreateFromURL(url
);
4813 params
.was_within_same_page
= false;
4814 params
.is_post
= true;
4816 main_test_rfh()->SendRendererInitiatedNavigationRequest(url
, false);
4817 main_test_rfh()->PrepareForCommit();
4818 contents()->GetMainFrame()->SendNavigateWithParams(¶ms
);
4820 // history.replaceState() is called.
4821 GURL
replace_url("http://foo#foo");
4823 params
.nav_entry_id
= 0;
4824 params
.did_create_new_entry
= false;
4825 params
.url
= replace_url
;
4826 params
.transition
= ui::PAGE_TRANSITION_LINK
;
4827 params
.gesture
= NavigationGestureUser
;
4828 params
.page_state
= PageState::CreateFromURL(replace_url
);
4829 params
.was_within_same_page
= true;
4830 params
.is_post
= false;
4831 params
.post_id
= -1;
4832 main_test_rfh()->SendRendererInitiatedNavigationRequest(replace_url
, false);
4833 main_test_rfh()->PrepareForCommit();
4834 contents()->GetMainFrame()->SendNavigateWithParams(¶ms
);
4836 // Now reload. replaceState overrides the POST, so we should not show a
4837 // repost warning dialog.
4838 controller_impl().Reload(true);
4839 EXPECT_EQ(0, delegate
->repost_form_warning_count());
4842 TEST_F(NavigationControllerTest
, UnreachableURLGivesErrorPage
) {
4843 GURL
url("http://foo");
4844 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
4846 params
.nav_entry_id
= 0;
4847 params
.did_create_new_entry
= true;
4849 params
.transition
= ui::PAGE_TRANSITION_LINK
;
4850 params
.gesture
= NavigationGestureUser
;
4851 params
.page_state
= PageState::CreateFromURL(url
);
4852 params
.was_within_same_page
= false;
4853 params
.is_post
= true;
4855 params
.url_is_unreachable
= true;
4857 // Navigate to new page.
4859 LoadCommittedDetails details
;
4860 controller_impl().RendererDidNavigate(main_test_rfh(), params
, &details
);
4861 EXPECT_EQ(PAGE_TYPE_ERROR
,
4862 controller_impl().GetLastCommittedEntry()->GetPageType());
4863 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE
, details
.type
);
4866 // Navigate to existing page.
4868 params
.did_create_new_entry
= false;
4869 LoadCommittedDetails details
;
4870 controller_impl().RendererDidNavigate(main_test_rfh(), params
, &details
);
4871 EXPECT_EQ(PAGE_TYPE_ERROR
,
4872 controller_impl().GetLastCommittedEntry()->GetPageType());
4873 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE
, details
.type
);
4876 // Navigate to same page.
4877 // Note: The call to LoadURL() creates a pending entry in order to trigger the
4878 // same-page transition.
4879 controller_impl().LoadURL(
4880 url
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
4881 params
.nav_entry_id
= controller_impl().GetPendingEntry()->GetUniqueID();
4882 params
.transition
= ui::PAGE_TRANSITION_TYPED
;
4884 LoadCommittedDetails details
;
4885 controller_impl().RendererDidNavigate(main_test_rfh(), params
, &details
);
4886 EXPECT_EQ(PAGE_TYPE_ERROR
,
4887 controller_impl().GetLastCommittedEntry()->GetPageType());
4888 EXPECT_EQ(NAVIGATION_TYPE_SAME_PAGE
, details
.type
);
4891 // Navigate in page.
4892 params
.url
= GURL("http://foo#foo");
4893 params
.transition
= ui::PAGE_TRANSITION_LINK
;
4894 params
.was_within_same_page
= true;
4896 LoadCommittedDetails details
;
4897 controller_impl().RendererDidNavigate(main_test_rfh(), params
, &details
);
4898 EXPECT_EQ(PAGE_TYPE_ERROR
,
4899 controller_impl().GetLastCommittedEntry()->GetPageType());
4900 EXPECT_EQ(NAVIGATION_TYPE_IN_PAGE
, details
.type
);
4904 } // namespace content