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/ssl_status_serialization.h"
26 #include "content/common/view_messages.h"
27 #include "content/public/browser/navigation_details.h"
28 #include "content/public/browser/notification_registrar.h"
29 #include "content/public/browser/notification_types.h"
30 #include "content/public/browser/render_view_host.h"
31 #include "content/public/browser/web_contents_delegate.h"
32 #include "content/public/browser/web_contents_observer.h"
33 #include "content/public/common/content_switches.h"
34 #include "content/public/common/page_state.h"
35 #include "content/public/common/page_type.h"
36 #include "content/public/common/url_constants.h"
37 #include "content/public/test/mock_render_process_host.h"
38 #include "content/public/test/test_notification_tracker.h"
39 #include "content/public/test/test_utils.h"
40 #include "content/test/test_render_frame_host.h"
41 #include "content/test/test_render_view_host.h"
42 #include "content/test/test_web_contents.h"
43 #include "net/base/net_util.h"
44 #include "skia/ext/platform_canvas.h"
45 #include "testing/gtest/include/gtest/gtest.h"
46 #include "third_party/WebKit/public/web/WebSandboxFlags.h"
52 // Creates an image with a 1x1 SkBitmap of the specified |color|.
53 gfx::Image
CreateImage(SkColor color
) {
55 bitmap
.allocN32Pixels(1, 1);
56 bitmap
.eraseColor(color
);
57 return gfx::Image::CreateFrom1xBitmap(bitmap
);
60 // Returns true if images |a| and |b| have the same pixel data.
61 bool DoImagesMatch(const gfx::Image
& a
, const gfx::Image
& b
) {
62 // Assume that if the 1x bitmaps match, the images match.
63 SkBitmap a_bitmap
= a
.AsBitmap();
64 SkBitmap b_bitmap
= b
.AsBitmap();
66 if (a_bitmap
.width() != b_bitmap
.width() ||
67 a_bitmap
.height() != b_bitmap
.height()) {
70 SkAutoLockPixels
a_bitmap_lock(a_bitmap
);
71 SkAutoLockPixels
b_bitmap_lock(b_bitmap
);
72 return memcmp(a_bitmap
.getPixels(),
74 a_bitmap
.getSize()) == 0;
77 class MockScreenshotManager
: public content::NavigationEntryScreenshotManager
{
79 explicit MockScreenshotManager(content::NavigationControllerImpl
* owner
)
80 : content::NavigationEntryScreenshotManager(owner
),
81 encoding_screenshot_in_progress_(false) {
84 ~MockScreenshotManager() override
{}
86 void TakeScreenshotFor(content::NavigationEntryImpl
* entry
) {
88 bitmap
.allocPixels(SkImageInfo::Make(
89 1, 1, kAlpha_8_SkColorType
, kPremul_SkAlphaType
));
90 bitmap
.eraseARGB(0, 0, 0, 0);
91 encoding_screenshot_in_progress_
= true;
92 OnScreenshotTaken(entry
->GetUniqueID(), bitmap
, content::READBACK_SUCCESS
);
93 WaitUntilScreenshotIsReady();
96 int GetScreenshotCount() {
97 return content::NavigationEntryScreenshotManager::GetScreenshotCount();
100 void WaitUntilScreenshotIsReady() {
101 if (!encoding_screenshot_in_progress_
)
103 message_loop_runner_
= new content::MessageLoopRunner
;
104 message_loop_runner_
->Run();
108 // Overridden from content::NavigationEntryScreenshotManager:
109 void TakeScreenshotImpl(content::RenderViewHost
* host
,
110 content::NavigationEntryImpl
* entry
) override
{}
112 void OnScreenshotSet(content::NavigationEntryImpl
* entry
) override
{
113 encoding_screenshot_in_progress_
= false;
114 NavigationEntryScreenshotManager::OnScreenshotSet(entry
);
115 if (message_loop_runner_
.get())
116 message_loop_runner_
->Quit();
119 scoped_refptr
<content::MessageLoopRunner
> message_loop_runner_
;
120 bool encoding_screenshot_in_progress_
;
122 DISALLOW_COPY_AND_ASSIGN(MockScreenshotManager
);
129 // TimeSmoother tests ----------------------------------------------------------
131 // With no duplicates, GetSmoothedTime should be the identity
133 TEST(TimeSmoother
, Basic
) {
134 NavigationControllerImpl::TimeSmoother smoother
;
135 for (int64 i
= 1; i
< 1000; ++i
) {
136 base::Time t
= base::Time::FromInternalValue(i
);
137 EXPECT_EQ(t
, smoother
.GetSmoothedTime(t
));
141 // With a single duplicate and timestamps thereafter increasing by one
142 // microsecond, the smoothed time should always be one behind.
143 TEST(TimeSmoother
, SingleDuplicate
) {
144 NavigationControllerImpl::TimeSmoother smoother
;
145 base::Time t
= base::Time::FromInternalValue(1);
146 EXPECT_EQ(t
, smoother
.GetSmoothedTime(t
));
147 for (int64 i
= 1; i
< 1000; ++i
) {
148 base::Time expected_t
= base::Time::FromInternalValue(i
+ 1);
149 t
= base::Time::FromInternalValue(i
);
150 EXPECT_EQ(expected_t
, smoother
.GetSmoothedTime(t
));
154 // With k duplicates and timestamps thereafter increasing by one
155 // microsecond, the smoothed time should always be k behind.
156 TEST(TimeSmoother
, ManyDuplicates
) {
157 const int64 kNumDuplicates
= 100;
158 NavigationControllerImpl::TimeSmoother smoother
;
159 base::Time t
= base::Time::FromInternalValue(1);
160 for (int64 i
= 0; i
< kNumDuplicates
; ++i
) {
161 base::Time expected_t
= base::Time::FromInternalValue(i
+ 1);
162 EXPECT_EQ(expected_t
, smoother
.GetSmoothedTime(t
));
164 for (int64 i
= 1; i
< 1000; ++i
) {
165 base::Time expected_t
=
166 base::Time::FromInternalValue(i
+ kNumDuplicates
);
167 t
= base::Time::FromInternalValue(i
);
168 EXPECT_EQ(expected_t
, smoother
.GetSmoothedTime(t
));
172 // If the clock jumps far back enough after a run of duplicates, it
173 // should immediately jump to that value.
174 TEST(TimeSmoother
, ClockBackwardsJump
) {
175 const int64 kNumDuplicates
= 100;
176 NavigationControllerImpl::TimeSmoother smoother
;
177 base::Time t
= base::Time::FromInternalValue(1000);
178 for (int64 i
= 0; i
< kNumDuplicates
; ++i
) {
179 base::Time expected_t
= base::Time::FromInternalValue(i
+ 1000);
180 EXPECT_EQ(expected_t
, smoother
.GetSmoothedTime(t
));
182 t
= base::Time::FromInternalValue(500);
183 EXPECT_EQ(t
, smoother
.GetSmoothedTime(t
));
186 // NavigationControllerTest ----------------------------------------------------
188 class NavigationControllerTest
189 : public RenderViewHostImplTestHarness
,
190 public WebContentsObserver
{
192 NavigationControllerTest() : navigation_entry_committed_counter_(0) {
195 void SetUp() override
{
196 RenderViewHostImplTestHarness::SetUp();
197 WebContents
* web_contents
= RenderViewHostImplTestHarness::web_contents();
198 ASSERT_TRUE(web_contents
); // The WebContents should be created by now.
199 WebContentsObserver::Observe(web_contents
);
202 // WebContentsObserver:
203 void DidStartNavigationToPendingEntry(
205 NavigationController::ReloadType reload_type
) override
{
206 navigated_url_
= url
;
209 void NavigationEntryCommitted(
210 const LoadCommittedDetails
& load_details
) override
{
211 navigation_entry_committed_counter_
++;
214 const GURL
& navigated_url() const {
215 return navigated_url_
;
218 NavigationControllerImpl
& controller_impl() {
219 return static_cast<NavigationControllerImpl
&>(controller());
222 bool HasNavigationRequest() {
223 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
224 switches::kEnableBrowserSideNavigation
)) {
225 return contents()->GetFrameTree()->root()->navigation_request() !=
228 return process()->sink().GetFirstMessageMatching(FrameMsg_Navigate::ID
)
232 const GURL
GetLastNavigationURL() {
233 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
234 switches::kEnableBrowserSideNavigation
)) {
235 NavigationRequest
* navigation_request
=
236 contents()->GetFrameTree()->root()->navigation_request();
237 CHECK(navigation_request
);
238 return navigation_request
->common_params().url
;
240 const IPC::Message
* message
=
241 process()->sink().GetFirstMessageMatching(FrameMsg_Navigate::ID
);
243 base::Tuple
<CommonNavigationParams
, StartNavigationParams
,
244 RequestNavigationParams
> nav_params
;
245 FrameMsg_Navigate::Read(message
, &nav_params
);
246 return base::get
<0>(nav_params
).url
;
251 size_t navigation_entry_committed_counter_
;
254 void RegisterForAllNavNotifications(TestNotificationTracker
* tracker
,
255 NavigationController
* controller
) {
256 tracker
->ListenFor(NOTIFICATION_NAV_LIST_PRUNED
,
257 Source
<NavigationController
>(controller
));
258 tracker
->ListenFor(NOTIFICATION_NAV_ENTRY_CHANGED
,
259 Source
<NavigationController
>(controller
));
262 class TestWebContentsDelegate
: public WebContentsDelegate
{
264 explicit TestWebContentsDelegate() :
265 navigation_state_change_count_(0),
266 repost_form_warning_count_(0) {}
268 int navigation_state_change_count() {
269 return navigation_state_change_count_
;
272 int repost_form_warning_count() {
273 return repost_form_warning_count_
;
276 // Keep track of whether the tab has notified us of a navigation state change.
277 void NavigationStateChanged(WebContents
* source
,
278 InvalidateTypes changed_flags
) override
{
279 navigation_state_change_count_
++;
282 void ShowRepostFormWarningDialog(WebContents
* source
) override
{
283 repost_form_warning_count_
++;
287 // The number of times NavigationStateChanged has been called.
288 int navigation_state_change_count_
;
290 // The number of times ShowRepostFormWarningDialog() was called.
291 int repost_form_warning_count_
;
294 // -----------------------------------------------------------------------------
296 TEST_F(NavigationControllerTest
, Defaults
) {
297 NavigationControllerImpl
& controller
= controller_impl();
299 EXPECT_FALSE(controller
.GetPendingEntry());
300 EXPECT_FALSE(controller
.GetVisibleEntry());
301 EXPECT_FALSE(controller
.GetLastCommittedEntry());
302 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
303 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), -1);
304 EXPECT_EQ(controller
.GetEntryCount(), 0);
305 EXPECT_FALSE(controller
.CanGoBack());
306 EXPECT_FALSE(controller
.CanGoForward());
309 TEST_F(NavigationControllerTest
, GoToOffset
) {
310 NavigationControllerImpl
& controller
= controller_impl();
311 TestNotificationTracker notifications
;
312 RegisterForAllNavNotifications(¬ifications
, &controller
);
314 const int kNumUrls
= 5;
315 std::vector
<GURL
> urls(kNumUrls
);
316 for (int i
= 0; i
< kNumUrls
; ++i
) {
317 urls
[i
] = GURL(base::StringPrintf("http://www.a.com/%d", i
));
320 main_test_rfh()->SendRendererInitiatedNavigationRequest(urls
[0], true);
321 main_test_rfh()->PrepareForCommit();
322 main_test_rfh()->SendNavigate(0, 0, true, urls
[0]);
323 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
324 navigation_entry_committed_counter_
= 0;
325 EXPECT_EQ(urls
[0], controller
.GetVisibleEntry()->GetVirtualURL());
326 EXPECT_FALSE(controller
.CanGoBack());
327 EXPECT_FALSE(controller
.CanGoForward());
328 EXPECT_FALSE(controller
.CanGoToOffset(1));
330 for (int i
= 1; i
<= 4; ++i
) {
331 main_test_rfh()->SendRendererInitiatedNavigationRequest(urls
[i
], true);
332 main_test_rfh()->PrepareForCommit();
333 main_test_rfh()->SendNavigate(i
, 0, true, urls
[i
]);
334 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
335 navigation_entry_committed_counter_
= 0;
336 EXPECT_EQ(urls
[i
], controller
.GetVisibleEntry()->GetVirtualURL());
337 EXPECT_TRUE(controller
.CanGoToOffset(-i
));
338 EXPECT_FALSE(controller
.CanGoToOffset(-(i
+ 1)));
339 EXPECT_FALSE(controller
.CanGoToOffset(1));
342 // We have loaded 5 pages, and are currently at the last-loaded page.
346 GO_TO_MIDDLE_PAGE
= -2,
349 GO_TO_BEGINNING
= -2,
354 const int test_offsets
[NUM_TESTS
] = {
362 for (int test
= 0; test
< NUM_TESTS
; ++test
) {
363 int offset
= test_offsets
[test
];
364 controller
.GoToOffset(offset
);
365 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
367 // Check that the GoToOffset will land on the expected page.
368 EXPECT_EQ(urls
[url_index
], controller
.GetPendingEntry()->GetVirtualURL());
369 main_test_rfh()->PrepareForCommit();
370 main_test_rfh()->SendNavigate(url_index
, entry_id
, false, urls
[url_index
]);
371 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
372 navigation_entry_committed_counter_
= 0;
373 // Check that we can go to any valid offset into the history.
374 for (size_t j
= 0; j
< urls
.size(); ++j
)
375 EXPECT_TRUE(controller
.CanGoToOffset(j
- url_index
));
376 // Check that we can't go beyond the beginning or end of the history.
377 EXPECT_FALSE(controller
.CanGoToOffset(-(url_index
+ 1)));
378 EXPECT_FALSE(controller
.CanGoToOffset(urls
.size() - url_index
));
382 TEST_F(NavigationControllerTest
, LoadURL
) {
383 NavigationControllerImpl
& controller
= controller_impl();
384 TestNotificationTracker notifications
;
385 RegisterForAllNavNotifications(¬ifications
, &controller
);
387 const GURL
url1("http://foo1");
388 const GURL
url2("http://foo2");
391 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
392 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
393 // Creating a pending notification should not have issued any of the
394 // notifications we're listening for.
395 EXPECT_EQ(0U, notifications
.size());
397 // The load should now be pending.
398 EXPECT_EQ(controller
.GetEntryCount(), 0);
399 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), -1);
400 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
401 EXPECT_FALSE(controller
.GetLastCommittedEntry());
402 ASSERT_TRUE(controller
.GetPendingEntry());
403 EXPECT_EQ(controller
.GetPendingEntry(), controller
.GetVisibleEntry());
404 EXPECT_FALSE(controller
.CanGoBack());
405 EXPECT_FALSE(controller
.CanGoForward());
406 EXPECT_EQ(contents()->GetMaxPageID(), -1);
408 // Neither the timestamp nor the status code should have been set yet.
409 EXPECT_TRUE(controller
.GetPendingEntry()->GetTimestamp().is_null());
410 EXPECT_EQ(0, controller
.GetPendingEntry()->GetHttpStatusCode());
412 // We should have gotten no notifications from the preceeding checks.
413 EXPECT_EQ(0U, notifications
.size());
415 main_test_rfh()->PrepareForCommit();
416 main_test_rfh()->SendNavigate(0, entry_id
, true, url1
);
417 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
418 navigation_entry_committed_counter_
= 0;
420 // The load should now be committed.
421 EXPECT_EQ(controller
.GetEntryCount(), 1);
422 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
423 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
424 EXPECT_TRUE(controller
.GetLastCommittedEntry());
425 EXPECT_FALSE(controller
.GetPendingEntry());
426 ASSERT_TRUE(controller
.GetVisibleEntry());
427 EXPECT_FALSE(controller
.CanGoBack());
428 EXPECT_FALSE(controller
.CanGoForward());
429 EXPECT_EQ(contents()->GetMaxPageID(), 0);
430 EXPECT_EQ(0, controller
.GetLastCommittedEntry()->bindings());
432 // The timestamp should have been set.
433 EXPECT_FALSE(controller
.GetVisibleEntry()->GetTimestamp().is_null());
437 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
438 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
440 // The load should now be pending.
441 EXPECT_EQ(controller
.GetEntryCount(), 1);
442 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
443 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
444 EXPECT_TRUE(controller
.GetLastCommittedEntry());
445 ASSERT_TRUE(controller
.GetPendingEntry());
446 EXPECT_EQ(controller
.GetPendingEntry(), controller
.GetVisibleEntry());
447 // TODO(darin): maybe this should really be true?
448 EXPECT_FALSE(controller
.CanGoBack());
449 EXPECT_FALSE(controller
.CanGoForward());
450 EXPECT_EQ(contents()->GetMaxPageID(), 0);
452 EXPECT_TRUE(controller
.GetPendingEntry()->GetTimestamp().is_null());
454 // Simulate the beforeunload ack for the cross-site transition, and then the
456 main_test_rfh()->PrepareForCommit();
457 contents()->GetPendingMainFrame()->SendNavigate(1, entry_id
, true, url2
);
458 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
459 navigation_entry_committed_counter_
= 0;
461 // The load should now be committed.
462 EXPECT_EQ(controller
.GetEntryCount(), 2);
463 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 1);
464 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
465 EXPECT_TRUE(controller
.GetLastCommittedEntry());
466 EXPECT_FALSE(controller
.GetPendingEntry());
467 ASSERT_TRUE(controller
.GetVisibleEntry());
468 EXPECT_TRUE(controller
.CanGoBack());
469 EXPECT_FALSE(controller
.CanGoForward());
470 EXPECT_EQ(contents()->GetMaxPageID(), 1);
472 EXPECT_FALSE(controller
.GetVisibleEntry()->GetTimestamp().is_null());
477 base::Time
GetFixedTime(base::Time time
) {
483 TEST_F(NavigationControllerTest
, LoadURLSameTime
) {
484 NavigationControllerImpl
& controller
= controller_impl();
485 TestNotificationTracker notifications
;
486 RegisterForAllNavNotifications(¬ifications
, &controller
);
488 // Set the clock to always return a timestamp of 1.
489 controller
.SetGetTimestampCallbackForTest(
490 base::Bind(&GetFixedTime
, base::Time::FromInternalValue(1)));
492 const GURL
url1("http://foo1");
493 const GURL
url2("http://foo2");
496 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
497 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
499 main_test_rfh()->PrepareForCommit();
500 main_test_rfh()->SendNavigate(0, entry_id
, true, url1
);
501 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
502 navigation_entry_committed_counter_
= 0;
506 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
507 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
509 // Simulate the beforeunload ack for the cross-site transition, and then the
511 main_test_rfh()->PrepareForCommit();
512 contents()->GetPendingMainFrame()->SendNavigate(1, entry_id
, true, url2
);
513 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
514 navigation_entry_committed_counter_
= 0;
516 // The two loads should now be committed.
517 ASSERT_EQ(controller
.GetEntryCount(), 2);
519 // Timestamps should be distinct despite the clock returning the
522 controller
.GetEntryAtIndex(0)->GetTimestamp().ToInternalValue());
524 controller
.GetEntryAtIndex(1)->GetTimestamp().ToInternalValue());
527 void CheckNavigationEntryMatchLoadParams(
528 NavigationController::LoadURLParams
& load_params
,
529 NavigationEntryImpl
* entry
) {
530 EXPECT_EQ(load_params
.url
, entry
->GetURL());
531 EXPECT_EQ(load_params
.referrer
.url
, entry
->GetReferrer().url
);
532 EXPECT_EQ(load_params
.referrer
.policy
, entry
->GetReferrer().policy
);
533 EXPECT_EQ(load_params
.transition_type
, entry
->GetTransitionType());
534 EXPECT_EQ(load_params
.extra_headers
, entry
->extra_headers());
536 EXPECT_EQ(load_params
.is_renderer_initiated
, entry
->is_renderer_initiated());
537 EXPECT_EQ(load_params
.base_url_for_data_url
, entry
->GetBaseURLForDataURL());
538 if (!load_params
.virtual_url_for_data_url
.is_empty()) {
539 EXPECT_EQ(load_params
.virtual_url_for_data_url
, entry
->GetVirtualURL());
541 if (NavigationController::UA_OVERRIDE_INHERIT
!=
542 load_params
.override_user_agent
) {
543 bool should_override
= (NavigationController::UA_OVERRIDE_TRUE
==
544 load_params
.override_user_agent
);
545 EXPECT_EQ(should_override
, entry
->GetIsOverridingUserAgent());
547 EXPECT_EQ(load_params
.browser_initiated_post_data
.get(),
548 entry
->GetBrowserInitiatedPostData());
549 EXPECT_EQ(load_params
.transferred_global_request_id
,
550 entry
->transferred_global_request_id());
553 TEST_F(NavigationControllerTest
, LoadURLWithParams
) {
554 NavigationControllerImpl
& controller
= controller_impl();
556 NavigationController::LoadURLParams
load_params(GURL("http://foo"));
557 load_params
.referrer
=
558 Referrer(GURL("http://referrer"), blink::WebReferrerPolicyDefault
);
559 load_params
.transition_type
= ui::PAGE_TRANSITION_GENERATED
;
560 load_params
.extra_headers
= "content-type: text/plain";
561 load_params
.load_type
= NavigationController::LOAD_TYPE_DEFAULT
;
562 load_params
.is_renderer_initiated
= true;
563 load_params
.override_user_agent
= NavigationController::UA_OVERRIDE_TRUE
;
564 load_params
.transferred_global_request_id
= GlobalRequestID(2, 3);
566 controller
.LoadURLWithParams(load_params
);
567 NavigationEntryImpl
* entry
= controller
.GetPendingEntry();
569 // The timestamp should not have been set yet.
571 EXPECT_TRUE(entry
->GetTimestamp().is_null());
573 CheckNavigationEntryMatchLoadParams(load_params
, entry
);
576 TEST_F(NavigationControllerTest
, LoadURLWithExtraParams_Data
) {
577 NavigationControllerImpl
& controller
= controller_impl();
579 NavigationController::LoadURLParams
load_params(
580 GURL("data:text/html,dataurl"));
581 load_params
.load_type
= NavigationController::LOAD_TYPE_DATA
;
582 load_params
.base_url_for_data_url
= GURL("http://foo");
583 load_params
.virtual_url_for_data_url
= GURL(url::kAboutBlankURL
);
584 load_params
.override_user_agent
= NavigationController::UA_OVERRIDE_FALSE
;
586 controller
.LoadURLWithParams(load_params
);
587 NavigationEntryImpl
* entry
= controller
.GetPendingEntry();
589 CheckNavigationEntryMatchLoadParams(load_params
, entry
);
592 TEST_F(NavigationControllerTest
, LoadURLWithExtraParams_HttpPost
) {
593 NavigationControllerImpl
& controller
= controller_impl();
595 NavigationController::LoadURLParams
load_params(GURL("https://posturl"));
596 load_params
.transition_type
= ui::PAGE_TRANSITION_TYPED
;
597 load_params
.load_type
=
598 NavigationController::LOAD_TYPE_BROWSER_INITIATED_HTTP_POST
;
599 load_params
.override_user_agent
= NavigationController::UA_OVERRIDE_TRUE
;
602 const unsigned char* raw_data
=
603 reinterpret_cast<const unsigned char*>("d\n\0a2");
604 const int length
= 5;
605 std::vector
<unsigned char> post_data_vector(raw_data
, raw_data
+length
);
606 scoped_refptr
<base::RefCountedBytes
> data
=
607 base::RefCountedBytes::TakeVector(&post_data_vector
);
608 load_params
.browser_initiated_post_data
= data
.get();
610 controller
.LoadURLWithParams(load_params
);
611 NavigationEntryImpl
* entry
= controller
.GetPendingEntry();
613 CheckNavigationEntryMatchLoadParams(load_params
, entry
);
616 // Tests what happens when the same page is loaded again. Should not create a
617 // new session history entry. This is what happens when you press enter in the
618 // URL bar to reload: a pending entry is created and then it is discarded when
619 // the load commits (because WebCore didn't actually make a new entry).
620 TEST_F(NavigationControllerTest
, LoadURL_SamePage
) {
621 NavigationControllerImpl
& controller
= controller_impl();
622 TestNotificationTracker notifications
;
623 RegisterForAllNavNotifications(¬ifications
, &controller
);
625 const GURL
url1("http://foo1");
628 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
629 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
630 EXPECT_EQ(0U, notifications
.size());
631 main_test_rfh()->PrepareForCommit();
632 main_test_rfh()->SendNavigateWithTransition(
633 0, entry_id
, true, url1
, ui::PAGE_TRANSITION_TYPED
);
634 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
635 navigation_entry_committed_counter_
= 0;
637 ASSERT_TRUE(controller
.GetVisibleEntry());
638 const base::Time timestamp
= controller
.GetVisibleEntry()->GetTimestamp();
639 EXPECT_FALSE(timestamp
.is_null());
642 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
643 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
644 EXPECT_EQ(0U, notifications
.size());
645 main_test_rfh()->PrepareForCommit();
646 main_test_rfh()->SendNavigateWithTransition(
647 0, entry_id
, false, url1
, ui::PAGE_TRANSITION_TYPED
);
648 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
649 navigation_entry_committed_counter_
= 0;
651 // We should not have produced a new session history entry.
652 EXPECT_EQ(controller
.GetEntryCount(), 1);
653 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
654 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
655 EXPECT_TRUE(controller
.GetLastCommittedEntry());
656 EXPECT_FALSE(controller
.GetPendingEntry());
657 ASSERT_TRUE(controller
.GetVisibleEntry());
658 EXPECT_FALSE(controller
.CanGoBack());
659 EXPECT_FALSE(controller
.CanGoForward());
661 // The timestamp should have been updated.
663 // TODO(akalin): Change this EXPECT_GE (and other similar ones) to
664 // EXPECT_GT once we guarantee that timestamps are unique.
665 EXPECT_GE(controller
.GetVisibleEntry()->GetTimestamp(), timestamp
);
668 // Load the same page twice, once as a GET and once as a POST.
669 // We should update the post state on the NavigationEntry.
670 TEST_F(NavigationControllerTest
, LoadURL_SamePage_DifferentMethod
) {
671 NavigationControllerImpl
& controller
= controller_impl();
672 TestNotificationTracker notifications
;
673 RegisterForAllNavNotifications(¬ifications
, &controller
);
675 const GURL
url1("http://foo1");
678 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
679 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
681 params
.nav_entry_id
= controller
.GetPendingEntry()->GetUniqueID();
682 params
.did_create_new_entry
= true;
684 params
.transition
= ui::PAGE_TRANSITION_TYPED
;
685 params
.is_post
= true;
686 params
.post_id
= 123;
687 params
.page_state
= PageState::CreateForTesting(url1
, false, 0, 0);
688 main_test_rfh()->PrepareForCommit();
689 main_test_rfh()->SendNavigateWithParams(¶ms
);
691 // The post data should be visible.
692 NavigationEntry
* entry
= controller
.GetVisibleEntry();
694 EXPECT_TRUE(entry
->GetHasPostData());
695 EXPECT_EQ(entry
->GetPostID(), 123);
698 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
699 main_test_rfh()->PrepareForCommit();
700 main_test_rfh()->SendNavigateWithTransition(
701 0, controller
.GetPendingEntry()->GetUniqueID(),
702 false, url1
, ui::PAGE_TRANSITION_TYPED
);
704 // We should not have produced a new session history entry.
705 ASSERT_EQ(controller
.GetVisibleEntry(), entry
);
707 // The post data should have been cleared due to the GET.
708 EXPECT_FALSE(entry
->GetHasPostData());
709 EXPECT_EQ(entry
->GetPostID(), 0);
712 // Tests loading a URL but discarding it before the load commits.
713 TEST_F(NavigationControllerTest
, LoadURL_Discarded
) {
714 NavigationControllerImpl
& controller
= controller_impl();
715 TestNotificationTracker notifications
;
716 RegisterForAllNavNotifications(¬ifications
, &controller
);
718 const GURL
url1("http://foo1");
719 const GURL
url2("http://foo2");
722 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
723 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
724 EXPECT_EQ(0U, notifications
.size());
725 main_test_rfh()->PrepareForCommit();
726 main_test_rfh()->SendNavigate(0, entry_id
, true, url1
);
727 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
728 navigation_entry_committed_counter_
= 0;
730 ASSERT_TRUE(controller
.GetVisibleEntry());
731 const base::Time timestamp
= controller
.GetVisibleEntry()->GetTimestamp();
732 EXPECT_FALSE(timestamp
.is_null());
735 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
736 controller
.DiscardNonCommittedEntries();
737 EXPECT_EQ(0U, notifications
.size());
739 // Should not have produced a new session history entry.
740 EXPECT_EQ(controller
.GetEntryCount(), 1);
741 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
742 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
743 EXPECT_TRUE(controller
.GetLastCommittedEntry());
744 EXPECT_FALSE(controller
.GetPendingEntry());
745 ASSERT_TRUE(controller
.GetVisibleEntry());
746 EXPECT_FALSE(controller
.CanGoBack());
747 EXPECT_FALSE(controller
.CanGoForward());
749 // Timestamp should not have changed.
750 EXPECT_EQ(timestamp
, controller
.GetVisibleEntry()->GetTimestamp());
753 // Tests navigations that come in unrequested. This happens when the user
754 // navigates from the web page, and here we test that there is no pending entry.
755 TEST_F(NavigationControllerTest
, LoadURL_NoPending
) {
756 NavigationControllerImpl
& controller
= controller_impl();
757 TestNotificationTracker notifications
;
758 RegisterForAllNavNotifications(¬ifications
, &controller
);
760 // First make an existing committed entry.
761 const GURL
kExistingURL1("http://eh");
763 kExistingURL1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
764 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
765 main_test_rfh()->PrepareForCommit();
766 main_test_rfh()->SendNavigate(0, entry_id
, true, kExistingURL1
);
767 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
768 navigation_entry_committed_counter_
= 0;
770 // Do a new navigation without making a pending one.
771 const GURL
kNewURL("http://see");
772 main_test_rfh()->NavigateAndCommitRendererInitiated(99, true, kNewURL
);
774 // There should no longer be any pending entry, and the second navigation we
775 // just made should be committed.
776 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
777 navigation_entry_committed_counter_
= 0;
778 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
779 EXPECT_EQ(1, controller
.GetLastCommittedEntryIndex());
780 EXPECT_EQ(kNewURL
, controller
.GetVisibleEntry()->GetURL());
783 // Tests navigating to a new URL when there is a new pending navigation that is
784 // not the one that just loaded. This will happen if the user types in a URL to
785 // somewhere slow, and then navigates the current page before the typed URL
787 TEST_F(NavigationControllerTest
, LoadURL_NewPending
) {
788 NavigationControllerImpl
& controller
= controller_impl();
789 TestNotificationTracker notifications
;
790 RegisterForAllNavNotifications(¬ifications
, &controller
);
792 // First make an existing committed entry.
793 const GURL
kExistingURL1("http://eh");
795 kExistingURL1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
796 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
797 main_test_rfh()->PrepareForCommit();
798 main_test_rfh()->SendNavigate(0, entry_id
, true, kExistingURL1
);
799 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
800 navigation_entry_committed_counter_
= 0;
802 // Make a pending entry to somewhere new.
803 const GURL
kExistingURL2("http://bee");
805 kExistingURL2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
806 EXPECT_EQ(0U, notifications
.size());
808 // After the beforeunload but before it commits...
809 main_test_rfh()->PrepareForCommit();
811 // ... Do a new navigation.
812 const GURL
kNewURL("http://see");
813 main_test_rfh()->SendRendererInitiatedNavigationRequest(kNewURL
, true);
814 main_test_rfh()->PrepareForCommit();
815 contents()->GetMainFrame()->SendNavigate(3, 0, true, kNewURL
);
817 // There should no longer be any pending entry, and the third navigation we
818 // just made should be committed.
819 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
820 navigation_entry_committed_counter_
= 0;
821 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
822 EXPECT_EQ(1, controller
.GetLastCommittedEntryIndex());
823 EXPECT_EQ(kNewURL
, controller
.GetVisibleEntry()->GetURL());
826 // Tests navigating to a new URL when there is a pending back/forward
827 // navigation. This will happen if the user hits back, but before that commits,
828 // they navigate somewhere new.
829 TEST_F(NavigationControllerTest
, LoadURL_ExistingPending
) {
830 NavigationControllerImpl
& controller
= controller_impl();
831 TestNotificationTracker notifications
;
832 RegisterForAllNavNotifications(¬ifications
, &controller
);
834 // First make some history.
835 const GURL
kExistingURL1("http://foo/eh");
837 kExistingURL1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
838 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
839 main_test_rfh()->PrepareForCommit();
840 main_test_rfh()->SendNavigate(0, entry_id
, true, kExistingURL1
);
841 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
842 navigation_entry_committed_counter_
= 0;
844 const GURL
kExistingURL2("http://foo/bee");
846 kExistingURL2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
847 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
848 main_test_rfh()->PrepareForCommit();
849 main_test_rfh()->SendNavigate(1, entry_id
, true, kExistingURL2
);
850 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
851 navigation_entry_committed_counter_
= 0;
853 // Now make a pending back/forward navigation. The zeroth entry should be
856 EXPECT_EQ(0U, notifications
.size());
857 EXPECT_EQ(0, controller
.GetPendingEntryIndex());
858 EXPECT_EQ(1, controller
.GetLastCommittedEntryIndex());
860 // Before that commits, do a new navigation.
861 const GURL
kNewURL("http://foo/see");
862 main_test_rfh()->SendRendererInitiatedNavigationRequest(kNewURL
, true);
863 main_test_rfh()->PrepareForCommit();
864 main_test_rfh()->SendNavigate(3, 0, true, kNewURL
);
866 // There should no longer be any pending entry, and the new navigation we
867 // just made should be committed.
868 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
869 navigation_entry_committed_counter_
= 0;
870 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
871 EXPECT_EQ(2, controller
.GetLastCommittedEntryIndex());
872 EXPECT_EQ(kNewURL
, controller
.GetVisibleEntry()->GetURL());
875 // Tests navigating to a new URL when there is a pending back/forward
876 // navigation to a cross-process, privileged URL. This will happen if the user
877 // hits back, but before that commits, they navigate somewhere new.
878 TEST_F(NavigationControllerTest
, LoadURL_PrivilegedPending
) {
879 NavigationControllerImpl
& controller
= controller_impl();
880 TestNotificationTracker notifications
;
881 RegisterForAllNavNotifications(¬ifications
, &controller
);
883 // First make some history, starting with a privileged URL.
884 const GURL
kExistingURL1("http://privileged");
886 kExistingURL1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
887 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
888 // Pretend it has bindings so we can tell if we incorrectly copy it.
889 main_test_rfh()->GetRenderViewHost()->AllowBindings(2);
890 main_test_rfh()->PrepareForCommit();
891 main_test_rfh()->SendNavigate(0, entry_id
, true, kExistingURL1
);
892 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
893 navigation_entry_committed_counter_
= 0;
895 // Navigate cross-process to a second URL.
896 const GURL
kExistingURL2("http://foo/eh");
898 kExistingURL2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
899 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
900 main_test_rfh()->PrepareForCommit();
901 TestRenderFrameHost
* foo_rfh
= contents()->GetPendingMainFrame();
902 foo_rfh
->SendNavigate(1, entry_id
, true, kExistingURL2
);
903 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
904 navigation_entry_committed_counter_
= 0;
906 // Now make a pending back/forward navigation to a privileged entry.
907 // The zeroth entry should be pending.
909 foo_rfh
->SendBeforeUnloadACK(true);
910 EXPECT_EQ(0U, notifications
.size());
911 EXPECT_EQ(0, controller
.GetPendingEntryIndex());
912 EXPECT_EQ(1, controller
.GetLastCommittedEntryIndex());
913 EXPECT_EQ(2, controller
.GetPendingEntry()->bindings());
915 // Before that commits, do a new navigation.
916 const GURL
kNewURL("http://foo/bee");
917 foo_rfh
->SendRendererInitiatedNavigationRequest(kNewURL
, true);
918 foo_rfh
->PrepareForCommit();
919 foo_rfh
->SendNavigate(3, 0, true, kNewURL
);
921 // There should no longer be any pending entry, and the new navigation we
922 // just made should be committed.
923 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
924 navigation_entry_committed_counter_
= 0;
925 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
926 EXPECT_EQ(2, controller
.GetLastCommittedEntryIndex());
927 EXPECT_EQ(kNewURL
, controller
.GetVisibleEntry()->GetURL());
928 EXPECT_EQ(0, controller
.GetLastCommittedEntry()->bindings());
931 // Tests navigating to an existing URL when there is a pending new navigation.
932 // This will happen if the user enters a URL, but before that commits, the
933 // current page fires history.back().
934 TEST_F(NavigationControllerTest
, LoadURL_BackPreemptsPending
) {
935 NavigationControllerImpl
& controller
= controller_impl();
936 TestNotificationTracker notifications
;
937 RegisterForAllNavNotifications(¬ifications
, &controller
);
939 // First make some history.
940 const GURL
kExistingURL1("http://foo/eh");
942 kExistingURL1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
943 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
944 main_test_rfh()->PrepareForCommit();
945 main_test_rfh()->SendNavigate(0, entry_id
, true, kExistingURL1
);
946 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
947 navigation_entry_committed_counter_
= 0;
949 const GURL
kExistingURL2("http://foo/bee");
951 kExistingURL2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
952 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
953 main_test_rfh()->PrepareForCommit();
954 main_test_rfh()->SendNavigate(1, entry_id
, true, kExistingURL2
);
955 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
956 navigation_entry_committed_counter_
= 0;
958 // A back navigation comes in from the renderer...
959 controller
.GoToOffset(-1);
960 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
962 // ...while the user tries to navigate to a new page...
963 const GURL
kNewURL("http://foo/see");
965 kNewURL
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
966 EXPECT_EQ(0U, notifications
.size());
967 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
968 EXPECT_EQ(1, controller
.GetLastCommittedEntryIndex());
970 // ...and the back navigation commits.
971 main_test_rfh()->PrepareForCommit();
972 main_test_rfh()->SendNavigate(0, entry_id
, false, kExistingURL1
);
974 // There should no longer be any pending entry, and the back navigation should
976 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
977 navigation_entry_committed_counter_
= 0;
978 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
979 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
980 EXPECT_EQ(kExistingURL1
, controller
.GetVisibleEntry()->GetURL());
983 // Tests an ignored navigation when there is a pending new navigation.
984 // This will happen if the user enters a URL, but before that commits, the
985 // current blank page reloads. See http://crbug.com/77507.
986 TEST_F(NavigationControllerTest
, LoadURL_IgnorePreemptsPending
) {
987 NavigationControllerImpl
& controller
= controller_impl();
988 TestNotificationTracker notifications
;
989 RegisterForAllNavNotifications(¬ifications
, &controller
);
991 // Set a WebContentsDelegate to listen for state changes.
992 scoped_ptr
<TestWebContentsDelegate
> delegate(new TestWebContentsDelegate());
993 EXPECT_FALSE(contents()->GetDelegate());
994 contents()->SetDelegate(delegate
.get());
996 // Without any navigations, the renderer starts at about:blank.
997 const GURL
kExistingURL(url::kAboutBlankURL
);
999 // Now make a pending new navigation.
1000 const GURL
kNewURL("http://eh");
1002 kNewURL
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1003 EXPECT_EQ(0U, notifications
.size());
1004 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
1005 EXPECT_TRUE(controller
.GetPendingEntry());
1006 EXPECT_EQ(-1, controller
.GetLastCommittedEntryIndex());
1007 EXPECT_EQ(1, delegate
->navigation_state_change_count());
1009 // Before that commits, a document.write and location.reload can cause the
1010 // renderer to send a FrameNavigate with page_id -1 and nav_entry_id 0.
1011 // PlzNavigate: this will stop the old navigation and start a new one.
1012 main_test_rfh()->SendRendererInitiatedNavigationRequest(kExistingURL
, true);
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 contents()->GetPendingMainFrame()->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_for_guests_only(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
1336 void SetOriginalURL(const GURL
& url
,
1337 FrameHostMsg_DidCommitProvisionalLoad_Params
* params
) {
1338 params
->original_request_url
= url
;
1342 TEST_F(NavigationControllerTest
, ReloadOriginalRequestURL
) {
1343 NavigationControllerImpl
& controller
= controller_impl();
1344 TestNotificationTracker notifications
;
1345 RegisterForAllNavNotifications(¬ifications
, &controller
);
1347 const GURL
original_url("http://foo1");
1348 const GURL
final_url("http://foo2");
1349 auto set_original_url_callback
= base::Bind(SetOriginalURL
, original_url
);
1351 // Load up the original URL, but get redirected.
1353 original_url
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1354 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1355 EXPECT_EQ(0U, notifications
.size());
1356 main_test_rfh()->PrepareForCommitWithServerRedirect(final_url
);
1357 main_test_rfh()->SendNavigateWithModificationCallback(
1358 0, entry_id
, true, final_url
, set_original_url_callback
);
1359 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1360 navigation_entry_committed_counter_
= 0;
1361 entry_id
= controller
.GetLastCommittedEntry()->GetUniqueID();
1363 // The NavigationEntry should save both the original URL and the final
1366 original_url
, controller
.GetVisibleEntry()->GetOriginalRequestURL());
1367 EXPECT_EQ(final_url
, controller
.GetVisibleEntry()->GetURL());
1369 // Reload using the original URL.
1370 controller
.GetVisibleEntry()->SetTitle(base::ASCIIToUTF16("Title"));
1371 controller
.ReloadOriginalRequestURL(false);
1372 EXPECT_EQ(0U, notifications
.size());
1374 // The reload is pending. The request should point to the original URL.
1375 EXPECT_EQ(original_url
, navigated_url());
1376 EXPECT_EQ(controller
.GetEntryCount(), 1);
1377 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1378 EXPECT_EQ(controller
.GetPendingEntryIndex(), 0);
1379 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1380 EXPECT_TRUE(controller
.GetPendingEntry());
1381 EXPECT_FALSE(controller
.CanGoBack());
1382 EXPECT_FALSE(controller
.CanGoForward());
1384 // Make sure the title has been cleared (will be redrawn just after reload).
1385 // Avoids a stale cached title when the new page being reloaded has no title.
1386 // See http://crbug.com/96041.
1387 EXPECT_TRUE(controller
.GetVisibleEntry()->GetTitle().empty());
1389 // Send that the navigation has proceeded; say it got redirected again.
1390 main_test_rfh()->PrepareForCommitWithServerRedirect(final_url
);
1391 main_test_rfh()->SendNavigate(0, entry_id
, false, final_url
);
1392 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1393 navigation_entry_committed_counter_
= 0;
1395 // Now the reload is committed.
1396 EXPECT_EQ(controller
.GetEntryCount(), 1);
1397 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1398 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1399 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1400 EXPECT_FALSE(controller
.GetPendingEntry());
1401 EXPECT_FALSE(controller
.CanGoBack());
1402 EXPECT_FALSE(controller
.CanGoForward());
1405 #endif // !defined(OS_ANDROID)
1407 // Test that certain non-persisted NavigationEntryImpl values get reset after
1409 TEST_F(NavigationControllerTest
, ResetEntryValuesAfterCommit
) {
1410 NavigationControllerImpl
& controller
= controller_impl();
1412 // The value of "should replace entry" will be tested, but it's an error to
1413 // specify it when there are no entries. Create a simple entry to be replaced.
1414 const GURL
url0("http://foo/0");
1416 url0
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1417 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1418 main_test_rfh()->PrepareForCommit();
1419 main_test_rfh()->SendNavigate(0, entry_id
, true, url0
);
1421 // Set up the pending entry.
1422 const GURL
url1("http://foo/1");
1424 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1425 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1427 // Set up some sample values.
1428 const unsigned char* raw_data
=
1429 reinterpret_cast<const unsigned char*>("post\n\n\0data");
1430 const int length
= 11;
1431 std::vector
<unsigned char> post_data_vector(raw_data
, raw_data
+length
);
1432 scoped_refptr
<base::RefCountedBytes
> post_data
=
1433 base::RefCountedBytes::TakeVector(&post_data_vector
);
1434 GlobalRequestID
transfer_id(3, 4);
1436 // Set non-persisted values on the pending entry.
1437 NavigationEntryImpl
* pending_entry
= controller
.GetPendingEntry();
1438 pending_entry
->SetBrowserInitiatedPostData(post_data
.get());
1439 pending_entry
->set_is_renderer_initiated(true);
1440 pending_entry
->set_transferred_global_request_id(transfer_id
);
1441 pending_entry
->set_should_replace_entry(true);
1442 pending_entry
->set_should_clear_history_list(true);
1443 EXPECT_EQ(post_data
.get(), pending_entry
->GetBrowserInitiatedPostData());
1444 EXPECT_TRUE(pending_entry
->is_renderer_initiated());
1445 EXPECT_EQ(transfer_id
, pending_entry
->transferred_global_request_id());
1446 EXPECT_TRUE(pending_entry
->should_replace_entry());
1447 EXPECT_TRUE(pending_entry
->should_clear_history_list());
1449 // Fake a commit response.
1450 main_test_rfh()->PrepareForCommit();
1451 main_test_rfh()->SendNavigate(1, entry_id
, true, url1
);
1453 // Certain values that are only used for pending entries get reset after
1455 NavigationEntryImpl
* committed_entry
= controller
.GetLastCommittedEntry();
1456 EXPECT_FALSE(committed_entry
->GetBrowserInitiatedPostData());
1457 EXPECT_FALSE(committed_entry
->is_renderer_initiated());
1458 EXPECT_EQ(GlobalRequestID(-1, -1),
1459 committed_entry
->transferred_global_request_id());
1460 EXPECT_FALSE(committed_entry
->should_replace_entry());
1461 EXPECT_FALSE(committed_entry
->should_clear_history_list());
1465 void SetRedirects(const std::vector
<GURL
>& redirects
,
1466 FrameHostMsg_DidCommitProvisionalLoad_Params
* params
) {
1467 params
->redirects
= redirects
;
1471 // Test that Redirects are preserved after a commit.
1472 TEST_F(NavigationControllerTest
, RedirectsAreNotResetByCommit
) {
1473 NavigationControllerImpl
& controller
= controller_impl();
1474 const GURL
url1("http://foo1");
1475 const GURL
url2("http://foo2");
1477 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1478 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1480 // Set up some redirect values.
1481 std::vector
<GURL
> redirects
;
1482 redirects
.push_back(url2
);
1483 auto set_redirects_callback
= base::Bind(SetRedirects
, redirects
);
1485 // Set redirects on the pending entry.
1486 NavigationEntryImpl
* pending_entry
= controller
.GetPendingEntry();
1487 pending_entry
->SetRedirectChain(redirects
);
1488 EXPECT_EQ(1U, pending_entry
->GetRedirectChain().size());
1489 EXPECT_EQ(url2
, pending_entry
->GetRedirectChain()[0]);
1491 // Normal navigation will preserve redirects in the committed entry.
1492 main_test_rfh()->PrepareForCommitWithServerRedirect(url2
);
1493 main_test_rfh()->SendNavigateWithModificationCallback(0, entry_id
, true, url1
,
1494 set_redirects_callback
);
1495 NavigationEntryImpl
* committed_entry
= controller
.GetLastCommittedEntry();
1496 ASSERT_EQ(1U, committed_entry
->GetRedirectChain().size());
1497 EXPECT_EQ(url2
, committed_entry
->GetRedirectChain()[0]);
1500 // Tests what happens when we navigate back successfully
1501 TEST_F(NavigationControllerTest
, Back
) {
1502 NavigationControllerImpl
& controller
= controller_impl();
1503 TestNotificationTracker notifications
;
1504 RegisterForAllNavNotifications(¬ifications
, &controller
);
1506 const GURL
url1("http://foo1");
1507 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url1
);
1508 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1509 navigation_entry_committed_counter_
= 0;
1511 const GURL
url2("http://foo2");
1512 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url2
);
1513 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1514 navigation_entry_committed_counter_
= 0;
1516 controller
.GoBack();
1517 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1518 EXPECT_EQ(0U, notifications
.size());
1520 // We should now have a pending navigation to go back.
1521 EXPECT_EQ(controller
.GetEntryCount(), 2);
1522 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 1);
1523 EXPECT_EQ(controller
.GetPendingEntryIndex(), 0);
1524 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1525 EXPECT_TRUE(controller
.GetPendingEntry());
1526 EXPECT_FALSE(controller
.CanGoBack());
1527 EXPECT_FALSE(controller
.CanGoToOffset(-1));
1528 EXPECT_TRUE(controller
.CanGoForward());
1529 EXPECT_TRUE(controller
.CanGoToOffset(1));
1530 EXPECT_FALSE(controller
.CanGoToOffset(2)); // Cannot go forward 2 steps.
1532 // Timestamp for entry 1 should be on or after that of entry 0.
1533 EXPECT_FALSE(controller
.GetEntryAtIndex(0)->GetTimestamp().is_null());
1534 EXPECT_GE(controller
.GetEntryAtIndex(1)->GetTimestamp(),
1535 controller
.GetEntryAtIndex(0)->GetTimestamp());
1537 main_test_rfh()->PrepareForCommit();
1538 main_test_rfh()->SendNavigate(0, entry_id
, false, url2
);
1539 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1540 navigation_entry_committed_counter_
= 0;
1542 // The back navigation completed successfully.
1543 EXPECT_EQ(controller
.GetEntryCount(), 2);
1544 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1545 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1546 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1547 EXPECT_FALSE(controller
.GetPendingEntry());
1548 EXPECT_FALSE(controller
.CanGoBack());
1549 EXPECT_FALSE(controller
.CanGoToOffset(-1));
1550 EXPECT_TRUE(controller
.CanGoForward());
1551 EXPECT_TRUE(controller
.CanGoToOffset(1));
1552 EXPECT_FALSE(controller
.CanGoToOffset(2)); // Cannot go foward 2 steps.
1554 // Timestamp for entry 0 should be on or after that of entry 1
1555 // (since we went back to it).
1556 EXPECT_GE(controller
.GetEntryAtIndex(0)->GetTimestamp(),
1557 controller
.GetEntryAtIndex(1)->GetTimestamp());
1560 // Tests what happens when a back navigation produces a new page.
1561 TEST_F(NavigationControllerTest
, Back_GeneratesNewPage
) {
1562 NavigationControllerImpl
& controller
= controller_impl();
1563 TestNotificationTracker notifications
;
1564 RegisterForAllNavNotifications(¬ifications
, &controller
);
1566 const GURL
url1("http://foo/1");
1567 const GURL
url2("http://foo/2");
1568 const GURL
url3("http://foo/3");
1571 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1572 int entry1_id
= controller
.GetPendingEntry()->GetUniqueID();
1573 main_test_rfh()->PrepareForCommit();
1574 main_test_rfh()->SendNavigate(0, entry1_id
, true, url1
);
1575 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1576 navigation_entry_committed_counter_
= 0;
1577 entry1_id
= controller
.GetLastCommittedEntry()->GetUniqueID();
1580 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1581 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1582 main_test_rfh()->PrepareForCommit();
1583 main_test_rfh()->SendNavigate(1, entry_id
, true, url2
);
1584 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1585 navigation_entry_committed_counter_
= 0;
1587 controller
.GoBack();
1588 EXPECT_EQ(0U, notifications
.size());
1590 // We should now have a pending navigation to go back.
1591 EXPECT_EQ(controller
.GetEntryCount(), 2);
1592 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 1);
1593 EXPECT_EQ(controller
.GetPendingEntryIndex(), 0);
1594 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1595 EXPECT_TRUE(controller
.GetPendingEntry());
1596 EXPECT_FALSE(controller
.CanGoBack());
1597 EXPECT_TRUE(controller
.CanGoForward());
1599 main_test_rfh()->PrepareForCommitWithServerRedirect(url3
);
1600 main_test_rfh()->SendNavigate(2, entry1_id
, true, url3
);
1601 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1602 navigation_entry_committed_counter_
= 0;
1604 // The back navigation resulted in a completely new navigation.
1605 // TODO(darin): perhaps this behavior will be confusing to users?
1606 EXPECT_EQ(controller
.GetEntryCount(), 3);
1607 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 2);
1608 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1609 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1610 EXPECT_FALSE(controller
.GetPendingEntry());
1611 EXPECT_TRUE(controller
.CanGoBack());
1612 EXPECT_FALSE(controller
.CanGoForward());
1615 // Receives a back message when there is a new pending navigation entry.
1616 TEST_F(NavigationControllerTest
, Back_NewPending
) {
1617 NavigationControllerImpl
& controller
= controller_impl();
1618 TestNotificationTracker notifications
;
1619 RegisterForAllNavNotifications(¬ifications
, &controller
);
1621 const GURL
kUrl1("http://foo1");
1622 const GURL
kUrl2("http://foo2");
1623 const GURL
kUrl3("http://foo3");
1625 // First navigate two places so we have some back history.
1626 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, kUrl1
);
1627 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1628 navigation_entry_committed_counter_
= 0;
1630 // controller.LoadURL(kUrl2, ui::PAGE_TRANSITION_TYPED);
1631 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, kUrl2
);
1632 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1633 navigation_entry_committed_counter_
= 0;
1635 // Now start a new pending navigation and go back before it commits.
1637 kUrl3
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1638 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
1639 EXPECT_EQ(kUrl3
, controller
.GetPendingEntry()->GetURL());
1640 controller
.GoBack();
1642 // The pending navigation should now be the "back" item and the new one
1644 EXPECT_EQ(0, controller
.GetPendingEntryIndex());
1645 EXPECT_EQ(kUrl1
, controller
.GetPendingEntry()->GetURL());
1648 // Tests what happens when we navigate forward successfully.
1649 TEST_F(NavigationControllerTest
, Forward
) {
1650 NavigationControllerImpl
& controller
= controller_impl();
1651 TestNotificationTracker notifications
;
1652 RegisterForAllNavNotifications(¬ifications
, &controller
);
1654 const GURL
url1("http://foo1");
1655 const GURL
url2("http://foo2");
1657 main_test_rfh()->SendRendererInitiatedNavigationRequest(url1
, true);
1658 main_test_rfh()->PrepareForCommit();
1659 main_test_rfh()->SendNavigate(0, 0, true, url1
);
1660 NavigationEntry
* entry1
= controller
.GetLastCommittedEntry();
1661 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1662 navigation_entry_committed_counter_
= 0;
1664 main_test_rfh()->SendRendererInitiatedNavigationRequest(url2
, true);
1665 main_test_rfh()->PrepareForCommit();
1666 main_test_rfh()->SendNavigate(1, 0, true, url2
);
1667 NavigationEntry
* entry2
= controller
.GetLastCommittedEntry();
1668 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1669 navigation_entry_committed_counter_
= 0;
1671 controller
.GoBack();
1672 main_test_rfh()->PrepareForCommit();
1673 main_test_rfh()->SendNavigate(0, entry1
->GetUniqueID(), false, url1
);
1674 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1675 navigation_entry_committed_counter_
= 0;
1677 controller
.GoForward();
1679 // We should now have a pending navigation to go forward.
1680 EXPECT_EQ(controller
.GetEntryCount(), 2);
1681 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1682 EXPECT_EQ(controller
.GetPendingEntryIndex(), 1);
1683 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1684 EXPECT_TRUE(controller
.GetPendingEntry());
1685 EXPECT_TRUE(controller
.CanGoBack());
1686 EXPECT_TRUE(controller
.CanGoToOffset(-1));
1687 EXPECT_FALSE(controller
.CanGoToOffset(-2)); // Cannot go back 2 steps.
1688 EXPECT_FALSE(controller
.CanGoForward());
1689 EXPECT_FALSE(controller
.CanGoToOffset(1));
1691 // Timestamp for entry 0 should be on or after that of entry 1
1692 // (since we went back to it).
1693 EXPECT_FALSE(controller
.GetEntryAtIndex(0)->GetTimestamp().is_null());
1694 EXPECT_GE(controller
.GetEntryAtIndex(0)->GetTimestamp(),
1695 controller
.GetEntryAtIndex(1)->GetTimestamp());
1697 main_test_rfh()->PrepareForCommit();
1698 main_test_rfh()->SendNavigate(1, entry2
->GetUniqueID(), false, url2
);
1699 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1700 navigation_entry_committed_counter_
= 0;
1702 // The forward navigation completed successfully.
1703 EXPECT_EQ(controller
.GetEntryCount(), 2);
1704 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 1);
1705 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1706 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1707 EXPECT_FALSE(controller
.GetPendingEntry());
1708 EXPECT_TRUE(controller
.CanGoBack());
1709 EXPECT_TRUE(controller
.CanGoToOffset(-1));
1710 EXPECT_FALSE(controller
.CanGoToOffset(-2)); // Cannot go back 2 steps.
1711 EXPECT_FALSE(controller
.CanGoForward());
1712 EXPECT_FALSE(controller
.CanGoToOffset(1));
1714 // Timestamp for entry 1 should be on or after that of entry 0
1715 // (since we went forward to it).
1716 EXPECT_GE(controller
.GetEntryAtIndex(1)->GetTimestamp(),
1717 controller
.GetEntryAtIndex(0)->GetTimestamp());
1720 // Tests what happens when a forward navigation produces a new page.
1721 TEST_F(NavigationControllerTest
, Forward_GeneratesNewPage
) {
1722 NavigationControllerImpl
& controller
= controller_impl();
1723 TestNotificationTracker notifications
;
1724 RegisterForAllNavNotifications(¬ifications
, &controller
);
1726 const GURL
url1("http://foo1");
1727 const GURL
url2("http://foo2");
1728 const GURL
url3("http://foo3");
1730 main_test_rfh()->SendRendererInitiatedNavigationRequest(url1
, true);
1731 main_test_rfh()->PrepareForCommit();
1732 main_test_rfh()->SendNavigate(0, 0, true, url1
);
1733 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1734 NavigationEntry
* entry1
= controller
.GetLastCommittedEntry();
1735 navigation_entry_committed_counter_
= 0;
1736 main_test_rfh()->SendRendererInitiatedNavigationRequest(url2
, true);
1737 main_test_rfh()->PrepareForCommit();
1738 main_test_rfh()->SendNavigate(1, 0, true, url2
);
1739 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1740 NavigationEntry
* entry2
= controller
.GetLastCommittedEntry();
1741 navigation_entry_committed_counter_
= 0;
1743 controller
.GoBack();
1744 main_test_rfh()->PrepareForCommit();
1745 main_test_rfh()->SendNavigate(0, entry1
->GetUniqueID(), false, url1
);
1746 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1747 navigation_entry_committed_counter_
= 0;
1749 controller
.GoForward();
1750 EXPECT_EQ(0U, notifications
.size());
1752 // Should now have a pending navigation to go forward.
1753 EXPECT_EQ(controller
.GetEntryCount(), 2);
1754 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1755 EXPECT_EQ(controller
.GetPendingEntryIndex(), 1);
1756 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1757 EXPECT_TRUE(controller
.GetPendingEntry());
1758 EXPECT_TRUE(controller
.CanGoBack());
1759 EXPECT_FALSE(controller
.CanGoForward());
1761 main_test_rfh()->PrepareForCommit();
1762 main_test_rfh()->SendNavigate(2, entry2
->GetUniqueID(), true, url3
);
1763 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1764 navigation_entry_committed_counter_
= 0;
1765 EXPECT_TRUE(notifications
.Check1AndReset(NOTIFICATION_NAV_LIST_PRUNED
));
1767 EXPECT_EQ(controller
.GetEntryCount(), 2);
1768 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 1);
1769 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1770 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1771 EXPECT_FALSE(controller
.GetPendingEntry());
1772 EXPECT_TRUE(controller
.CanGoBack());
1773 EXPECT_FALSE(controller
.CanGoForward());
1776 // Two consecutive navigations for the same URL entered in should be considered
1777 // as SAME_PAGE navigation even when we are redirected to some other page.
1778 TEST_F(NavigationControllerTest
, Redirect
) {
1779 NavigationControllerImpl
& controller
= controller_impl();
1780 TestNotificationTracker notifications
;
1781 RegisterForAllNavNotifications(¬ifications
, &controller
);
1783 const GURL
url1("http://foo1");
1784 const GURL
url2("http://foo2"); // Redirection target
1788 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1789 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1791 EXPECT_EQ(0U, notifications
.size());
1793 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
1795 params
.nav_entry_id
= entry_id
;
1796 params
.did_create_new_entry
= true;
1798 params
.transition
= ui::PAGE_TRANSITION_SERVER_REDIRECT
;
1799 params
.redirects
.push_back(GURL("http://foo1"));
1800 params
.redirects
.push_back(GURL("http://foo2"));
1801 params
.should_update_history
= false;
1802 params
.gesture
= NavigationGestureAuto
;
1803 params
.is_post
= false;
1804 params
.page_state
= PageState::CreateFromURL(url2
);
1806 LoadCommittedDetails details
;
1808 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
1810 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1811 navigation_entry_committed_counter_
= 0;
1815 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1816 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1818 EXPECT_TRUE(controller
.GetPendingEntry());
1819 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1820 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
1822 params
.nav_entry_id
= entry_id
;
1823 params
.did_create_new_entry
= false;
1825 EXPECT_EQ(0U, notifications
.size());
1826 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
1828 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1829 navigation_entry_committed_counter_
= 0;
1831 EXPECT_TRUE(details
.type
== NAVIGATION_TYPE_SAME_PAGE
);
1832 EXPECT_EQ(controller
.GetEntryCount(), 1);
1833 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1834 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1835 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1836 EXPECT_FALSE(controller
.GetPendingEntry());
1837 EXPECT_EQ(url2
, controller
.GetVisibleEntry()->GetURL());
1839 EXPECT_FALSE(controller
.CanGoBack());
1840 EXPECT_FALSE(controller
.CanGoForward());
1843 // Similar to Redirect above, but the first URL is requested by POST,
1844 // the second URL is requested by GET. NavigationEntry::has_post_data_
1845 // must be cleared. http://crbug.com/21245
1846 TEST_F(NavigationControllerTest
, PostThenRedirect
) {
1847 NavigationControllerImpl
& controller
= controller_impl();
1848 TestNotificationTracker notifications
;
1849 RegisterForAllNavNotifications(¬ifications
, &controller
);
1851 const GURL
url1("http://foo1");
1852 const GURL
url2("http://foo2"); // Redirection target
1854 // First request as POST.
1856 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1857 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1858 controller
.GetVisibleEntry()->SetHasPostData(true);
1860 EXPECT_EQ(0U, notifications
.size());
1862 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
1864 params
.nav_entry_id
= entry_id
;
1865 params
.did_create_new_entry
= true;
1867 params
.transition
= ui::PAGE_TRANSITION_SERVER_REDIRECT
;
1868 params
.redirects
.push_back(GURL("http://foo1"));
1869 params
.redirects
.push_back(GURL("http://foo2"));
1870 params
.should_update_history
= false;
1871 params
.gesture
= NavigationGestureAuto
;
1872 params
.is_post
= true;
1873 params
.page_state
= PageState::CreateFromURL(url2
);
1875 LoadCommittedDetails details
;
1877 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
1879 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1880 navigation_entry_committed_counter_
= 0;
1884 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1885 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1887 EXPECT_TRUE(controller
.GetPendingEntry());
1888 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1889 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
1891 params
.nav_entry_id
= entry_id
;
1892 params
.did_create_new_entry
= false;
1893 params
.is_post
= false;
1895 EXPECT_EQ(0U, notifications
.size());
1896 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
1898 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1899 navigation_entry_committed_counter_
= 0;
1901 EXPECT_TRUE(details
.type
== NAVIGATION_TYPE_SAME_PAGE
);
1902 EXPECT_EQ(controller
.GetEntryCount(), 1);
1903 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1904 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1905 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1906 EXPECT_FALSE(controller
.GetPendingEntry());
1907 EXPECT_EQ(url2
, controller
.GetVisibleEntry()->GetURL());
1908 EXPECT_FALSE(controller
.GetVisibleEntry()->GetHasPostData());
1910 EXPECT_FALSE(controller
.CanGoBack());
1911 EXPECT_FALSE(controller
.CanGoForward());
1914 // A redirect right off the bat should be a NEW_PAGE.
1915 TEST_F(NavigationControllerTest
, ImmediateRedirect
) {
1916 NavigationControllerImpl
& controller
= controller_impl();
1917 TestNotificationTracker notifications
;
1918 RegisterForAllNavNotifications(¬ifications
, &controller
);
1920 const GURL
url1("http://foo1");
1921 const GURL
url2("http://foo2"); // Redirection target
1925 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1926 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1928 EXPECT_TRUE(controller
.GetPendingEntry());
1929 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1930 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
1932 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
1934 params
.nav_entry_id
= entry_id
;
1935 params
.did_create_new_entry
= true;
1937 params
.transition
= ui::PAGE_TRANSITION_SERVER_REDIRECT
;
1938 params
.redirects
.push_back(GURL("http://foo1"));
1939 params
.redirects
.push_back(GURL("http://foo2"));
1940 params
.should_update_history
= false;
1941 params
.gesture
= NavigationGestureAuto
;
1942 params
.is_post
= false;
1943 params
.page_state
= PageState::CreateFromURL(url2
);
1945 LoadCommittedDetails details
;
1947 EXPECT_EQ(0U, notifications
.size());
1948 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
1950 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1951 navigation_entry_committed_counter_
= 0;
1953 EXPECT_TRUE(details
.type
== NAVIGATION_TYPE_NEW_PAGE
);
1954 EXPECT_EQ(controller
.GetEntryCount(), 1);
1955 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1956 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1957 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1958 EXPECT_FALSE(controller
.GetPendingEntry());
1959 EXPECT_EQ(url2
, controller
.GetVisibleEntry()->GetURL());
1961 EXPECT_FALSE(controller
.CanGoBack());
1962 EXPECT_FALSE(controller
.CanGoForward());
1965 // If something is pumping the event loop in the browser process and is loading
1966 // pages rapidly one after the other, there can be a race with two closely-
1967 // spaced load requests. Once the first load request is sent, will the renderer
1968 // be fast enough to get the load committed, send a DidCommitProvisionalLoad
1969 // IPC, and have the browser process handle that IPC before the caller makes
1970 // another load request, replacing the pending entry of the first request?
1972 // This test is about what happens in such a race when that pending entry
1973 // replacement happens. If it happens, and the first load had the same URL as
1974 // the page before it, we must make sure that the replacement of the pending
1975 // entry correctly turns a SAME_PAGE classification into an EXISTING_PAGE one.
1977 // (This is a unit test rather than a browser test because it's not currently
1978 // possible to force this sequence of events with a browser test.)
1979 TEST_F(NavigationControllerTest
,
1980 NavigationTypeClassification_ExistingPageRace
) {
1981 NavigationControllerImpl
& controller
= controller_impl();
1982 const GURL
url1("http://foo1");
1983 const GURL
url2("http://foo2");
1985 // Start with a loaded page.
1986 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url1
);
1987 EXPECT_EQ(nullptr, controller_impl().GetPendingEntry());
1989 // Start a load of the same page again.
1991 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1992 int entry_id1
= controller
.GetPendingEntry()->GetUniqueID();
1994 // Immediately start loading a different page...
1996 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1997 int entry_id2
= controller
.GetPendingEntry()->GetUniqueID();
1998 EXPECT_NE(entry_id1
, entry_id2
);
2000 // ... and now the renderer sends a commit for the first navigation.
2001 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2003 params
.nav_entry_id
= entry_id1
;
2004 params
.intended_as_new_entry
= true;
2005 params
.did_create_new_entry
= false;
2007 params
.transition
= ui::PAGE_TRANSITION_TYPED
;
2008 params
.page_state
= PageState::CreateFromURL(url1
);
2010 LoadCommittedDetails details
;
2012 main_test_rfh()->PrepareForCommit();
2013 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
2015 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE
, details
.type
);
2018 // Tests navigation via link click within a subframe. A new navigation entry
2019 // should be created.
2020 TEST_F(NavigationControllerTest
, NewSubframe
) {
2021 NavigationControllerImpl
& controller
= controller_impl();
2022 TestNotificationTracker notifications
;
2023 RegisterForAllNavNotifications(¬ifications
, &controller
);
2025 const GURL
url1("http://foo1");
2026 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url1
);
2027 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2028 navigation_entry_committed_counter_
= 0;
2030 // Prereq: add a subframe with an initial auto-subframe navigation.
2031 main_test_rfh()->OnCreateChildFrame(
2032 MSG_ROUTING_NONE
, blink::WebTreeScopeType::Document
, std::string(),
2033 blink::WebSandboxFlags::None
);
2034 RenderFrameHostImpl
* subframe
=
2035 contents()->GetFrameTree()->root()->child_at(0)->current_frame_host();
2036 const GURL
subframe_url("http://foo1/subframe");
2038 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2040 params
.nav_entry_id
= 0;
2041 params
.did_create_new_entry
= false;
2042 params
.url
= subframe_url
;
2043 params
.transition
= ui::PAGE_TRANSITION_AUTO_SUBFRAME
;
2044 params
.should_update_history
= false;
2045 params
.gesture
= NavigationGestureUser
;
2046 params
.is_post
= false;
2047 params
.page_state
= PageState::CreateFromURL(subframe_url
);
2049 // Navigating should do nothing.
2050 LoadCommittedDetails details
;
2051 EXPECT_FALSE(controller
.RendererDidNavigate(subframe
, params
, &details
));
2052 EXPECT_EQ(0U, notifications
.size());
2055 // Now do a new navigation in the frame.
2056 const GURL
url2("http://foo2");
2057 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2059 params
.nav_entry_id
= 0;
2060 params
.did_create_new_entry
= true;
2062 params
.transition
= ui::PAGE_TRANSITION_MANUAL_SUBFRAME
;
2063 params
.should_update_history
= false;
2064 params
.gesture
= NavigationGestureUser
;
2065 params
.is_post
= false;
2066 params
.page_state
= PageState::CreateFromURL(url2
);
2068 LoadCommittedDetails details
;
2069 EXPECT_TRUE(controller
.RendererDidNavigate(subframe
, params
, &details
));
2070 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2071 navigation_entry_committed_counter_
= 0;
2072 EXPECT_EQ(url1
, details
.previous_url
);
2073 EXPECT_FALSE(details
.is_in_page
);
2074 EXPECT_FALSE(details
.is_main_frame
);
2076 // The new entry should be appended.
2077 NavigationEntryImpl
* entry
= controller
.GetLastCommittedEntry();
2078 EXPECT_EQ(2, controller
.GetEntryCount());
2079 EXPECT_EQ(entry
, details
.entry
);
2081 // New entry should refer to the new page, but the old URL (entries only
2082 // reflect the toplevel URL).
2083 EXPECT_EQ(url1
, entry
->GetURL());
2084 EXPECT_EQ(params
.page_id
, entry
->GetPageID());
2086 // Verify subframe entries if we're in --site-per-process mode.
2087 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
2088 switches::kSitePerProcess
)) {
2089 // The entry should have a subframe FrameNavigationEntry.
2090 ASSERT_EQ(1U, entry
->root_node()->children
.size());
2091 EXPECT_EQ(url2
, entry
->root_node()->children
[0]->frame_entry
->url());
2093 // There are no subframe FrameNavigationEntries by default.
2094 EXPECT_EQ(0U, entry
->root_node()->children
.size());
2098 // Auto subframes are ones the page loads automatically like ads. They should
2099 // not create new navigation entries.
2100 // TODO(creis): Test updating entries for history auto subframe navigations.
2101 TEST_F(NavigationControllerTest
, AutoSubframe
) {
2102 NavigationControllerImpl
& controller
= controller_impl();
2103 TestNotificationTracker notifications
;
2104 RegisterForAllNavNotifications(¬ifications
, &controller
);
2106 const GURL
url1("http://foo/1");
2107 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url1
);
2108 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2109 navigation_entry_committed_counter_
= 0;
2111 // Add a subframe and navigate it.
2112 main_test_rfh()->OnCreateChildFrame(
2113 MSG_ROUTING_NONE
, blink::WebTreeScopeType::Document
, std::string(),
2114 blink::WebSandboxFlags::None
);
2115 RenderFrameHostImpl
* subframe
=
2116 contents()->GetFrameTree()->root()->child_at(0)->current_frame_host();
2117 const GURL
url2("http://foo/2");
2119 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2121 params
.nav_entry_id
= 0;
2122 params
.did_create_new_entry
= false;
2124 params
.transition
= ui::PAGE_TRANSITION_AUTO_SUBFRAME
;
2125 params
.should_update_history
= false;
2126 params
.gesture
= NavigationGestureUser
;
2127 params
.is_post
= false;
2128 params
.page_state
= PageState::CreateFromURL(url2
);
2130 // Navigating should do nothing.
2131 LoadCommittedDetails details
;
2132 EXPECT_FALSE(controller
.RendererDidNavigate(subframe
, params
, &details
));
2133 EXPECT_EQ(0U, notifications
.size());
2136 // There should still be only one entry.
2137 EXPECT_EQ(1, controller
.GetEntryCount());
2138 NavigationEntryImpl
* entry
= controller
.GetLastCommittedEntry();
2139 EXPECT_EQ(url1
, entry
->GetURL());
2140 EXPECT_EQ(1, entry
->GetPageID());
2141 FrameNavigationEntry
* root_entry
= entry
->root_node()->frame_entry
.get();
2142 EXPECT_EQ(url1
, root_entry
->url());
2144 // Verify subframe entries if we're in --site-per-process mode.
2145 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
2146 switches::kSitePerProcess
)) {
2147 // The entry should now have a subframe FrameNavigationEntry.
2148 ASSERT_EQ(1U, entry
->root_node()->children
.size());
2149 FrameNavigationEntry
* frame_entry
=
2150 entry
->root_node()->children
[0]->frame_entry
.get();
2151 EXPECT_EQ(url2
, frame_entry
->url());
2153 // There are no subframe FrameNavigationEntries by default.
2154 EXPECT_EQ(0U, entry
->root_node()->children
.size());
2157 // Add a second subframe and navigate.
2158 main_test_rfh()->OnCreateChildFrame(
2159 MSG_ROUTING_NONE
, blink::WebTreeScopeType::Document
, std::string(),
2160 blink::WebSandboxFlags::None
);
2161 RenderFrameHostImpl
* subframe2
=
2162 contents()->GetFrameTree()->root()->child_at(1)->current_frame_host();
2163 const GURL
url3("http://foo/3");
2165 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2167 params
.nav_entry_id
= 0;
2168 params
.did_create_new_entry
= false;
2170 params
.transition
= ui::PAGE_TRANSITION_AUTO_SUBFRAME
;
2171 params
.should_update_history
= false;
2172 params
.gesture
= NavigationGestureUser
;
2173 params
.is_post
= false;
2174 params
.page_state
= PageState::CreateFromURL(url3
);
2176 // Navigating should do nothing.
2177 LoadCommittedDetails details
;
2178 EXPECT_FALSE(controller
.RendererDidNavigate(subframe2
, params
, &details
));
2179 EXPECT_EQ(0U, notifications
.size());
2182 // There should still be only one entry, mostly unchanged.
2183 EXPECT_EQ(1, controller
.GetEntryCount());
2184 EXPECT_EQ(entry
, controller
.GetLastCommittedEntry());
2185 EXPECT_EQ(url1
, entry
->GetURL());
2186 EXPECT_EQ(1, entry
->GetPageID());
2187 EXPECT_EQ(root_entry
, entry
->root_node()->frame_entry
.get());
2188 EXPECT_EQ(url1
, root_entry
->url());
2190 // Verify subframe entries if we're in --site-per-process mode.
2191 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
2192 switches::kSitePerProcess
)) {
2193 // The entry should now have 2 subframe FrameNavigationEntries.
2194 ASSERT_EQ(2U, entry
->root_node()->children
.size());
2195 FrameNavigationEntry
* new_frame_entry
=
2196 entry
->root_node()->children
[1]->frame_entry
.get();
2197 EXPECT_EQ(url3
, new_frame_entry
->url());
2199 // There are no subframe FrameNavigationEntries by default.
2200 EXPECT_EQ(0U, entry
->root_node()->children
.size());
2203 // Add a nested subframe and navigate.
2204 subframe
->OnCreateChildFrame(MSG_ROUTING_NONE
,
2205 blink::WebTreeScopeType::Document
, std::string(),
2206 blink::WebSandboxFlags::None
);
2207 RenderFrameHostImpl
* subframe3
= contents()
2212 ->current_frame_host();
2213 const GURL
url4("http://foo/4");
2215 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2217 params
.nav_entry_id
= 0;
2218 params
.did_create_new_entry
= false;
2220 params
.transition
= ui::PAGE_TRANSITION_AUTO_SUBFRAME
;
2221 params
.should_update_history
= false;
2222 params
.gesture
= NavigationGestureUser
;
2223 params
.is_post
= false;
2224 params
.page_state
= PageState::CreateFromURL(url4
);
2226 // Navigating should do nothing.
2227 LoadCommittedDetails details
;
2228 EXPECT_FALSE(controller
.RendererDidNavigate(subframe3
, params
, &details
));
2229 EXPECT_EQ(0U, notifications
.size());
2232 // There should still be only one entry, mostly unchanged.
2233 EXPECT_EQ(1, controller
.GetEntryCount());
2234 EXPECT_EQ(entry
, controller
.GetLastCommittedEntry());
2235 EXPECT_EQ(url1
, entry
->GetURL());
2236 EXPECT_EQ(1, entry
->GetPageID());
2237 EXPECT_EQ(root_entry
, entry
->root_node()->frame_entry
.get());
2238 EXPECT_EQ(url1
, root_entry
->url());
2240 // Verify subframe entries if we're in --site-per-process mode.
2241 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
2242 switches::kSitePerProcess
)) {
2243 // The entry should now have a nested FrameNavigationEntry.
2244 EXPECT_EQ(2U, entry
->root_node()->children
.size());
2245 ASSERT_EQ(1U, entry
->root_node()->children
[0]->children
.size());
2246 FrameNavigationEntry
* new_frame_entry
=
2247 entry
->root_node()->children
[0]->children
[0]->frame_entry
.get();
2248 EXPECT_EQ(url4
, new_frame_entry
->url());
2250 // There are no subframe FrameNavigationEntries by default.
2251 EXPECT_EQ(0U, entry
->root_node()->children
.size());
2255 // Tests navigation and then going back to a subframe navigation.
2256 TEST_F(NavigationControllerTest
, BackSubframe
) {
2257 NavigationControllerImpl
& controller
= controller_impl();
2258 TestNotificationTracker notifications
;
2259 RegisterForAllNavNotifications(¬ifications
, &controller
);
2262 const GURL
url1("http://foo1");
2263 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url1
);
2264 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2265 NavigationEntry
* entry1
= controller
.GetLastCommittedEntry();
2266 navigation_entry_committed_counter_
= 0;
2268 // Prereq: add a subframe with an initial auto-subframe navigation.
2269 main_test_rfh()->OnCreateChildFrame(
2270 MSG_ROUTING_NONE
, blink::WebTreeScopeType::Document
, std::string(),
2271 blink::WebSandboxFlags::None
);
2272 RenderFrameHostImpl
* subframe
=
2273 contents()->GetFrameTree()->root()->child_at(0)->current_frame_host();
2274 const GURL
subframe_url("http://foo1/subframe");
2276 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2278 params
.nav_entry_id
= 0;
2279 params
.did_create_new_entry
= false;
2280 params
.url
= subframe_url
;
2281 params
.transition
= ui::PAGE_TRANSITION_AUTO_SUBFRAME
;
2282 params
.should_update_history
= false;
2283 params
.gesture
= NavigationGestureUser
;
2284 params
.is_post
= false;
2285 params
.page_state
= PageState::CreateFromURL(subframe_url
);
2287 // Navigating should do nothing.
2288 LoadCommittedDetails details
;
2289 EXPECT_FALSE(controller
.RendererDidNavigate(subframe
, params
, &details
));
2290 EXPECT_EQ(0U, notifications
.size());
2293 // First manual subframe navigation.
2294 const GURL
url2("http://foo2");
2295 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2297 params
.nav_entry_id
= 0;
2298 params
.did_create_new_entry
= true;
2300 params
.transition
= ui::PAGE_TRANSITION_MANUAL_SUBFRAME
;
2301 params
.should_update_history
= false;
2302 params
.gesture
= NavigationGestureUser
;
2303 params
.is_post
= false;
2304 params
.page_state
= PageState::CreateFromURL(url2
);
2306 // This should generate a new entry.
2307 LoadCommittedDetails details
;
2308 EXPECT_TRUE(controller
.RendererDidNavigate(subframe
, params
, &details
));
2309 NavigationEntryImpl
* entry2
= controller
.GetLastCommittedEntry();
2310 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2311 navigation_entry_committed_counter_
= 0;
2312 EXPECT_EQ(2, controller
.GetEntryCount());
2314 // Verify subframe entries if we're in --site-per-process mode.
2315 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
2316 switches::kSitePerProcess
)) {
2317 // The entry should have a subframe FrameNavigationEntry.
2318 ASSERT_EQ(1U, entry2
->root_node()->children
.size());
2319 EXPECT_EQ(url2
, entry2
->root_node()->children
[0]->frame_entry
->url());
2321 // There are no subframe FrameNavigationEntries by default.
2322 EXPECT_EQ(0U, entry2
->root_node()->children
.size());
2325 // Second manual subframe navigation should also make a new entry.
2326 const GURL
url3("http://foo3");
2328 params
.nav_entry_id
= 0;
2329 params
.did_create_new_entry
= true;
2331 params
.transition
= ui::PAGE_TRANSITION_MANUAL_SUBFRAME
;
2332 EXPECT_TRUE(controller
.RendererDidNavigate(subframe
, params
, &details
));
2333 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2334 navigation_entry_committed_counter_
= 0;
2335 NavigationEntryImpl
* entry3
= controller
.GetLastCommittedEntry();
2336 EXPECT_EQ(3, controller
.GetEntryCount());
2337 EXPECT_EQ(2, controller
.GetCurrentEntryIndex());
2339 // Verify subframe entries if we're in --site-per-process mode.
2340 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
2341 switches::kSitePerProcess
)) {
2342 // The entry should have a subframe FrameNavigationEntry.
2343 ASSERT_EQ(1U, entry3
->root_node()->children
.size());
2344 EXPECT_EQ(url3
, entry3
->root_node()->children
[0]->frame_entry
->url());
2346 // There are no subframe FrameNavigationEntries by default.
2347 EXPECT_EQ(0U, entry3
->root_node()->children
.size());
2351 controller
.GoBack();
2353 params
.nav_entry_id
= entry2
->GetUniqueID();
2354 params
.did_create_new_entry
= false;
2356 params
.transition
= ui::PAGE_TRANSITION_AUTO_SUBFRAME
;
2357 EXPECT_TRUE(controller
.RendererDidNavigate(subframe
, params
, &details
));
2358 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2359 navigation_entry_committed_counter_
= 0;
2360 EXPECT_EQ(entry2
, controller
.GetLastCommittedEntry());
2361 EXPECT_EQ(3, controller
.GetEntryCount());
2362 EXPECT_EQ(1, controller
.GetCurrentEntryIndex());
2363 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
2364 EXPECT_FALSE(controller
.GetPendingEntry());
2366 // Go back one more.
2367 controller
.GoBack();
2369 params
.nav_entry_id
= entry1
->GetUniqueID();
2370 params
.did_create_new_entry
= false;
2372 params
.transition
= ui::PAGE_TRANSITION_AUTO_SUBFRAME
;
2373 EXPECT_TRUE(controller
.RendererDidNavigate(subframe
, params
, &details
));
2374 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2375 navigation_entry_committed_counter_
= 0;
2376 EXPECT_EQ(entry1
, controller
.GetLastCommittedEntry());
2377 EXPECT_EQ(3, controller
.GetEntryCount());
2378 EXPECT_EQ(0, controller
.GetCurrentEntryIndex());
2379 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
2380 EXPECT_FALSE(controller
.GetPendingEntry());
2383 TEST_F(NavigationControllerTest
, LinkClick
) {
2384 NavigationControllerImpl
& controller
= controller_impl();
2385 TestNotificationTracker notifications
;
2386 RegisterForAllNavNotifications(¬ifications
, &controller
);
2388 const GURL
url1("http://foo1");
2389 const GURL
url2("http://foo2");
2391 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url1
);
2392 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2393 navigation_entry_committed_counter_
= 0;
2395 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url2
);
2396 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2397 navigation_entry_committed_counter_
= 0;
2399 // Should have produced a new session history entry.
2400 EXPECT_EQ(controller
.GetEntryCount(), 2);
2401 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 1);
2402 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
2403 EXPECT_TRUE(controller
.GetLastCommittedEntry());
2404 EXPECT_FALSE(controller
.GetPendingEntry());
2405 EXPECT_TRUE(controller
.CanGoBack());
2406 EXPECT_FALSE(controller
.CanGoForward());
2409 TEST_F(NavigationControllerTest
, InPage
) {
2410 NavigationControllerImpl
& controller
= controller_impl();
2411 TestNotificationTracker notifications
;
2412 RegisterForAllNavNotifications(¬ifications
, &controller
);
2415 const GURL
url1("http://foo");
2416 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url1
);
2417 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2418 navigation_entry_committed_counter_
= 0;
2420 // Ensure main page navigation to same url respects the was_within_same_page
2421 // hint provided in the params.
2422 FrameHostMsg_DidCommitProvisionalLoad_Params self_params
;
2423 self_params
.page_id
= 0;
2424 self_params
.nav_entry_id
= 0;
2425 self_params
.did_create_new_entry
= false;
2426 self_params
.url
= url1
;
2427 self_params
.transition
= ui::PAGE_TRANSITION_LINK
;
2428 self_params
.should_update_history
= false;
2429 self_params
.gesture
= NavigationGestureUser
;
2430 self_params
.is_post
= false;
2431 self_params
.page_state
= PageState::CreateFromURL(url1
);
2432 self_params
.was_within_same_page
= true;
2434 LoadCommittedDetails details
;
2435 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), self_params
,
2437 NavigationEntry
* entry1
= controller
.GetLastCommittedEntry();
2438 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2439 navigation_entry_committed_counter_
= 0;
2440 EXPECT_TRUE(details
.is_in_page
);
2441 EXPECT_TRUE(details
.did_replace_entry
);
2442 EXPECT_EQ(1, controller
.GetEntryCount());
2444 // Fragment navigation to a new page_id.
2445 const GURL
url2("http://foo#a");
2446 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2448 params
.nav_entry_id
= 0;
2449 params
.did_create_new_entry
= true;
2451 params
.transition
= ui::PAGE_TRANSITION_LINK
;
2452 params
.should_update_history
= false;
2453 params
.gesture
= NavigationGestureUser
;
2454 params
.is_post
= false;
2455 params
.page_state
= PageState::CreateFromURL(url2
);
2456 params
.was_within_same_page
= true;
2458 // This should generate a new entry.
2459 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
2461 NavigationEntry
* entry2
= controller
.GetLastCommittedEntry();
2462 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2463 navigation_entry_committed_counter_
= 0;
2464 EXPECT_TRUE(details
.is_in_page
);
2465 EXPECT_FALSE(details
.did_replace_entry
);
2466 EXPECT_EQ(2, controller
.GetEntryCount());
2469 FrameHostMsg_DidCommitProvisionalLoad_Params
back_params(params
);
2470 controller
.GoBack();
2471 back_params
.url
= url1
;
2472 back_params
.page_id
= 0;
2473 back_params
.nav_entry_id
= entry1
->GetUniqueID();
2474 back_params
.did_create_new_entry
= false;
2475 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), back_params
,
2477 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2478 navigation_entry_committed_counter_
= 0;
2479 EXPECT_TRUE(details
.is_in_page
);
2480 EXPECT_EQ(2, controller
.GetEntryCount());
2481 EXPECT_EQ(0, controller
.GetCurrentEntryIndex());
2482 EXPECT_EQ(back_params
.url
, controller
.GetVisibleEntry()->GetURL());
2485 FrameHostMsg_DidCommitProvisionalLoad_Params
forward_params(params
);
2486 controller
.GoForward();
2487 forward_params
.url
= url2
;
2488 forward_params
.page_id
= 1;
2489 forward_params
.nav_entry_id
= entry2
->GetUniqueID();
2490 forward_params
.did_create_new_entry
= false;
2491 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), forward_params
,
2493 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2494 navigation_entry_committed_counter_
= 0;
2495 EXPECT_TRUE(details
.is_in_page
);
2496 EXPECT_EQ(2, controller
.GetEntryCount());
2497 EXPECT_EQ(1, controller
.GetCurrentEntryIndex());
2498 EXPECT_EQ(forward_params
.url
,
2499 controller
.GetVisibleEntry()->GetURL());
2501 // Now go back and forward again. This is to work around a bug where we would
2502 // compare the incoming URL with the last committed entry rather than the
2503 // one identified by an existing page ID. This would result in the second URL
2504 // losing the reference fragment when you navigate away from it and then back.
2505 controller
.GoBack();
2506 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), back_params
,
2508 controller
.GoForward();
2509 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), forward_params
,
2511 EXPECT_EQ(forward_params
.url
,
2512 controller
.GetVisibleEntry()->GetURL());
2514 // Finally, navigate to an unrelated URL to make sure in_page is not sticky.
2515 const GURL
url3("http://bar");
2517 params
.nav_entry_id
= 0;
2518 params
.did_create_new_entry
= true;
2520 navigation_entry_committed_counter_
= 0;
2521 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
2523 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2524 navigation_entry_committed_counter_
= 0;
2525 EXPECT_FALSE(details
.is_in_page
);
2526 EXPECT_EQ(3, controller
.GetEntryCount());
2527 EXPECT_EQ(2, controller
.GetCurrentEntryIndex());
2530 TEST_F(NavigationControllerTest
, InPage_Replace
) {
2531 NavigationControllerImpl
& controller
= controller_impl();
2532 TestNotificationTracker notifications
;
2533 RegisterForAllNavNotifications(¬ifications
, &controller
);
2536 const GURL
url1("http://foo");
2537 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url1
);
2538 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2539 navigation_entry_committed_counter_
= 0;
2541 // First navigation.
2542 const GURL
url2("http://foo#a");
2543 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2544 params
.page_id
= 0; // Same page_id
2545 params
.nav_entry_id
= 0;
2546 params
.did_create_new_entry
= false;
2548 params
.transition
= ui::PAGE_TRANSITION_LINK
;
2549 params
.should_update_history
= false;
2550 params
.gesture
= NavigationGestureUser
;
2551 params
.is_post
= false;
2552 params
.page_state
= PageState::CreateFromURL(url2
);
2553 params
.was_within_same_page
= true;
2555 // This should NOT generate a new entry, nor prune the list.
2556 LoadCommittedDetails details
;
2557 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
2559 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2560 navigation_entry_committed_counter_
= 0;
2561 EXPECT_TRUE(details
.is_in_page
);
2562 EXPECT_TRUE(details
.did_replace_entry
);
2563 EXPECT_EQ(1, controller
.GetEntryCount());
2566 // Tests for http://crbug.com/40395
2569 // window.location.replace("#a");
2570 // window.location='http://foo3/';
2572 TEST_F(NavigationControllerTest
, ClientRedirectAfterInPageNavigation
) {
2573 NavigationControllerImpl
& controller
= controller_impl();
2574 TestNotificationTracker notifications
;
2575 RegisterForAllNavNotifications(¬ifications
, &controller
);
2577 // Load an initial page.
2579 const GURL
url("http://foo/");
2580 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url
);
2581 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2582 navigation_entry_committed_counter_
= 0;
2585 // Navigate to a new page.
2587 const GURL
url("http://foo2/");
2588 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url
);
2589 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2590 navigation_entry_committed_counter_
= 0;
2593 // Navigate within the page.
2595 const GURL
url("http://foo2/#a");
2596 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2597 params
.page_id
= 1; // Same page_id
2598 params
.nav_entry_id
= 0;
2599 params
.did_create_new_entry
= false;
2601 params
.transition
= ui::PAGE_TRANSITION_LINK
;
2602 params
.redirects
.push_back(url
);
2603 params
.should_update_history
= true;
2604 params
.gesture
= NavigationGestureUnknown
;
2605 params
.is_post
= false;
2606 params
.page_state
= PageState::CreateFromURL(url
);
2607 params
.was_within_same_page
= true;
2609 // This should NOT generate a new entry, nor prune the list.
2610 LoadCommittedDetails details
;
2611 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
2613 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2614 navigation_entry_committed_counter_
= 0;
2615 EXPECT_TRUE(details
.is_in_page
);
2616 EXPECT_TRUE(details
.did_replace_entry
);
2617 EXPECT_EQ(2, controller
.GetEntryCount());
2620 // Perform a client redirect to a new page.
2622 const GURL
url("http://foo3/");
2623 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2624 params
.page_id
= 2; // New page_id
2625 params
.nav_entry_id
= 0;
2626 params
.did_create_new_entry
= true;
2628 params
.transition
= ui::PAGE_TRANSITION_CLIENT_REDIRECT
;
2629 params
.redirects
.push_back(GURL("http://foo2/#a"));
2630 params
.redirects
.push_back(url
);
2631 params
.should_update_history
= true;
2632 params
.gesture
= NavigationGestureUnknown
;
2633 params
.is_post
= false;
2634 params
.page_state
= PageState::CreateFromURL(url
);
2636 // This SHOULD generate a new entry.
2637 LoadCommittedDetails details
;
2638 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
2640 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2641 navigation_entry_committed_counter_
= 0;
2642 EXPECT_FALSE(details
.is_in_page
);
2643 EXPECT_EQ(3, controller
.GetEntryCount());
2646 // Verify that BACK brings us back to http://foo2/.
2648 const GURL
url("http://foo2/");
2649 controller
.GoBack();
2650 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2651 main_test_rfh()->PrepareForCommit();
2652 main_test_rfh()->SendNavigate(1, entry_id
, false, url
);
2653 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2654 navigation_entry_committed_counter_
= 0;
2655 EXPECT_EQ(url
, controller
.GetVisibleEntry()->GetURL());
2659 TEST_F(NavigationControllerTest
, PushStateWithoutPreviousEntry
)
2661 ASSERT_FALSE(controller_impl().GetLastCommittedEntry());
2662 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2663 GURL
url("http://foo");
2665 params
.nav_entry_id
= 0;
2666 params
.did_create_new_entry
= true;
2668 params
.page_state
= PageState::CreateFromURL(url
);
2669 params
.was_within_same_page
= true;
2670 main_test_rfh()->SendRendererInitiatedNavigationRequest(url
, false);
2671 main_test_rfh()->PrepareForCommit();
2672 contents()->GetMainFrame()->SendNavigateWithParams(¶ms
);
2673 // We pass if we don't crash.
2676 // NotificationObserver implementation used in verifying we've received the
2677 // NOTIFICATION_NAV_LIST_PRUNED method.
2678 class PrunedListener
: public NotificationObserver
{
2680 explicit PrunedListener(NavigationControllerImpl
* controller
)
2681 : notification_count_(0) {
2682 registrar_
.Add(this, NOTIFICATION_NAV_LIST_PRUNED
,
2683 Source
<NavigationController
>(controller
));
2686 void Observe(int type
,
2687 const NotificationSource
& source
,
2688 const NotificationDetails
& details
) override
{
2689 if (type
== NOTIFICATION_NAV_LIST_PRUNED
) {
2690 notification_count_
++;
2691 details_
= *(Details
<PrunedDetails
>(details
).ptr());
2695 // Number of times NAV_LIST_PRUNED has been observed.
2696 int notification_count_
;
2698 // Details from the last NAV_LIST_PRUNED.
2699 PrunedDetails details_
;
2702 NotificationRegistrar registrar_
;
2704 DISALLOW_COPY_AND_ASSIGN(PrunedListener
);
2707 // Tests that we limit the number of navigation entries created correctly.
2708 TEST_F(NavigationControllerTest
, EnforceMaxNavigationCount
) {
2709 NavigationControllerImpl
& controller
= controller_impl();
2710 size_t original_count
= NavigationControllerImpl::max_entry_count();
2711 const int kMaxEntryCount
= 5;
2713 NavigationControllerImpl::set_max_entry_count_for_testing(kMaxEntryCount
);
2716 // Load up to the max count, all entries should be there.
2717 for (url_index
= 0; url_index
< kMaxEntryCount
; url_index
++) {
2718 GURL
url(base::StringPrintf("http://www.a.com/%d", url_index
));
2720 url
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2721 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2722 main_test_rfh()->PrepareForCommit();
2723 main_test_rfh()->SendNavigate(url_index
, entry_id
, true, url
);
2726 EXPECT_EQ(controller
.GetEntryCount(), kMaxEntryCount
);
2728 // Created a PrunedListener to observe prune notifications.
2729 PrunedListener
listener(&controller
);
2731 // Navigate some more.
2732 GURL
url(base::StringPrintf("http://www.a.com/%d", url_index
));
2734 url
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2735 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2736 main_test_rfh()->PrepareForCommit();
2737 main_test_rfh()->SendNavigate(url_index
, entry_id
, true, url
);
2740 // We should have got a pruned navigation.
2741 EXPECT_EQ(1, listener
.notification_count_
);
2742 EXPECT_TRUE(listener
.details_
.from_front
);
2743 EXPECT_EQ(1, listener
.details_
.count
);
2745 // We expect http://www.a.com/0 to be gone.
2746 EXPECT_EQ(controller
.GetEntryCount(), kMaxEntryCount
);
2747 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(),
2748 GURL("http://www.a.com/1"));
2750 // More navigations.
2751 for (int i
= 0; i
< 3; i
++) {
2752 url
= GURL(base::StringPrintf("http://www.a.com/%d", url_index
));
2754 url
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2755 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2756 main_test_rfh()->PrepareForCommit();
2757 main_test_rfh()->SendNavigate(url_index
, entry_id
, true, url
);
2760 EXPECT_EQ(controller
.GetEntryCount(), kMaxEntryCount
);
2761 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(),
2762 GURL("http://www.a.com/4"));
2764 NavigationControllerImpl::set_max_entry_count_for_testing(original_count
);
2767 // Tests that we can do a restore and navigate to the restored entries and
2768 // everything is updated properly. This can be tricky since there is no
2769 // SiteInstance for the entries created initially.
2770 TEST_F(NavigationControllerTest
, RestoreNavigate
) {
2771 // Create a NavigationController with a restored set of tabs.
2772 GURL
url("http://foo");
2773 ScopedVector
<NavigationEntry
> entries
;
2774 scoped_ptr
<NavigationEntry
> entry
=
2775 NavigationControllerImpl::CreateNavigationEntry(
2776 url
, Referrer(), ui::PAGE_TRANSITION_RELOAD
, false, std::string(),
2778 entry
->SetPageID(0);
2779 entry
->SetTitle(base::ASCIIToUTF16("Title"));
2780 entry
->SetPageState(PageState::CreateFromEncodedData("state"));
2781 const base::Time timestamp
= base::Time::Now();
2782 entry
->SetTimestamp(timestamp
);
2783 entries
.push_back(entry
.Pass());
2784 scoped_ptr
<WebContentsImpl
> our_contents(static_cast<WebContentsImpl
*>(
2785 WebContents::Create(WebContents::CreateParams(browser_context()))));
2786 NavigationControllerImpl
& our_controller
= our_contents
->GetController();
2787 our_controller
.Restore(
2789 NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY
,
2791 ASSERT_EQ(0u, entries
.size());
2793 // Before navigating to the restored entry, it should have a restore_type
2794 // and no SiteInstance.
2795 ASSERT_EQ(1, our_controller
.GetEntryCount());
2796 EXPECT_EQ(NavigationEntryImpl::RESTORE_LAST_SESSION_EXITED_CLEANLY
,
2797 our_controller
.GetEntryAtIndex(0)->restore_type());
2798 EXPECT_FALSE(our_controller
.GetEntryAtIndex(0)->site_instance());
2800 // After navigating, we should have one entry, and it should be "pending".
2801 // It should now have a SiteInstance and no restore_type.
2802 our_controller
.GoToIndex(0);
2803 EXPECT_EQ(1, our_controller
.GetEntryCount());
2804 EXPECT_EQ(our_controller
.GetEntryAtIndex(0),
2805 our_controller
.GetPendingEntry());
2806 EXPECT_EQ(0, our_controller
.GetEntryAtIndex(0)->GetPageID());
2807 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE
,
2808 our_controller
.GetEntryAtIndex(0)->restore_type());
2809 EXPECT_TRUE(our_controller
.GetEntryAtIndex(0)->site_instance());
2811 // Timestamp should remain the same before the navigation finishes.
2812 EXPECT_EQ(timestamp
, our_controller
.GetEntryAtIndex(0)->GetTimestamp());
2814 // Say we navigated to that entry.
2815 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2817 params
.nav_entry_id
= our_controller
.GetPendingEntry()->GetUniqueID();
2818 params
.did_create_new_entry
= false;
2820 params
.transition
= ui::PAGE_TRANSITION_LINK
;
2821 params
.should_update_history
= false;
2822 params
.gesture
= NavigationGestureUser
;
2823 params
.is_post
= false;
2824 params
.page_state
= PageState::CreateFromURL(url
);
2825 LoadCommittedDetails details
;
2826 our_controller
.RendererDidNavigate(our_contents
->GetMainFrame(), params
,
2829 // There should be no longer any pending entry and one committed one. This
2830 // means that we were able to locate the entry, assign its site instance, and
2831 // commit it properly.
2832 EXPECT_EQ(1, our_controller
.GetEntryCount());
2833 EXPECT_EQ(0, our_controller
.GetLastCommittedEntryIndex());
2834 EXPECT_FALSE(our_controller
.GetPendingEntry());
2837 our_controller
.GetLastCommittedEntry()->site_instance()->GetSiteURL());
2838 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE
,
2839 our_controller
.GetEntryAtIndex(0)->restore_type());
2841 // Timestamp should have been updated.
2842 EXPECT_GE(our_controller
.GetEntryAtIndex(0)->GetTimestamp(), timestamp
);
2845 // Tests that we can still navigate to a restored entry after a different
2846 // navigation fails and clears the pending entry. http://crbug.com/90085
2847 TEST_F(NavigationControllerTest
, RestoreNavigateAfterFailure
) {
2848 // Create a NavigationController with a restored set of tabs.
2849 GURL
url("http://foo");
2850 ScopedVector
<NavigationEntry
> entries
;
2851 scoped_ptr
<NavigationEntry
> new_entry
=
2852 NavigationControllerImpl::CreateNavigationEntry(
2853 url
, Referrer(), ui::PAGE_TRANSITION_RELOAD
, false, std::string(),
2855 new_entry
->SetPageID(0);
2856 new_entry
->SetTitle(base::ASCIIToUTF16("Title"));
2857 new_entry
->SetPageState(PageState::CreateFromEncodedData("state"));
2858 entries
.push_back(new_entry
.Pass());
2859 scoped_ptr
<WebContentsImpl
> our_contents(static_cast<WebContentsImpl
*>(
2860 WebContents::Create(WebContents::CreateParams(browser_context()))));
2861 NavigationControllerImpl
& our_controller
= our_contents
->GetController();
2862 our_controller
.Restore(
2863 0, NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY
, &entries
);
2864 ASSERT_EQ(0u, entries
.size());
2866 // Ensure the RenderFrame is initialized before simulating events coming from
2868 main_test_rfh()->InitializeRenderFrameIfNeeded();
2870 // Before navigating to the restored entry, it should have a restore_type
2871 // and no SiteInstance.
2872 NavigationEntry
* entry
= our_controller
.GetEntryAtIndex(0);
2873 EXPECT_EQ(NavigationEntryImpl::RESTORE_LAST_SESSION_EXITED_CLEANLY
,
2874 our_controller
.GetEntryAtIndex(0)->restore_type());
2875 EXPECT_FALSE(our_controller
.GetEntryAtIndex(0)->site_instance());
2877 // After navigating, we should have one entry, and it should be "pending".
2878 // It should now have a SiteInstance and no restore_type.
2879 our_controller
.GoToIndex(0);
2880 EXPECT_EQ(1, our_controller
.GetEntryCount());
2881 EXPECT_EQ(our_controller
.GetEntryAtIndex(0),
2882 our_controller
.GetPendingEntry());
2883 EXPECT_EQ(0, our_controller
.GetEntryAtIndex(0)->GetPageID());
2884 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE
,
2885 our_controller
.GetEntryAtIndex(0)->restore_type());
2886 EXPECT_TRUE(our_controller
.GetEntryAtIndex(0)->site_instance());
2888 // This pending navigation may have caused a different navigation to fail,
2889 // which causes the pending entry to be cleared.
2890 FrameHostMsg_DidFailProvisionalLoadWithError_Params fail_load_params
;
2891 fail_load_params
.error_code
= net::ERR_ABORTED
;
2892 fail_load_params
.error_description
= base::string16();
2893 fail_load_params
.url
= url
;
2894 fail_load_params
.showing_repost_interstitial
= false;
2895 main_test_rfh()->InitializeRenderFrameIfNeeded();
2896 main_test_rfh()->OnMessageReceived(
2897 FrameHostMsg_DidFailProvisionalLoadWithError(0, // routing_id
2900 // Now the pending restored entry commits.
2901 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2903 params
.nav_entry_id
= entry
->GetUniqueID();
2904 params
.did_create_new_entry
= false;
2906 params
.transition
= ui::PAGE_TRANSITION_LINK
;
2907 params
.should_update_history
= false;
2908 params
.gesture
= NavigationGestureUser
;
2909 params
.is_post
= false;
2910 params
.page_state
= PageState::CreateFromURL(url
);
2911 LoadCommittedDetails details
;
2912 our_controller
.RendererDidNavigate(our_contents
->GetMainFrame(), params
,
2915 // There should be no pending entry and one committed one.
2916 EXPECT_EQ(1, our_controller
.GetEntryCount());
2917 EXPECT_EQ(0, our_controller
.GetLastCommittedEntryIndex());
2918 EXPECT_FALSE(our_controller
.GetPendingEntry());
2921 our_controller
.GetLastCommittedEntry()->site_instance()->GetSiteURL());
2922 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE
,
2923 our_controller
.GetEntryAtIndex(0)->restore_type());
2926 // Make sure that the page type and stuff is correct after an interstitial.
2927 TEST_F(NavigationControllerTest
, Interstitial
) {
2928 NavigationControllerImpl
& controller
= controller_impl();
2929 // First navigate somewhere normal.
2930 const GURL
url1("http://foo");
2932 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2933 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2934 main_test_rfh()->PrepareForCommit();
2935 main_test_rfh()->SendNavigate(0, entry_id
, true, url1
);
2937 // Now navigate somewhere with an interstitial.
2938 const GURL
url2("http://bar");
2939 controller
.LoadURL(url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
,
2941 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2942 controller
.GetPendingEntry()->set_page_type(PAGE_TYPE_INTERSTITIAL
);
2944 // At this point the interstitial will be displayed and the load will still
2945 // be pending. If the user continues, the load will commit.
2946 main_test_rfh()->PrepareForCommit();
2947 main_test_rfh()->SendNavigate(1, entry_id
, true, url2
);
2949 // The page should be a normal page again.
2950 EXPECT_EQ(url2
, controller
.GetLastCommittedEntry()->GetURL());
2951 EXPECT_EQ(PAGE_TYPE_NORMAL
,
2952 controller
.GetLastCommittedEntry()->GetPageType());
2955 TEST_F(NavigationControllerTest
, RemoveEntry
) {
2956 NavigationControllerImpl
& controller
= controller_impl();
2957 const GURL
url1("http://foo/1");
2958 const GURL
url2("http://foo/2");
2959 const GURL
url3("http://foo/3");
2960 const GURL
url4("http://foo/4");
2961 const GURL
url5("http://foo/5");
2962 const GURL
pending_url("http://foo/pending");
2963 const GURL
default_url("http://foo/default");
2966 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2967 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2968 main_test_rfh()->PrepareForCommit();
2969 main_test_rfh()->SendNavigate(0, entry_id
, true, url1
);
2971 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2972 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2973 main_test_rfh()->PrepareForCommit();
2974 main_test_rfh()->SendNavigate(1, entry_id
, true, url2
);
2976 url3
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2977 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2978 main_test_rfh()->PrepareForCommit();
2979 main_test_rfh()->SendNavigate(2, entry_id
, true, url3
);
2981 url4
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2982 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2983 main_test_rfh()->PrepareForCommit();
2984 main_test_rfh()->SendNavigate(3, entry_id
, true, url4
);
2986 url5
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2987 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2988 main_test_rfh()->PrepareForCommit();
2989 main_test_rfh()->SendNavigate(4, entry_id
, true, url5
);
2991 // Try to remove the last entry. Will fail because it is the current entry.
2992 EXPECT_FALSE(controller
.RemoveEntryAtIndex(controller
.GetEntryCount() - 1));
2993 EXPECT_EQ(5, controller
.GetEntryCount());
2994 EXPECT_EQ(4, controller
.GetLastCommittedEntryIndex());
2996 // Go back, but don't commit yet. Check that we can't delete the current
2997 // and pending entries.
2998 controller
.GoBack();
2999 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3000 EXPECT_FALSE(controller
.RemoveEntryAtIndex(controller
.GetEntryCount() - 1));
3001 EXPECT_FALSE(controller
.RemoveEntryAtIndex(controller
.GetEntryCount() - 2));
3003 // Now commit and delete the last entry.
3004 main_test_rfh()->PrepareForCommit();
3005 main_test_rfh()->SendNavigate(3, entry_id
, false, url4
);
3006 EXPECT_TRUE(controller
.RemoveEntryAtIndex(controller
.GetEntryCount() - 1));
3007 EXPECT_EQ(4, controller
.GetEntryCount());
3008 EXPECT_EQ(3, controller
.GetLastCommittedEntryIndex());
3009 EXPECT_FALSE(controller
.GetPendingEntry());
3011 // Remove an entry which is not the last committed one.
3012 EXPECT_TRUE(controller
.RemoveEntryAtIndex(0));
3013 EXPECT_EQ(3, controller
.GetEntryCount());
3014 EXPECT_EQ(2, controller
.GetLastCommittedEntryIndex());
3015 EXPECT_FALSE(controller
.GetPendingEntry());
3017 // Remove the 2 remaining entries.
3018 controller
.RemoveEntryAtIndex(1);
3019 controller
.RemoveEntryAtIndex(0);
3021 // This should leave us with only the last committed entry.
3022 EXPECT_EQ(1, controller
.GetEntryCount());
3023 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
3026 TEST_F(NavigationControllerTest
, RemoveEntryWithPending
) {
3027 NavigationControllerImpl
& controller
= controller_impl();
3028 const GURL
url1("http://foo/1");
3029 const GURL
url2("http://foo/2");
3030 const GURL
url3("http://foo/3");
3031 const GURL
default_url("http://foo/default");
3034 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3035 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3036 main_test_rfh()->PrepareForCommit();
3037 main_test_rfh()->SendNavigate(0, entry_id
, true, url1
);
3039 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3040 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3041 main_test_rfh()->PrepareForCommit();
3042 main_test_rfh()->SendNavigate(1, entry_id
, true, url2
);
3044 url3
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3045 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3046 main_test_rfh()->PrepareForCommit();
3047 main_test_rfh()->SendNavigate(2, entry_id
, true, url3
);
3049 // Go back, but don't commit yet. Check that we can't delete the current
3050 // and pending entries.
3051 controller
.GoBack();
3052 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3053 EXPECT_FALSE(controller
.RemoveEntryAtIndex(2));
3054 EXPECT_FALSE(controller
.RemoveEntryAtIndex(1));
3056 // Remove the first entry, while there is a pending entry. This is expected
3057 // to discard the pending entry.
3058 EXPECT_TRUE(controller
.RemoveEntryAtIndex(0));
3059 EXPECT_FALSE(controller
.GetPendingEntry());
3060 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
3062 // We should update the last committed entry index.
3063 EXPECT_EQ(1, controller
.GetLastCommittedEntryIndex());
3065 // Now commit and ensure we land on the right entry.
3066 main_test_rfh()->PrepareForCommit();
3067 main_test_rfh()->SendNavigate(1, entry_id
, false, url2
);
3068 EXPECT_EQ(2, controller
.GetEntryCount());
3069 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
3070 EXPECT_FALSE(controller
.GetPendingEntry());
3073 // Tests the transient entry, making sure it goes away with all navigations.
3074 TEST_F(NavigationControllerTest
, TransientEntry
) {
3075 NavigationControllerImpl
& controller
= controller_impl();
3076 TestNotificationTracker notifications
;
3077 RegisterForAllNavNotifications(¬ifications
, &controller
);
3079 const GURL
url0("http://foo/0");
3080 const GURL
url1("http://foo/1");
3081 const GURL
url2("http://foo/2");
3082 const GURL
url3("http://foo/3");
3083 const GURL
url3_ref("http://foo/3#bar");
3084 const GURL
url4("http://foo/4");
3085 const GURL
transient_url("http://foo/transient");
3088 url0
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3089 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3090 main_test_rfh()->PrepareForCommit();
3091 main_test_rfh()->SendNavigate(0, entry_id
, true, url0
);
3093 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3094 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3095 main_test_rfh()->PrepareForCommit();
3096 main_test_rfh()->SendNavigate(1, entry_id
, true, url1
);
3098 notifications
.Reset();
3100 // Adding a transient with no pending entry.
3101 scoped_ptr
<NavigationEntry
> transient_entry(new NavigationEntryImpl
);
3102 transient_entry
->SetURL(transient_url
);
3103 controller
.SetTransientEntry(transient_entry
.Pass());
3105 // We should not have received any notifications.
3106 EXPECT_EQ(0U, notifications
.size());
3109 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3110 EXPECT_EQ(controller
.GetEntryCount(), 3);
3111 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 1);
3112 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
3113 EXPECT_TRUE(controller
.GetLastCommittedEntry());
3114 EXPECT_FALSE(controller
.GetPendingEntry());
3115 EXPECT_TRUE(controller
.CanGoBack());
3116 EXPECT_FALSE(controller
.CanGoForward());
3117 EXPECT_EQ(contents()->GetMaxPageID(), 1);
3121 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3122 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3123 main_test_rfh()->PrepareForCommit();
3124 main_test_rfh()->SendNavigate(2, entry_id
, true, url2
);
3126 // We should have navigated, transient entry should be gone.
3127 EXPECT_EQ(url2
, controller
.GetVisibleEntry()->GetURL());
3128 EXPECT_EQ(controller
.GetEntryCount(), 3);
3130 // Add a transient again, then navigate with no pending entry this time.
3131 transient_entry
.reset(new NavigationEntryImpl
);
3132 transient_entry
->SetURL(transient_url
);
3133 controller
.SetTransientEntry(transient_entry
.Pass());
3134 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3135 main_test_rfh()->SendRendererInitiatedNavigationRequest(url3
, true);
3136 main_test_rfh()->PrepareForCommit();
3137 main_test_rfh()->SendNavigate(3, 0, true, url3
);
3138 // Transient entry should be gone.
3139 EXPECT_EQ(url3
, controller
.GetVisibleEntry()->GetURL());
3140 EXPECT_EQ(controller
.GetEntryCount(), 4);
3142 // Initiate a navigation, add a transient then commit navigation.
3144 url4
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3145 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3146 transient_entry
.reset(new NavigationEntryImpl
);
3147 transient_entry
->SetURL(transient_url
);
3148 controller
.SetTransientEntry(transient_entry
.Pass());
3149 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3150 main_test_rfh()->PrepareForCommit();
3151 main_test_rfh()->SendNavigate(4, entry_id
, true, url4
);
3152 EXPECT_EQ(url4
, controller
.GetVisibleEntry()->GetURL());
3153 EXPECT_EQ(controller
.GetEntryCount(), 5);
3155 // Add a transient and go back. This should simply remove the transient.
3156 transient_entry
.reset(new NavigationEntryImpl
);
3157 transient_entry
->SetURL(transient_url
);
3158 controller
.SetTransientEntry(transient_entry
.Pass());
3159 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3160 EXPECT_TRUE(controller
.CanGoBack());
3161 EXPECT_FALSE(controller
.CanGoForward());
3162 controller
.GoBack();
3163 // Transient entry should be gone.
3164 EXPECT_EQ(url4
, controller
.GetVisibleEntry()->GetURL());
3165 EXPECT_EQ(controller
.GetEntryCount(), 5);
3167 // Suppose the page requested a history navigation backward.
3168 controller
.GoToOffset(-1);
3169 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3170 main_test_rfh()->PrepareForCommit();
3171 main_test_rfh()->SendNavigate(3, entry_id
, false, url3
);
3173 // Add a transient and go to an entry before the current one.
3174 transient_entry
.reset(new NavigationEntryImpl
);
3175 transient_entry
->SetURL(transient_url
);
3176 controller
.SetTransientEntry(transient_entry
.Pass());
3177 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3178 controller
.GoToIndex(1);
3179 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3180 // The navigation should have been initiated, transient entry should be gone.
3181 EXPECT_FALSE(controller
.GetTransientEntry());
3182 EXPECT_EQ(url1
, controller
.GetPendingEntry()->GetURL());
3183 // Visible entry does not update for history navigations until commit.
3184 EXPECT_EQ(url3
, controller
.GetVisibleEntry()->GetURL());
3185 main_test_rfh()->PrepareForCommit();
3186 main_test_rfh()->SendNavigate(1, entry_id
, false, url1
);
3187 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
3189 // Add a transient and go to an entry after the current one.
3190 transient_entry
.reset(new NavigationEntryImpl
);
3191 transient_entry
->SetURL(transient_url
);
3192 controller
.SetTransientEntry(transient_entry
.Pass());
3193 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3194 controller
.GoToIndex(3);
3195 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3196 // The navigation should have been initiated, transient entry should be gone.
3197 // Because of the transient entry that is removed, going to index 3 makes us
3198 // land on url2 (which is visible after the commit).
3199 EXPECT_EQ(url2
, controller
.GetPendingEntry()->GetURL());
3200 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
3201 main_test_rfh()->PrepareForCommit();
3202 main_test_rfh()->SendNavigate(2, entry_id
, false, url2
);
3203 EXPECT_EQ(url2
, controller
.GetVisibleEntry()->GetURL());
3205 // Add a transient and go forward.
3206 transient_entry
.reset(new NavigationEntryImpl
);
3207 transient_entry
->SetURL(transient_url
);
3208 controller
.SetTransientEntry(transient_entry
.Pass());
3209 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3210 EXPECT_TRUE(controller
.CanGoForward());
3211 controller
.GoForward();
3212 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3213 // We should have navigated, transient entry should be gone.
3214 EXPECT_FALSE(controller
.GetTransientEntry());
3215 EXPECT_EQ(url3
, controller
.GetPendingEntry()->GetURL());
3216 EXPECT_EQ(url2
, controller
.GetVisibleEntry()->GetURL());
3217 main_test_rfh()->PrepareForCommit();
3218 main_test_rfh()->SendNavigate(3, entry_id
, false, url3
);
3219 EXPECT_EQ(url3
, controller
.GetVisibleEntry()->GetURL());
3221 // Add a transient and do an in-page navigation, replacing the current entry.
3222 transient_entry
.reset(new NavigationEntryImpl
);
3223 transient_entry
->SetURL(transient_url
);
3224 controller
.SetTransientEntry(transient_entry
.Pass());
3225 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3227 main_test_rfh()->SendRendererInitiatedNavigationRequest(url3_ref
, false);
3228 main_test_rfh()->PrepareForCommit();
3229 main_test_rfh()->SendNavigate(3, 0, false, url3_ref
);
3230 // Transient entry should be gone.
3231 EXPECT_FALSE(controller
.GetTransientEntry());
3232 EXPECT_EQ(url3_ref
, controller
.GetVisibleEntry()->GetURL());
3234 // Ensure the URLs are correct.
3235 EXPECT_EQ(controller
.GetEntryCount(), 5);
3236 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(), url0
);
3237 EXPECT_EQ(controller
.GetEntryAtIndex(1)->GetURL(), url1
);
3238 EXPECT_EQ(controller
.GetEntryAtIndex(2)->GetURL(), url2
);
3239 EXPECT_EQ(controller
.GetEntryAtIndex(3)->GetURL(), url3_ref
);
3240 EXPECT_EQ(controller
.GetEntryAtIndex(4)->GetURL(), url4
);
3243 // Test that Reload initiates a new navigation to a transient entry's URL.
3244 TEST_F(NavigationControllerTest
, ReloadTransient
) {
3245 NavigationControllerImpl
& controller
= controller_impl();
3246 const GURL
url0("http://foo/0");
3247 const GURL
url1("http://foo/1");
3248 const GURL
transient_url("http://foo/transient");
3250 // Load |url0|, and start a pending navigation to |url1|.
3252 url0
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3253 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3254 main_test_rfh()->PrepareForCommit();
3255 main_test_rfh()->SendNavigate(0, entry_id
, true, url0
);
3257 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3259 // A transient entry is added, interrupting the navigation.
3260 scoped_ptr
<NavigationEntry
> transient_entry(new NavigationEntryImpl
);
3261 transient_entry
->SetURL(transient_url
);
3262 controller
.SetTransientEntry(transient_entry
.Pass());
3263 EXPECT_TRUE(controller
.GetTransientEntry());
3264 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3266 // The page is reloaded, which should remove the pending entry for |url1| and
3267 // the transient entry for |transient_url|, and start a navigation to
3269 controller
.Reload(true);
3270 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3271 EXPECT_FALSE(controller
.GetTransientEntry());
3272 EXPECT_TRUE(controller
.GetPendingEntry());
3273 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3274 ASSERT_EQ(controller
.GetEntryCount(), 1);
3275 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(), url0
);
3277 // Load of |transient_url| completes.
3278 main_test_rfh()->PrepareForCommit();
3279 main_test_rfh()->SendNavigate(1, entry_id
, true, transient_url
);
3280 ASSERT_EQ(controller
.GetEntryCount(), 2);
3281 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(), url0
);
3282 EXPECT_EQ(controller
.GetEntryAtIndex(1)->GetURL(), transient_url
);
3285 // Ensure that renderer initiated pending entries get replaced, so that we
3286 // don't show a stale virtual URL when a navigation commits.
3287 // See http://crbug.com/266922.
3288 TEST_F(NavigationControllerTest
, RendererInitiatedPendingEntries
) {
3289 NavigationControllerImpl
& controller
= controller_impl();
3290 Navigator
* navigator
=
3291 contents()->GetFrameTree()->root()->navigator();
3293 const GURL
url1("nonexistent:12121");
3294 const GURL
url1_fixed("http://nonexistent:12121/");
3295 const GURL
url2("http://foo");
3297 // We create pending entries for renderer-initiated navigations so that we
3298 // can show them in new tabs when it is safe.
3299 main_test_rfh()->SendRendererInitiatedNavigationRequest(url1
, false);
3300 main_test_rfh()->PrepareForCommit();
3301 navigator
->DidStartProvisionalLoad(main_test_rfh(), url1
);
3303 // Simulate what happens if a BrowserURLHandler rewrites the URL, causing
3304 // the virtual URL to differ from the URL.
3305 controller
.GetPendingEntry()->SetURL(url1_fixed
);
3306 controller
.GetPendingEntry()->SetVirtualURL(url1
);
3308 EXPECT_EQ(url1_fixed
, controller
.GetPendingEntry()->GetURL());
3309 EXPECT_EQ(url1
, controller
.GetPendingEntry()->GetVirtualURL());
3310 EXPECT_TRUE(controller
.GetPendingEntry()->is_renderer_initiated());
3312 // If the user clicks another link, we should replace the pending entry.
3313 main_test_rfh()->SendRendererInitiatedNavigationRequest(url2
, false);
3314 main_test_rfh()->PrepareForCommit();
3315 navigator
->DidStartProvisionalLoad(main_test_rfh(), url2
);
3316 EXPECT_EQ(url2
, controller
.GetPendingEntry()->GetURL());
3317 EXPECT_EQ(url2
, controller
.GetPendingEntry()->GetVirtualURL());
3319 // Once it commits, the URL and virtual URL should reflect the actual page.
3320 main_test_rfh()->SendNavigate(0, 0, true, url2
);
3321 EXPECT_EQ(url2
, controller
.GetLastCommittedEntry()->GetURL());
3322 EXPECT_EQ(url2
, controller
.GetLastCommittedEntry()->GetVirtualURL());
3324 // We should not replace the pending entry for an error URL.
3325 navigator
->DidStartProvisionalLoad(main_test_rfh(), url1
);
3326 EXPECT_EQ(url1
, controller
.GetPendingEntry()->GetURL());
3327 navigator
->DidStartProvisionalLoad(main_test_rfh(),
3328 GURL(kUnreachableWebDataURL
));
3329 EXPECT_EQ(url1
, controller
.GetPendingEntry()->GetURL());
3331 // We should remember if the pending entry will replace the current one.
3332 // http://crbug.com/308444.
3333 navigator
->DidStartProvisionalLoad(main_test_rfh(), url1
);
3334 controller
.GetPendingEntry()->set_should_replace_entry(true);
3336 main_test_rfh()->SendRendererInitiatedNavigationRequest(url2
, false);
3337 main_test_rfh()->PrepareForCommit();
3338 navigator
->DidStartProvisionalLoad(main_test_rfh(), url2
);
3339 EXPECT_TRUE(controller
.GetPendingEntry()->should_replace_entry());
3340 main_test_rfh()->SendNavigate(0, 0, false, url2
);
3341 EXPECT_EQ(url2
, controller
.GetLastCommittedEntry()->GetURL());
3344 // Tests that the URLs for renderer-initiated navigations are not displayed to
3345 // the user until the navigation commits, to prevent URL spoof attacks.
3346 // See http://crbug.com/99016.
3347 TEST_F(NavigationControllerTest
, DontShowRendererURLUntilCommit
) {
3348 NavigationControllerImpl
& controller
= controller_impl();
3349 TestNotificationTracker notifications
;
3350 RegisterForAllNavNotifications(¬ifications
, &controller
);
3352 const GURL
url0("http://foo/0");
3353 const GURL
url1("http://foo/1");
3355 // For typed navigations (browser-initiated), both pending and visible entries
3356 // should update before commit.
3358 url0
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3359 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3360 EXPECT_EQ(url0
, controller
.GetPendingEntry()->GetURL());
3361 EXPECT_EQ(url0
, controller
.GetVisibleEntry()->GetURL());
3362 main_test_rfh()->PrepareForCommit();
3363 main_test_rfh()->SendNavigate(0, entry_id
, true, url0
);
3365 // For link clicks (renderer-initiated navigations), the pending entry should
3366 // update before commit but the visible should not.
3367 NavigationController::LoadURLParams
load_url_params(url1
);
3368 load_url_params
.is_renderer_initiated
= true;
3369 controller
.LoadURLWithParams(load_url_params
);
3370 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3371 EXPECT_EQ(url0
, controller
.GetVisibleEntry()->GetURL());
3372 EXPECT_EQ(url1
, controller
.GetPendingEntry()->GetURL());
3373 EXPECT_TRUE(controller
.GetPendingEntry()->is_renderer_initiated());
3375 // After commit, both visible should be updated, there should be no pending
3376 // entry, and we should no longer treat the entry as renderer-initiated.
3377 main_test_rfh()->PrepareForCommit();
3378 main_test_rfh()->SendNavigate(1, entry_id
, true, url1
);
3379 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
3380 EXPECT_FALSE(controller
.GetPendingEntry());
3381 EXPECT_FALSE(controller
.GetLastCommittedEntry()->is_renderer_initiated());
3383 notifications
.Reset();
3386 // Tests that the URLs for renderer-initiated navigations in new tabs are
3387 // displayed to the user before commit, as long as the initial about:blank
3388 // page has not been modified. If so, we must revert to showing about:blank.
3389 // See http://crbug.com/9682.
3390 TEST_F(NavigationControllerTest
, ShowRendererURLInNewTabUntilModified
) {
3391 NavigationControllerImpl
& controller
= controller_impl();
3392 TestNotificationTracker notifications
;
3393 RegisterForAllNavNotifications(¬ifications
, &controller
);
3395 const GURL
url("http://foo");
3397 // For renderer-initiated navigations in new tabs (with no committed entries),
3398 // we show the pending entry's URL as long as the about:blank page is not
3400 NavigationController::LoadURLParams
load_url_params(url
);
3401 load_url_params
.transition_type
= ui::PAGE_TRANSITION_LINK
;
3402 load_url_params
.is_renderer_initiated
= true;
3403 controller
.LoadURLWithParams(load_url_params
);
3404 EXPECT_EQ(url
, controller
.GetVisibleEntry()->GetURL());
3405 EXPECT_EQ(url
, controller
.GetPendingEntry()->GetURL());
3406 EXPECT_TRUE(controller
.GetPendingEntry()->is_renderer_initiated());
3407 EXPECT_TRUE(controller
.IsInitialNavigation());
3408 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3410 // There should be no title yet.
3411 EXPECT_TRUE(contents()->GetTitle().empty());
3413 // If something else modifies the contents of the about:blank page, then
3414 // we must revert to showing about:blank to avoid a URL spoof.
3415 main_test_rfh()->OnMessageReceived(FrameHostMsg_DidAccessInitialDocument(0));
3416 EXPECT_TRUE(contents()->HasAccessedInitialDocument());
3417 EXPECT_FALSE(controller
.GetVisibleEntry());
3418 EXPECT_EQ(url
, controller
.GetPendingEntry()->GetURL());
3420 notifications
.Reset();
3423 // Tests that the URLs for browser-initiated navigations in new tabs are
3424 // displayed to the user even after they fail, as long as the initial
3425 // about:blank page has not been modified. If so, we must revert to showing
3426 // about:blank. See http://crbug.com/355537.
3427 TEST_F(NavigationControllerTest
, ShowBrowserURLAfterFailUntilModified
) {
3428 NavigationControllerImpl
& controller
= controller_impl();
3429 TestNotificationTracker notifications
;
3430 RegisterForAllNavNotifications(¬ifications
, &controller
);
3432 const GURL
url("http://foo");
3434 // For browser-initiated navigations in new tabs (with no committed entries),
3435 // we show the pending entry's URL as long as the about:blank page is not
3436 // modified. This is possible in cases that the user types a URL into a popup
3437 // tab created with a slow URL.
3438 NavigationController::LoadURLParams
load_url_params(url
);
3439 load_url_params
.transition_type
= ui::PAGE_TRANSITION_TYPED
;
3440 load_url_params
.is_renderer_initiated
= false;
3441 controller
.LoadURLWithParams(load_url_params
);
3442 EXPECT_EQ(url
, controller
.GetVisibleEntry()->GetURL());
3443 EXPECT_EQ(url
, controller
.GetPendingEntry()->GetURL());
3444 EXPECT_FALSE(controller
.GetPendingEntry()->is_renderer_initiated());
3445 EXPECT_TRUE(controller
.IsInitialNavigation());
3446 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3448 // There should be no title yet.
3449 EXPECT_TRUE(contents()->GetTitle().empty());
3451 // Suppose it aborts before committing, if it's a 204 or download or due to a
3452 // stop or a new navigation from the user. The URL should remain visible.
3453 FrameHostMsg_DidFailProvisionalLoadWithError_Params params
;
3454 params
.error_code
= net::ERR_ABORTED
;
3455 params
.error_description
= base::string16();
3457 params
.showing_repost_interstitial
= false;
3458 main_test_rfh()->OnMessageReceived(
3459 FrameHostMsg_DidFailProvisionalLoadWithError(0, params
));
3460 contents()->SetIsLoading(false, true, NULL
);
3461 EXPECT_EQ(url
, controller
.GetVisibleEntry()->GetURL());
3463 // If something else later modifies the contents of the about:blank page, then
3464 // we must revert to showing about:blank to avoid a URL spoof.
3465 main_test_rfh()->OnMessageReceived(FrameHostMsg_DidAccessInitialDocument(0));
3466 EXPECT_TRUE(contents()->HasAccessedInitialDocument());
3467 EXPECT_FALSE(controller
.GetVisibleEntry());
3468 EXPECT_FALSE(controller
.GetPendingEntry());
3470 notifications
.Reset();
3473 // Tests that the URLs for renderer-initiated navigations in new tabs are
3474 // displayed to the user even after they fail, as long as the initial
3475 // about:blank page has not been modified. If so, we must revert to showing
3476 // about:blank. See http://crbug.com/355537.
3477 TEST_F(NavigationControllerTest
, ShowRendererURLAfterFailUntilModified
) {
3478 NavigationControllerImpl
& controller
= controller_impl();
3479 TestNotificationTracker notifications
;
3480 RegisterForAllNavNotifications(¬ifications
, &controller
);
3482 const GURL
url("http://foo");
3484 // For renderer-initiated navigations in new tabs (with no committed entries),
3485 // we show the pending entry's URL as long as the about:blank page is not
3487 NavigationController::LoadURLParams
load_url_params(url
);
3488 load_url_params
.transition_type
= ui::PAGE_TRANSITION_LINK
;
3489 load_url_params
.is_renderer_initiated
= true;
3490 controller
.LoadURLWithParams(load_url_params
);
3491 EXPECT_EQ(url
, controller
.GetVisibleEntry()->GetURL());
3492 EXPECT_EQ(url
, controller
.GetPendingEntry()->GetURL());
3493 EXPECT_TRUE(controller
.GetPendingEntry()->is_renderer_initiated());
3494 EXPECT_TRUE(controller
.IsInitialNavigation());
3495 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3497 // There should be no title yet.
3498 EXPECT_TRUE(contents()->GetTitle().empty());
3500 // Suppose it aborts before committing, if it's a 204 or download or due to a
3501 // stop or a new navigation from the user. The URL should remain visible.
3502 FrameHostMsg_DidFailProvisionalLoadWithError_Params params
;
3503 params
.error_code
= net::ERR_ABORTED
;
3504 params
.error_description
= base::string16();
3506 params
.showing_repost_interstitial
= false;
3507 main_test_rfh()->OnMessageReceived(
3508 FrameHostMsg_DidFailProvisionalLoadWithError(0, params
));
3509 EXPECT_EQ(url
, controller
.GetVisibleEntry()->GetURL());
3511 // If something else later modifies the contents of the about:blank page, then
3512 // we must revert to showing about:blank to avoid a URL spoof.
3513 main_test_rfh()->OnMessageReceived(FrameHostMsg_DidAccessInitialDocument(0));
3514 EXPECT_TRUE(contents()->HasAccessedInitialDocument());
3515 EXPECT_FALSE(controller
.GetVisibleEntry());
3516 EXPECT_EQ(url
, controller
.GetPendingEntry()->GetURL());
3518 notifications
.Reset();
3521 TEST_F(NavigationControllerTest
, DontShowRendererURLInNewTabAfterCommit
) {
3522 NavigationControllerImpl
& controller
= controller_impl();
3523 TestNotificationTracker notifications
;
3524 RegisterForAllNavNotifications(¬ifications
, &controller
);
3526 const GURL
url1("http://foo/eh");
3527 const GURL
url2("http://foo/bee");
3529 // For renderer-initiated navigations in new tabs (with no committed entries),
3530 // we show the pending entry's URL as long as the about:blank page is not
3532 NavigationController::LoadURLParams
load_url_params(url1
);
3533 load_url_params
.transition_type
= ui::PAGE_TRANSITION_LINK
;
3534 load_url_params
.is_renderer_initiated
= true;
3535 controller
.LoadURLWithParams(load_url_params
);
3536 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3537 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
3538 EXPECT_TRUE(controller
.GetPendingEntry()->is_renderer_initiated());
3539 EXPECT_TRUE(controller
.IsInitialNavigation());
3540 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3542 // Simulate a commit and then starting a new pending navigation.
3543 main_test_rfh()->PrepareForCommit();
3544 main_test_rfh()->SendNavigate(0, entry_id
, true, url1
);
3545 NavigationController::LoadURLParams
load_url2_params(url2
);
3546 load_url2_params
.transition_type
= ui::PAGE_TRANSITION_LINK
;
3547 load_url2_params
.is_renderer_initiated
= true;
3548 controller
.LoadURLWithParams(load_url2_params
);
3550 // We should not consider this an initial navigation, and thus should
3551 // not show the pending URL.
3552 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3553 EXPECT_FALSE(controller
.IsInitialNavigation());
3554 EXPECT_TRUE(controller
.GetVisibleEntry());
3555 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
3557 notifications
.Reset();
3560 // Tests that IsInPageNavigation returns appropriate results. Prevents
3561 // regression for bug 1126349.
3562 TEST_F(NavigationControllerTest
, IsInPageNavigation
) {
3563 NavigationControllerImpl
& controller
= controller_impl();
3564 const GURL
url("http://www.google.com/home.html");
3566 // If the renderer claims it performed an in-page navigation from
3567 // about:blank, trust the renderer.
3568 // This can happen when an iframe is created and populated via
3569 // document.write(), then tries to perform a fragment navigation.
3570 // TODO(japhet): We should only trust the renderer if the about:blank
3571 // was the first document in the given frame, but we don't have enough
3572 // information to identify that case currently.
3573 const GURL
blank_url(url::kAboutBlankURL
);
3574 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, blank_url
);
3575 EXPECT_TRUE(controller
.IsURLInPageNavigation(url
, true,
3578 // Navigate to URL with no refs.
3579 main_test_rfh()->NavigateAndCommitRendererInitiated(0, false, url
);
3581 // Reloading the page is not an in-page navigation.
3582 EXPECT_FALSE(controller
.IsURLInPageNavigation(url
, false, main_test_rfh()));
3583 const GURL
other_url("http://www.google.com/add.html");
3584 EXPECT_FALSE(controller
.IsURLInPageNavigation(other_url
, false,
3586 const GURL
url_with_ref("http://www.google.com/home.html#my_ref");
3587 EXPECT_TRUE(controller
.IsURLInPageNavigation(url_with_ref
, true,
3590 // Navigate to URL with refs.
3591 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url_with_ref
);
3593 // Reloading the page is not an in-page navigation.
3594 EXPECT_FALSE(controller
.IsURLInPageNavigation(url_with_ref
, false,
3596 EXPECT_FALSE(controller
.IsURLInPageNavigation(url
, false,
3598 EXPECT_FALSE(controller
.IsURLInPageNavigation(other_url
, false,
3600 const GURL
other_url_with_ref("http://www.google.com/home.html#my_other_ref");
3601 EXPECT_TRUE(controller
.IsURLInPageNavigation(other_url_with_ref
, true,
3604 // Going to the same url again will be considered in-page
3605 // if the renderer says it is even if the navigation type isn't IN_PAGE.
3606 EXPECT_TRUE(controller
.IsURLInPageNavigation(url_with_ref
, true,
3609 // Going back to the non ref url will be considered in-page if the navigation
3611 EXPECT_TRUE(controller
.IsURLInPageNavigation(url
, true,
3614 // If the renderer says this is a same-origin in-page navigation, believe it.
3615 // This is the pushState/replaceState case.
3616 EXPECT_TRUE(controller
.IsURLInPageNavigation(other_url
, true,
3619 // Test allow_universal_access_from_file_urls flag.
3620 const GURL
different_origin_url("http://www.example.com");
3621 MockRenderProcessHost
* rph
= main_test_rfh()->GetProcess();
3622 WebPreferences prefs
= test_rvh()->GetWebkitPreferences();
3623 prefs
.allow_universal_access_from_file_urls
= true;
3624 test_rvh()->UpdateWebkitPreferences(prefs
);
3625 prefs
= test_rvh()->GetWebkitPreferences();
3626 EXPECT_TRUE(prefs
.allow_universal_access_from_file_urls
);
3627 // Allow in page navigation if existing URL is file scheme.
3628 const GURL
file_url("file:///foo/index.html");
3629 main_test_rfh()->NavigateAndCommitRendererInitiated(0, false, file_url
);
3630 EXPECT_EQ(0, rph
->bad_msg_count());
3631 EXPECT_TRUE(controller
.IsURLInPageNavigation(different_origin_url
, true,
3633 EXPECT_EQ(0, rph
->bad_msg_count());
3634 // Don't honor allow_universal_access_from_file_urls if existing URL is
3636 main_test_rfh()->NavigateAndCommitRendererInitiated(0, false, url
);
3637 EXPECT_FALSE(controller
.IsURLInPageNavigation(different_origin_url
, true,
3639 EXPECT_EQ(1, rph
->bad_msg_count());
3641 // Remove allow_universal_access_from_file_urls flag.
3642 prefs
.allow_universal_access_from_file_urls
= false;
3643 test_rvh()->UpdateWebkitPreferences(prefs
);
3644 prefs
= test_rvh()->GetWebkitPreferences();
3645 EXPECT_FALSE(prefs
.allow_universal_access_from_file_urls
);
3647 // Don't believe the renderer if it claims a cross-origin navigation is
3649 EXPECT_EQ(1, rph
->bad_msg_count());
3650 EXPECT_FALSE(controller
.IsURLInPageNavigation(different_origin_url
, true,
3652 EXPECT_EQ(2, rph
->bad_msg_count());
3655 // Some pages can have subframes with the same base URL (minus the reference) as
3656 // the main page. Even though this is hard, it can happen, and we don't want
3657 // these subframe navigations to affect the toplevel document. They should
3658 // instead be ignored. http://crbug.com/5585
3659 TEST_F(NavigationControllerTest
, SameSubframe
) {
3660 NavigationControllerImpl
& controller
= controller_impl();
3661 // Navigate the main frame.
3662 const GURL
url("http://www.google.com/");
3663 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url
);
3665 // We should be at the first navigation entry.
3666 EXPECT_EQ(controller
.GetEntryCount(), 1);
3667 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
3669 // Add and navigate a subframe that would normally count as in-page.
3670 main_test_rfh()->OnCreateChildFrame(
3671 MSG_ROUTING_NONE
, blink::WebTreeScopeType::Document
, std::string(),
3672 blink::WebSandboxFlags::None
);
3673 RenderFrameHostImpl
* subframe
=
3674 contents()->GetFrameTree()->root()->child_at(0)->current_frame_host();
3675 const GURL
subframe_url("http://www.google.com/#");
3676 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
3678 params
.nav_entry_id
= 0;
3679 params
.did_create_new_entry
= false;
3680 params
.url
= subframe_url
;
3681 params
.transition
= ui::PAGE_TRANSITION_AUTO_SUBFRAME
;
3682 params
.should_update_history
= false;
3683 params
.gesture
= NavigationGestureAuto
;
3684 params
.is_post
= false;
3685 params
.page_state
= PageState::CreateFromURL(subframe_url
);
3686 LoadCommittedDetails details
;
3687 EXPECT_FALSE(controller
.RendererDidNavigate(subframe
, params
, &details
));
3689 // Nothing should have changed.
3690 EXPECT_EQ(controller
.GetEntryCount(), 1);
3691 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
3694 // Make sure that on cloning a WebContentsImpl and going back needs_reload is
3696 TEST_F(NavigationControllerTest
, CloneAndGoBack
) {
3697 NavigationControllerImpl
& controller
= controller_impl();
3698 const GURL
url1("http://foo1");
3699 const GURL
url2("http://foo2");
3700 const base::string16
title(base::ASCIIToUTF16("Title"));
3702 NavigateAndCommit(url1
);
3703 controller
.GetVisibleEntry()->SetTitle(title
);
3704 NavigateAndCommit(url2
);
3706 scoped_ptr
<WebContents
> clone(controller
.GetWebContents()->Clone());
3708 ASSERT_EQ(2, clone
->GetController().GetEntryCount());
3709 EXPECT_TRUE(clone
->GetController().NeedsReload());
3710 clone
->GetController().GoBack();
3711 // Navigating back should have triggered needs_reload_ to go false.
3712 EXPECT_FALSE(clone
->GetController().NeedsReload());
3714 // Ensure that the pending URL and its title are visible.
3715 EXPECT_EQ(url1
, clone
->GetController().GetVisibleEntry()->GetURL());
3716 EXPECT_EQ(title
, clone
->GetTitle());
3719 // Make sure that reloading a cloned tab doesn't change its pending entry index.
3720 // See http://crbug.com/234491.
3721 TEST_F(NavigationControllerTest
, CloneAndReload
) {
3722 NavigationControllerImpl
& controller
= controller_impl();
3723 const GURL
url1("http://foo1");
3724 const GURL
url2("http://foo2");
3725 const base::string16
title(base::ASCIIToUTF16("Title"));
3727 NavigateAndCommit(url1
);
3728 controller
.GetVisibleEntry()->SetTitle(title
);
3729 NavigateAndCommit(url2
);
3731 scoped_ptr
<WebContents
> clone(controller
.GetWebContents()->Clone());
3732 clone
->GetController().LoadIfNecessary();
3734 ASSERT_EQ(2, clone
->GetController().GetEntryCount());
3735 EXPECT_EQ(1, clone
->GetController().GetPendingEntryIndex());
3737 clone
->GetController().Reload(true);
3738 EXPECT_EQ(1, clone
->GetController().GetPendingEntryIndex());
3741 // Make sure that cloning a WebContentsImpl doesn't copy interstitials.
3742 TEST_F(NavigationControllerTest
, CloneOmitsInterstitials
) {
3743 NavigationControllerImpl
& controller
= controller_impl();
3744 const GURL
url1("http://foo1");
3745 const GURL
url2("http://foo2");
3747 NavigateAndCommit(url1
);
3748 NavigateAndCommit(url2
);
3750 // Add an interstitial entry. Should be deleted with controller.
3751 NavigationEntryImpl
* interstitial_entry
= new NavigationEntryImpl();
3752 interstitial_entry
->set_page_type(PAGE_TYPE_INTERSTITIAL
);
3753 controller
.SetTransientEntry(make_scoped_ptr(interstitial_entry
));
3755 scoped_ptr
<WebContents
> clone(controller
.GetWebContents()->Clone());
3757 ASSERT_EQ(2, clone
->GetController().GetEntryCount());
3760 // Test requesting and triggering a lazy reload.
3761 TEST_F(NavigationControllerTest
, LazyReload
) {
3762 NavigationControllerImpl
& controller
= controller_impl();
3763 const GURL
url("http://foo");
3764 NavigateAndCommit(url
);
3765 ASSERT_FALSE(controller
.NeedsReload());
3766 EXPECT_NE(ui::PAGE_TRANSITION_RELOAD
,
3767 controller
.GetLastCommittedEntry()->GetTransitionType());
3769 // Request a reload to happen when the controller becomes active (e.g. after
3770 // the renderer gets killed in background on Android).
3771 controller
.SetNeedsReload();
3772 ASSERT_TRUE(controller
.NeedsReload());
3773 EXPECT_EQ(ui::PAGE_TRANSITION_RELOAD
,
3774 controller
.GetLastCommittedEntry()->GetTransitionType());
3776 // Set the controller as active, triggering the requested reload.
3777 controller
.SetActive(true);
3778 ASSERT_FALSE(controller
.NeedsReload());
3779 EXPECT_EQ(ui::PAGE_TRANSITION_RELOAD
,
3780 controller
.GetPendingEntry()->GetTransitionType());
3783 // Test requesting and triggering a lazy reload without any committed entry.
3784 TEST_F(NavigationControllerTest
, LazyReloadWithoutCommittedEntry
) {
3785 NavigationControllerImpl
& controller
= controller_impl();
3786 const GURL
url("http://foo");
3787 controller
.LoadURL(url
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3788 ASSERT_FALSE(controller
.NeedsReload());
3789 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED
,
3790 controller
.GetPendingEntry()->GetTransitionType());
3792 // Request a reload to happen when the controller becomes active (e.g. after
3793 // the renderer gets killed in background on Android).
3794 controller
.SetNeedsReload();
3795 ASSERT_TRUE(controller
.NeedsReload());
3796 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED
,
3797 controller
.GetPendingEntry()->GetTransitionType());
3799 // Set the controller as active, triggering the requested reload.
3800 controller
.SetActive(true);
3801 ASSERT_FALSE(controller
.NeedsReload());
3802 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED
,
3803 controller
.GetPendingEntry()->GetTransitionType());
3806 // Tests a subframe navigation while a toplevel navigation is pending.
3807 // http://crbug.com/43967
3808 TEST_F(NavigationControllerTest
, SubframeWhilePending
) {
3809 NavigationControllerImpl
& controller
= controller_impl();
3810 // Load the first page.
3811 const GURL
url1("http://foo/");
3812 NavigateAndCommit(url1
);
3814 // Now start a pending load to a totally different page, but don't commit it.
3815 const GURL
url2("http://bar/");
3817 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3819 // Send a subframe update from the first page, as if one had just
3820 // automatically loaded. Auto subframes don't increment the page ID.
3821 main_test_rfh()->OnCreateChildFrame(
3822 MSG_ROUTING_NONE
, blink::WebTreeScopeType::Document
, std::string(),
3823 blink::WebSandboxFlags::None
);
3824 RenderFrameHostImpl
* subframe
=
3825 contents()->GetFrameTree()->root()->child_at(0)->current_frame_host();
3826 const GURL
url1_sub("http://foo/subframe");
3827 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
3828 params
.page_id
= controller
.GetLastCommittedEntry()->GetPageID();
3829 params
.nav_entry_id
= 0;
3830 params
.did_create_new_entry
= false;
3831 params
.url
= url1_sub
;
3832 params
.transition
= ui::PAGE_TRANSITION_AUTO_SUBFRAME
;
3833 params
.should_update_history
= false;
3834 params
.gesture
= NavigationGestureAuto
;
3835 params
.is_post
= false;
3836 params
.page_state
= PageState::CreateFromURL(url1_sub
);
3837 LoadCommittedDetails details
;
3839 // This should return false meaning that nothing was actually updated.
3840 EXPECT_FALSE(controller
.RendererDidNavigate(subframe
, params
, &details
));
3842 // The notification should have updated the last committed one, and not
3843 // the pending load.
3844 EXPECT_EQ(url1
, controller
.GetLastCommittedEntry()->GetURL());
3846 // The active entry should be unchanged by the subframe load.
3847 EXPECT_EQ(url2
, controller
.GetVisibleEntry()->GetURL());
3850 // Test CopyStateFrom with 2 urls, the first selected and nothing in the target.
3851 TEST_F(NavigationControllerTest
, CopyStateFrom
) {
3852 NavigationControllerImpl
& controller
= controller_impl();
3853 const GURL
url1("http://foo1");
3854 const GURL
url2("http://foo2");
3856 NavigateAndCommit(url1
);
3857 NavigateAndCommit(url2
);
3858 controller
.GoBack();
3859 contents()->CommitPendingNavigation();
3861 scoped_ptr
<TestWebContents
> other_contents(
3862 static_cast<TestWebContents
*>(CreateTestWebContents()));
3863 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
3864 other_controller
.CopyStateFrom(controller
);
3866 // other_controller should now contain 2 urls.
3867 ASSERT_EQ(2, other_controller
.GetEntryCount());
3868 // We should be looking at the first one.
3869 ASSERT_EQ(0, other_controller
.GetCurrentEntryIndex());
3871 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
3872 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(0)->GetPageID());
3873 EXPECT_EQ(url2
, other_controller
.GetEntryAtIndex(1)->GetURL());
3874 // This is a different site than url1, so the IDs start again at 0.
3875 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(1)->GetPageID());
3877 // The max page ID map should be copied over and updated with the max page ID
3878 // from the current tab.
3879 SiteInstance
* instance1
=
3880 other_controller
.GetEntryAtIndex(0)->site_instance();
3881 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
3883 // Ensure the SessionStorageNamespaceMaps are the same size and have
3884 // the same partitons loaded.
3886 // TODO(ajwong): We should load a url from a different partition earlier
3887 // to make sure this map has more than one entry.
3888 const SessionStorageNamespaceMap
& session_storage_namespace_map
=
3889 controller
.GetSessionStorageNamespaceMap();
3890 const SessionStorageNamespaceMap
& other_session_storage_namespace_map
=
3891 other_controller
.GetSessionStorageNamespaceMap();
3892 EXPECT_EQ(session_storage_namespace_map
.size(),
3893 other_session_storage_namespace_map
.size());
3894 for (SessionStorageNamespaceMap::const_iterator it
=
3895 session_storage_namespace_map
.begin();
3896 it
!= session_storage_namespace_map
.end();
3898 SessionStorageNamespaceMap::const_iterator other
=
3899 other_session_storage_namespace_map
.find(it
->first
);
3900 EXPECT_TRUE(other
!= other_session_storage_namespace_map
.end());
3904 // Tests CopyStateFromAndPrune with 2 urls in source, 1 in dest.
3905 TEST_F(NavigationControllerTest
, CopyStateFromAndPrune
) {
3906 NavigationControllerImpl
& controller
= controller_impl();
3907 const GURL
url1("http://foo/1");
3908 const GURL
url2("http://foo/2");
3909 const GURL
url3("http://foo/3");
3911 NavigateAndCommit(url1
);
3912 NavigateAndCommit(url2
);
3914 // First two entries should have the same SiteInstance.
3915 SiteInstance
* instance1
= controller
.GetEntryAtIndex(0)->site_instance();
3916 SiteInstance
* instance2
= controller
.GetEntryAtIndex(1)->site_instance();
3917 EXPECT_EQ(instance1
, instance2
);
3918 EXPECT_EQ(0, controller
.GetEntryAtIndex(0)->GetPageID());
3919 EXPECT_EQ(1, controller
.GetEntryAtIndex(1)->GetPageID());
3920 EXPECT_EQ(1, contents()->GetMaxPageIDForSiteInstance(instance1
));
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(2, 3);
3927 other_controller
.CopyStateFromAndPrune(&controller
, false);
3929 // other_controller should now contain the 3 urls: url1, url2 and url3.
3931 ASSERT_EQ(3, other_controller
.GetEntryCount());
3933 ASSERT_EQ(2, other_controller
.GetCurrentEntryIndex());
3935 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
3936 EXPECT_EQ(url2
, other_controller
.GetEntryAtIndex(1)->GetURL());
3937 EXPECT_EQ(url3
, other_controller
.GetEntryAtIndex(2)->GetURL());
3938 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(0)->GetPageID());
3939 EXPECT_EQ(1, other_controller
.GetEntryAtIndex(1)->GetPageID());
3940 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(2)->GetPageID());
3942 // A new SiteInstance in a different BrowsingInstance should be used for the
3944 SiteInstance
* instance3
=
3945 other_controller
.GetEntryAtIndex(2)->site_instance();
3946 EXPECT_NE(instance3
, instance1
);
3947 EXPECT_FALSE(instance3
->IsRelatedSiteInstance(instance1
));
3949 // The max page ID map should be copied over and updated with the max page ID
3950 // from the current tab.
3951 EXPECT_EQ(1, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
3952 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance3
));
3955 // Test CopyStateFromAndPrune with 2 urls, the first selected and 1 entry in
3957 TEST_F(NavigationControllerTest
, CopyStateFromAndPrune2
) {
3958 NavigationControllerImpl
& controller
= controller_impl();
3959 const GURL
url1("http://foo1");
3960 const GURL
url2("http://foo2");
3961 const GURL
url3("http://foo3");
3963 NavigateAndCommit(url1
);
3964 NavigateAndCommit(url2
);
3965 controller
.GoBack();
3966 contents()->CommitPendingNavigation();
3968 scoped_ptr
<TestWebContents
> other_contents(
3969 static_cast<TestWebContents
*>(CreateTestWebContents()));
3970 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
3971 other_contents
->NavigateAndCommit(url3
);
3972 other_contents
->ExpectSetHistoryOffsetAndLength(1, 2);
3973 other_controller
.CopyStateFromAndPrune(&controller
, false);
3975 // other_controller should now contain: url1, url3
3977 ASSERT_EQ(2, other_controller
.GetEntryCount());
3978 ASSERT_EQ(1, other_controller
.GetCurrentEntryIndex());
3980 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
3981 EXPECT_EQ(url3
, other_controller
.GetEntryAtIndex(1)->GetURL());
3982 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(1)->GetPageID());
3984 // The max page ID map should be copied over and updated with the max page ID
3985 // from the current tab.
3986 SiteInstance
* instance1
=
3987 other_controller
.GetEntryAtIndex(1)->site_instance();
3988 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
3991 // Test CopyStateFromAndPrune with 2 urls, the last selected and 2 entries in
3993 TEST_F(NavigationControllerTest
, CopyStateFromAndPrune3
) {
3994 NavigationControllerImpl
& controller
= controller_impl();
3995 const GURL
url1("http://foo1");
3996 const GURL
url2("http://foo2");
3997 const GURL
url3("http://foo3");
3998 const GURL
url4("http://foo4");
4000 NavigateAndCommit(url1
);
4001 NavigateAndCommit(url2
);
4003 scoped_ptr
<TestWebContents
> other_contents(
4004 static_cast<TestWebContents
*>(CreateTestWebContents()));
4005 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
4006 other_contents
->NavigateAndCommit(url3
);
4007 other_contents
->NavigateAndCommit(url4
);
4008 other_contents
->ExpectSetHistoryOffsetAndLength(2, 3);
4009 other_controller
.CopyStateFromAndPrune(&controller
, false);
4011 // other_controller should now contain: url1, url2, url4
4013 ASSERT_EQ(3, other_controller
.GetEntryCount());
4014 ASSERT_EQ(2, other_controller
.GetCurrentEntryIndex());
4016 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
4017 EXPECT_EQ(url2
, other_controller
.GetEntryAtIndex(1)->GetURL());
4018 EXPECT_EQ(url4
, other_controller
.GetEntryAtIndex(2)->GetURL());
4020 // The max page ID map should be copied over and updated with the max page ID
4021 // from the current tab.
4022 SiteInstance
* instance1
=
4023 other_controller
.GetEntryAtIndex(2)->site_instance();
4024 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
4027 // Test CopyStateFromAndPrune with 2 urls, 2 entries in the target, with
4028 // not the last entry selected in the target.
4029 TEST_F(NavigationControllerTest
, CopyStateFromAndPruneNotLast
) {
4030 NavigationControllerImpl
& controller
= controller_impl();
4031 const GURL
url1("http://foo1");
4032 const GURL
url2("http://foo2");
4033 const GURL
url3("http://foo3");
4034 const GURL
url4("http://foo4");
4036 NavigateAndCommit(url1
);
4037 NavigateAndCommit(url2
);
4039 scoped_ptr
<TestWebContents
> other_contents(
4040 static_cast<TestWebContents
*>(CreateTestWebContents()));
4041 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
4042 other_contents
->NavigateAndCommit(url3
);
4043 other_contents
->NavigateAndCommit(url4
);
4044 other_controller
.GoBack();
4045 other_contents
->CommitPendingNavigation();
4046 other_contents
->ExpectSetHistoryOffsetAndLength(2, 3);
4047 other_controller
.CopyStateFromAndPrune(&controller
, false);
4049 // other_controller should now contain: url1, url2, url3
4051 ASSERT_EQ(3, other_controller
.GetEntryCount());
4052 ASSERT_EQ(2, other_controller
.GetCurrentEntryIndex());
4054 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
4055 EXPECT_EQ(url2
, other_controller
.GetEntryAtIndex(1)->GetURL());
4056 EXPECT_EQ(url3
, other_controller
.GetEntryAtIndex(2)->GetURL());
4058 // The max page ID map should be copied over and updated with the max page ID
4059 // from the current tab.
4060 SiteInstance
* instance1
=
4061 other_controller
.GetEntryAtIndex(2)->site_instance();
4062 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
4065 // Test CopyStateFromAndPrune with 2 urls, the first selected and 1 entry plus
4066 // a pending entry in the target.
4067 TEST_F(NavigationControllerTest
, CopyStateFromAndPruneTargetPending
) {
4068 NavigationControllerImpl
& controller
= controller_impl();
4069 const GURL
url1("http://foo1");
4070 const GURL
url2("http://foo2");
4071 const GURL
url3("http://foo3");
4072 const GURL
url4("http://foo4");
4074 NavigateAndCommit(url1
);
4075 NavigateAndCommit(url2
);
4076 controller
.GoBack();
4077 contents()->CommitPendingNavigation();
4079 scoped_ptr
<TestWebContents
> other_contents(
4080 static_cast<TestWebContents
*>(CreateTestWebContents()));
4081 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
4082 other_contents
->NavigateAndCommit(url3
);
4083 other_controller
.LoadURL(
4084 url4
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
4085 other_contents
->ExpectSetHistoryOffsetAndLength(1, 2);
4086 other_controller
.CopyStateFromAndPrune(&controller
, false);
4088 // other_controller should now contain url1, url3, and a pending entry
4091 ASSERT_EQ(2, other_controller
.GetEntryCount());
4092 EXPECT_EQ(1, other_controller
.GetCurrentEntryIndex());
4094 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
4095 EXPECT_EQ(url3
, other_controller
.GetEntryAtIndex(1)->GetURL());
4097 // And there should be a pending entry for url4.
4098 ASSERT_TRUE(other_controller
.GetPendingEntry());
4099 EXPECT_EQ(url4
, other_controller
.GetPendingEntry()->GetURL());
4101 // The max page ID map should be copied over and updated with the max page ID
4102 // from the current tab.
4103 SiteInstance
* instance1
=
4104 other_controller
.GetEntryAtIndex(0)->site_instance();
4105 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
4108 // Test CopyStateFromAndPrune with 1 url in the source, 1 entry and a pending
4109 // client redirect entry (with the same page ID) in the target. This used to
4110 // crash because the last committed entry would be pruned but max_page_id
4111 // remembered the page ID (http://crbug.com/234809).
4112 TEST_F(NavigationControllerTest
, CopyStateFromAndPruneTargetPending2
) {
4113 NavigationControllerImpl
& controller
= controller_impl();
4114 const GURL
url1("http://foo1");
4115 const GURL
url2a("http://foo2/a");
4116 const GURL
url2b("http://foo2/b");
4118 NavigateAndCommit(url1
);
4120 scoped_ptr
<TestWebContents
> other_contents(
4121 static_cast<TestWebContents
*>(CreateTestWebContents()));
4122 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
4123 other_contents
->NavigateAndCommit(url2a
);
4124 // Simulate a client redirect, which has the same page ID as entry 2a.
4125 other_controller
.LoadURL(
4126 url2b
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
4127 NavigationEntry
* entry
= other_controller
.GetPendingEntry();
4128 entry
->SetPageID(other_controller
.GetLastCommittedEntry()->GetPageID());
4130 other_contents
->ExpectSetHistoryOffsetAndLength(1, 2);
4131 other_controller
.CopyStateFromAndPrune(&controller
, false);
4133 // other_controller should now contain url1, url2a, and a pending entry
4136 ASSERT_EQ(2, other_controller
.GetEntryCount());
4137 EXPECT_EQ(1, other_controller
.GetCurrentEntryIndex());
4139 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
4140 EXPECT_EQ(url2a
, other_controller
.GetEntryAtIndex(1)->GetURL());
4142 // And there should be a pending entry for url4.
4143 ASSERT_TRUE(other_controller
.GetPendingEntry());
4144 EXPECT_EQ(url2b
, other_controller
.GetPendingEntry()->GetURL());
4146 // Let the pending entry commit.
4147 other_contents
->TestDidNavigate(other_contents
->GetMainFrame(),
4148 entry
->GetPageID(), 0, false, url2b
,
4149 ui::PAGE_TRANSITION_LINK
);
4151 // The max page ID map should be copied over and updated with the max page ID
4152 // from the current tab.
4153 SiteInstance
* instance1
=
4154 other_controller
.GetEntryAtIndex(1)->site_instance();
4155 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
4158 // Test CopyStateFromAndPrune with 2 urls, a back navigation pending in the
4159 // source, and 1 entry in the target. The back pending entry should be ignored.
4160 TEST_F(NavigationControllerTest
, CopyStateFromAndPruneSourcePending
) {
4161 NavigationControllerImpl
& controller
= controller_impl();
4162 const GURL
url1("http://foo1");
4163 const GURL
url2("http://foo2");
4164 const GURL
url3("http://foo3");
4166 NavigateAndCommit(url1
);
4167 NavigateAndCommit(url2
);
4168 controller
.GoBack();
4170 scoped_ptr
<TestWebContents
> other_contents(
4171 static_cast<TestWebContents
*>(CreateTestWebContents()));
4172 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
4173 other_contents
->NavigateAndCommit(url3
);
4174 other_contents
->ExpectSetHistoryOffsetAndLength(2, 3);
4175 other_controller
.CopyStateFromAndPrune(&controller
, false);
4177 // other_controller should now contain: url1, url2, url3
4179 ASSERT_EQ(3, other_controller
.GetEntryCount());
4180 ASSERT_EQ(2, other_controller
.GetCurrentEntryIndex());
4182 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
4183 EXPECT_EQ(url2
, other_controller
.GetEntryAtIndex(1)->GetURL());
4184 EXPECT_EQ(url3
, other_controller
.GetEntryAtIndex(2)->GetURL());
4185 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(2)->GetPageID());
4187 // The max page ID map should be copied over and updated with the max page ID
4188 // from the current tab.
4189 SiteInstance
* instance1
=
4190 other_controller
.GetEntryAtIndex(2)->site_instance();
4191 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
4194 // Tests CopyStateFromAndPrune with 3 urls in source, 1 in dest,
4195 // when the max entry count is 3. We should prune one entry.
4196 TEST_F(NavigationControllerTest
, CopyStateFromAndPruneMaxEntries
) {
4197 NavigationControllerImpl
& controller
= controller_impl();
4198 size_t original_count
= NavigationControllerImpl::max_entry_count();
4199 const int kMaxEntryCount
= 3;
4201 NavigationControllerImpl::set_max_entry_count_for_testing(kMaxEntryCount
);
4203 const GURL
url1("http://foo/1");
4204 const GURL
url2("http://foo/2");
4205 const GURL
url3("http://foo/3");
4206 const GURL
url4("http://foo/4");
4208 // Create a PrunedListener to observe prune notifications.
4209 PrunedListener
listener(&controller
);
4211 NavigateAndCommit(url1
);
4212 NavigateAndCommit(url2
);
4213 NavigateAndCommit(url3
);
4215 scoped_ptr
<TestWebContents
> other_contents(
4216 static_cast<TestWebContents
*>(CreateTestWebContents()));
4217 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
4218 other_contents
->NavigateAndCommit(url4
);
4219 other_contents
->ExpectSetHistoryOffsetAndLength(2, 3);
4220 other_controller
.CopyStateFromAndPrune(&controller
, false);
4222 // We should have received a pruned notification.
4223 EXPECT_EQ(1, listener
.notification_count_
);
4224 EXPECT_TRUE(listener
.details_
.from_front
);
4225 EXPECT_EQ(1, listener
.details_
.count
);
4227 // other_controller should now contain only 3 urls: url2, url3 and url4.
4229 ASSERT_EQ(3, other_controller
.GetEntryCount());
4231 ASSERT_EQ(2, other_controller
.GetCurrentEntryIndex());
4233 EXPECT_EQ(url2
, other_controller
.GetEntryAtIndex(0)->GetURL());
4234 EXPECT_EQ(url3
, other_controller
.GetEntryAtIndex(1)->GetURL());
4235 EXPECT_EQ(url4
, other_controller
.GetEntryAtIndex(2)->GetURL());
4236 EXPECT_EQ(1, other_controller
.GetEntryAtIndex(0)->GetPageID());
4237 EXPECT_EQ(2, other_controller
.GetEntryAtIndex(1)->GetPageID());
4238 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(2)->GetPageID());
4240 NavigationControllerImpl::set_max_entry_count_for_testing(original_count
);
4243 // Tests CopyStateFromAndPrune with 2 urls in source, 1 in dest, with
4244 // replace_entry set to true.
4245 TEST_F(NavigationControllerTest
, CopyStateFromAndPruneReplaceEntry
) {
4246 NavigationControllerImpl
& controller
= controller_impl();
4247 const GURL
url1("http://foo/1");
4248 const GURL
url2("http://foo/2");
4249 const GURL
url3("http://foo/3");
4251 NavigateAndCommit(url1
);
4252 NavigateAndCommit(url2
);
4254 // First two entries should have the same SiteInstance.
4255 SiteInstance
* instance1
= controller
.GetEntryAtIndex(0)->site_instance();
4256 SiteInstance
* instance2
= controller
.GetEntryAtIndex(1)->site_instance();
4257 EXPECT_EQ(instance1
, instance2
);
4258 EXPECT_EQ(0, controller
.GetEntryAtIndex(0)->GetPageID());
4259 EXPECT_EQ(1, controller
.GetEntryAtIndex(1)->GetPageID());
4260 EXPECT_EQ(1, contents()->GetMaxPageIDForSiteInstance(instance1
));
4262 scoped_ptr
<TestWebContents
> other_contents(
4263 static_cast<TestWebContents
*>(CreateTestWebContents()));
4264 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
4265 other_contents
->NavigateAndCommit(url3
);
4266 other_contents
->ExpectSetHistoryOffsetAndLength(1, 2);
4267 other_controller
.CopyStateFromAndPrune(&controller
, true);
4269 // other_controller should now contain the 2 urls: url1 and url3.
4271 ASSERT_EQ(2, other_controller
.GetEntryCount());
4273 ASSERT_EQ(1, other_controller
.GetCurrentEntryIndex());
4275 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
4276 EXPECT_EQ(url3
, other_controller
.GetEntryAtIndex(1)->GetURL());
4277 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(0)->GetPageID());
4278 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(1)->GetPageID());
4280 // A new SiteInstance in a different BrowsingInstance should be used for the
4282 SiteInstance
* instance3
=
4283 other_controller
.GetEntryAtIndex(1)->site_instance();
4284 EXPECT_NE(instance3
, instance1
);
4285 EXPECT_FALSE(instance3
->IsRelatedSiteInstance(instance1
));
4287 // The max page ID map should be copied over and updated with the max page ID
4288 // from the current tab.
4289 EXPECT_EQ(1, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
4290 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance3
));
4293 // Tests CopyStateFromAndPrune with 3 urls in source, 1 in dest, when the max
4294 // entry count is 3 and replace_entry is true. We should not prune entries.
4295 TEST_F(NavigationControllerTest
, CopyStateFromAndPruneMaxEntriesReplaceEntry
) {
4296 NavigationControllerImpl
& controller
= controller_impl();
4297 size_t original_count
= NavigationControllerImpl::max_entry_count();
4298 const int kMaxEntryCount
= 3;
4300 NavigationControllerImpl::set_max_entry_count_for_testing(kMaxEntryCount
);
4302 const GURL
url1("http://foo/1");
4303 const GURL
url2("http://foo/2");
4304 const GURL
url3("http://foo/3");
4305 const GURL
url4("http://foo/4");
4307 // Create a PrunedListener to observe prune notifications.
4308 PrunedListener
listener(&controller
);
4310 NavigateAndCommit(url1
);
4311 NavigateAndCommit(url2
);
4312 NavigateAndCommit(url3
);
4314 scoped_ptr
<TestWebContents
> other_contents(
4315 static_cast<TestWebContents
*>(CreateTestWebContents()));
4316 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
4317 other_contents
->NavigateAndCommit(url4
);
4318 other_contents
->ExpectSetHistoryOffsetAndLength(2, 3);
4319 other_controller
.CopyStateFromAndPrune(&controller
, true);
4321 // We should have received no pruned notification.
4322 EXPECT_EQ(0, listener
.notification_count_
);
4324 // other_controller should now contain only 3 urls: url1, url2 and url4.
4326 ASSERT_EQ(3, other_controller
.GetEntryCount());
4328 ASSERT_EQ(2, other_controller
.GetCurrentEntryIndex());
4330 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
4331 EXPECT_EQ(url2
, other_controller
.GetEntryAtIndex(1)->GetURL());
4332 EXPECT_EQ(url4
, other_controller
.GetEntryAtIndex(2)->GetURL());
4333 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(0)->GetPageID());
4334 EXPECT_EQ(1, other_controller
.GetEntryAtIndex(1)->GetPageID());
4335 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(2)->GetPageID());
4337 NavigationControllerImpl::set_max_entry_count_for_testing(original_count
);
4340 // Tests that we can navigate to the restored entries
4341 // imported by CopyStateFromAndPrune.
4342 TEST_F(NavigationControllerTest
, CopyRestoredStateAndNavigate
) {
4343 const GURL kRestoredUrls
[] = {
4344 GURL("http://site1.com"),
4345 GURL("http://site2.com"),
4347 const GURL
kInitialUrl("http://site3.com");
4349 ScopedVector
<NavigationEntry
> entries
;
4350 for (size_t i
= 0; i
< arraysize(kRestoredUrls
); ++i
) {
4351 scoped_ptr
<NavigationEntry
> entry
=
4352 NavigationControllerImpl::CreateNavigationEntry(
4353 kRestoredUrls
[i
], Referrer(), ui::PAGE_TRANSITION_RELOAD
, false,
4354 std::string(), browser_context());
4355 entry
->SetPageID(static_cast<int>(i
));
4356 entries
.push_back(entry
.Pass());
4359 // Create a WebContents with restored entries.
4360 scoped_ptr
<TestWebContents
> source_contents(
4361 static_cast<TestWebContents
*>(CreateTestWebContents()));
4362 NavigationControllerImpl
& source_controller
=
4363 source_contents
->GetController();
4364 source_controller
.Restore(
4366 NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY
,
4368 ASSERT_EQ(0u, entries
.size());
4369 source_controller
.LoadIfNecessary();
4370 source_contents
->CommitPendingNavigation();
4372 // Load a page, then copy state from |source_contents|.
4373 NavigateAndCommit(kInitialUrl
);
4374 contents()->ExpectSetHistoryOffsetAndLength(2, 3);
4375 controller_impl().CopyStateFromAndPrune(&source_controller
, false);
4376 ASSERT_EQ(3, controller_impl().GetEntryCount());
4378 // Go back to the first entry one at a time and
4379 // verify that it works as expected.
4380 EXPECT_EQ(2, controller_impl().GetCurrentEntryIndex());
4381 EXPECT_EQ(kInitialUrl
, controller_impl().GetActiveEntry()->GetURL());
4383 controller_impl().GoBack();
4384 contents()->CommitPendingNavigation();
4385 EXPECT_EQ(1, controller_impl().GetCurrentEntryIndex());
4386 EXPECT_EQ(kRestoredUrls
[1], controller_impl().GetActiveEntry()->GetURL());
4388 controller_impl().GoBack();
4389 contents()->CommitPendingNavigation();
4390 EXPECT_EQ(0, controller_impl().GetCurrentEntryIndex());
4391 EXPECT_EQ(kRestoredUrls
[0], controller_impl().GetActiveEntry()->GetURL());
4394 // Tests that navigations initiated from the page (with the history object)
4395 // work as expected, creating pending entries.
4396 TEST_F(NavigationControllerTest
, HistoryNavigate
) {
4397 NavigationControllerImpl
& controller
= controller_impl();
4398 const GURL
url1("http://foo/1");
4399 const GURL
url2("http://foo/2");
4400 const GURL
url3("http://foo/3");
4402 NavigateAndCommit(url1
);
4403 NavigateAndCommit(url2
);
4404 NavigateAndCommit(url3
);
4405 controller
.GoBack();
4406 contents()->CommitPendingNavigation();
4407 process()->sink().ClearMessages();
4409 // Simulate the page calling history.back(). It should create a pending entry.
4410 contents()->OnGoToEntryAtOffset(-1);
4411 EXPECT_EQ(0, controller
.GetPendingEntryIndex());
4412 // The actual cross-navigation is suspended until the current RVH tells us
4413 // it unloaded, simulate that.
4414 contents()->ProceedWithCrossSiteNavigation();
4415 // Also make sure we told the page to navigate.
4416 GURL nav_url
= GetLastNavigationURL();
4417 EXPECT_EQ(url1
, nav_url
);
4418 contents()->CommitPendingNavigation();
4419 process()->sink().ClearMessages();
4421 // Now test history.forward()
4422 contents()->OnGoToEntryAtOffset(2);
4423 EXPECT_EQ(2, controller
.GetPendingEntryIndex());
4424 // The actual cross-navigation is suspended until the current RVH tells us
4425 // it unloaded, simulate that.
4426 contents()->ProceedWithCrossSiteNavigation();
4427 nav_url
= GetLastNavigationURL();
4428 EXPECT_EQ(url3
, nav_url
);
4429 contents()->CommitPendingNavigation();
4430 process()->sink().ClearMessages();
4432 controller
.DiscardNonCommittedEntries();
4434 // Make sure an extravagant history.go() doesn't break.
4435 contents()->OnGoToEntryAtOffset(120); // Out of bounds.
4436 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4437 EXPECT_FALSE(HasNavigationRequest());
4440 // Test call to PruneAllButLastCommitted for the only entry.
4441 TEST_F(NavigationControllerTest
, PruneAllButLastCommittedForSingle
) {
4442 NavigationControllerImpl
& controller
= controller_impl();
4443 const GURL
url1("http://foo1");
4444 NavigateAndCommit(url1
);
4446 contents()->ExpectSetHistoryOffsetAndLength(0, 1);
4448 controller
.PruneAllButLastCommitted();
4450 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4451 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(), url1
);
4454 // Test call to PruneAllButLastCommitted for first entry.
4455 TEST_F(NavigationControllerTest
, PruneAllButLastCommittedForFirst
) {
4456 NavigationControllerImpl
& controller
= controller_impl();
4457 const GURL
url1("http://foo/1");
4458 const GURL
url2("http://foo/2");
4459 const GURL
url3("http://foo/3");
4461 NavigateAndCommit(url1
);
4462 NavigateAndCommit(url2
);
4463 NavigateAndCommit(url3
);
4464 controller
.GoBack();
4465 controller
.GoBack();
4466 contents()->CommitPendingNavigation();
4468 contents()->ExpectSetHistoryOffsetAndLength(0, 1);
4470 controller
.PruneAllButLastCommitted();
4472 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4473 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(), url1
);
4476 // Test call to PruneAllButLastCommitted for intermediate entry.
4477 TEST_F(NavigationControllerTest
, PruneAllButLastCommittedForIntermediate
) {
4478 NavigationControllerImpl
& controller
= controller_impl();
4479 const GURL
url1("http://foo/1");
4480 const GURL
url2("http://foo/2");
4481 const GURL
url3("http://foo/3");
4483 NavigateAndCommit(url1
);
4484 NavigateAndCommit(url2
);
4485 NavigateAndCommit(url3
);
4486 controller
.GoBack();
4487 contents()->CommitPendingNavigation();
4489 contents()->ExpectSetHistoryOffsetAndLength(0, 1);
4491 controller
.PruneAllButLastCommitted();
4493 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4494 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(), url2
);
4497 // Test call to PruneAllButLastCommitted for a pending entry that is not yet in
4498 // the list of entries.
4499 TEST_F(NavigationControllerTest
, PruneAllButLastCommittedForPendingNotInList
) {
4500 NavigationControllerImpl
& controller
= controller_impl();
4501 const GURL
url1("http://foo/1");
4502 const GURL
url2("http://foo/2");
4503 const GURL
url3("http://foo/3");
4505 NavigateAndCommit(url1
);
4506 NavigateAndCommit(url2
);
4508 // Create a pending entry that is not in the entry list.
4510 url3
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
4511 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
4512 EXPECT_TRUE(controller
.GetPendingEntry());
4513 EXPECT_EQ(2, controller
.GetEntryCount());
4515 contents()->ExpectSetHistoryOffsetAndLength(0, 1);
4516 controller
.PruneAllButLastCommitted();
4518 // We should only have the last committed and pending entries at this point,
4519 // and the pending entry should still not be in the entry list.
4520 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
4521 EXPECT_EQ(url2
, controller
.GetEntryAtIndex(0)->GetURL());
4522 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4523 EXPECT_TRUE(controller
.GetPendingEntry());
4524 EXPECT_EQ(1, controller
.GetEntryCount());
4526 // Try to commit the pending entry.
4527 main_test_rfh()->PrepareForCommit();
4528 main_test_rfh()->SendNavigate(2, entry_id
, true, url3
);
4529 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4530 EXPECT_FALSE(controller
.GetPendingEntry());
4531 EXPECT_EQ(2, controller
.GetEntryCount());
4532 EXPECT_EQ(url3
, controller
.GetEntryAtIndex(1)->GetURL());
4535 // Test to ensure that when we do a history navigation back to the current
4536 // committed page (e.g., going forward to a slow-loading page, then pressing
4537 // the back button), we just stop the navigation to prevent the throbber from
4538 // running continuously. Otherwise, the RenderViewHost forces the throbber to
4539 // start, but WebKit essentially ignores the navigation and never sends a
4540 // message to stop the throbber.
4541 TEST_F(NavigationControllerTest
, StopOnHistoryNavigationToCurrentPage
) {
4542 NavigationControllerImpl
& controller
= controller_impl();
4543 const GURL
url0("http://foo/0");
4544 const GURL
url1("http://foo/1");
4546 NavigateAndCommit(url0
);
4547 NavigateAndCommit(url1
);
4549 // Go back to the original page, then forward to the slow page, then back
4550 controller
.GoBack();
4551 contents()->CommitPendingNavigation();
4553 controller
.GoForward();
4554 EXPECT_EQ(1, controller
.GetPendingEntryIndex());
4556 controller
.GoBack();
4557 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4560 TEST_F(NavigationControllerTest
, IsInitialNavigation
) {
4561 NavigationControllerImpl
& controller
= controller_impl();
4562 TestNotificationTracker notifications
;
4563 RegisterForAllNavNotifications(¬ifications
, &controller
);
4566 EXPECT_TRUE(controller
.IsInitialNavigation());
4568 // After commit, it stays false.
4569 const GURL
url1("http://foo1");
4570 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url1
);
4571 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4572 navigation_entry_committed_counter_
= 0;
4573 EXPECT_FALSE(controller
.IsInitialNavigation());
4575 // After starting a new navigation, it stays false.
4576 const GURL
url2("http://foo2");
4578 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
4581 // Check that the favicon is not reused across a client redirect.
4582 // (crbug.com/28515)
4583 TEST_F(NavigationControllerTest
, ClearFaviconOnRedirect
) {
4584 const GURL
kPageWithFavicon("http://withfavicon.html");
4585 const GURL
kPageWithoutFavicon("http://withoutfavicon.html");
4586 const GURL
kIconURL("http://withfavicon.ico");
4587 const gfx::Image kDefaultFavicon
= FaviconStatus().image
;
4589 NavigationControllerImpl
& controller
= controller_impl();
4590 TestNotificationTracker notifications
;
4591 RegisterForAllNavNotifications(¬ifications
, &controller
);
4593 main_test_rfh()->NavigateAndCommitRendererInitiated(
4594 0, true, kPageWithFavicon
);
4595 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4596 navigation_entry_committed_counter_
= 0;
4598 NavigationEntry
* entry
= controller
.GetLastCommittedEntry();
4600 EXPECT_EQ(kPageWithFavicon
, entry
->GetURL());
4602 // Simulate Chromium having set the favicon for |kPageWithFavicon|.
4603 content::FaviconStatus
& favicon_status
= entry
->GetFavicon();
4604 favicon_status
.image
= CreateImage(SK_ColorWHITE
);
4605 favicon_status
.url
= kIconURL
;
4606 favicon_status
.valid
= true;
4607 EXPECT_FALSE(DoImagesMatch(kDefaultFavicon
, entry
->GetFavicon().image
));
4609 main_test_rfh()->SendRendererInitiatedNavigationRequest(kPageWithoutFavicon
,
4611 main_test_rfh()->PrepareForCommit();
4612 main_test_rfh()->SendNavigateWithTransition(
4615 false, // no new entry
4616 kPageWithoutFavicon
, ui::PAGE_TRANSITION_CLIENT_REDIRECT
);
4617 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4618 navigation_entry_committed_counter_
= 0;
4620 entry
= controller
.GetLastCommittedEntry();
4622 EXPECT_EQ(kPageWithoutFavicon
, entry
->GetURL());
4624 EXPECT_TRUE(DoImagesMatch(kDefaultFavicon
, entry
->GetFavicon().image
));
4627 // Check that the favicon is not cleared for NavigationEntries which were
4628 // previously navigated to.
4629 TEST_F(NavigationControllerTest
, BackNavigationDoesNotClearFavicon
) {
4630 const GURL
kUrl1("http://www.a.com/1");
4631 const GURL
kUrl2("http://www.a.com/2");
4632 const GURL
kIconURL("http://www.a.com/1/favicon.ico");
4634 NavigationControllerImpl
& controller
= controller_impl();
4635 TestNotificationTracker notifications
;
4636 RegisterForAllNavNotifications(¬ifications
, &controller
);
4638 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, kUrl1
);
4639 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4640 navigation_entry_committed_counter_
= 0;
4642 // Simulate Chromium having set the favicon for |kUrl1|.
4643 gfx::Image favicon_image
= CreateImage(SK_ColorWHITE
);
4644 content::NavigationEntry
* entry
= controller
.GetLastCommittedEntry();
4646 content::FaviconStatus
& favicon_status
= entry
->GetFavicon();
4647 favicon_status
.image
= favicon_image
;
4648 favicon_status
.url
= kIconURL
;
4649 favicon_status
.valid
= true;
4651 // Navigate to another page and go back to the original page.
4652 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, kUrl2
);
4653 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4654 navigation_entry_committed_counter_
= 0;
4655 main_test_rfh()->SendRendererInitiatedNavigationRequest(kUrl1
, false);
4656 main_test_rfh()->PrepareForCommit();
4657 main_test_rfh()->SendNavigateWithTransition(
4658 0, controller
.GetEntryAtIndex(0)->GetUniqueID(), false, kUrl1
,
4659 ui::PAGE_TRANSITION_FORWARD_BACK
);
4660 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4661 navigation_entry_committed_counter_
= 0;
4663 // Verify that the favicon for the page at |kUrl1| was not cleared.
4664 entry
= controller
.GetEntryAtIndex(0);
4666 EXPECT_EQ(kUrl1
, entry
->GetURL());
4667 EXPECT_TRUE(DoImagesMatch(favicon_image
, entry
->GetFavicon().image
));
4670 // The test crashes on android: http://crbug.com/170449
4671 #if defined(OS_ANDROID)
4672 #define MAYBE_PurgeScreenshot DISABLED_PurgeScreenshot
4674 #define MAYBE_PurgeScreenshot PurgeScreenshot
4676 // Tests that screenshot are purged correctly.
4677 TEST_F(NavigationControllerTest
, MAYBE_PurgeScreenshot
) {
4678 NavigationControllerImpl
& controller
= controller_impl();
4680 NavigationEntryImpl
* entry
;
4682 // Navigate enough times to make sure that some screenshots are purged.
4683 for (int i
= 0; i
< 12; ++i
) {
4684 const GURL
url(base::StringPrintf("http://foo%d/", i
));
4685 NavigateAndCommit(url
);
4686 EXPECT_EQ(i
, controller
.GetCurrentEntryIndex());
4689 MockScreenshotManager
* screenshot_manager
=
4690 new MockScreenshotManager(&controller
);
4691 controller
.SetScreenshotManager(screenshot_manager
);
4692 for (int i
= 0; i
< controller
.GetEntryCount(); ++i
) {
4693 entry
= controller
.GetEntryAtIndex(i
);
4694 screenshot_manager
->TakeScreenshotFor(entry
);
4695 EXPECT_TRUE(entry
->screenshot().get());
4698 NavigateAndCommit(GURL("https://foo/"));
4699 EXPECT_EQ(13, controller
.GetEntryCount());
4700 entry
= controller
.GetEntryAtIndex(11);
4701 screenshot_manager
->TakeScreenshotFor(entry
);
4703 for (int i
= 0; i
< 2; ++i
) {
4704 entry
= controller
.GetEntryAtIndex(i
);
4705 EXPECT_FALSE(entry
->screenshot().get()) << "Screenshot " << i
4709 for (int i
= 2; i
< controller
.GetEntryCount() - 1; ++i
) {
4710 entry
= controller
.GetEntryAtIndex(i
);
4711 EXPECT_TRUE(entry
->screenshot().get()) << "Screenshot not found for " << i
;
4714 // Navigate to index 5 and then try to assign screenshot to all entries.
4715 controller
.GoToIndex(5);
4716 contents()->CommitPendingNavigation();
4717 EXPECT_EQ(5, controller
.GetCurrentEntryIndex());
4718 for (int i
= 0; i
< controller
.GetEntryCount() - 1; ++i
) {
4719 entry
= controller
.GetEntryAtIndex(i
);
4720 screenshot_manager
->TakeScreenshotFor(entry
);
4723 for (int i
= 10; i
<= 12; ++i
) {
4724 entry
= controller
.GetEntryAtIndex(i
);
4725 EXPECT_FALSE(entry
->screenshot().get()) << "Screenshot " << i
4727 screenshot_manager
->TakeScreenshotFor(entry
);
4730 // Navigate to index 7 and assign screenshot to all entries.
4731 controller
.GoToIndex(7);
4732 contents()->CommitPendingNavigation();
4733 EXPECT_EQ(7, controller
.GetCurrentEntryIndex());
4734 for (int i
= 0; i
< controller
.GetEntryCount() - 1; ++i
) {
4735 entry
= controller
.GetEntryAtIndex(i
);
4736 screenshot_manager
->TakeScreenshotFor(entry
);
4739 for (int i
= 0; i
< 2; ++i
) {
4740 entry
= controller
.GetEntryAtIndex(i
);
4741 EXPECT_FALSE(entry
->screenshot().get()) << "Screenshot " << i
4745 // Clear all screenshots.
4746 EXPECT_EQ(13, controller
.GetEntryCount());
4747 EXPECT_EQ(10, screenshot_manager
->GetScreenshotCount());
4748 controller
.ClearAllScreenshots();
4749 EXPECT_EQ(0, screenshot_manager
->GetScreenshotCount());
4750 for (int i
= 0; i
< controller
.GetEntryCount(); ++i
) {
4751 entry
= controller
.GetEntryAtIndex(i
);
4752 EXPECT_FALSE(entry
->screenshot().get()) << "Screenshot " << i
4757 TEST_F(NavigationControllerTest
, PushStateUpdatesTitleAndFavicon
) {
4759 main_test_rfh()->NavigateAndCommitRendererInitiated(
4760 1, true, GURL("http://foo"));
4762 // Set title and favicon.
4763 base::string16
title(base::ASCIIToUTF16("Title"));
4764 FaviconStatus favicon
;
4765 favicon
.valid
= true;
4766 favicon
.url
= GURL("http://foo/favicon.ico");
4767 controller().GetLastCommittedEntry()->SetTitle(title
);
4768 controller().GetLastCommittedEntry()->GetFavicon() = favicon
;
4770 // history.pushState() is called.
4771 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
4772 GURL
kUrl2("http://foo#foo");
4774 params
.nav_entry_id
= 0;
4775 params
.did_create_new_entry
= true;
4777 params
.page_state
= PageState::CreateFromURL(kUrl2
);
4778 params
.was_within_same_page
= true;
4779 main_test_rfh()->SendRendererInitiatedNavigationRequest(kUrl2
, false);
4780 main_test_rfh()->PrepareForCommit();
4781 main_test_rfh()->SendNavigateWithParams(¶ms
);
4783 // The title should immediately be visible on the new NavigationEntry.
4784 base::string16 new_title
=
4785 controller().GetLastCommittedEntry()->GetTitleForDisplay(std::string());
4786 EXPECT_EQ(title
, new_title
);
4787 FaviconStatus new_favicon
=
4788 controller().GetLastCommittedEntry()->GetFavicon();
4789 EXPECT_EQ(favicon
.valid
, new_favicon
.valid
);
4790 EXPECT_EQ(favicon
.url
, new_favicon
.url
);
4793 // Test that the navigation controller clears its session history when a
4794 // navigation commits with the clear history list flag set.
4795 TEST_F(NavigationControllerTest
, ClearHistoryList
) {
4796 const GURL
url1("http://foo1");
4797 const GURL
url2("http://foo2");
4798 const GURL
url3("http://foo3");
4799 const GURL
url4("http://foo4");
4801 NavigationControllerImpl
& controller
= controller_impl();
4803 // Create a session history with three entries, second entry is active.
4804 NavigateAndCommit(url1
);
4805 NavigateAndCommit(url2
);
4806 NavigateAndCommit(url3
);
4807 controller
.GoBack();
4808 contents()->CommitPendingNavigation();
4809 EXPECT_EQ(3, controller
.GetEntryCount());
4810 EXPECT_EQ(1, controller
.GetCurrentEntryIndex());
4812 // Create a new pending navigation, and indicate that the session history
4813 // should be cleared.
4814 NavigationController::LoadURLParams
params(url4
);
4815 params
.should_clear_history_list
= true;
4816 controller
.LoadURLWithParams(params
);
4818 // Verify that the pending entry correctly indicates that the session history
4819 // should be cleared.
4820 NavigationEntryImpl
* entry
= controller
.GetPendingEntry();
4822 EXPECT_TRUE(entry
->should_clear_history_list());
4824 // Assume that the RenderFrame correctly cleared its history and commit the
4826 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
4827 switches::kEnableBrowserSideNavigation
)) {
4828 contents()->GetMainFrame()->SendBeforeUnloadACK(true);
4830 contents()->GetPendingMainFrame()->
4831 set_simulate_history_list_was_cleared(true);
4832 contents()->CommitPendingNavigation();
4834 // Verify that the NavigationController's session history was correctly
4836 EXPECT_EQ(1, controller
.GetEntryCount());
4837 EXPECT_EQ(0, controller
.GetCurrentEntryIndex());
4838 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
4839 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4840 EXPECT_FALSE(controller
.CanGoBack());
4841 EXPECT_FALSE(controller
.CanGoForward());
4842 EXPECT_EQ(url4
, controller
.GetVisibleEntry()->GetURL());
4845 TEST_F(NavigationControllerTest
, PostThenReplaceStateThenReload
) {
4846 scoped_ptr
<TestWebContentsDelegate
> delegate(new TestWebContentsDelegate());
4847 EXPECT_FALSE(contents()->GetDelegate());
4848 contents()->SetDelegate(delegate
.get());
4851 GURL
url("http://foo");
4852 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
4854 params
.nav_entry_id
= 0;
4855 params
.did_create_new_entry
= true;
4857 params
.transition
= ui::PAGE_TRANSITION_FORM_SUBMIT
;
4858 params
.gesture
= NavigationGestureUser
;
4859 params
.page_state
= PageState::CreateFromURL(url
);
4860 params
.was_within_same_page
= false;
4861 params
.is_post
= true;
4863 main_test_rfh()->SendRendererInitiatedNavigationRequest(url
, false);
4864 main_test_rfh()->PrepareForCommit();
4865 contents()->GetMainFrame()->SendNavigateWithParams(¶ms
);
4867 // history.replaceState() is called.
4868 GURL
replace_url("http://foo#foo");
4870 params
.nav_entry_id
= 0;
4871 params
.did_create_new_entry
= false;
4872 params
.url
= replace_url
;
4873 params
.transition
= ui::PAGE_TRANSITION_LINK
;
4874 params
.gesture
= NavigationGestureUser
;
4875 params
.page_state
= PageState::CreateFromURL(replace_url
);
4876 params
.was_within_same_page
= true;
4877 params
.is_post
= false;
4878 params
.post_id
= -1;
4879 main_test_rfh()->SendRendererInitiatedNavigationRequest(replace_url
, false);
4880 main_test_rfh()->PrepareForCommit();
4881 contents()->GetMainFrame()->SendNavigateWithParams(¶ms
);
4883 // Now reload. replaceState overrides the POST, so we should not show a
4884 // repost warning dialog.
4885 controller_impl().Reload(true);
4886 EXPECT_EQ(0, delegate
->repost_form_warning_count());
4889 TEST_F(NavigationControllerTest
, UnreachableURLGivesErrorPage
) {
4890 GURL
url("http://foo");
4891 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
4893 params
.nav_entry_id
= 0;
4894 params
.did_create_new_entry
= true;
4896 params
.transition
= ui::PAGE_TRANSITION_LINK
;
4897 params
.gesture
= NavigationGestureUser
;
4898 params
.page_state
= PageState::CreateFromURL(url
);
4899 params
.was_within_same_page
= false;
4900 params
.is_post
= true;
4902 params
.url_is_unreachable
= true;
4904 // Navigate to new page.
4906 LoadCommittedDetails details
;
4907 controller_impl().RendererDidNavigate(main_test_rfh(), params
, &details
);
4908 EXPECT_EQ(PAGE_TYPE_ERROR
,
4909 controller_impl().GetLastCommittedEntry()->GetPageType());
4910 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE
, details
.type
);
4913 // Navigate to existing page.
4915 params
.did_create_new_entry
= false;
4916 LoadCommittedDetails details
;
4917 controller_impl().RendererDidNavigate(main_test_rfh(), params
, &details
);
4918 EXPECT_EQ(PAGE_TYPE_ERROR
,
4919 controller_impl().GetLastCommittedEntry()->GetPageType());
4920 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE
, details
.type
);
4923 // Navigate to same page.
4924 // Note: The call to LoadURL() creates a pending entry in order to trigger the
4925 // same-page transition.
4926 controller_impl().LoadURL(
4927 url
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
4928 params
.nav_entry_id
= controller_impl().GetPendingEntry()->GetUniqueID();
4929 params
.transition
= ui::PAGE_TRANSITION_TYPED
;
4931 LoadCommittedDetails details
;
4932 controller_impl().RendererDidNavigate(main_test_rfh(), params
, &details
);
4933 EXPECT_EQ(PAGE_TYPE_ERROR
,
4934 controller_impl().GetLastCommittedEntry()->GetPageType());
4935 EXPECT_EQ(NAVIGATION_TYPE_SAME_PAGE
, details
.type
);
4938 // Navigate in page.
4939 params
.url
= GURL("http://foo#foo");
4940 params
.transition
= ui::PAGE_TRANSITION_LINK
;
4941 params
.was_within_same_page
= true;
4943 LoadCommittedDetails details
;
4944 controller_impl().RendererDidNavigate(main_test_rfh(), params
, &details
);
4945 EXPECT_EQ(PAGE_TYPE_ERROR
,
4946 controller_impl().GetLastCommittedEntry()->GetPageType());
4947 EXPECT_TRUE(details
.is_in_page
);
4951 // Tests that if a stale navigation comes back from the renderer, it is properly
4953 TEST_F(NavigationControllerTest
, StaleNavigationsResurrected
) {
4954 NavigationControllerImpl
& controller
= controller_impl();
4955 TestNotificationTracker notifications
;
4956 RegisterForAllNavNotifications(¬ifications
, &controller
);
4959 const GURL
url_a("http://foo.com/a");
4960 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url_a
);
4961 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4962 navigation_entry_committed_counter_
= 0;
4963 EXPECT_EQ(1, controller
.GetEntryCount());
4964 EXPECT_EQ(0, controller
.GetCurrentEntryIndex());
4967 const GURL
url_b("http://foo.com/b");
4968 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url_b
);
4969 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4970 navigation_entry_committed_counter_
= 0;
4971 EXPECT_EQ(2, controller
.GetEntryCount());
4972 EXPECT_EQ(1, controller
.GetCurrentEntryIndex());
4973 int b_entry_id
= controller
.GetLastCommittedEntry()->GetUniqueID();
4974 int b_page_id
= controller
.GetLastCommittedEntry()->GetPageID();
4977 controller
.GoBack();
4978 contents()->CommitPendingNavigation();
4979 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4980 navigation_entry_committed_counter_
= 0;
4981 EXPECT_EQ(2, controller
.GetEntryCount());
4982 EXPECT_EQ(0, controller
.GetCurrentEntryIndex());
4984 // Start going forward to page B.
4985 controller
.GoForward();
4987 // But the renderer unilaterally navigates to page C, pruning B.
4988 const GURL
url_c("http://foo.com/c");
4989 main_test_rfh()->NavigateAndCommitRendererInitiated(2, true, url_c
);
4990 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4991 navigation_entry_committed_counter_
= 0;
4992 EXPECT_EQ(2, controller
.GetEntryCount());
4993 EXPECT_EQ(1, controller
.GetCurrentEntryIndex());
4994 int c_entry_id
= controller
.GetLastCommittedEntry()->GetUniqueID();
4995 EXPECT_NE(c_entry_id
, b_entry_id
);
4997 // And then the navigation to B gets committed.
4998 main_test_rfh()->SendNavigate(b_page_id
, b_entry_id
, false, url_b
);
4999 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
5000 navigation_entry_committed_counter_
= 0;
5002 // Even though we were doing a history navigation, because the entry was
5003 // pruned it will end up as a *new* entry at the end of the entry list. This
5004 // means that occasionally a navigation conflict will end up with one entry
5005 // bubbling to the end of the entry list, but that's the least-bad option.
5006 EXPECT_EQ(3, controller
.GetEntryCount());
5007 EXPECT_EQ(2, controller
.GetCurrentEntryIndex());
5008 EXPECT_EQ(url_a
, controller
.GetEntryAtIndex(0)->GetURL());
5009 EXPECT_EQ(url_c
, controller
.GetEntryAtIndex(1)->GetURL());
5010 EXPECT_EQ(url_b
, controller
.GetEntryAtIndex(2)->GetURL());
5013 // Test that if a renderer provides bogus security info (that fails to
5014 // deserialize properly) when reporting a navigation, the renderer gets
5016 TEST_F(NavigationControllerTest
, RendererNavigateBogusSecurityInfo
) {
5017 GURL
url("http://foo.test");
5018 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
5020 params
.nav_entry_id
= 0;
5021 params
.did_create_new_entry
= true;
5023 params
.transition
= ui::PAGE_TRANSITION_LINK
;
5024 params
.should_update_history
= true;
5025 params
.gesture
= NavigationGestureUser
;
5026 params
.is_post
= false;
5027 params
.page_state
= PageState::CreateFromURL(url
);
5028 params
.was_within_same_page
= false;
5029 params
.security_info
= "bogus security info!";
5031 LoadCommittedDetails details
;
5032 EXPECT_EQ(0, main_test_rfh()->GetProcess()->bad_msg_count());
5034 controller_impl().RendererDidNavigate(main_test_rfh(), params
, &details
));
5036 SSLStatus default_ssl_status
;
5037 EXPECT_EQ(default_ssl_status
.security_style
,
5038 details
.ssl_status
.security_style
);
5039 EXPECT_EQ(default_ssl_status
.cert_id
, details
.ssl_status
.cert_id
);
5040 EXPECT_EQ(default_ssl_status
.cert_status
, details
.ssl_status
.cert_status
);
5041 EXPECT_EQ(default_ssl_status
.security_bits
, details
.ssl_status
.security_bits
);
5042 EXPECT_EQ(default_ssl_status
.connection_status
,
5043 details
.ssl_status
.connection_status
);
5044 EXPECT_EQ(default_ssl_status
.content_status
,
5045 details
.ssl_status
.content_status
);
5046 EXPECT_EQ(0u, details
.ssl_status
.signed_certificate_timestamp_ids
.size());
5048 EXPECT_EQ(1, main_test_rfh()->GetProcess()->bad_msg_count());
5051 } // namespace content