1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/basictypes.h"
7 #include "base/command_line.h"
8 #include "base/files/file_util.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/stl_util.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "base/time/time.h"
14 #include "content/browser/frame_host/cross_site_transferring_request.h"
15 #include "content/browser/frame_host/frame_navigation_entry.h"
16 #include "content/browser/frame_host/navigation_controller_impl.h"
17 #include "content/browser/frame_host/navigation_entry_impl.h"
18 #include "content/browser/frame_host/navigation_entry_screenshot_manager.h"
19 #include "content/browser/frame_host/navigation_request.h"
20 #include "content/browser/frame_host/navigator.h"
21 #include "content/browser/frame_host/navigator_impl.h"
22 #include "content/browser/site_instance_impl.h"
23 #include "content/browser/web_contents/web_contents_impl.h"
24 #include "content/common/frame_messages.h"
25 #include "content/common/site_isolation_policy.h"
26 #include "content/common/ssl_status_serialization.h"
27 #include "content/common/view_messages.h"
28 #include "content/public/browser/navigation_details.h"
29 #include "content/public/browser/notification_registrar.h"
30 #include "content/public/browser/notification_types.h"
31 #include "content/public/browser/render_view_host.h"
32 #include "content/public/browser/web_contents_delegate.h"
33 #include "content/public/browser/web_contents_observer.h"
34 #include "content/public/common/content_switches.h"
35 #include "content/public/common/page_state.h"
36 #include "content/public/common/page_type.h"
37 #include "content/public/common/url_constants.h"
38 #include "content/public/test/mock_render_process_host.h"
39 #include "content/public/test/test_notification_tracker.h"
40 #include "content/public/test/test_utils.h"
41 #include "content/test/test_render_frame_host.h"
42 #include "content/test/test_render_view_host.h"
43 #include "content/test/test_web_contents.h"
44 #include "net/base/net_util.h"
45 #include "skia/ext/platform_canvas.h"
46 #include "testing/gtest/include/gtest/gtest.h"
47 #include "third_party/WebKit/public/web/WebSandboxFlags.h"
53 // Creates an image with a 1x1 SkBitmap of the specified |color|.
54 gfx::Image
CreateImage(SkColor color
) {
56 bitmap
.allocN32Pixels(1, 1);
57 bitmap
.eraseColor(color
);
58 return gfx::Image::CreateFrom1xBitmap(bitmap
);
61 // Returns true if images |a| and |b| have the same pixel data.
62 bool DoImagesMatch(const gfx::Image
& a
, const gfx::Image
& b
) {
63 // Assume that if the 1x bitmaps match, the images match.
64 SkBitmap a_bitmap
= a
.AsBitmap();
65 SkBitmap b_bitmap
= b
.AsBitmap();
67 if (a_bitmap
.width() != b_bitmap
.width() ||
68 a_bitmap
.height() != b_bitmap
.height()) {
71 SkAutoLockPixels
a_bitmap_lock(a_bitmap
);
72 SkAutoLockPixels
b_bitmap_lock(b_bitmap
);
73 return memcmp(a_bitmap
.getPixels(),
75 a_bitmap
.getSize()) == 0;
78 class MockScreenshotManager
: public content::NavigationEntryScreenshotManager
{
80 explicit MockScreenshotManager(content::NavigationControllerImpl
* owner
)
81 : content::NavigationEntryScreenshotManager(owner
),
82 encoding_screenshot_in_progress_(false) {
85 ~MockScreenshotManager() override
{}
87 void TakeScreenshotFor(content::NavigationEntryImpl
* entry
) {
89 bitmap
.allocPixels(SkImageInfo::Make(
90 1, 1, kAlpha_8_SkColorType
, kPremul_SkAlphaType
));
91 bitmap
.eraseARGB(0, 0, 0, 0);
92 encoding_screenshot_in_progress_
= true;
93 OnScreenshotTaken(entry
->GetUniqueID(), bitmap
, content::READBACK_SUCCESS
);
94 WaitUntilScreenshotIsReady();
97 int GetScreenshotCount() {
98 return content::NavigationEntryScreenshotManager::GetScreenshotCount();
101 void WaitUntilScreenshotIsReady() {
102 if (!encoding_screenshot_in_progress_
)
104 message_loop_runner_
= new content::MessageLoopRunner
;
105 message_loop_runner_
->Run();
109 // Overridden from content::NavigationEntryScreenshotManager:
110 void TakeScreenshotImpl(content::RenderViewHost
* host
,
111 content::NavigationEntryImpl
* entry
) override
{}
113 void OnScreenshotSet(content::NavigationEntryImpl
* entry
) override
{
114 encoding_screenshot_in_progress_
= false;
115 NavigationEntryScreenshotManager::OnScreenshotSet(entry
);
116 if (message_loop_runner_
.get())
117 message_loop_runner_
->Quit();
120 scoped_refptr
<content::MessageLoopRunner
> message_loop_runner_
;
121 bool encoding_screenshot_in_progress_
;
123 DISALLOW_COPY_AND_ASSIGN(MockScreenshotManager
);
130 // TimeSmoother tests ----------------------------------------------------------
132 // With no duplicates, GetSmoothedTime should be the identity
134 TEST(TimeSmoother
, Basic
) {
135 NavigationControllerImpl::TimeSmoother smoother
;
136 for (int64 i
= 1; i
< 1000; ++i
) {
137 base::Time t
= base::Time::FromInternalValue(i
);
138 EXPECT_EQ(t
, smoother
.GetSmoothedTime(t
));
142 // With a single duplicate and timestamps thereafter increasing by one
143 // microsecond, the smoothed time should always be one behind.
144 TEST(TimeSmoother
, SingleDuplicate
) {
145 NavigationControllerImpl::TimeSmoother smoother
;
146 base::Time t
= base::Time::FromInternalValue(1);
147 EXPECT_EQ(t
, smoother
.GetSmoothedTime(t
));
148 for (int64 i
= 1; i
< 1000; ++i
) {
149 base::Time expected_t
= base::Time::FromInternalValue(i
+ 1);
150 t
= base::Time::FromInternalValue(i
);
151 EXPECT_EQ(expected_t
, smoother
.GetSmoothedTime(t
));
155 // With k duplicates and timestamps thereafter increasing by one
156 // microsecond, the smoothed time should always be k behind.
157 TEST(TimeSmoother
, ManyDuplicates
) {
158 const int64 kNumDuplicates
= 100;
159 NavigationControllerImpl::TimeSmoother smoother
;
160 base::Time t
= base::Time::FromInternalValue(1);
161 for (int64 i
= 0; i
< kNumDuplicates
; ++i
) {
162 base::Time expected_t
= base::Time::FromInternalValue(i
+ 1);
163 EXPECT_EQ(expected_t
, smoother
.GetSmoothedTime(t
));
165 for (int64 i
= 1; i
< 1000; ++i
) {
166 base::Time expected_t
=
167 base::Time::FromInternalValue(i
+ kNumDuplicates
);
168 t
= base::Time::FromInternalValue(i
);
169 EXPECT_EQ(expected_t
, smoother
.GetSmoothedTime(t
));
173 // If the clock jumps far back enough after a run of duplicates, it
174 // should immediately jump to that value.
175 TEST(TimeSmoother
, ClockBackwardsJump
) {
176 const int64 kNumDuplicates
= 100;
177 NavigationControllerImpl::TimeSmoother smoother
;
178 base::Time t
= base::Time::FromInternalValue(1000);
179 for (int64 i
= 0; i
< kNumDuplicates
; ++i
) {
180 base::Time expected_t
= base::Time::FromInternalValue(i
+ 1000);
181 EXPECT_EQ(expected_t
, smoother
.GetSmoothedTime(t
));
183 t
= base::Time::FromInternalValue(500);
184 EXPECT_EQ(t
, smoother
.GetSmoothedTime(t
));
187 // NavigationControllerTest ----------------------------------------------------
189 class NavigationControllerTest
190 : public RenderViewHostImplTestHarness
,
191 public WebContentsObserver
{
193 NavigationControllerTest() : navigation_entry_committed_counter_(0) {
196 void SetUp() override
{
197 RenderViewHostImplTestHarness::SetUp();
198 WebContents
* web_contents
= RenderViewHostImplTestHarness::web_contents();
199 ASSERT_TRUE(web_contents
); // The WebContents should be created by now.
200 WebContentsObserver::Observe(web_contents
);
203 // WebContentsObserver:
204 void DidStartNavigationToPendingEntry(
206 NavigationController::ReloadType reload_type
) override
{
207 navigated_url_
= url
;
210 void NavigationEntryCommitted(
211 const LoadCommittedDetails
& load_details
) override
{
212 navigation_entry_committed_counter_
++;
215 const GURL
& navigated_url() const {
216 return navigated_url_
;
219 NavigationControllerImpl
& controller_impl() {
220 return static_cast<NavigationControllerImpl
&>(controller());
223 bool HasNavigationRequest() {
224 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
225 switches::kEnableBrowserSideNavigation
)) {
226 return contents()->GetFrameTree()->root()->navigation_request() !=
229 return process()->sink().GetFirstMessageMatching(FrameMsg_Navigate::ID
)
233 const GURL
GetLastNavigationURL() {
234 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
235 switches::kEnableBrowserSideNavigation
)) {
236 NavigationRequest
* navigation_request
=
237 contents()->GetFrameTree()->root()->navigation_request();
238 CHECK(navigation_request
);
239 return navigation_request
->common_params().url
;
241 const IPC::Message
* message
=
242 process()->sink().GetFirstMessageMatching(FrameMsg_Navigate::ID
);
244 base::Tuple
<CommonNavigationParams
, StartNavigationParams
,
245 RequestNavigationParams
> nav_params
;
246 FrameMsg_Navigate::Read(message
, &nav_params
);
247 return base::get
<0>(nav_params
).url
;
252 size_t navigation_entry_committed_counter_
;
255 void RegisterForAllNavNotifications(TestNotificationTracker
* tracker
,
256 NavigationController
* controller
) {
257 tracker
->ListenFor(NOTIFICATION_NAV_LIST_PRUNED
,
258 Source
<NavigationController
>(controller
));
259 tracker
->ListenFor(NOTIFICATION_NAV_ENTRY_CHANGED
,
260 Source
<NavigationController
>(controller
));
263 class TestWebContentsDelegate
: public WebContentsDelegate
{
265 explicit TestWebContentsDelegate() :
266 navigation_state_change_count_(0),
267 repost_form_warning_count_(0) {}
269 int navigation_state_change_count() {
270 return navigation_state_change_count_
;
273 int repost_form_warning_count() {
274 return repost_form_warning_count_
;
277 // Keep track of whether the tab has notified us of a navigation state change.
278 void NavigationStateChanged(WebContents
* source
,
279 InvalidateTypes changed_flags
) override
{
280 navigation_state_change_count_
++;
283 void ShowRepostFormWarningDialog(WebContents
* source
) override
{
284 repost_form_warning_count_
++;
288 // The number of times NavigationStateChanged has been called.
289 int navigation_state_change_count_
;
291 // The number of times ShowRepostFormWarningDialog() was called.
292 int repost_form_warning_count_
;
295 // -----------------------------------------------------------------------------
297 TEST_F(NavigationControllerTest
, Defaults
) {
298 NavigationControllerImpl
& controller
= controller_impl();
300 EXPECT_FALSE(controller
.GetPendingEntry());
301 EXPECT_FALSE(controller
.GetVisibleEntry());
302 EXPECT_FALSE(controller
.GetLastCommittedEntry());
303 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
304 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), -1);
305 EXPECT_EQ(controller
.GetEntryCount(), 0);
306 EXPECT_FALSE(controller
.CanGoBack());
307 EXPECT_FALSE(controller
.CanGoForward());
310 TEST_F(NavigationControllerTest
, GoToOffset
) {
311 NavigationControllerImpl
& controller
= controller_impl();
312 TestNotificationTracker notifications
;
313 RegisterForAllNavNotifications(¬ifications
, &controller
);
315 const int kNumUrls
= 5;
316 std::vector
<GURL
> urls(kNumUrls
);
317 for (int i
= 0; i
< kNumUrls
; ++i
) {
318 urls
[i
] = GURL(base::StringPrintf("http://www.a.com/%d", i
));
321 main_test_rfh()->SendRendererInitiatedNavigationRequest(urls
[0], true);
322 main_test_rfh()->PrepareForCommit();
323 main_test_rfh()->SendNavigate(0, 0, true, urls
[0]);
324 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
325 navigation_entry_committed_counter_
= 0;
326 EXPECT_EQ(urls
[0], controller
.GetVisibleEntry()->GetVirtualURL());
327 EXPECT_FALSE(controller
.CanGoBack());
328 EXPECT_FALSE(controller
.CanGoForward());
329 EXPECT_FALSE(controller
.CanGoToOffset(1));
331 for (int i
= 1; i
<= 4; ++i
) {
332 main_test_rfh()->SendRendererInitiatedNavigationRequest(urls
[i
], true);
333 main_test_rfh()->PrepareForCommit();
334 main_test_rfh()->SendNavigate(i
, 0, true, urls
[i
]);
335 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
336 navigation_entry_committed_counter_
= 0;
337 EXPECT_EQ(urls
[i
], controller
.GetVisibleEntry()->GetVirtualURL());
338 EXPECT_TRUE(controller
.CanGoToOffset(-i
));
339 EXPECT_FALSE(controller
.CanGoToOffset(-(i
+ 1)));
340 EXPECT_FALSE(controller
.CanGoToOffset(1));
343 // We have loaded 5 pages, and are currently at the last-loaded page.
347 GO_TO_MIDDLE_PAGE
= -2,
350 GO_TO_BEGINNING
= -2,
355 const int test_offsets
[NUM_TESTS
] = {
363 for (int test
= 0; test
< NUM_TESTS
; ++test
) {
364 int offset
= test_offsets
[test
];
365 controller
.GoToOffset(offset
);
366 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
368 // Check that the GoToOffset will land on the expected page.
369 EXPECT_EQ(urls
[url_index
], controller
.GetPendingEntry()->GetVirtualURL());
370 main_test_rfh()->PrepareForCommit();
371 main_test_rfh()->SendNavigate(url_index
, entry_id
, false, urls
[url_index
]);
372 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
373 navigation_entry_committed_counter_
= 0;
374 // Check that we can go to any valid offset into the history.
375 for (size_t j
= 0; j
< urls
.size(); ++j
)
376 EXPECT_TRUE(controller
.CanGoToOffset(j
- url_index
));
377 // Check that we can't go beyond the beginning or end of the history.
378 EXPECT_FALSE(controller
.CanGoToOffset(-(url_index
+ 1)));
379 EXPECT_FALSE(controller
.CanGoToOffset(urls
.size() - url_index
));
383 TEST_F(NavigationControllerTest
, LoadURL
) {
384 NavigationControllerImpl
& controller
= controller_impl();
385 TestNotificationTracker notifications
;
386 RegisterForAllNavNotifications(¬ifications
, &controller
);
388 const GURL
url1("http://foo1");
389 const GURL
url2("http://foo2");
392 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
393 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
394 // Creating a pending notification should not have issued any of the
395 // notifications we're listening for.
396 EXPECT_EQ(0U, notifications
.size());
398 // The load should now be pending.
399 EXPECT_EQ(controller
.GetEntryCount(), 0);
400 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), -1);
401 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
402 EXPECT_FALSE(controller
.GetLastCommittedEntry());
403 ASSERT_TRUE(controller
.GetPendingEntry());
404 EXPECT_EQ(controller
.GetPendingEntry(), controller
.GetVisibleEntry());
405 EXPECT_FALSE(controller
.CanGoBack());
406 EXPECT_FALSE(controller
.CanGoForward());
407 EXPECT_EQ(contents()->GetMaxPageID(), -1);
409 // Neither the timestamp nor the status code should have been set yet.
410 EXPECT_TRUE(controller
.GetPendingEntry()->GetTimestamp().is_null());
411 EXPECT_EQ(0, controller
.GetPendingEntry()->GetHttpStatusCode());
413 // We should have gotten no notifications from the preceeding checks.
414 EXPECT_EQ(0U, notifications
.size());
416 main_test_rfh()->PrepareForCommit();
417 main_test_rfh()->SendNavigate(0, entry_id
, true, url1
);
418 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
419 navigation_entry_committed_counter_
= 0;
421 // The load should now be committed.
422 EXPECT_EQ(controller
.GetEntryCount(), 1);
423 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
424 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
425 EXPECT_TRUE(controller
.GetLastCommittedEntry());
426 EXPECT_FALSE(controller
.GetPendingEntry());
427 ASSERT_TRUE(controller
.GetVisibleEntry());
428 EXPECT_FALSE(controller
.CanGoBack());
429 EXPECT_FALSE(controller
.CanGoForward());
430 EXPECT_EQ(contents()->GetMaxPageID(), 0);
431 EXPECT_EQ(0, controller
.GetLastCommittedEntry()->bindings());
433 // The timestamp should have been set.
434 EXPECT_FALSE(controller
.GetVisibleEntry()->GetTimestamp().is_null());
438 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
439 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
441 // The load should now be pending.
442 EXPECT_EQ(controller
.GetEntryCount(), 1);
443 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
444 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
445 EXPECT_TRUE(controller
.GetLastCommittedEntry());
446 ASSERT_TRUE(controller
.GetPendingEntry());
447 EXPECT_EQ(controller
.GetPendingEntry(), controller
.GetVisibleEntry());
448 // TODO(darin): maybe this should really be true?
449 EXPECT_FALSE(controller
.CanGoBack());
450 EXPECT_FALSE(controller
.CanGoForward());
451 EXPECT_EQ(contents()->GetMaxPageID(), 0);
453 EXPECT_TRUE(controller
.GetPendingEntry()->GetTimestamp().is_null());
455 // Simulate the beforeunload ack for the cross-site transition, and then the
457 main_test_rfh()->PrepareForCommit();
458 contents()->GetPendingMainFrame()->SendNavigate(1, entry_id
, true, url2
);
459 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
460 navigation_entry_committed_counter_
= 0;
462 // The load should now be committed.
463 EXPECT_EQ(controller
.GetEntryCount(), 2);
464 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 1);
465 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
466 EXPECT_TRUE(controller
.GetLastCommittedEntry());
467 EXPECT_FALSE(controller
.GetPendingEntry());
468 ASSERT_TRUE(controller
.GetVisibleEntry());
469 EXPECT_TRUE(controller
.CanGoBack());
470 EXPECT_FALSE(controller
.CanGoForward());
471 EXPECT_EQ(contents()->GetMaxPageID(), 1);
473 EXPECT_FALSE(controller
.GetVisibleEntry()->GetTimestamp().is_null());
478 base::Time
GetFixedTime(base::Time time
) {
484 TEST_F(NavigationControllerTest
, LoadURLSameTime
) {
485 NavigationControllerImpl
& controller
= controller_impl();
486 TestNotificationTracker notifications
;
487 RegisterForAllNavNotifications(¬ifications
, &controller
);
489 // Set the clock to always return a timestamp of 1.
490 controller
.SetGetTimestampCallbackForTest(
491 base::Bind(&GetFixedTime
, base::Time::FromInternalValue(1)));
493 const GURL
url1("http://foo1");
494 const GURL
url2("http://foo2");
497 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
498 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
500 main_test_rfh()->PrepareForCommit();
501 main_test_rfh()->SendNavigate(0, entry_id
, true, url1
);
502 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
503 navigation_entry_committed_counter_
= 0;
507 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
508 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
510 // Simulate the beforeunload ack for the cross-site transition, and then the
512 main_test_rfh()->PrepareForCommit();
513 contents()->GetPendingMainFrame()->SendNavigate(1, entry_id
, true, url2
);
514 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
515 navigation_entry_committed_counter_
= 0;
517 // The two loads should now be committed.
518 ASSERT_EQ(controller
.GetEntryCount(), 2);
520 // Timestamps should be distinct despite the clock returning the
523 controller
.GetEntryAtIndex(0)->GetTimestamp().ToInternalValue());
525 controller
.GetEntryAtIndex(1)->GetTimestamp().ToInternalValue());
528 void CheckNavigationEntryMatchLoadParams(
529 NavigationController::LoadURLParams
& load_params
,
530 NavigationEntryImpl
* entry
) {
531 EXPECT_EQ(load_params
.url
, entry
->GetURL());
532 EXPECT_EQ(load_params
.referrer
.url
, entry
->GetReferrer().url
);
533 EXPECT_EQ(load_params
.referrer
.policy
, entry
->GetReferrer().policy
);
534 EXPECT_EQ(load_params
.transition_type
, entry
->GetTransitionType());
535 EXPECT_EQ(load_params
.extra_headers
, entry
->extra_headers());
537 EXPECT_EQ(load_params
.is_renderer_initiated
, entry
->is_renderer_initiated());
538 EXPECT_EQ(load_params
.base_url_for_data_url
, entry
->GetBaseURLForDataURL());
539 if (!load_params
.virtual_url_for_data_url
.is_empty()) {
540 EXPECT_EQ(load_params
.virtual_url_for_data_url
, entry
->GetVirtualURL());
542 if (NavigationController::UA_OVERRIDE_INHERIT
!=
543 load_params
.override_user_agent
) {
544 bool should_override
= (NavigationController::UA_OVERRIDE_TRUE
==
545 load_params
.override_user_agent
);
546 EXPECT_EQ(should_override
, entry
->GetIsOverridingUserAgent());
548 EXPECT_EQ(load_params
.browser_initiated_post_data
.get(),
549 entry
->GetBrowserInitiatedPostData());
550 EXPECT_EQ(load_params
.transferred_global_request_id
,
551 entry
->transferred_global_request_id());
554 TEST_F(NavigationControllerTest
, LoadURLWithParams
) {
555 // Start a navigation in order to have enough state to fake a transfer.
556 contents()->NavigateAndCommit(GURL("http://foo"));
557 contents()->StartNavigation(GURL("http://bar"));
559 NavigationControllerImpl
& controller
= controller_impl();
561 NavigationController::LoadURLParams
load_params(GURL("http://foo/2"));
562 load_params
.referrer
=
563 Referrer(GURL("http://referrer"), blink::WebReferrerPolicyDefault
);
564 load_params
.transition_type
= ui::PAGE_TRANSITION_GENERATED
;
565 load_params
.extra_headers
= "content-type: text/plain";
566 load_params
.load_type
= NavigationController::LOAD_TYPE_DEFAULT
;
567 load_params
.is_renderer_initiated
= true;
568 load_params
.override_user_agent
= NavigationController::UA_OVERRIDE_TRUE
;
569 load_params
.transferred_global_request_id
= GlobalRequestID(2, 3);
571 controller
.LoadURLWithParams(load_params
);
572 NavigationEntryImpl
* entry
= controller
.GetPendingEntry();
574 // The timestamp should not have been set yet.
576 EXPECT_TRUE(entry
->GetTimestamp().is_null());
578 CheckNavigationEntryMatchLoadParams(load_params
, entry
);
581 TEST_F(NavigationControllerTest
, LoadURLWithExtraParams_Data
) {
582 NavigationControllerImpl
& controller
= controller_impl();
584 NavigationController::LoadURLParams
load_params(
585 GURL("data:text/html,dataurl"));
586 load_params
.load_type
= NavigationController::LOAD_TYPE_DATA
;
587 load_params
.base_url_for_data_url
= GURL("http://foo");
588 load_params
.virtual_url_for_data_url
= GURL(url::kAboutBlankURL
);
589 load_params
.override_user_agent
= NavigationController::UA_OVERRIDE_FALSE
;
591 controller
.LoadURLWithParams(load_params
);
592 NavigationEntryImpl
* entry
= controller
.GetPendingEntry();
594 CheckNavigationEntryMatchLoadParams(load_params
, entry
);
597 TEST_F(NavigationControllerTest
, LoadURLWithExtraParams_HttpPost
) {
598 NavigationControllerImpl
& controller
= controller_impl();
600 NavigationController::LoadURLParams
load_params(GURL("https://posturl"));
601 load_params
.transition_type
= ui::PAGE_TRANSITION_TYPED
;
602 load_params
.load_type
=
603 NavigationController::LOAD_TYPE_BROWSER_INITIATED_HTTP_POST
;
604 load_params
.override_user_agent
= NavigationController::UA_OVERRIDE_TRUE
;
607 const unsigned char* raw_data
=
608 reinterpret_cast<const unsigned char*>("d\n\0a2");
609 const int length
= 5;
610 std::vector
<unsigned char> post_data_vector(raw_data
, raw_data
+length
);
611 scoped_refptr
<base::RefCountedBytes
> data
=
612 base::RefCountedBytes::TakeVector(&post_data_vector
);
613 load_params
.browser_initiated_post_data
= data
.get();
615 controller
.LoadURLWithParams(load_params
);
616 NavigationEntryImpl
* entry
= controller
.GetPendingEntry();
618 CheckNavigationEntryMatchLoadParams(load_params
, entry
);
621 // Tests what happens when the same page is loaded again. Should not create a
622 // new session history entry. This is what happens when you press enter in the
623 // URL bar to reload: a pending entry is created and then it is discarded when
624 // the load commits (because WebCore didn't actually make a new entry).
625 TEST_F(NavigationControllerTest
, LoadURL_SamePage
) {
626 NavigationControllerImpl
& controller
= controller_impl();
627 TestNotificationTracker notifications
;
628 RegisterForAllNavNotifications(¬ifications
, &controller
);
630 const GURL
url1("http://foo1");
633 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
634 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
635 EXPECT_EQ(0U, notifications
.size());
636 main_test_rfh()->PrepareForCommit();
637 main_test_rfh()->SendNavigateWithTransition(
638 0, entry_id
, true, url1
, ui::PAGE_TRANSITION_TYPED
);
639 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
640 navigation_entry_committed_counter_
= 0;
642 ASSERT_TRUE(controller
.GetVisibleEntry());
643 const base::Time timestamp
= controller
.GetVisibleEntry()->GetTimestamp();
644 EXPECT_FALSE(timestamp
.is_null());
647 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
648 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
649 EXPECT_EQ(0U, notifications
.size());
650 main_test_rfh()->PrepareForCommit();
651 main_test_rfh()->SendNavigateWithTransition(
652 0, entry_id
, false, url1
, ui::PAGE_TRANSITION_TYPED
);
653 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
654 navigation_entry_committed_counter_
= 0;
656 // We should not have produced a new session history entry.
657 EXPECT_EQ(controller
.GetEntryCount(), 1);
658 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
659 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
660 EXPECT_TRUE(controller
.GetLastCommittedEntry());
661 EXPECT_FALSE(controller
.GetPendingEntry());
662 ASSERT_TRUE(controller
.GetVisibleEntry());
663 EXPECT_FALSE(controller
.CanGoBack());
664 EXPECT_FALSE(controller
.CanGoForward());
666 // The timestamp should have been updated.
668 // TODO(akalin): Change this EXPECT_GE (and other similar ones) to
669 // EXPECT_GT once we guarantee that timestamps are unique.
670 EXPECT_GE(controller
.GetVisibleEntry()->GetTimestamp(), timestamp
);
673 // Load the same page twice, once as a GET and once as a POST.
674 // We should update the post state on the NavigationEntry.
675 TEST_F(NavigationControllerTest
, LoadURL_SamePage_DifferentMethod
) {
676 NavigationControllerImpl
& controller
= controller_impl();
677 TestNotificationTracker notifications
;
678 RegisterForAllNavNotifications(¬ifications
, &controller
);
680 const GURL
url1("http://foo1");
683 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
684 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
686 params
.nav_entry_id
= controller
.GetPendingEntry()->GetUniqueID();
687 params
.did_create_new_entry
= true;
689 params
.transition
= ui::PAGE_TRANSITION_TYPED
;
690 params
.is_post
= true;
691 params
.post_id
= 123;
692 params
.page_state
= PageState::CreateForTesting(url1
, false, 0, 0);
693 main_test_rfh()->PrepareForCommit();
694 main_test_rfh()->SendNavigateWithParams(¶ms
);
696 // The post data should be visible.
697 NavigationEntry
* entry
= controller
.GetVisibleEntry();
699 EXPECT_TRUE(entry
->GetHasPostData());
700 EXPECT_EQ(entry
->GetPostID(), 123);
703 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
704 main_test_rfh()->PrepareForCommit();
705 main_test_rfh()->SendNavigateWithTransition(
706 0, controller
.GetPendingEntry()->GetUniqueID(),
707 false, url1
, ui::PAGE_TRANSITION_TYPED
);
709 // We should not have produced a new session history entry.
710 ASSERT_EQ(controller
.GetVisibleEntry(), entry
);
712 // The post data should have been cleared due to the GET.
713 EXPECT_FALSE(entry
->GetHasPostData());
714 EXPECT_EQ(entry
->GetPostID(), 0);
717 // Tests loading a URL but discarding it before the load commits.
718 TEST_F(NavigationControllerTest
, LoadURL_Discarded
) {
719 NavigationControllerImpl
& controller
= controller_impl();
720 TestNotificationTracker notifications
;
721 RegisterForAllNavNotifications(¬ifications
, &controller
);
723 const GURL
url1("http://foo1");
724 const GURL
url2("http://foo2");
727 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
728 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
729 EXPECT_EQ(0U, notifications
.size());
730 main_test_rfh()->PrepareForCommit();
731 main_test_rfh()->SendNavigate(0, entry_id
, true, url1
);
732 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
733 navigation_entry_committed_counter_
= 0;
735 ASSERT_TRUE(controller
.GetVisibleEntry());
736 const base::Time timestamp
= controller
.GetVisibleEntry()->GetTimestamp();
737 EXPECT_FALSE(timestamp
.is_null());
740 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
741 controller
.DiscardNonCommittedEntries();
742 EXPECT_EQ(0U, notifications
.size());
744 // Should not have produced a new session history entry.
745 EXPECT_EQ(controller
.GetEntryCount(), 1);
746 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
747 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
748 EXPECT_TRUE(controller
.GetLastCommittedEntry());
749 EXPECT_FALSE(controller
.GetPendingEntry());
750 ASSERT_TRUE(controller
.GetVisibleEntry());
751 EXPECT_FALSE(controller
.CanGoBack());
752 EXPECT_FALSE(controller
.CanGoForward());
754 // Timestamp should not have changed.
755 EXPECT_EQ(timestamp
, controller
.GetVisibleEntry()->GetTimestamp());
758 // Tests navigations that come in unrequested. This happens when the user
759 // navigates from the web page, and here we test that there is no pending entry.
760 TEST_F(NavigationControllerTest
, LoadURL_NoPending
) {
761 NavigationControllerImpl
& controller
= controller_impl();
762 TestNotificationTracker notifications
;
763 RegisterForAllNavNotifications(¬ifications
, &controller
);
765 // First make an existing committed entry.
766 const GURL
kExistingURL1("http://eh");
768 kExistingURL1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
769 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
770 main_test_rfh()->PrepareForCommit();
771 main_test_rfh()->SendNavigate(0, entry_id
, true, kExistingURL1
);
772 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
773 navigation_entry_committed_counter_
= 0;
775 // Do a new navigation without making a pending one.
776 const GURL
kNewURL("http://see");
777 main_test_rfh()->NavigateAndCommitRendererInitiated(99, true, kNewURL
);
779 // There should no longer be any pending entry, and the second navigation we
780 // just made should be committed.
781 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
782 navigation_entry_committed_counter_
= 0;
783 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
784 EXPECT_EQ(1, controller
.GetLastCommittedEntryIndex());
785 EXPECT_EQ(kNewURL
, controller
.GetVisibleEntry()->GetURL());
788 // Tests navigating to a new URL when there is a new pending navigation that is
789 // not the one that just loaded. This will happen if the user types in a URL to
790 // somewhere slow, and then navigates the current page before the typed URL
792 TEST_F(NavigationControllerTest
, LoadURL_NewPending
) {
793 NavigationControllerImpl
& controller
= controller_impl();
794 TestNotificationTracker notifications
;
795 RegisterForAllNavNotifications(¬ifications
, &controller
);
797 // First make an existing committed entry.
798 const GURL
kExistingURL1("http://eh");
800 kExistingURL1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
801 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
802 main_test_rfh()->PrepareForCommit();
803 main_test_rfh()->SendNavigate(0, entry_id
, true, kExistingURL1
);
804 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
805 navigation_entry_committed_counter_
= 0;
807 // Make a pending entry to somewhere new.
808 const GURL
kExistingURL2("http://bee");
810 kExistingURL2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
811 EXPECT_EQ(0U, notifications
.size());
813 // After the beforeunload but before it commits...
814 main_test_rfh()->PrepareForCommit();
816 // ... Do a new navigation.
817 const GURL
kNewURL("http://see");
818 main_test_rfh()->SendRendererInitiatedNavigationRequest(kNewURL
, true);
819 main_test_rfh()->PrepareForCommit();
820 contents()->GetMainFrame()->SendNavigate(3, 0, true, kNewURL
);
822 // There should no longer be any pending entry, and the third navigation we
823 // just made should be committed.
824 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
825 navigation_entry_committed_counter_
= 0;
826 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
827 EXPECT_EQ(1, controller
.GetLastCommittedEntryIndex());
828 EXPECT_EQ(kNewURL
, controller
.GetVisibleEntry()->GetURL());
831 // Tests navigating to a new URL when there is a pending back/forward
832 // navigation. This will happen if the user hits back, but before that commits,
833 // they navigate somewhere new.
834 TEST_F(NavigationControllerTest
, LoadURL_ExistingPending
) {
835 NavigationControllerImpl
& controller
= controller_impl();
836 TestNotificationTracker notifications
;
837 RegisterForAllNavNotifications(¬ifications
, &controller
);
839 // First make some history.
840 const GURL
kExistingURL1("http://foo/eh");
842 kExistingURL1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
843 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
844 main_test_rfh()->PrepareForCommit();
845 main_test_rfh()->SendNavigate(0, entry_id
, true, kExistingURL1
);
846 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
847 navigation_entry_committed_counter_
= 0;
849 const GURL
kExistingURL2("http://foo/bee");
851 kExistingURL2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
852 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
853 main_test_rfh()->PrepareForCommit();
854 main_test_rfh()->SendNavigate(1, entry_id
, true, kExistingURL2
);
855 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
856 navigation_entry_committed_counter_
= 0;
858 // Now make a pending back/forward navigation. The zeroth entry should be
861 EXPECT_EQ(0U, notifications
.size());
862 EXPECT_EQ(0, controller
.GetPendingEntryIndex());
863 EXPECT_EQ(1, controller
.GetLastCommittedEntryIndex());
865 // Before that commits, do a new navigation.
866 const GURL
kNewURL("http://foo/see");
867 main_test_rfh()->SendRendererInitiatedNavigationRequest(kNewURL
, true);
868 main_test_rfh()->PrepareForCommit();
869 main_test_rfh()->SendNavigate(3, 0, true, kNewURL
);
871 // There should no longer be any pending entry, and the new navigation we
872 // just made should be committed.
873 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
874 navigation_entry_committed_counter_
= 0;
875 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
876 EXPECT_EQ(2, controller
.GetLastCommittedEntryIndex());
877 EXPECT_EQ(kNewURL
, controller
.GetVisibleEntry()->GetURL());
880 // Tests navigating to a new URL when there is a pending back/forward
881 // navigation to a cross-process, privileged URL. This will happen if the user
882 // hits back, but before that commits, they navigate somewhere new.
883 TEST_F(NavigationControllerTest
, LoadURL_PrivilegedPending
) {
884 NavigationControllerImpl
& controller
= controller_impl();
885 TestNotificationTracker notifications
;
886 RegisterForAllNavNotifications(¬ifications
, &controller
);
888 // First make some history, starting with a privileged URL.
889 const GURL
kExistingURL1("http://privileged");
891 kExistingURL1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
892 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
893 // Pretend it has bindings so we can tell if we incorrectly copy it.
894 main_test_rfh()->GetRenderViewHost()->AllowBindings(2);
895 main_test_rfh()->PrepareForCommit();
896 main_test_rfh()->SendNavigate(0, entry_id
, true, kExistingURL1
);
897 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
898 navigation_entry_committed_counter_
= 0;
900 // Navigate cross-process to a second URL.
901 const GURL
kExistingURL2("http://foo/eh");
903 kExistingURL2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
904 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
905 main_test_rfh()->PrepareForCommit();
906 TestRenderFrameHost
* foo_rfh
= contents()->GetPendingMainFrame();
907 foo_rfh
->SendNavigate(1, entry_id
, true, kExistingURL2
);
908 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
909 navigation_entry_committed_counter_
= 0;
911 // Now make a pending back/forward navigation to a privileged entry.
912 // The zeroth entry should be pending.
914 foo_rfh
->SendBeforeUnloadACK(true);
915 EXPECT_EQ(0U, notifications
.size());
916 EXPECT_EQ(0, controller
.GetPendingEntryIndex());
917 EXPECT_EQ(1, controller
.GetLastCommittedEntryIndex());
918 EXPECT_EQ(2, controller
.GetPendingEntry()->bindings());
920 // Before that commits, do a new navigation.
921 const GURL
kNewURL("http://foo/bee");
922 foo_rfh
->SendRendererInitiatedNavigationRequest(kNewURL
, true);
923 foo_rfh
->PrepareForCommit();
924 foo_rfh
->SendNavigate(3, 0, true, kNewURL
);
926 // There should no longer be any pending entry, and the new navigation we
927 // just made should be committed.
928 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
929 navigation_entry_committed_counter_
= 0;
930 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
931 EXPECT_EQ(2, controller
.GetLastCommittedEntryIndex());
932 EXPECT_EQ(kNewURL
, controller
.GetVisibleEntry()->GetURL());
933 EXPECT_EQ(0, controller
.GetLastCommittedEntry()->bindings());
936 // Tests navigating to an existing URL when there is a pending new navigation.
937 // This will happen if the user enters a URL, but before that commits, the
938 // current page fires history.back().
939 TEST_F(NavigationControllerTest
, LoadURL_BackPreemptsPending
) {
940 NavigationControllerImpl
& controller
= controller_impl();
941 TestNotificationTracker notifications
;
942 RegisterForAllNavNotifications(¬ifications
, &controller
);
944 // First make some history.
945 const GURL
kExistingURL1("http://foo/eh");
947 kExistingURL1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
948 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
949 main_test_rfh()->PrepareForCommit();
950 main_test_rfh()->SendNavigate(0, entry_id
, true, kExistingURL1
);
951 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
952 navigation_entry_committed_counter_
= 0;
954 const GURL
kExistingURL2("http://foo/bee");
956 kExistingURL2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
957 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
958 main_test_rfh()->PrepareForCommit();
959 main_test_rfh()->SendNavigate(1, entry_id
, true, kExistingURL2
);
960 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
961 navigation_entry_committed_counter_
= 0;
963 // A back navigation comes in from the renderer...
964 controller
.GoToOffset(-1);
965 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
967 // ...while the user tries to navigate to a new page...
968 const GURL
kNewURL("http://foo/see");
970 kNewURL
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
971 EXPECT_EQ(0U, notifications
.size());
972 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
973 EXPECT_EQ(1, controller
.GetLastCommittedEntryIndex());
975 // ...and the back navigation commits.
976 main_test_rfh()->PrepareForCommit();
977 main_test_rfh()->SendNavigate(0, entry_id
, false, kExistingURL1
);
979 // There should no longer be any pending entry, and the back navigation should
981 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
982 navigation_entry_committed_counter_
= 0;
983 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
984 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
985 EXPECT_EQ(kExistingURL1
, controller
.GetVisibleEntry()->GetURL());
988 // Tests an ignored navigation when there is a pending new navigation.
989 // This will happen if the user enters a URL, but before that commits, the
990 // current blank page reloads. See http://crbug.com/77507.
991 TEST_F(NavigationControllerTest
, LoadURL_IgnorePreemptsPending
) {
992 NavigationControllerImpl
& controller
= controller_impl();
993 TestNotificationTracker notifications
;
994 RegisterForAllNavNotifications(¬ifications
, &controller
);
996 // Set a WebContentsDelegate to listen for state changes.
997 scoped_ptr
<TestWebContentsDelegate
> delegate(new TestWebContentsDelegate());
998 EXPECT_FALSE(contents()->GetDelegate());
999 contents()->SetDelegate(delegate
.get());
1001 // Without any navigations, the renderer starts at about:blank.
1002 const GURL
kExistingURL(url::kAboutBlankURL
);
1004 // Now make a pending new navigation.
1005 const GURL
kNewURL("http://eh");
1007 kNewURL
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1008 EXPECT_EQ(0U, notifications
.size());
1009 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
1010 EXPECT_TRUE(controller
.GetPendingEntry());
1011 EXPECT_EQ(-1, controller
.GetLastCommittedEntryIndex());
1012 EXPECT_EQ(1, delegate
->navigation_state_change_count());
1014 // Before that commits, a document.write and location.reload can cause the
1015 // renderer to send a FrameNavigate with page_id -1 and nav_entry_id 0.
1016 // PlzNavigate: this will stop the old navigation and start a new one.
1017 main_test_rfh()->SendRendererInitiatedNavigationRequest(kExistingURL
, true);
1018 main_test_rfh()->SendNavigate(-1, 0, false, kExistingURL
);
1020 // This should clear the pending entry and notify of a navigation state
1021 // change, so that we do not keep displaying kNewURL.
1022 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
1023 EXPECT_FALSE(controller
.GetPendingEntry());
1024 EXPECT_EQ(-1, controller
.GetLastCommittedEntryIndex());
1025 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
1026 switches::kEnableBrowserSideNavigation
))
1027 EXPECT_EQ(4, delegate
->navigation_state_change_count());
1029 EXPECT_EQ(2, delegate
->navigation_state_change_count());
1031 contents()->SetDelegate(NULL
);
1034 // Tests that the pending entry state is correct after an abort.
1035 // We do not want to clear the pending entry, so that the user doesn't
1036 // lose a typed URL. (See http://crbug.com/9682.)
1037 TEST_F(NavigationControllerTest
, LoadURL_AbortDoesntCancelPending
) {
1038 NavigationControllerImpl
& controller
= controller_impl();
1039 TestNotificationTracker notifications
;
1040 RegisterForAllNavNotifications(¬ifications
, &controller
);
1042 // Set a WebContentsDelegate to listen for state changes.
1043 scoped_ptr
<TestWebContentsDelegate
> delegate(new TestWebContentsDelegate());
1044 EXPECT_FALSE(contents()->GetDelegate());
1045 contents()->SetDelegate(delegate
.get());
1047 // Start with a pending new navigation.
1048 const GURL
kNewURL("http://eh");
1050 kNewURL
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1051 main_test_rfh()->PrepareForCommit();
1052 EXPECT_EQ(0U, notifications
.size());
1053 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
1054 EXPECT_TRUE(controller
.GetPendingEntry());
1055 EXPECT_EQ(-1, controller
.GetLastCommittedEntryIndex());
1056 EXPECT_EQ(1, delegate
->navigation_state_change_count());
1058 // It may abort before committing, if it's a download or due to a stop or
1059 // a new navigation from the user.
1060 FrameHostMsg_DidFailProvisionalLoadWithError_Params params
;
1061 params
.error_code
= net::ERR_ABORTED
;
1062 params
.error_description
= base::string16();
1063 params
.url
= kNewURL
;
1064 params
.showing_repost_interstitial
= false;
1065 main_test_rfh()->OnMessageReceived(
1066 FrameHostMsg_DidFailProvisionalLoadWithError(0, // routing_id
1069 // This should not clear the pending entry or notify of a navigation state
1070 // change, so that we keep displaying kNewURL (until the user clears it).
1071 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
1072 EXPECT_TRUE(controller
.GetPendingEntry());
1073 EXPECT_EQ(-1, controller
.GetLastCommittedEntryIndex());
1074 EXPECT_EQ(1, delegate
->navigation_state_change_count());
1075 NavigationEntry
* pending_entry
= controller
.GetPendingEntry();
1077 // Ensure that a reload keeps the same pending entry.
1078 controller
.Reload(true);
1079 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
1080 EXPECT_TRUE(controller
.GetPendingEntry());
1081 EXPECT_EQ(pending_entry
, controller
.GetPendingEntry());
1082 EXPECT_EQ(-1, controller
.GetLastCommittedEntryIndex());
1084 contents()->SetDelegate(NULL
);
1087 // Tests that the pending URL is not visible during a renderer-initiated
1088 // redirect and abort. See http://crbug.com/83031.
1089 TEST_F(NavigationControllerTest
, LoadURL_RedirectAbortDoesntShowPendingURL
) {
1090 NavigationControllerImpl
& controller
= controller_impl();
1091 TestNotificationTracker notifications
;
1092 RegisterForAllNavNotifications(¬ifications
, &controller
);
1094 // First make an existing committed entry.
1095 const GURL
kExistingURL("http://foo/eh");
1096 controller
.LoadURL(kExistingURL
, content::Referrer(),
1097 ui::PAGE_TRANSITION_TYPED
, std::string());
1098 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1099 main_test_rfh()->PrepareForCommit();
1100 main_test_rfh()->SendNavigate(1, entry_id
, true, kExistingURL
);
1101 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1102 navigation_entry_committed_counter_
= 0;
1104 // Set a WebContentsDelegate to listen for state changes.
1105 scoped_ptr
<TestWebContentsDelegate
> delegate(new TestWebContentsDelegate());
1106 EXPECT_FALSE(contents()->GetDelegate());
1107 contents()->SetDelegate(delegate
.get());
1109 // Now make a pending new navigation, initiated by the renderer.
1110 const GURL
kNewURL("http://foo/bee");
1111 main_test_rfh()->SimulateNavigationStart(kNewURL
);
1112 EXPECT_EQ(0U, notifications
.size());
1113 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
1114 EXPECT_TRUE(controller
.GetPendingEntry());
1115 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
1116 EXPECT_EQ(1, delegate
->navigation_state_change_count());
1118 // The visible entry should be the last committed URL, not the pending one.
1119 EXPECT_EQ(kExistingURL
, controller
.GetVisibleEntry()->GetURL());
1121 // Now the navigation redirects.
1122 const GURL
kRedirectURL("http://foo/see");
1123 main_test_rfh()->SimulateRedirect(kRedirectURL
);
1125 // We don't want to change the NavigationEntry's url, in case it cancels.
1126 // Prevents regression of http://crbug.com/77786.
1127 EXPECT_EQ(kNewURL
, controller
.GetPendingEntry()->GetURL());
1129 // It may abort before committing, if it's a download or due to a stop or
1130 // a new navigation from the user.
1131 main_test_rfh()->SimulateNavigationError(kRedirectURL
, net::ERR_ABORTED
);
1133 // Because the pending entry is renderer initiated and not visible, we
1134 // clear it when it fails.
1135 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
1136 EXPECT_FALSE(controller
.GetPendingEntry());
1137 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
1138 EXPECT_EQ(2, delegate
->navigation_state_change_count());
1140 // The visible entry should be the last committed URL, not the pending one,
1141 // so that no spoof is possible.
1142 EXPECT_EQ(kExistingURL
, controller
.GetVisibleEntry()->GetURL());
1144 contents()->SetDelegate(NULL
);
1147 // Ensure that NavigationEntries track which bindings their RenderViewHost had
1148 // at the time they committed. http://crbug.com/173672.
1149 TEST_F(NavigationControllerTest
, LoadURL_WithBindings
) {
1150 NavigationControllerImpl
& controller
= controller_impl();
1151 TestNotificationTracker notifications
;
1152 RegisterForAllNavNotifications(¬ifications
, &controller
);
1153 std::vector
<GURL
> url_chain
;
1155 const GURL
url1("http://foo1");
1156 const GURL
url2("http://foo2");
1158 // Navigate to a first, unprivileged URL.
1160 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1161 EXPECT_EQ(NavigationEntryImpl::kInvalidBindings
,
1162 controller
.GetPendingEntry()->bindings());
1163 int entry1_id
= controller
.GetPendingEntry()->GetUniqueID();
1166 TestRenderFrameHost
* orig_rfh
= contents()->GetMainFrame();
1167 orig_rfh
->PrepareForCommit();
1168 orig_rfh
->SendNavigate(0, entry1_id
, true, url1
);
1169 EXPECT_EQ(controller
.GetEntryCount(), 1);
1170 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
1171 EXPECT_EQ(0, controller
.GetLastCommittedEntry()->bindings());
1172 entry1_id
= controller
.GetLastCommittedEntry()->GetUniqueID();
1174 // Manually increase the number of active frames in the SiteInstance
1175 // that orig_rfh belongs to, to prevent it from being destroyed when
1176 // it gets swapped out, so that we can reuse orig_rfh when the
1177 // controller goes back.
1178 orig_rfh
->GetSiteInstance()->increment_active_frame_count();
1180 // Navigate to a second URL, simulate the beforeunload ack for the cross-site
1181 // transition, and set bindings on the pending RenderViewHost to simulate a
1184 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1185 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1186 orig_rfh
->PrepareForCommit();
1187 TestRenderFrameHost
* new_rfh
= contents()->GetPendingMainFrame();
1188 new_rfh
->GetRenderViewHost()->AllowBindings(1);
1189 new_rfh
->SendNavigate(1, entry_id
, true, url2
);
1191 // The second load should be committed, and bindings should be remembered.
1192 EXPECT_EQ(controller
.GetEntryCount(), 2);
1193 EXPECT_EQ(1, controller
.GetLastCommittedEntryIndex());
1194 EXPECT_TRUE(controller
.CanGoBack());
1195 EXPECT_EQ(1, controller
.GetLastCommittedEntry()->bindings());
1197 // Going back, the first entry should still appear unprivileged.
1198 controller
.GoBack();
1199 new_rfh
->PrepareForCommit();
1200 contents()->GetPendingMainFrame()->SendNavigate(0, entry1_id
, false, url1
);
1201 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
1202 EXPECT_EQ(0, controller
.GetLastCommittedEntry()->bindings());
1205 TEST_F(NavigationControllerTest
, Reload
) {
1206 NavigationControllerImpl
& controller
= controller_impl();
1207 TestNotificationTracker notifications
;
1208 RegisterForAllNavNotifications(¬ifications
, &controller
);
1210 const GURL
url1("http://foo1");
1213 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1214 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1215 EXPECT_EQ(0U, notifications
.size());
1216 main_test_rfh()->PrepareForCommit();
1217 main_test_rfh()->SendNavigate(0, entry_id
, true, url1
);
1218 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1219 navigation_entry_committed_counter_
= 0;
1220 ASSERT_TRUE(controller
.GetVisibleEntry());
1221 controller
.GetVisibleEntry()->SetTitle(base::ASCIIToUTF16("Title"));
1222 entry_id
= controller
.GetLastCommittedEntry()->GetUniqueID();
1224 controller
.Reload(true);
1225 EXPECT_EQ(0U, notifications
.size());
1227 const base::Time timestamp
= controller
.GetVisibleEntry()->GetTimestamp();
1228 EXPECT_FALSE(timestamp
.is_null());
1230 // The reload is pending.
1231 EXPECT_EQ(controller
.GetEntryCount(), 1);
1232 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1233 EXPECT_EQ(controller
.GetPendingEntryIndex(), 0);
1234 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1235 EXPECT_TRUE(controller
.GetPendingEntry());
1236 EXPECT_FALSE(controller
.CanGoBack());
1237 EXPECT_FALSE(controller
.CanGoForward());
1238 // Make sure the title has been cleared (will be redrawn just after reload).
1239 // Avoids a stale cached title when the new page being reloaded has no title.
1240 // See http://crbug.com/96041.
1241 EXPECT_TRUE(controller
.GetVisibleEntry()->GetTitle().empty());
1243 main_test_rfh()->PrepareForCommit();
1244 main_test_rfh()->SendNavigate(0, entry_id
, false, url1
);
1245 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1246 navigation_entry_committed_counter_
= 0;
1248 // Now the reload is committed.
1249 EXPECT_EQ(controller
.GetEntryCount(), 1);
1250 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1251 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1252 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1253 EXPECT_FALSE(controller
.GetPendingEntry());
1254 EXPECT_FALSE(controller
.CanGoBack());
1255 EXPECT_FALSE(controller
.CanGoForward());
1257 // The timestamp should have been updated.
1258 ASSERT_TRUE(controller
.GetVisibleEntry());
1259 EXPECT_GE(controller
.GetVisibleEntry()->GetTimestamp(), timestamp
);
1262 // Tests what happens when a reload navigation produces a new page.
1263 TEST_F(NavigationControllerTest
, Reload_GeneratesNewPage
) {
1264 NavigationControllerImpl
& controller
= controller_impl();
1265 TestNotificationTracker notifications
;
1266 RegisterForAllNavNotifications(¬ifications
, &controller
);
1268 const GURL
url1("http://foo1");
1269 const GURL
url2("http://foo2");
1272 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1273 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1274 main_test_rfh()->PrepareForCommit();
1275 main_test_rfh()->SendNavigate(0, entry_id
, true, url1
);
1276 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1277 navigation_entry_committed_counter_
= 0;
1278 entry_id
= controller
.GetLastCommittedEntry()->GetUniqueID();
1280 controller
.Reload(true);
1281 EXPECT_EQ(0U, notifications
.size());
1283 main_test_rfh()->PrepareForCommitWithServerRedirect(url2
);
1284 main_test_rfh()->SendNavigate(1, entry_id
, true, url2
);
1285 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1286 navigation_entry_committed_counter_
= 0;
1288 // Now the reload is committed.
1289 EXPECT_EQ(controller
.GetEntryCount(), 2);
1290 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 1);
1291 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1292 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1293 EXPECT_FALSE(controller
.GetPendingEntry());
1294 EXPECT_TRUE(controller
.CanGoBack());
1295 EXPECT_FALSE(controller
.CanGoForward());
1298 // This test ensures that when a guest renderer reloads, the reload goes through
1299 // without ending up in the "we have a wrong process for the URL" branch in
1300 // NavigationControllerImpl::ReloadInternal.
1301 TEST_F(NavigationControllerTest
, ReloadWithGuest
) {
1302 NavigationControllerImpl
& controller
= controller_impl();
1304 const GURL
url1("http://foo1");
1306 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1307 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1308 main_test_rfh()->PrepareForCommit();
1309 main_test_rfh()->SendNavigate(0, entry_id
, true, url1
);
1310 ASSERT_TRUE(controller
.GetVisibleEntry());
1312 // Make the entry believe its RenderProcessHost is a guest.
1313 NavigationEntryImpl
* entry1
= controller
.GetVisibleEntry();
1314 reinterpret_cast<MockRenderProcessHost
*>(
1315 entry1
->site_instance()->GetProcess())->set_is_for_guests_only(true);
1318 controller
.Reload(true);
1320 // The reload is pending. Check that the NavigationEntry didn't get replaced
1321 // because of having the wrong process.
1322 EXPECT_EQ(controller
.GetEntryCount(), 1);
1323 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1324 EXPECT_EQ(controller
.GetPendingEntryIndex(), 0);
1326 NavigationEntryImpl
* entry2
= controller
.GetPendingEntry();
1327 EXPECT_EQ(entry1
, entry2
);
1330 #if !defined(OS_ANDROID) // http://crbug.com/157428
1332 void SetOriginalURL(const GURL
& url
,
1333 FrameHostMsg_DidCommitProvisionalLoad_Params
* params
) {
1334 params
->original_request_url
= url
;
1338 TEST_F(NavigationControllerTest
, ReloadOriginalRequestURL
) {
1339 NavigationControllerImpl
& controller
= controller_impl();
1340 TestNotificationTracker notifications
;
1341 RegisterForAllNavNotifications(¬ifications
, &controller
);
1343 const GURL
original_url("http://foo1");
1344 const GURL
final_url("http://foo2");
1345 auto set_original_url_callback
= base::Bind(SetOriginalURL
, original_url
);
1347 // Load up the original URL, but get redirected.
1349 original_url
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1350 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1351 EXPECT_EQ(0U, notifications
.size());
1352 main_test_rfh()->PrepareForCommitWithServerRedirect(final_url
);
1353 main_test_rfh()->SendNavigateWithModificationCallback(
1354 0, entry_id
, true, final_url
, set_original_url_callback
);
1355 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1356 navigation_entry_committed_counter_
= 0;
1357 entry_id
= controller
.GetLastCommittedEntry()->GetUniqueID();
1359 // The NavigationEntry should save both the original URL and the final
1362 original_url
, controller
.GetVisibleEntry()->GetOriginalRequestURL());
1363 EXPECT_EQ(final_url
, controller
.GetVisibleEntry()->GetURL());
1365 // Reload using the original URL.
1366 controller
.GetVisibleEntry()->SetTitle(base::ASCIIToUTF16("Title"));
1367 controller
.ReloadOriginalRequestURL(false);
1368 EXPECT_EQ(0U, notifications
.size());
1370 // The reload is pending. The request should point to the original URL.
1371 EXPECT_EQ(original_url
, navigated_url());
1372 EXPECT_EQ(controller
.GetEntryCount(), 1);
1373 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1374 EXPECT_EQ(controller
.GetPendingEntryIndex(), 0);
1375 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1376 EXPECT_TRUE(controller
.GetPendingEntry());
1377 EXPECT_FALSE(controller
.CanGoBack());
1378 EXPECT_FALSE(controller
.CanGoForward());
1380 // Make sure the title has been cleared (will be redrawn just after reload).
1381 // Avoids a stale cached title when the new page being reloaded has no title.
1382 // See http://crbug.com/96041.
1383 EXPECT_TRUE(controller
.GetVisibleEntry()->GetTitle().empty());
1385 // Send that the navigation has proceeded; say it got redirected again.
1386 main_test_rfh()->PrepareForCommitWithServerRedirect(final_url
);
1387 main_test_rfh()->SendNavigate(0, entry_id
, false, final_url
);
1388 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1389 navigation_entry_committed_counter_
= 0;
1391 // Now the reload is committed.
1392 EXPECT_EQ(controller
.GetEntryCount(), 1);
1393 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1394 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1395 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1396 EXPECT_FALSE(controller
.GetPendingEntry());
1397 EXPECT_FALSE(controller
.CanGoBack());
1398 EXPECT_FALSE(controller
.CanGoForward());
1401 #endif // !defined(OS_ANDROID)
1403 // Test that certain non-persisted NavigationEntryImpl values get reset after
1405 TEST_F(NavigationControllerTest
, ResetEntryValuesAfterCommit
) {
1406 NavigationControllerImpl
& controller
= controller_impl();
1408 // The value of "should replace entry" will be tested, but it's an error to
1409 // specify it when there are no entries. Create a simple entry to be replaced.
1410 const GURL
url0("http://foo/0");
1412 url0
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1413 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1414 main_test_rfh()->PrepareForCommit();
1415 main_test_rfh()->SendNavigate(0, entry_id
, true, url0
);
1417 // Set up the pending entry.
1418 const GURL
url1("http://foo/1");
1420 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1421 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1423 // Set up some sample values.
1424 const unsigned char* raw_data
=
1425 reinterpret_cast<const unsigned char*>("post\n\n\0data");
1426 const int length
= 11;
1427 std::vector
<unsigned char> post_data_vector(raw_data
, raw_data
+length
);
1428 scoped_refptr
<base::RefCountedBytes
> post_data
=
1429 base::RefCountedBytes::TakeVector(&post_data_vector
);
1430 GlobalRequestID
transfer_id(3, 4);
1432 // Set non-persisted values on the pending entry.
1433 NavigationEntryImpl
* pending_entry
= controller
.GetPendingEntry();
1434 pending_entry
->SetBrowserInitiatedPostData(post_data
.get());
1435 pending_entry
->set_is_renderer_initiated(true);
1436 pending_entry
->set_transferred_global_request_id(transfer_id
);
1437 pending_entry
->set_should_replace_entry(true);
1438 pending_entry
->set_should_clear_history_list(true);
1439 EXPECT_EQ(post_data
.get(), pending_entry
->GetBrowserInitiatedPostData());
1440 EXPECT_TRUE(pending_entry
->is_renderer_initiated());
1441 EXPECT_EQ(transfer_id
, pending_entry
->transferred_global_request_id());
1442 EXPECT_TRUE(pending_entry
->should_replace_entry());
1443 EXPECT_TRUE(pending_entry
->should_clear_history_list());
1445 // Fake a commit response.
1446 main_test_rfh()->PrepareForCommit();
1447 main_test_rfh()->SendNavigate(1, entry_id
, true, url1
);
1449 // Certain values that are only used for pending entries get reset after
1451 NavigationEntryImpl
* committed_entry
= controller
.GetLastCommittedEntry();
1452 EXPECT_FALSE(committed_entry
->GetBrowserInitiatedPostData());
1453 EXPECT_FALSE(committed_entry
->is_renderer_initiated());
1454 EXPECT_EQ(GlobalRequestID(-1, -1),
1455 committed_entry
->transferred_global_request_id());
1456 EXPECT_FALSE(committed_entry
->should_replace_entry());
1457 EXPECT_FALSE(committed_entry
->should_clear_history_list());
1461 void SetRedirects(const std::vector
<GURL
>& redirects
,
1462 FrameHostMsg_DidCommitProvisionalLoad_Params
* params
) {
1463 params
->redirects
= redirects
;
1467 // Test that Redirects are preserved after a commit.
1468 TEST_F(NavigationControllerTest
, RedirectsAreNotResetByCommit
) {
1469 NavigationControllerImpl
& controller
= controller_impl();
1470 const GURL
url1("http://foo1");
1471 const GURL
url2("http://foo2");
1473 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1474 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1476 // Set up some redirect values.
1477 std::vector
<GURL
> redirects
;
1478 redirects
.push_back(url2
);
1479 auto set_redirects_callback
= base::Bind(SetRedirects
, redirects
);
1481 // Set redirects on the pending entry.
1482 NavigationEntryImpl
* pending_entry
= controller
.GetPendingEntry();
1483 pending_entry
->SetRedirectChain(redirects
);
1484 EXPECT_EQ(1U, pending_entry
->GetRedirectChain().size());
1485 EXPECT_EQ(url2
, pending_entry
->GetRedirectChain()[0]);
1487 // Normal navigation will preserve redirects in the committed entry.
1488 main_test_rfh()->PrepareForCommitWithServerRedirect(url2
);
1489 main_test_rfh()->SendNavigateWithModificationCallback(0, entry_id
, true, url1
,
1490 set_redirects_callback
);
1491 NavigationEntryImpl
* committed_entry
= controller
.GetLastCommittedEntry();
1492 ASSERT_EQ(1U, committed_entry
->GetRedirectChain().size());
1493 EXPECT_EQ(url2
, committed_entry
->GetRedirectChain()[0]);
1496 // Tests what happens when we navigate back successfully
1497 TEST_F(NavigationControllerTest
, Back
) {
1498 NavigationControllerImpl
& controller
= controller_impl();
1499 TestNotificationTracker notifications
;
1500 RegisterForAllNavNotifications(¬ifications
, &controller
);
1502 const GURL
url1("http://foo1");
1503 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url1
);
1504 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1505 navigation_entry_committed_counter_
= 0;
1507 const GURL
url2("http://foo2");
1508 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url2
);
1509 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1510 navigation_entry_committed_counter_
= 0;
1512 controller
.GoBack();
1513 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1514 EXPECT_EQ(0U, notifications
.size());
1516 // We should now have a pending navigation to go back.
1517 EXPECT_EQ(controller
.GetEntryCount(), 2);
1518 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 1);
1519 EXPECT_EQ(controller
.GetPendingEntryIndex(), 0);
1520 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1521 EXPECT_TRUE(controller
.GetPendingEntry());
1522 EXPECT_FALSE(controller
.CanGoBack());
1523 EXPECT_FALSE(controller
.CanGoToOffset(-1));
1524 EXPECT_TRUE(controller
.CanGoForward());
1525 EXPECT_TRUE(controller
.CanGoToOffset(1));
1526 EXPECT_FALSE(controller
.CanGoToOffset(2)); // Cannot go forward 2 steps.
1528 // Timestamp for entry 1 should be on or after that of entry 0.
1529 EXPECT_FALSE(controller
.GetEntryAtIndex(0)->GetTimestamp().is_null());
1530 EXPECT_GE(controller
.GetEntryAtIndex(1)->GetTimestamp(),
1531 controller
.GetEntryAtIndex(0)->GetTimestamp());
1533 main_test_rfh()->PrepareForCommit();
1534 main_test_rfh()->SendNavigate(0, entry_id
, false, url2
);
1535 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1536 navigation_entry_committed_counter_
= 0;
1538 // The back navigation completed successfully.
1539 EXPECT_EQ(controller
.GetEntryCount(), 2);
1540 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1541 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1542 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1543 EXPECT_FALSE(controller
.GetPendingEntry());
1544 EXPECT_FALSE(controller
.CanGoBack());
1545 EXPECT_FALSE(controller
.CanGoToOffset(-1));
1546 EXPECT_TRUE(controller
.CanGoForward());
1547 EXPECT_TRUE(controller
.CanGoToOffset(1));
1548 EXPECT_FALSE(controller
.CanGoToOffset(2)); // Cannot go foward 2 steps.
1550 // Timestamp for entry 0 should be on or after that of entry 1
1551 // (since we went back to it).
1552 EXPECT_GE(controller
.GetEntryAtIndex(0)->GetTimestamp(),
1553 controller
.GetEntryAtIndex(1)->GetTimestamp());
1556 // Tests what happens when a back navigation produces a new page.
1557 TEST_F(NavigationControllerTest
, Back_GeneratesNewPage
) {
1558 NavigationControllerImpl
& controller
= controller_impl();
1559 TestNotificationTracker notifications
;
1560 RegisterForAllNavNotifications(¬ifications
, &controller
);
1562 const GURL
url1("http://foo/1");
1563 const GURL
url2("http://foo/2");
1564 const GURL
url3("http://foo/3");
1567 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1568 int entry1_id
= controller
.GetPendingEntry()->GetUniqueID();
1569 main_test_rfh()->PrepareForCommit();
1570 main_test_rfh()->SendNavigate(0, entry1_id
, true, url1
);
1571 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1572 navigation_entry_committed_counter_
= 0;
1573 entry1_id
= controller
.GetLastCommittedEntry()->GetUniqueID();
1576 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1577 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1578 main_test_rfh()->PrepareForCommit();
1579 main_test_rfh()->SendNavigate(1, entry_id
, true, url2
);
1580 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1581 navigation_entry_committed_counter_
= 0;
1583 controller
.GoBack();
1584 EXPECT_EQ(0U, notifications
.size());
1586 // We should now have a pending navigation to go back.
1587 EXPECT_EQ(controller
.GetEntryCount(), 2);
1588 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 1);
1589 EXPECT_EQ(controller
.GetPendingEntryIndex(), 0);
1590 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1591 EXPECT_TRUE(controller
.GetPendingEntry());
1592 EXPECT_FALSE(controller
.CanGoBack());
1593 EXPECT_TRUE(controller
.CanGoForward());
1595 main_test_rfh()->PrepareForCommitWithServerRedirect(url3
);
1596 main_test_rfh()->SendNavigate(2, entry1_id
, true, url3
);
1597 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1598 navigation_entry_committed_counter_
= 0;
1600 // The back navigation resulted in a completely new navigation.
1601 // TODO(darin): perhaps this behavior will be confusing to users?
1602 EXPECT_EQ(controller
.GetEntryCount(), 3);
1603 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 2);
1604 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1605 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1606 EXPECT_FALSE(controller
.GetPendingEntry());
1607 EXPECT_TRUE(controller
.CanGoBack());
1608 EXPECT_FALSE(controller
.CanGoForward());
1611 // Receives a back message when there is a new pending navigation entry.
1612 TEST_F(NavigationControllerTest
, Back_NewPending
) {
1613 NavigationControllerImpl
& controller
= controller_impl();
1614 TestNotificationTracker notifications
;
1615 RegisterForAllNavNotifications(¬ifications
, &controller
);
1617 const GURL
kUrl1("http://foo1");
1618 const GURL
kUrl2("http://foo2");
1619 const GURL
kUrl3("http://foo3");
1621 // First navigate two places so we have some back history.
1622 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, kUrl1
);
1623 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1624 navigation_entry_committed_counter_
= 0;
1626 // controller.LoadURL(kUrl2, ui::PAGE_TRANSITION_TYPED);
1627 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, kUrl2
);
1628 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1629 navigation_entry_committed_counter_
= 0;
1631 // Now start a new pending navigation and go back before it commits.
1633 kUrl3
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1634 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
1635 EXPECT_EQ(kUrl3
, controller
.GetPendingEntry()->GetURL());
1636 controller
.GoBack();
1638 // The pending navigation should now be the "back" item and the new one
1640 EXPECT_EQ(0, controller
.GetPendingEntryIndex());
1641 EXPECT_EQ(kUrl1
, controller
.GetPendingEntry()->GetURL());
1644 // Tests what happens when we navigate forward successfully.
1645 TEST_F(NavigationControllerTest
, Forward
) {
1646 NavigationControllerImpl
& controller
= controller_impl();
1647 TestNotificationTracker notifications
;
1648 RegisterForAllNavNotifications(¬ifications
, &controller
);
1650 const GURL
url1("http://foo1");
1651 const GURL
url2("http://foo2");
1653 main_test_rfh()->SendRendererInitiatedNavigationRequest(url1
, true);
1654 main_test_rfh()->PrepareForCommit();
1655 main_test_rfh()->SendNavigate(0, 0, true, url1
);
1656 NavigationEntry
* entry1
= controller
.GetLastCommittedEntry();
1657 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1658 navigation_entry_committed_counter_
= 0;
1660 main_test_rfh()->SendRendererInitiatedNavigationRequest(url2
, true);
1661 main_test_rfh()->PrepareForCommit();
1662 main_test_rfh()->SendNavigate(1, 0, true, url2
);
1663 NavigationEntry
* entry2
= controller
.GetLastCommittedEntry();
1664 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1665 navigation_entry_committed_counter_
= 0;
1667 controller
.GoBack();
1668 main_test_rfh()->PrepareForCommit();
1669 main_test_rfh()->SendNavigate(0, entry1
->GetUniqueID(), false, url1
);
1670 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1671 navigation_entry_committed_counter_
= 0;
1673 controller
.GoForward();
1675 // We should now have a pending navigation to go forward.
1676 EXPECT_EQ(controller
.GetEntryCount(), 2);
1677 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1678 EXPECT_EQ(controller
.GetPendingEntryIndex(), 1);
1679 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1680 EXPECT_TRUE(controller
.GetPendingEntry());
1681 EXPECT_TRUE(controller
.CanGoBack());
1682 EXPECT_TRUE(controller
.CanGoToOffset(-1));
1683 EXPECT_FALSE(controller
.CanGoToOffset(-2)); // Cannot go back 2 steps.
1684 EXPECT_FALSE(controller
.CanGoForward());
1685 EXPECT_FALSE(controller
.CanGoToOffset(1));
1687 // Timestamp for entry 0 should be on or after that of entry 1
1688 // (since we went back to it).
1689 EXPECT_FALSE(controller
.GetEntryAtIndex(0)->GetTimestamp().is_null());
1690 EXPECT_GE(controller
.GetEntryAtIndex(0)->GetTimestamp(),
1691 controller
.GetEntryAtIndex(1)->GetTimestamp());
1693 main_test_rfh()->PrepareForCommit();
1694 main_test_rfh()->SendNavigate(1, entry2
->GetUniqueID(), false, url2
);
1695 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1696 navigation_entry_committed_counter_
= 0;
1698 // The forward navigation completed successfully.
1699 EXPECT_EQ(controller
.GetEntryCount(), 2);
1700 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 1);
1701 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1702 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1703 EXPECT_FALSE(controller
.GetPendingEntry());
1704 EXPECT_TRUE(controller
.CanGoBack());
1705 EXPECT_TRUE(controller
.CanGoToOffset(-1));
1706 EXPECT_FALSE(controller
.CanGoToOffset(-2)); // Cannot go back 2 steps.
1707 EXPECT_FALSE(controller
.CanGoForward());
1708 EXPECT_FALSE(controller
.CanGoToOffset(1));
1710 // Timestamp for entry 1 should be on or after that of entry 0
1711 // (since we went forward to it).
1712 EXPECT_GE(controller
.GetEntryAtIndex(1)->GetTimestamp(),
1713 controller
.GetEntryAtIndex(0)->GetTimestamp());
1716 // Tests what happens when a forward navigation produces a new page.
1717 TEST_F(NavigationControllerTest
, Forward_GeneratesNewPage
) {
1718 NavigationControllerImpl
& controller
= controller_impl();
1719 TestNotificationTracker notifications
;
1720 RegisterForAllNavNotifications(¬ifications
, &controller
);
1722 const GURL
url1("http://foo1");
1723 const GURL
url2("http://foo2");
1724 const GURL
url3("http://foo3");
1726 main_test_rfh()->SendRendererInitiatedNavigationRequest(url1
, true);
1727 main_test_rfh()->PrepareForCommit();
1728 main_test_rfh()->SendNavigate(0, 0, true, url1
);
1729 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1730 NavigationEntry
* entry1
= controller
.GetLastCommittedEntry();
1731 navigation_entry_committed_counter_
= 0;
1732 main_test_rfh()->SendRendererInitiatedNavigationRequest(url2
, true);
1733 main_test_rfh()->PrepareForCommit();
1734 main_test_rfh()->SendNavigate(1, 0, true, url2
);
1735 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1736 NavigationEntry
* entry2
= controller
.GetLastCommittedEntry();
1737 navigation_entry_committed_counter_
= 0;
1739 controller
.GoBack();
1740 main_test_rfh()->PrepareForCommit();
1741 main_test_rfh()->SendNavigate(0, entry1
->GetUniqueID(), false, url1
);
1742 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1743 navigation_entry_committed_counter_
= 0;
1745 controller
.GoForward();
1746 EXPECT_EQ(0U, notifications
.size());
1748 // Should now have a pending navigation to go forward.
1749 EXPECT_EQ(controller
.GetEntryCount(), 2);
1750 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1751 EXPECT_EQ(controller
.GetPendingEntryIndex(), 1);
1752 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1753 EXPECT_TRUE(controller
.GetPendingEntry());
1754 EXPECT_TRUE(controller
.CanGoBack());
1755 EXPECT_FALSE(controller
.CanGoForward());
1757 main_test_rfh()->PrepareForCommit();
1758 main_test_rfh()->SendNavigate(2, entry2
->GetUniqueID(), true, url3
);
1759 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1760 navigation_entry_committed_counter_
= 0;
1761 EXPECT_TRUE(notifications
.Check1AndReset(NOTIFICATION_NAV_LIST_PRUNED
));
1763 EXPECT_EQ(controller
.GetEntryCount(), 2);
1764 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 1);
1765 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1766 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1767 EXPECT_FALSE(controller
.GetPendingEntry());
1768 EXPECT_TRUE(controller
.CanGoBack());
1769 EXPECT_FALSE(controller
.CanGoForward());
1772 // Two consecutive navigations for the same URL entered in should be considered
1773 // as SAME_PAGE navigation even when we are redirected to some other page.
1774 TEST_F(NavigationControllerTest
, Redirect
) {
1775 NavigationControllerImpl
& controller
= controller_impl();
1776 TestNotificationTracker notifications
;
1777 RegisterForAllNavNotifications(¬ifications
, &controller
);
1779 const GURL
url1("http://foo1");
1780 const GURL
url2("http://foo2"); // Redirection target
1784 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1785 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1787 EXPECT_EQ(0U, notifications
.size());
1789 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
1791 params
.nav_entry_id
= entry_id
;
1792 params
.did_create_new_entry
= true;
1794 params
.transition
= ui::PAGE_TRANSITION_SERVER_REDIRECT
;
1795 params
.redirects
.push_back(GURL("http://foo1"));
1796 params
.redirects
.push_back(GURL("http://foo2"));
1797 params
.should_update_history
= false;
1798 params
.gesture
= NavigationGestureAuto
;
1799 params
.is_post
= false;
1800 params
.page_state
= PageState::CreateFromURL(url2
);
1802 LoadCommittedDetails details
;
1804 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
1806 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1807 navigation_entry_committed_counter_
= 0;
1811 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1812 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1814 EXPECT_TRUE(controller
.GetPendingEntry());
1815 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1816 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
1818 params
.nav_entry_id
= entry_id
;
1819 params
.did_create_new_entry
= false;
1821 EXPECT_EQ(0U, notifications
.size());
1822 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
1824 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1825 navigation_entry_committed_counter_
= 0;
1827 EXPECT_TRUE(details
.type
== NAVIGATION_TYPE_SAME_PAGE
);
1828 EXPECT_EQ(controller
.GetEntryCount(), 1);
1829 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1830 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1831 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1832 EXPECT_FALSE(controller
.GetPendingEntry());
1833 EXPECT_EQ(url2
, controller
.GetVisibleEntry()->GetURL());
1835 EXPECT_FALSE(controller
.CanGoBack());
1836 EXPECT_FALSE(controller
.CanGoForward());
1839 // Similar to Redirect above, but the first URL is requested by POST,
1840 // the second URL is requested by GET. NavigationEntry::has_post_data_
1841 // must be cleared. http://crbug.com/21245
1842 TEST_F(NavigationControllerTest
, PostThenRedirect
) {
1843 NavigationControllerImpl
& controller
= controller_impl();
1844 TestNotificationTracker notifications
;
1845 RegisterForAllNavNotifications(¬ifications
, &controller
);
1847 const GURL
url1("http://foo1");
1848 const GURL
url2("http://foo2"); // Redirection target
1850 // First request as POST.
1852 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1853 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1854 controller
.GetVisibleEntry()->SetHasPostData(true);
1856 EXPECT_EQ(0U, notifications
.size());
1858 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
1860 params
.nav_entry_id
= entry_id
;
1861 params
.did_create_new_entry
= true;
1863 params
.transition
= ui::PAGE_TRANSITION_SERVER_REDIRECT
;
1864 params
.redirects
.push_back(GURL("http://foo1"));
1865 params
.redirects
.push_back(GURL("http://foo2"));
1866 params
.should_update_history
= false;
1867 params
.gesture
= NavigationGestureAuto
;
1868 params
.is_post
= true;
1869 params
.page_state
= PageState::CreateFromURL(url2
);
1871 LoadCommittedDetails details
;
1873 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
1875 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1876 navigation_entry_committed_counter_
= 0;
1880 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1881 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1883 EXPECT_TRUE(controller
.GetPendingEntry());
1884 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1885 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
1887 params
.nav_entry_id
= entry_id
;
1888 params
.did_create_new_entry
= false;
1889 params
.is_post
= false;
1891 EXPECT_EQ(0U, notifications
.size());
1892 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
1894 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1895 navigation_entry_committed_counter_
= 0;
1897 EXPECT_TRUE(details
.type
== NAVIGATION_TYPE_SAME_PAGE
);
1898 EXPECT_EQ(controller
.GetEntryCount(), 1);
1899 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1900 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1901 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1902 EXPECT_FALSE(controller
.GetPendingEntry());
1903 EXPECT_EQ(url2
, controller
.GetVisibleEntry()->GetURL());
1904 EXPECT_FALSE(controller
.GetVisibleEntry()->GetHasPostData());
1906 EXPECT_FALSE(controller
.CanGoBack());
1907 EXPECT_FALSE(controller
.CanGoForward());
1910 // A redirect right off the bat should be a NEW_PAGE.
1911 TEST_F(NavigationControllerTest
, ImmediateRedirect
) {
1912 NavigationControllerImpl
& controller
= controller_impl();
1913 TestNotificationTracker notifications
;
1914 RegisterForAllNavNotifications(¬ifications
, &controller
);
1916 const GURL
url1("http://foo1");
1917 const GURL
url2("http://foo2"); // Redirection target
1921 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1922 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
1924 EXPECT_TRUE(controller
.GetPendingEntry());
1925 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1926 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
1928 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
1930 params
.nav_entry_id
= entry_id
;
1931 params
.did_create_new_entry
= true;
1933 params
.transition
= ui::PAGE_TRANSITION_SERVER_REDIRECT
;
1934 params
.redirects
.push_back(GURL("http://foo1"));
1935 params
.redirects
.push_back(GURL("http://foo2"));
1936 params
.should_update_history
= false;
1937 params
.gesture
= NavigationGestureAuto
;
1938 params
.is_post
= false;
1939 params
.page_state
= PageState::CreateFromURL(url2
);
1941 LoadCommittedDetails details
;
1943 EXPECT_EQ(0U, notifications
.size());
1944 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
1946 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
1947 navigation_entry_committed_counter_
= 0;
1949 EXPECT_TRUE(details
.type
== NAVIGATION_TYPE_NEW_PAGE
);
1950 EXPECT_EQ(controller
.GetEntryCount(), 1);
1951 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
1952 EXPECT_TRUE(controller
.GetLastCommittedEntry());
1953 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
1954 EXPECT_FALSE(controller
.GetPendingEntry());
1955 EXPECT_EQ(url2
, controller
.GetVisibleEntry()->GetURL());
1957 EXPECT_FALSE(controller
.CanGoBack());
1958 EXPECT_FALSE(controller
.CanGoForward());
1961 // If something is pumping the event loop in the browser process and is loading
1962 // pages rapidly one after the other, there can be a race with two closely-
1963 // spaced load requests. Once the first load request is sent, will the renderer
1964 // be fast enough to get the load committed, send a DidCommitProvisionalLoad
1965 // IPC, and have the browser process handle that IPC before the caller makes
1966 // another load request, replacing the pending entry of the first request?
1968 // This test is about what happens in such a race when that pending entry
1969 // replacement happens. If it happens, and the first load had the same URL as
1970 // the page before it, we must make sure that the replacement of the pending
1971 // entry correctly turns a SAME_PAGE classification into an EXISTING_PAGE one.
1973 // (This is a unit test rather than a browser test because it's not currently
1974 // possible to force this sequence of events with a browser test.)
1975 TEST_F(NavigationControllerTest
,
1976 NavigationTypeClassification_ExistingPageRace
) {
1977 NavigationControllerImpl
& controller
= controller_impl();
1978 const GURL
url1("http://foo1");
1979 const GURL
url2("http://foo2");
1981 // Start with a loaded page.
1982 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url1
);
1983 EXPECT_EQ(nullptr, controller_impl().GetPendingEntry());
1985 // Start a load of the same page again.
1987 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1988 int entry_id1
= controller
.GetPendingEntry()->GetUniqueID();
1990 // Immediately start loading a different page...
1992 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
1993 int entry_id2
= controller
.GetPendingEntry()->GetUniqueID();
1994 EXPECT_NE(entry_id1
, entry_id2
);
1996 // ... and now the renderer sends a commit for the first navigation.
1997 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
1999 params
.nav_entry_id
= entry_id1
;
2000 params
.intended_as_new_entry
= true;
2001 params
.did_create_new_entry
= false;
2003 params
.transition
= ui::PAGE_TRANSITION_TYPED
;
2004 params
.page_state
= PageState::CreateFromURL(url1
);
2006 LoadCommittedDetails details
;
2008 main_test_rfh()->PrepareForCommit();
2009 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
2011 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE
, details
.type
);
2014 // Tests navigation via link click within a subframe. A new navigation entry
2015 // should be created.
2016 TEST_F(NavigationControllerTest
, NewSubframe
) {
2017 NavigationControllerImpl
& controller
= controller_impl();
2018 TestNotificationTracker notifications
;
2019 RegisterForAllNavNotifications(¬ifications
, &controller
);
2021 const GURL
url1("http://foo1");
2022 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url1
);
2023 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2024 navigation_entry_committed_counter_
= 0;
2026 // Prereq: add a subframe with an initial auto-subframe navigation.
2027 main_test_rfh()->OnCreateChildFrame(
2028 MSG_ROUTING_NONE
, blink::WebTreeScopeType::Document
, std::string(),
2029 blink::WebSandboxFlags::None
);
2030 RenderFrameHostImpl
* subframe
=
2031 contents()->GetFrameTree()->root()->child_at(0)->current_frame_host();
2032 const GURL
subframe_url("http://foo1/subframe");
2034 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2036 params
.nav_entry_id
= 0;
2037 params
.did_create_new_entry
= false;
2038 params
.url
= subframe_url
;
2039 params
.transition
= ui::PAGE_TRANSITION_AUTO_SUBFRAME
;
2040 params
.should_update_history
= false;
2041 params
.gesture
= NavigationGestureUser
;
2042 params
.is_post
= false;
2043 params
.page_state
= PageState::CreateFromURL(subframe_url
);
2045 // Navigating should do nothing.
2046 LoadCommittedDetails details
;
2047 EXPECT_FALSE(controller
.RendererDidNavigate(subframe
, params
, &details
));
2048 EXPECT_EQ(0U, notifications
.size());
2051 // Now do a new navigation in the frame.
2052 const GURL
url2("http://foo2");
2053 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2055 params
.nav_entry_id
= 0;
2056 params
.did_create_new_entry
= true;
2058 params
.transition
= ui::PAGE_TRANSITION_MANUAL_SUBFRAME
;
2059 params
.should_update_history
= false;
2060 params
.gesture
= NavigationGestureUser
;
2061 params
.is_post
= false;
2062 params
.page_state
= PageState::CreateFromURL(url2
);
2064 LoadCommittedDetails details
;
2065 EXPECT_TRUE(controller
.RendererDidNavigate(subframe
, params
, &details
));
2066 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2067 navigation_entry_committed_counter_
= 0;
2068 EXPECT_EQ(url1
, details
.previous_url
);
2069 EXPECT_FALSE(details
.is_in_page
);
2070 EXPECT_FALSE(details
.is_main_frame
);
2072 // The new entry should be appended.
2073 NavigationEntryImpl
* entry
= controller
.GetLastCommittedEntry();
2074 EXPECT_EQ(2, controller
.GetEntryCount());
2075 EXPECT_EQ(entry
, details
.entry
);
2077 // New entry should refer to the new page, but the old URL (entries only
2078 // reflect the toplevel URL).
2079 EXPECT_EQ(url1
, entry
->GetURL());
2080 EXPECT_EQ(params
.page_id
, entry
->GetPageID());
2082 // Verify subframe entries if they're enabled (e.g. in --site-per-process).
2083 if (SiteIsolationPolicy::UseSubframeNavigationEntries()) {
2084 // The entry should have a subframe FrameNavigationEntry.
2085 ASSERT_EQ(1U, entry
->root_node()->children
.size());
2086 EXPECT_EQ(url2
, entry
->root_node()->children
[0]->frame_entry
->url());
2088 // There are no subframe FrameNavigationEntries by default.
2089 EXPECT_EQ(0U, entry
->root_node()->children
.size());
2093 // Auto subframes are ones the page loads automatically like ads. They should
2094 // not create new navigation entries.
2095 // TODO(creis): Test updating entries for history auto subframe navigations.
2096 TEST_F(NavigationControllerTest
, AutoSubframe
) {
2097 NavigationControllerImpl
& controller
= controller_impl();
2098 TestNotificationTracker notifications
;
2099 RegisterForAllNavNotifications(¬ifications
, &controller
);
2101 const GURL
url1("http://foo/1");
2102 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url1
);
2103 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2104 navigation_entry_committed_counter_
= 0;
2106 // Add a subframe and navigate it.
2107 main_test_rfh()->OnCreateChildFrame(
2108 MSG_ROUTING_NONE
, blink::WebTreeScopeType::Document
, std::string(),
2109 blink::WebSandboxFlags::None
);
2110 RenderFrameHostImpl
* subframe
=
2111 contents()->GetFrameTree()->root()->child_at(0)->current_frame_host();
2112 const GURL
url2("http://foo/2");
2114 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2116 params
.nav_entry_id
= 0;
2117 params
.did_create_new_entry
= false;
2119 params
.transition
= ui::PAGE_TRANSITION_AUTO_SUBFRAME
;
2120 params
.should_update_history
= false;
2121 params
.gesture
= NavigationGestureUser
;
2122 params
.is_post
= false;
2123 params
.page_state
= PageState::CreateFromURL(url2
);
2125 // Navigating should do nothing.
2126 LoadCommittedDetails details
;
2127 EXPECT_FALSE(controller
.RendererDidNavigate(subframe
, params
, &details
));
2128 EXPECT_EQ(0U, notifications
.size());
2131 // There should still be only one entry.
2132 EXPECT_EQ(1, controller
.GetEntryCount());
2133 NavigationEntryImpl
* entry
= controller
.GetLastCommittedEntry();
2134 EXPECT_EQ(url1
, entry
->GetURL());
2135 EXPECT_EQ(1, entry
->GetPageID());
2136 FrameNavigationEntry
* root_entry
= entry
->root_node()->frame_entry
.get();
2137 EXPECT_EQ(url1
, root_entry
->url());
2139 // Verify subframe entries if they're enabled (e.g. in --site-per-process).
2140 if (SiteIsolationPolicy::UseSubframeNavigationEntries()) {
2141 // The entry should now have a subframe FrameNavigationEntry.
2142 ASSERT_EQ(1U, entry
->root_node()->children
.size());
2143 FrameNavigationEntry
* frame_entry
=
2144 entry
->root_node()->children
[0]->frame_entry
.get();
2145 EXPECT_EQ(url2
, frame_entry
->url());
2147 // There are no subframe FrameNavigationEntries by default.
2148 EXPECT_EQ(0U, entry
->root_node()->children
.size());
2151 // Add a second subframe and navigate.
2152 main_test_rfh()->OnCreateChildFrame(
2153 MSG_ROUTING_NONE
, blink::WebTreeScopeType::Document
, std::string(),
2154 blink::WebSandboxFlags::None
);
2155 RenderFrameHostImpl
* subframe2
=
2156 contents()->GetFrameTree()->root()->child_at(1)->current_frame_host();
2157 const GURL
url3("http://foo/3");
2159 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2161 params
.nav_entry_id
= 0;
2162 params
.did_create_new_entry
= false;
2164 params
.transition
= ui::PAGE_TRANSITION_AUTO_SUBFRAME
;
2165 params
.should_update_history
= false;
2166 params
.gesture
= NavigationGestureUser
;
2167 params
.is_post
= false;
2168 params
.page_state
= PageState::CreateFromURL(url3
);
2170 // Navigating should do nothing.
2171 LoadCommittedDetails details
;
2172 EXPECT_FALSE(controller
.RendererDidNavigate(subframe2
, params
, &details
));
2173 EXPECT_EQ(0U, notifications
.size());
2176 // There should still be only one entry, mostly unchanged.
2177 EXPECT_EQ(1, controller
.GetEntryCount());
2178 EXPECT_EQ(entry
, controller
.GetLastCommittedEntry());
2179 EXPECT_EQ(url1
, entry
->GetURL());
2180 EXPECT_EQ(1, entry
->GetPageID());
2181 EXPECT_EQ(root_entry
, entry
->root_node()->frame_entry
.get());
2182 EXPECT_EQ(url1
, root_entry
->url());
2184 // Verify subframe entries if they're enabled (e.g. in --site-per-process).
2185 if (SiteIsolationPolicy::UseSubframeNavigationEntries()) {
2186 // The entry should now have 2 subframe FrameNavigationEntries.
2187 ASSERT_EQ(2U, entry
->root_node()->children
.size());
2188 FrameNavigationEntry
* new_frame_entry
=
2189 entry
->root_node()->children
[1]->frame_entry
.get();
2190 EXPECT_EQ(url3
, new_frame_entry
->url());
2192 // There are no subframe FrameNavigationEntries by default.
2193 EXPECT_EQ(0U, entry
->root_node()->children
.size());
2196 // Add a nested subframe and navigate.
2197 subframe
->OnCreateChildFrame(MSG_ROUTING_NONE
,
2198 blink::WebTreeScopeType::Document
, std::string(),
2199 blink::WebSandboxFlags::None
);
2200 RenderFrameHostImpl
* subframe3
= contents()
2205 ->current_frame_host();
2206 const GURL
url4("http://foo/4");
2208 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2210 params
.nav_entry_id
= 0;
2211 params
.did_create_new_entry
= false;
2213 params
.transition
= ui::PAGE_TRANSITION_AUTO_SUBFRAME
;
2214 params
.should_update_history
= false;
2215 params
.gesture
= NavigationGestureUser
;
2216 params
.is_post
= false;
2217 params
.page_state
= PageState::CreateFromURL(url4
);
2219 // Navigating should do nothing.
2220 LoadCommittedDetails details
;
2221 EXPECT_FALSE(controller
.RendererDidNavigate(subframe3
, params
, &details
));
2222 EXPECT_EQ(0U, notifications
.size());
2225 // There should still be only one entry, mostly unchanged.
2226 EXPECT_EQ(1, controller
.GetEntryCount());
2227 EXPECT_EQ(entry
, controller
.GetLastCommittedEntry());
2228 EXPECT_EQ(url1
, entry
->GetURL());
2229 EXPECT_EQ(1, entry
->GetPageID());
2230 EXPECT_EQ(root_entry
, entry
->root_node()->frame_entry
.get());
2231 EXPECT_EQ(url1
, root_entry
->url());
2233 // Verify subframe entries if they're enabled (e.g. in --site-per-process).
2234 if (SiteIsolationPolicy::UseSubframeNavigationEntries()) {
2235 // The entry should now have a nested FrameNavigationEntry.
2236 EXPECT_EQ(2U, entry
->root_node()->children
.size());
2237 ASSERT_EQ(1U, entry
->root_node()->children
[0]->children
.size());
2238 FrameNavigationEntry
* new_frame_entry
=
2239 entry
->root_node()->children
[0]->children
[0]->frame_entry
.get();
2240 EXPECT_EQ(url4
, new_frame_entry
->url());
2242 // There are no subframe FrameNavigationEntries by default.
2243 EXPECT_EQ(0U, entry
->root_node()->children
.size());
2247 // Tests navigation and then going back to a subframe navigation.
2248 TEST_F(NavigationControllerTest
, BackSubframe
) {
2249 NavigationControllerImpl
& controller
= controller_impl();
2250 TestNotificationTracker notifications
;
2251 RegisterForAllNavNotifications(¬ifications
, &controller
);
2254 const GURL
url1("http://foo1");
2255 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url1
);
2256 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2257 NavigationEntry
* entry1
= controller
.GetLastCommittedEntry();
2258 navigation_entry_committed_counter_
= 0;
2260 // Prereq: add a subframe with an initial auto-subframe navigation.
2261 main_test_rfh()->OnCreateChildFrame(
2262 MSG_ROUTING_NONE
, blink::WebTreeScopeType::Document
, std::string(),
2263 blink::WebSandboxFlags::None
);
2264 RenderFrameHostImpl
* subframe
=
2265 contents()->GetFrameTree()->root()->child_at(0)->current_frame_host();
2266 const GURL
subframe_url("http://foo1/subframe");
2268 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2270 params
.nav_entry_id
= 0;
2271 params
.did_create_new_entry
= false;
2272 params
.url
= subframe_url
;
2273 params
.transition
= ui::PAGE_TRANSITION_AUTO_SUBFRAME
;
2274 params
.should_update_history
= false;
2275 params
.gesture
= NavigationGestureUser
;
2276 params
.is_post
= false;
2277 params
.page_state
= PageState::CreateFromURL(subframe_url
);
2279 // Navigating should do nothing.
2280 LoadCommittedDetails details
;
2281 EXPECT_FALSE(controller
.RendererDidNavigate(subframe
, params
, &details
));
2282 EXPECT_EQ(0U, notifications
.size());
2285 // First manual subframe navigation.
2286 const GURL
url2("http://foo2");
2287 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2289 params
.nav_entry_id
= 0;
2290 params
.did_create_new_entry
= true;
2292 params
.transition
= ui::PAGE_TRANSITION_MANUAL_SUBFRAME
;
2293 params
.should_update_history
= false;
2294 params
.gesture
= NavigationGestureUser
;
2295 params
.is_post
= false;
2296 params
.page_state
= PageState::CreateFromURL(url2
);
2298 // This should generate a new entry.
2299 LoadCommittedDetails details
;
2300 EXPECT_TRUE(controller
.RendererDidNavigate(subframe
, params
, &details
));
2301 NavigationEntryImpl
* entry2
= controller
.GetLastCommittedEntry();
2302 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2303 navigation_entry_committed_counter_
= 0;
2304 EXPECT_EQ(2, controller
.GetEntryCount());
2306 // Verify subframe entries if they're enabled (e.g. in --site-per-process).
2307 if (SiteIsolationPolicy::UseSubframeNavigationEntries()) {
2308 // The entry should have a subframe FrameNavigationEntry.
2309 ASSERT_EQ(1U, entry2
->root_node()->children
.size());
2310 EXPECT_EQ(url2
, entry2
->root_node()->children
[0]->frame_entry
->url());
2312 // There are no subframe FrameNavigationEntries by default.
2313 EXPECT_EQ(0U, entry2
->root_node()->children
.size());
2316 // Second manual subframe navigation should also make a new entry.
2317 const GURL
url3("http://foo3");
2319 params
.nav_entry_id
= 0;
2320 params
.did_create_new_entry
= true;
2322 params
.transition
= ui::PAGE_TRANSITION_MANUAL_SUBFRAME
;
2323 EXPECT_TRUE(controller
.RendererDidNavigate(subframe
, params
, &details
));
2324 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2325 navigation_entry_committed_counter_
= 0;
2326 NavigationEntryImpl
* entry3
= controller
.GetLastCommittedEntry();
2327 EXPECT_EQ(3, controller
.GetEntryCount());
2328 EXPECT_EQ(2, controller
.GetCurrentEntryIndex());
2330 // Verify subframe entries if they're enabled (e.g. in --site-per-process).
2331 if (SiteIsolationPolicy::UseSubframeNavigationEntries()) {
2332 // The entry should have a subframe FrameNavigationEntry.
2333 ASSERT_EQ(1U, entry3
->root_node()->children
.size());
2334 EXPECT_EQ(url3
, entry3
->root_node()->children
[0]->frame_entry
->url());
2336 // There are no subframe FrameNavigationEntries by default.
2337 EXPECT_EQ(0U, entry3
->root_node()->children
.size());
2341 controller
.GoBack();
2343 params
.nav_entry_id
= entry2
->GetUniqueID();
2344 params
.did_create_new_entry
= false;
2346 params
.transition
= ui::PAGE_TRANSITION_AUTO_SUBFRAME
;
2347 EXPECT_TRUE(controller
.RendererDidNavigate(subframe
, params
, &details
));
2348 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2349 navigation_entry_committed_counter_
= 0;
2350 EXPECT_EQ(entry2
, controller
.GetLastCommittedEntry());
2351 EXPECT_EQ(3, controller
.GetEntryCount());
2352 EXPECT_EQ(1, controller
.GetCurrentEntryIndex());
2353 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
2354 EXPECT_FALSE(controller
.GetPendingEntry());
2356 // Go back one more.
2357 controller
.GoBack();
2359 params
.nav_entry_id
= entry1
->GetUniqueID();
2360 params
.did_create_new_entry
= false;
2362 params
.transition
= ui::PAGE_TRANSITION_AUTO_SUBFRAME
;
2363 EXPECT_TRUE(controller
.RendererDidNavigate(subframe
, params
, &details
));
2364 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2365 navigation_entry_committed_counter_
= 0;
2366 EXPECT_EQ(entry1
, controller
.GetLastCommittedEntry());
2367 EXPECT_EQ(3, controller
.GetEntryCount());
2368 EXPECT_EQ(0, controller
.GetCurrentEntryIndex());
2369 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
2370 EXPECT_FALSE(controller
.GetPendingEntry());
2373 TEST_F(NavigationControllerTest
, LinkClick
) {
2374 NavigationControllerImpl
& controller
= controller_impl();
2375 TestNotificationTracker notifications
;
2376 RegisterForAllNavNotifications(¬ifications
, &controller
);
2378 const GURL
url1("http://foo1");
2379 const GURL
url2("http://foo2");
2381 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url1
);
2382 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2383 navigation_entry_committed_counter_
= 0;
2385 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url2
);
2386 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2387 navigation_entry_committed_counter_
= 0;
2389 // Should have produced a new session history entry.
2390 EXPECT_EQ(controller
.GetEntryCount(), 2);
2391 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 1);
2392 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
2393 EXPECT_TRUE(controller
.GetLastCommittedEntry());
2394 EXPECT_FALSE(controller
.GetPendingEntry());
2395 EXPECT_TRUE(controller
.CanGoBack());
2396 EXPECT_FALSE(controller
.CanGoForward());
2399 TEST_F(NavigationControllerTest
, InPage
) {
2400 NavigationControllerImpl
& controller
= controller_impl();
2401 TestNotificationTracker notifications
;
2402 RegisterForAllNavNotifications(¬ifications
, &controller
);
2405 const GURL
url1("http://foo");
2406 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url1
);
2407 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2408 navigation_entry_committed_counter_
= 0;
2410 // Ensure main page navigation to same url respects the was_within_same_page
2411 // hint provided in the params.
2412 FrameHostMsg_DidCommitProvisionalLoad_Params self_params
;
2413 self_params
.page_id
= 0;
2414 self_params
.nav_entry_id
= 0;
2415 self_params
.did_create_new_entry
= false;
2416 self_params
.url
= url1
;
2417 self_params
.transition
= ui::PAGE_TRANSITION_LINK
;
2418 self_params
.should_update_history
= false;
2419 self_params
.gesture
= NavigationGestureUser
;
2420 self_params
.is_post
= false;
2421 self_params
.page_state
= PageState::CreateFromURL(url1
);
2422 self_params
.was_within_same_page
= true;
2424 LoadCommittedDetails details
;
2425 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), self_params
,
2427 NavigationEntry
* entry1
= controller
.GetLastCommittedEntry();
2428 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2429 navigation_entry_committed_counter_
= 0;
2430 EXPECT_TRUE(details
.is_in_page
);
2431 EXPECT_TRUE(details
.did_replace_entry
);
2432 EXPECT_EQ(1, controller
.GetEntryCount());
2434 // Fragment navigation to a new page_id.
2435 const GURL
url2("http://foo#a");
2436 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2438 params
.nav_entry_id
= 0;
2439 params
.did_create_new_entry
= true;
2441 params
.transition
= ui::PAGE_TRANSITION_LINK
;
2442 params
.should_update_history
= false;
2443 params
.gesture
= NavigationGestureUser
;
2444 params
.is_post
= false;
2445 params
.page_state
= PageState::CreateFromURL(url2
);
2446 params
.was_within_same_page
= true;
2448 // This should generate a new entry.
2449 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
2451 NavigationEntry
* entry2
= controller
.GetLastCommittedEntry();
2452 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2453 navigation_entry_committed_counter_
= 0;
2454 EXPECT_TRUE(details
.is_in_page
);
2455 EXPECT_FALSE(details
.did_replace_entry
);
2456 EXPECT_EQ(2, controller
.GetEntryCount());
2459 FrameHostMsg_DidCommitProvisionalLoad_Params
back_params(params
);
2460 controller
.GoBack();
2461 back_params
.url
= url1
;
2462 back_params
.page_id
= 0;
2463 back_params
.nav_entry_id
= entry1
->GetUniqueID();
2464 back_params
.did_create_new_entry
= false;
2465 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), back_params
,
2467 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2468 navigation_entry_committed_counter_
= 0;
2469 EXPECT_TRUE(details
.is_in_page
);
2470 EXPECT_EQ(2, controller
.GetEntryCount());
2471 EXPECT_EQ(0, controller
.GetCurrentEntryIndex());
2472 EXPECT_EQ(back_params
.url
, controller
.GetVisibleEntry()->GetURL());
2475 FrameHostMsg_DidCommitProvisionalLoad_Params
forward_params(params
);
2476 controller
.GoForward();
2477 forward_params
.url
= url2
;
2478 forward_params
.page_id
= 1;
2479 forward_params
.nav_entry_id
= entry2
->GetUniqueID();
2480 forward_params
.did_create_new_entry
= false;
2481 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), forward_params
,
2483 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2484 navigation_entry_committed_counter_
= 0;
2485 EXPECT_TRUE(details
.is_in_page
);
2486 EXPECT_EQ(2, controller
.GetEntryCount());
2487 EXPECT_EQ(1, controller
.GetCurrentEntryIndex());
2488 EXPECT_EQ(forward_params
.url
,
2489 controller
.GetVisibleEntry()->GetURL());
2491 // Now go back and forward again. This is to work around a bug where we would
2492 // compare the incoming URL with the last committed entry rather than the
2493 // one identified by an existing page ID. This would result in the second URL
2494 // losing the reference fragment when you navigate away from it and then back.
2495 controller
.GoBack();
2496 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), back_params
,
2498 controller
.GoForward();
2499 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), forward_params
,
2501 EXPECT_EQ(forward_params
.url
,
2502 controller
.GetVisibleEntry()->GetURL());
2504 // Finally, navigate to an unrelated URL to make sure in_page is not sticky.
2505 const GURL
url3("http://bar");
2507 params
.nav_entry_id
= 0;
2508 params
.did_create_new_entry
= true;
2510 navigation_entry_committed_counter_
= 0;
2511 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
2513 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2514 navigation_entry_committed_counter_
= 0;
2515 EXPECT_FALSE(details
.is_in_page
);
2516 EXPECT_EQ(3, controller
.GetEntryCount());
2517 EXPECT_EQ(2, controller
.GetCurrentEntryIndex());
2520 TEST_F(NavigationControllerTest
, InPage_Replace
) {
2521 NavigationControllerImpl
& controller
= controller_impl();
2522 TestNotificationTracker notifications
;
2523 RegisterForAllNavNotifications(¬ifications
, &controller
);
2526 const GURL
url1("http://foo");
2527 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url1
);
2528 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2529 navigation_entry_committed_counter_
= 0;
2531 // First navigation.
2532 const GURL
url2("http://foo#a");
2533 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2534 params
.page_id
= 0; // Same page_id
2535 params
.nav_entry_id
= 0;
2536 params
.did_create_new_entry
= false;
2538 params
.transition
= ui::PAGE_TRANSITION_LINK
;
2539 params
.should_update_history
= false;
2540 params
.gesture
= NavigationGestureUser
;
2541 params
.is_post
= false;
2542 params
.page_state
= PageState::CreateFromURL(url2
);
2543 params
.was_within_same_page
= true;
2545 // This should NOT generate a new entry, nor prune the list.
2546 LoadCommittedDetails details
;
2547 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
2549 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2550 navigation_entry_committed_counter_
= 0;
2551 EXPECT_TRUE(details
.is_in_page
);
2552 EXPECT_TRUE(details
.did_replace_entry
);
2553 EXPECT_EQ(1, controller
.GetEntryCount());
2556 // Tests for http://crbug.com/40395
2559 // window.location.replace("#a");
2560 // window.location='http://foo3/';
2562 TEST_F(NavigationControllerTest
, ClientRedirectAfterInPageNavigation
) {
2563 NavigationControllerImpl
& controller
= controller_impl();
2564 TestNotificationTracker notifications
;
2565 RegisterForAllNavNotifications(¬ifications
, &controller
);
2567 // Load an initial page.
2569 const GURL
url("http://foo/");
2570 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url
);
2571 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2572 navigation_entry_committed_counter_
= 0;
2575 // Navigate to a new page.
2577 const GURL
url("http://foo2/");
2578 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url
);
2579 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2580 navigation_entry_committed_counter_
= 0;
2583 // Navigate within the page.
2585 const GURL
url("http://foo2/#a");
2586 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2587 params
.page_id
= 1; // Same page_id
2588 params
.nav_entry_id
= 0;
2589 params
.did_create_new_entry
= false;
2591 params
.transition
= ui::PAGE_TRANSITION_LINK
;
2592 params
.redirects
.push_back(url
);
2593 params
.should_update_history
= true;
2594 params
.gesture
= NavigationGestureUnknown
;
2595 params
.is_post
= false;
2596 params
.page_state
= PageState::CreateFromURL(url
);
2597 params
.was_within_same_page
= true;
2599 // This should NOT generate a new entry, nor prune the list.
2600 LoadCommittedDetails details
;
2601 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
2603 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2604 navigation_entry_committed_counter_
= 0;
2605 EXPECT_TRUE(details
.is_in_page
);
2606 EXPECT_TRUE(details
.did_replace_entry
);
2607 EXPECT_EQ(2, controller
.GetEntryCount());
2610 // Perform a client redirect to a new page.
2612 const GURL
url("http://foo3/");
2613 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2614 params
.page_id
= 2; // New page_id
2615 params
.nav_entry_id
= 0;
2616 params
.did_create_new_entry
= true;
2618 params
.transition
= ui::PAGE_TRANSITION_CLIENT_REDIRECT
;
2619 params
.redirects
.push_back(GURL("http://foo2/#a"));
2620 params
.redirects
.push_back(url
);
2621 params
.should_update_history
= true;
2622 params
.gesture
= NavigationGestureUnknown
;
2623 params
.is_post
= false;
2624 params
.page_state
= PageState::CreateFromURL(url
);
2626 // This SHOULD generate a new entry.
2627 LoadCommittedDetails details
;
2628 EXPECT_TRUE(controller
.RendererDidNavigate(main_test_rfh(), params
,
2630 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2631 navigation_entry_committed_counter_
= 0;
2632 EXPECT_FALSE(details
.is_in_page
);
2633 EXPECT_EQ(3, controller
.GetEntryCount());
2636 // Verify that BACK brings us back to http://foo2/.
2638 const GURL
url("http://foo2/");
2639 controller
.GoBack();
2640 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2641 main_test_rfh()->PrepareForCommit();
2642 main_test_rfh()->SendNavigate(1, entry_id
, false, url
);
2643 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
2644 navigation_entry_committed_counter_
= 0;
2645 EXPECT_EQ(url
, controller
.GetVisibleEntry()->GetURL());
2649 TEST_F(NavigationControllerTest
, PushStateWithoutPreviousEntry
)
2651 ASSERT_FALSE(controller_impl().GetLastCommittedEntry());
2652 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2653 GURL
url("http://foo");
2655 params
.nav_entry_id
= 0;
2656 params
.did_create_new_entry
= true;
2658 params
.page_state
= PageState::CreateFromURL(url
);
2659 params
.was_within_same_page
= true;
2660 main_test_rfh()->SendRendererInitiatedNavigationRequest(url
, false);
2661 main_test_rfh()->PrepareForCommit();
2662 contents()->GetMainFrame()->SendNavigateWithParams(¶ms
);
2663 // We pass if we don't crash.
2666 // NotificationObserver implementation used in verifying we've received the
2667 // NOTIFICATION_NAV_LIST_PRUNED method.
2668 class PrunedListener
: public NotificationObserver
{
2670 explicit PrunedListener(NavigationControllerImpl
* controller
)
2671 : notification_count_(0) {
2672 registrar_
.Add(this, NOTIFICATION_NAV_LIST_PRUNED
,
2673 Source
<NavigationController
>(controller
));
2676 void Observe(int type
,
2677 const NotificationSource
& source
,
2678 const NotificationDetails
& details
) override
{
2679 if (type
== NOTIFICATION_NAV_LIST_PRUNED
) {
2680 notification_count_
++;
2681 details_
= *(Details
<PrunedDetails
>(details
).ptr());
2685 // Number of times NAV_LIST_PRUNED has been observed.
2686 int notification_count_
;
2688 // Details from the last NAV_LIST_PRUNED.
2689 PrunedDetails details_
;
2692 NotificationRegistrar registrar_
;
2694 DISALLOW_COPY_AND_ASSIGN(PrunedListener
);
2697 // Tests that we limit the number of navigation entries created correctly.
2698 TEST_F(NavigationControllerTest
, EnforceMaxNavigationCount
) {
2699 NavigationControllerImpl
& controller
= controller_impl();
2700 size_t original_count
= NavigationControllerImpl::max_entry_count();
2701 const int kMaxEntryCount
= 5;
2703 NavigationControllerImpl::set_max_entry_count_for_testing(kMaxEntryCount
);
2706 // Load up to the max count, all entries should be there.
2707 for (url_index
= 0; url_index
< kMaxEntryCount
; url_index
++) {
2708 GURL
url(base::StringPrintf("http://www.a.com/%d", url_index
));
2710 url
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2711 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2712 main_test_rfh()->PrepareForCommit();
2713 main_test_rfh()->SendNavigate(url_index
, entry_id
, true, url
);
2716 EXPECT_EQ(controller
.GetEntryCount(), kMaxEntryCount
);
2718 // Created a PrunedListener to observe prune notifications.
2719 PrunedListener
listener(&controller
);
2721 // Navigate some more.
2722 GURL
url(base::StringPrintf("http://www.a.com/%d", url_index
));
2724 url
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2725 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2726 main_test_rfh()->PrepareForCommit();
2727 main_test_rfh()->SendNavigate(url_index
, entry_id
, true, url
);
2730 // We should have got a pruned navigation.
2731 EXPECT_EQ(1, listener
.notification_count_
);
2732 EXPECT_TRUE(listener
.details_
.from_front
);
2733 EXPECT_EQ(1, listener
.details_
.count
);
2735 // We expect http://www.a.com/0 to be gone.
2736 EXPECT_EQ(controller
.GetEntryCount(), kMaxEntryCount
);
2737 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(),
2738 GURL("http://www.a.com/1"));
2740 // More navigations.
2741 for (int i
= 0; i
< 3; i
++) {
2742 url
= GURL(base::StringPrintf("http://www.a.com/%d", url_index
));
2744 url
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2745 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2746 main_test_rfh()->PrepareForCommit();
2747 main_test_rfh()->SendNavigate(url_index
, entry_id
, true, url
);
2750 EXPECT_EQ(controller
.GetEntryCount(), kMaxEntryCount
);
2751 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(),
2752 GURL("http://www.a.com/4"));
2754 NavigationControllerImpl::set_max_entry_count_for_testing(original_count
);
2757 // Tests that we can do a restore and navigate to the restored entries and
2758 // everything is updated properly. This can be tricky since there is no
2759 // SiteInstance for the entries created initially.
2760 TEST_F(NavigationControllerTest
, RestoreNavigate
) {
2761 // Create a NavigationController with a restored set of tabs.
2762 GURL
url("http://foo");
2763 ScopedVector
<NavigationEntry
> entries
;
2764 scoped_ptr
<NavigationEntry
> entry
=
2765 NavigationControllerImpl::CreateNavigationEntry(
2766 url
, Referrer(), ui::PAGE_TRANSITION_RELOAD
, false, std::string(),
2768 entry
->SetPageID(0);
2769 entry
->SetTitle(base::ASCIIToUTF16("Title"));
2770 entry
->SetPageState(PageState::CreateFromEncodedData("state"));
2771 const base::Time timestamp
= base::Time::Now();
2772 entry
->SetTimestamp(timestamp
);
2773 entries
.push_back(entry
.Pass());
2774 scoped_ptr
<WebContentsImpl
> our_contents(static_cast<WebContentsImpl
*>(
2775 WebContents::Create(WebContents::CreateParams(browser_context()))));
2776 NavigationControllerImpl
& our_controller
= our_contents
->GetController();
2777 our_controller
.Restore(
2779 NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY
,
2781 ASSERT_EQ(0u, entries
.size());
2783 // Before navigating to the restored entry, it should have a restore_type
2784 // and no SiteInstance.
2785 ASSERT_EQ(1, our_controller
.GetEntryCount());
2786 EXPECT_EQ(NavigationEntryImpl::RESTORE_LAST_SESSION_EXITED_CLEANLY
,
2787 our_controller
.GetEntryAtIndex(0)->restore_type());
2788 EXPECT_FALSE(our_controller
.GetEntryAtIndex(0)->site_instance());
2790 // After navigating, we should have one entry, and it should be "pending".
2791 our_controller
.GoToIndex(0);
2792 EXPECT_EQ(1, our_controller
.GetEntryCount());
2793 EXPECT_EQ(our_controller
.GetEntryAtIndex(0),
2794 our_controller
.GetPendingEntry());
2795 EXPECT_EQ(0, our_controller
.GetEntryAtIndex(0)->GetPageID());
2797 // Timestamp should remain the same before the navigation finishes.
2798 EXPECT_EQ(timestamp
, our_controller
.GetEntryAtIndex(0)->GetTimestamp());
2800 // Say we navigated to that entry.
2801 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2803 params
.nav_entry_id
= our_controller
.GetPendingEntry()->GetUniqueID();
2804 params
.did_create_new_entry
= false;
2806 params
.transition
= ui::PAGE_TRANSITION_LINK
;
2807 params
.should_update_history
= false;
2808 params
.gesture
= NavigationGestureUser
;
2809 params
.is_post
= false;
2810 params
.page_state
= PageState::CreateFromURL(url
);
2811 LoadCommittedDetails details
;
2812 our_controller
.RendererDidNavigate(our_contents
->GetMainFrame(), params
,
2815 // There should be no longer any pending entry and one committed one. This
2816 // means that we were able to locate the entry, assign its site instance, and
2817 // commit it properly.
2818 EXPECT_EQ(1, our_controller
.GetEntryCount());
2819 EXPECT_EQ(0, our_controller
.GetLastCommittedEntryIndex());
2820 EXPECT_FALSE(our_controller
.GetPendingEntry());
2823 our_controller
.GetLastCommittedEntry()->site_instance()->GetSiteURL());
2824 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE
,
2825 our_controller
.GetEntryAtIndex(0)->restore_type());
2827 // Timestamp should have been updated.
2828 EXPECT_GE(our_controller
.GetEntryAtIndex(0)->GetTimestamp(), timestamp
);
2831 // Tests that we can still navigate to a restored entry after a different
2832 // navigation fails and clears the pending entry. http://crbug.com/90085
2833 TEST_F(NavigationControllerTest
, RestoreNavigateAfterFailure
) {
2834 // Create a NavigationController with a restored set of tabs.
2835 GURL
url("http://foo");
2836 ScopedVector
<NavigationEntry
> entries
;
2837 scoped_ptr
<NavigationEntry
> new_entry
=
2838 NavigationControllerImpl::CreateNavigationEntry(
2839 url
, Referrer(), ui::PAGE_TRANSITION_RELOAD
, false, std::string(),
2841 new_entry
->SetPageID(0);
2842 new_entry
->SetTitle(base::ASCIIToUTF16("Title"));
2843 new_entry
->SetPageState(PageState::CreateFromEncodedData("state"));
2844 entries
.push_back(new_entry
.Pass());
2845 scoped_ptr
<WebContentsImpl
> our_contents(static_cast<WebContentsImpl
*>(
2846 WebContents::Create(WebContents::CreateParams(browser_context()))));
2847 NavigationControllerImpl
& our_controller
= our_contents
->GetController();
2848 our_controller
.Restore(
2849 0, NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY
, &entries
);
2850 ASSERT_EQ(0u, entries
.size());
2852 // Ensure the RenderFrame is initialized before simulating events coming from
2854 main_test_rfh()->InitializeRenderFrameIfNeeded();
2856 // Before navigating to the restored entry, it should have a restore_type
2857 // and no SiteInstance.
2858 NavigationEntry
* entry
= our_controller
.GetEntryAtIndex(0);
2859 EXPECT_EQ(NavigationEntryImpl::RESTORE_LAST_SESSION_EXITED_CLEANLY
,
2860 our_controller
.GetEntryAtIndex(0)->restore_type());
2861 EXPECT_FALSE(our_controller
.GetEntryAtIndex(0)->site_instance());
2863 // After navigating, we should have one entry, and it should be "pending".
2864 our_controller
.GoToIndex(0);
2865 EXPECT_EQ(1, our_controller
.GetEntryCount());
2866 EXPECT_EQ(our_controller
.GetEntryAtIndex(0),
2867 our_controller
.GetPendingEntry());
2868 EXPECT_EQ(0, our_controller
.GetEntryAtIndex(0)->GetPageID());
2870 // This pending navigation may have caused a different navigation to fail,
2871 // which causes the pending entry to be cleared.
2872 FrameHostMsg_DidFailProvisionalLoadWithError_Params fail_load_params
;
2873 fail_load_params
.error_code
= net::ERR_ABORTED
;
2874 fail_load_params
.error_description
= base::string16();
2875 fail_load_params
.url
= url
;
2876 fail_load_params
.showing_repost_interstitial
= false;
2877 main_test_rfh()->InitializeRenderFrameIfNeeded();
2878 main_test_rfh()->OnMessageReceived(
2879 FrameHostMsg_DidFailProvisionalLoadWithError(0, // routing_id
2882 // Now the pending restored entry commits.
2883 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
2885 params
.nav_entry_id
= entry
->GetUniqueID();
2886 params
.did_create_new_entry
= false;
2888 params
.transition
= ui::PAGE_TRANSITION_LINK
;
2889 params
.should_update_history
= false;
2890 params
.gesture
= NavigationGestureUser
;
2891 params
.is_post
= false;
2892 params
.page_state
= PageState::CreateFromURL(url
);
2893 LoadCommittedDetails details
;
2894 our_controller
.RendererDidNavigate(our_contents
->GetMainFrame(), params
,
2897 // There should be no pending entry and one committed one.
2898 EXPECT_EQ(1, our_controller
.GetEntryCount());
2899 EXPECT_EQ(0, our_controller
.GetLastCommittedEntryIndex());
2900 EXPECT_FALSE(our_controller
.GetPendingEntry());
2903 our_controller
.GetLastCommittedEntry()->site_instance()->GetSiteURL());
2904 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE
,
2905 our_controller
.GetEntryAtIndex(0)->restore_type());
2908 // Make sure that the page type and stuff is correct after an interstitial.
2909 TEST_F(NavigationControllerTest
, Interstitial
) {
2910 NavigationControllerImpl
& controller
= controller_impl();
2911 // First navigate somewhere normal.
2912 const GURL
url1("http://foo");
2914 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2915 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2916 main_test_rfh()->PrepareForCommit();
2917 main_test_rfh()->SendNavigate(0, entry_id
, true, url1
);
2919 // Now navigate somewhere with an interstitial.
2920 const GURL
url2("http://bar");
2921 controller
.LoadURL(url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
,
2923 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2924 controller
.GetPendingEntry()->set_page_type(PAGE_TYPE_INTERSTITIAL
);
2926 // At this point the interstitial will be displayed and the load will still
2927 // be pending. If the user continues, the load will commit.
2928 main_test_rfh()->PrepareForCommit();
2929 main_test_rfh()->SendNavigate(1, entry_id
, true, url2
);
2931 // The page should be a normal page again.
2932 EXPECT_EQ(url2
, controller
.GetLastCommittedEntry()->GetURL());
2933 EXPECT_EQ(PAGE_TYPE_NORMAL
,
2934 controller
.GetLastCommittedEntry()->GetPageType());
2937 TEST_F(NavigationControllerTest
, RemoveEntry
) {
2938 NavigationControllerImpl
& controller
= controller_impl();
2939 const GURL
url1("http://foo/1");
2940 const GURL
url2("http://foo/2");
2941 const GURL
url3("http://foo/3");
2942 const GURL
url4("http://foo/4");
2943 const GURL
url5("http://foo/5");
2944 const GURL
pending_url("http://foo/pending");
2945 const GURL
default_url("http://foo/default");
2948 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2949 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2950 main_test_rfh()->PrepareForCommit();
2951 main_test_rfh()->SendNavigate(0, entry_id
, true, url1
);
2953 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2954 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2955 main_test_rfh()->PrepareForCommit();
2956 main_test_rfh()->SendNavigate(1, entry_id
, true, url2
);
2958 url3
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2959 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2960 main_test_rfh()->PrepareForCommit();
2961 main_test_rfh()->SendNavigate(2, entry_id
, true, url3
);
2963 url4
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2964 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2965 main_test_rfh()->PrepareForCommit();
2966 main_test_rfh()->SendNavigate(3, entry_id
, true, url4
);
2968 url5
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
2969 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2970 main_test_rfh()->PrepareForCommit();
2971 main_test_rfh()->SendNavigate(4, entry_id
, true, url5
);
2973 // Try to remove the last entry. Will fail because it is the current entry.
2974 EXPECT_FALSE(controller
.RemoveEntryAtIndex(controller
.GetEntryCount() - 1));
2975 EXPECT_EQ(5, controller
.GetEntryCount());
2976 EXPECT_EQ(4, controller
.GetLastCommittedEntryIndex());
2978 // Go back, but don't commit yet. Check that we can't delete the current
2979 // and pending entries.
2980 controller
.GoBack();
2981 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
2982 EXPECT_FALSE(controller
.RemoveEntryAtIndex(controller
.GetEntryCount() - 1));
2983 EXPECT_FALSE(controller
.RemoveEntryAtIndex(controller
.GetEntryCount() - 2));
2985 // Now commit and delete the last entry.
2986 main_test_rfh()->PrepareForCommit();
2987 main_test_rfh()->SendNavigate(3, entry_id
, false, url4
);
2988 EXPECT_TRUE(controller
.RemoveEntryAtIndex(controller
.GetEntryCount() - 1));
2989 EXPECT_EQ(4, controller
.GetEntryCount());
2990 EXPECT_EQ(3, controller
.GetLastCommittedEntryIndex());
2991 EXPECT_FALSE(controller
.GetPendingEntry());
2993 // Remove an entry which is not the last committed one.
2994 EXPECT_TRUE(controller
.RemoveEntryAtIndex(0));
2995 EXPECT_EQ(3, controller
.GetEntryCount());
2996 EXPECT_EQ(2, controller
.GetLastCommittedEntryIndex());
2997 EXPECT_FALSE(controller
.GetPendingEntry());
2999 // Remove the 2 remaining entries.
3000 controller
.RemoveEntryAtIndex(1);
3001 controller
.RemoveEntryAtIndex(0);
3003 // This should leave us with only the last committed entry.
3004 EXPECT_EQ(1, controller
.GetEntryCount());
3005 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
3008 TEST_F(NavigationControllerTest
, RemoveEntryWithPending
) {
3009 NavigationControllerImpl
& controller
= controller_impl();
3010 const GURL
url1("http://foo/1");
3011 const GURL
url2("http://foo/2");
3012 const GURL
url3("http://foo/3");
3013 const GURL
default_url("http://foo/default");
3016 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3017 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3018 main_test_rfh()->PrepareForCommit();
3019 main_test_rfh()->SendNavigate(0, entry_id
, true, url1
);
3021 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3022 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3023 main_test_rfh()->PrepareForCommit();
3024 main_test_rfh()->SendNavigate(1, entry_id
, true, url2
);
3026 url3
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3027 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3028 main_test_rfh()->PrepareForCommit();
3029 main_test_rfh()->SendNavigate(2, entry_id
, true, url3
);
3031 // Go back, but don't commit yet. Check that we can't delete the current
3032 // and pending entries.
3033 controller
.GoBack();
3034 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3035 EXPECT_FALSE(controller
.RemoveEntryAtIndex(2));
3036 EXPECT_FALSE(controller
.RemoveEntryAtIndex(1));
3038 // Remove the first entry, while there is a pending entry. This is expected
3039 // to discard the pending entry.
3040 EXPECT_TRUE(controller
.RemoveEntryAtIndex(0));
3041 EXPECT_FALSE(controller
.GetPendingEntry());
3042 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
3044 // We should update the last committed entry index.
3045 EXPECT_EQ(1, controller
.GetLastCommittedEntryIndex());
3047 // Now commit and ensure we land on the right entry.
3048 main_test_rfh()->PrepareForCommit();
3049 main_test_rfh()->SendNavigate(1, entry_id
, false, url2
);
3050 EXPECT_EQ(2, controller
.GetEntryCount());
3051 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
3052 EXPECT_FALSE(controller
.GetPendingEntry());
3055 // Tests the transient entry, making sure it goes away with all navigations.
3056 TEST_F(NavigationControllerTest
, TransientEntry
) {
3057 NavigationControllerImpl
& controller
= controller_impl();
3058 TestNotificationTracker notifications
;
3059 RegisterForAllNavNotifications(¬ifications
, &controller
);
3061 const GURL
url0("http://foo/0");
3062 const GURL
url1("http://foo/1");
3063 const GURL
url2("http://foo/2");
3064 const GURL
url3("http://foo/3");
3065 const GURL
url3_ref("http://foo/3#bar");
3066 const GURL
url4("http://foo/4");
3067 const GURL
transient_url("http://foo/transient");
3070 url0
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3071 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3072 main_test_rfh()->PrepareForCommit();
3073 main_test_rfh()->SendNavigate(0, entry_id
, true, url0
);
3075 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3076 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3077 main_test_rfh()->PrepareForCommit();
3078 main_test_rfh()->SendNavigate(1, entry_id
, true, url1
);
3080 notifications
.Reset();
3082 // Adding a transient with no pending entry.
3083 scoped_ptr
<NavigationEntry
> transient_entry(new NavigationEntryImpl
);
3084 transient_entry
->SetURL(transient_url
);
3085 controller
.SetTransientEntry(transient_entry
.Pass());
3087 // We should not have received any notifications.
3088 EXPECT_EQ(0U, notifications
.size());
3091 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3092 EXPECT_EQ(controller
.GetEntryCount(), 3);
3093 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 1);
3094 EXPECT_EQ(controller
.GetPendingEntryIndex(), -1);
3095 EXPECT_TRUE(controller
.GetLastCommittedEntry());
3096 EXPECT_FALSE(controller
.GetPendingEntry());
3097 EXPECT_TRUE(controller
.CanGoBack());
3098 EXPECT_FALSE(controller
.CanGoForward());
3099 EXPECT_EQ(contents()->GetMaxPageID(), 1);
3103 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3104 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3105 main_test_rfh()->PrepareForCommit();
3106 main_test_rfh()->SendNavigate(2, entry_id
, true, url2
);
3108 // We should have navigated, transient entry should be gone.
3109 EXPECT_EQ(url2
, controller
.GetVisibleEntry()->GetURL());
3110 EXPECT_EQ(controller
.GetEntryCount(), 3);
3112 // Add a transient again, then navigate with no pending entry this time.
3113 transient_entry
.reset(new NavigationEntryImpl
);
3114 transient_entry
->SetURL(transient_url
);
3115 controller
.SetTransientEntry(transient_entry
.Pass());
3116 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3117 main_test_rfh()->SendRendererInitiatedNavigationRequest(url3
, true);
3118 main_test_rfh()->PrepareForCommit();
3119 main_test_rfh()->SendNavigate(3, 0, true, url3
);
3120 // Transient entry should be gone.
3121 EXPECT_EQ(url3
, controller
.GetVisibleEntry()->GetURL());
3122 EXPECT_EQ(controller
.GetEntryCount(), 4);
3124 // Initiate a navigation, add a transient then commit navigation.
3126 url4
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3127 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3128 transient_entry
.reset(new NavigationEntryImpl
);
3129 transient_entry
->SetURL(transient_url
);
3130 controller
.SetTransientEntry(transient_entry
.Pass());
3131 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3132 main_test_rfh()->PrepareForCommit();
3133 main_test_rfh()->SendNavigate(4, entry_id
, true, url4
);
3134 EXPECT_EQ(url4
, controller
.GetVisibleEntry()->GetURL());
3135 EXPECT_EQ(controller
.GetEntryCount(), 5);
3137 // Add a transient and go back. This should simply remove the transient.
3138 transient_entry
.reset(new NavigationEntryImpl
);
3139 transient_entry
->SetURL(transient_url
);
3140 controller
.SetTransientEntry(transient_entry
.Pass());
3141 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3142 EXPECT_TRUE(controller
.CanGoBack());
3143 EXPECT_FALSE(controller
.CanGoForward());
3144 controller
.GoBack();
3145 // Transient entry should be gone.
3146 EXPECT_EQ(url4
, controller
.GetVisibleEntry()->GetURL());
3147 EXPECT_EQ(controller
.GetEntryCount(), 5);
3149 // Suppose the page requested a history navigation backward.
3150 controller
.GoToOffset(-1);
3151 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3152 main_test_rfh()->PrepareForCommit();
3153 main_test_rfh()->SendNavigate(3, entry_id
, false, url3
);
3155 // Add a transient and go to an entry before the current one.
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 controller
.GoToIndex(1);
3161 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3162 // The navigation should have been initiated, transient entry should be gone.
3163 EXPECT_FALSE(controller
.GetTransientEntry());
3164 EXPECT_EQ(url1
, controller
.GetPendingEntry()->GetURL());
3165 // Visible entry does not update for history navigations until commit.
3166 EXPECT_EQ(url3
, controller
.GetVisibleEntry()->GetURL());
3167 main_test_rfh()->PrepareForCommit();
3168 main_test_rfh()->SendNavigate(1, entry_id
, false, url1
);
3169 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
3171 // Add a transient and go to an entry after the current one.
3172 transient_entry
.reset(new NavigationEntryImpl
);
3173 transient_entry
->SetURL(transient_url
);
3174 controller
.SetTransientEntry(transient_entry
.Pass());
3175 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3176 controller
.GoToIndex(3);
3177 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3178 // The navigation should have been initiated, transient entry should be gone.
3179 // Because of the transient entry that is removed, going to index 3 makes us
3180 // land on url2 (which is visible after the commit).
3181 EXPECT_EQ(url2
, controller
.GetPendingEntry()->GetURL());
3182 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
3183 main_test_rfh()->PrepareForCommit();
3184 main_test_rfh()->SendNavigate(2, entry_id
, false, url2
);
3185 EXPECT_EQ(url2
, controller
.GetVisibleEntry()->GetURL());
3187 // Add a transient and go forward.
3188 transient_entry
.reset(new NavigationEntryImpl
);
3189 transient_entry
->SetURL(transient_url
);
3190 controller
.SetTransientEntry(transient_entry
.Pass());
3191 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3192 EXPECT_TRUE(controller
.CanGoForward());
3193 controller
.GoForward();
3194 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3195 // We should have navigated, transient entry should be gone.
3196 EXPECT_FALSE(controller
.GetTransientEntry());
3197 EXPECT_EQ(url3
, controller
.GetPendingEntry()->GetURL());
3198 EXPECT_EQ(url2
, controller
.GetVisibleEntry()->GetURL());
3199 main_test_rfh()->PrepareForCommit();
3200 main_test_rfh()->SendNavigate(3, entry_id
, false, url3
);
3201 EXPECT_EQ(url3
, controller
.GetVisibleEntry()->GetURL());
3203 // Add a transient and do an in-page navigation, replacing the current entry.
3204 transient_entry
.reset(new NavigationEntryImpl
);
3205 transient_entry
->SetURL(transient_url
);
3206 controller
.SetTransientEntry(transient_entry
.Pass());
3207 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3209 main_test_rfh()->SendRendererInitiatedNavigationRequest(url3_ref
, false);
3210 main_test_rfh()->PrepareForCommit();
3211 main_test_rfh()->SendNavigate(3, 0, false, url3_ref
);
3212 // Transient entry should be gone.
3213 EXPECT_FALSE(controller
.GetTransientEntry());
3214 EXPECT_EQ(url3_ref
, controller
.GetVisibleEntry()->GetURL());
3216 // Ensure the URLs are correct.
3217 EXPECT_EQ(controller
.GetEntryCount(), 5);
3218 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(), url0
);
3219 EXPECT_EQ(controller
.GetEntryAtIndex(1)->GetURL(), url1
);
3220 EXPECT_EQ(controller
.GetEntryAtIndex(2)->GetURL(), url2
);
3221 EXPECT_EQ(controller
.GetEntryAtIndex(3)->GetURL(), url3_ref
);
3222 EXPECT_EQ(controller
.GetEntryAtIndex(4)->GetURL(), url4
);
3225 // Test that Reload initiates a new navigation to a transient entry's URL.
3226 TEST_F(NavigationControllerTest
, ReloadTransient
) {
3227 NavigationControllerImpl
& controller
= controller_impl();
3228 const GURL
url0("http://foo/0");
3229 const GURL
url1("http://foo/1");
3230 const GURL
transient_url("http://foo/transient");
3232 // Load |url0|, and start a pending navigation to |url1|.
3234 url0
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3235 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3236 main_test_rfh()->PrepareForCommit();
3237 main_test_rfh()->SendNavigate(0, entry_id
, true, url0
);
3239 url1
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3241 // A transient entry is added, interrupting the navigation.
3242 scoped_ptr
<NavigationEntry
> transient_entry(new NavigationEntryImpl
);
3243 transient_entry
->SetURL(transient_url
);
3244 controller
.SetTransientEntry(transient_entry
.Pass());
3245 EXPECT_TRUE(controller
.GetTransientEntry());
3246 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3248 // The page is reloaded, which should remove the pending entry for |url1| and
3249 // the transient entry for |transient_url|, and start a navigation to
3251 controller
.Reload(true);
3252 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3253 EXPECT_FALSE(controller
.GetTransientEntry());
3254 EXPECT_TRUE(controller
.GetPendingEntry());
3255 EXPECT_EQ(transient_url
, controller
.GetVisibleEntry()->GetURL());
3256 ASSERT_EQ(controller
.GetEntryCount(), 1);
3257 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(), url0
);
3259 // Load of |transient_url| completes.
3260 main_test_rfh()->PrepareForCommit();
3261 main_test_rfh()->SendNavigate(1, entry_id
, true, transient_url
);
3262 ASSERT_EQ(controller
.GetEntryCount(), 2);
3263 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(), url0
);
3264 EXPECT_EQ(controller
.GetEntryAtIndex(1)->GetURL(), transient_url
);
3267 // Ensure that renderer initiated pending entries get replaced, so that we
3268 // don't show a stale virtual URL when a navigation commits.
3269 // See http://crbug.com/266922.
3270 TEST_F(NavigationControllerTest
, RendererInitiatedPendingEntries
) {
3271 NavigationControllerImpl
& controller
= controller_impl();
3272 Navigator
* navigator
=
3273 contents()->GetFrameTree()->root()->navigator();
3275 const GURL
url1("nonexistent:12121");
3276 const GURL
url1_fixed("http://nonexistent:12121/");
3277 const GURL
url2("http://foo");
3279 // We create pending entries for renderer-initiated navigations so that we
3280 // can show them in new tabs when it is safe.
3281 main_test_rfh()->SendRendererInitiatedNavigationRequest(url1
, false);
3282 main_test_rfh()->PrepareForCommit();
3283 navigator
->DidStartProvisionalLoad(main_test_rfh(), url1
);
3285 // Simulate what happens if a BrowserURLHandler rewrites the URL, causing
3286 // the virtual URL to differ from the URL.
3287 controller
.GetPendingEntry()->SetURL(url1_fixed
);
3288 controller
.GetPendingEntry()->SetVirtualURL(url1
);
3290 EXPECT_EQ(url1_fixed
, controller
.GetPendingEntry()->GetURL());
3291 EXPECT_EQ(url1
, controller
.GetPendingEntry()->GetVirtualURL());
3292 EXPECT_TRUE(controller
.GetPendingEntry()->is_renderer_initiated());
3294 // If the user clicks another link, we should replace the pending entry.
3295 main_test_rfh()->SendRendererInitiatedNavigationRequest(url2
, false);
3296 main_test_rfh()->PrepareForCommit();
3297 navigator
->DidStartProvisionalLoad(main_test_rfh(), url2
);
3298 EXPECT_EQ(url2
, controller
.GetPendingEntry()->GetURL());
3299 EXPECT_EQ(url2
, controller
.GetPendingEntry()->GetVirtualURL());
3301 // Once it commits, the URL and virtual URL should reflect the actual page.
3302 main_test_rfh()->SendNavigate(0, 0, true, url2
);
3303 EXPECT_EQ(url2
, controller
.GetLastCommittedEntry()->GetURL());
3304 EXPECT_EQ(url2
, controller
.GetLastCommittedEntry()->GetVirtualURL());
3306 // We should not replace the pending entry for an error URL.
3307 navigator
->DidStartProvisionalLoad(main_test_rfh(), url1
);
3308 EXPECT_EQ(url1
, controller
.GetPendingEntry()->GetURL());
3309 navigator
->DidStartProvisionalLoad(main_test_rfh(),
3310 GURL(kUnreachableWebDataURL
));
3311 EXPECT_EQ(url1
, controller
.GetPendingEntry()->GetURL());
3313 // We should remember if the pending entry will replace the current one.
3314 // http://crbug.com/308444.
3315 navigator
->DidStartProvisionalLoad(main_test_rfh(), url1
);
3316 controller
.GetPendingEntry()->set_should_replace_entry(true);
3318 main_test_rfh()->SendRendererInitiatedNavigationRequest(url2
, false);
3319 main_test_rfh()->PrepareForCommit();
3320 navigator
->DidStartProvisionalLoad(main_test_rfh(), url2
);
3321 EXPECT_TRUE(controller
.GetPendingEntry()->should_replace_entry());
3322 main_test_rfh()->SendNavigate(0, 0, false, url2
);
3323 EXPECT_EQ(url2
, controller
.GetLastCommittedEntry()->GetURL());
3326 // Tests that the URLs for renderer-initiated navigations are not displayed to
3327 // the user until the navigation commits, to prevent URL spoof attacks.
3328 // See http://crbug.com/99016.
3329 TEST_F(NavigationControllerTest
, DontShowRendererURLUntilCommit
) {
3330 NavigationControllerImpl
& controller
= controller_impl();
3331 TestNotificationTracker notifications
;
3332 RegisterForAllNavNotifications(¬ifications
, &controller
);
3334 const GURL
url0("http://foo/0");
3335 const GURL
url1("http://foo/1");
3337 // For typed navigations (browser-initiated), both pending and visible entries
3338 // should update before commit.
3340 url0
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3341 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3342 EXPECT_EQ(url0
, controller
.GetPendingEntry()->GetURL());
3343 EXPECT_EQ(url0
, controller
.GetVisibleEntry()->GetURL());
3344 main_test_rfh()->PrepareForCommit();
3345 main_test_rfh()->SendNavigate(0, entry_id
, true, url0
);
3347 // For link clicks (renderer-initiated navigations), the pending entry should
3348 // update before commit but the visible should not.
3349 NavigationController::LoadURLParams
load_url_params(url1
);
3350 load_url_params
.is_renderer_initiated
= true;
3351 controller
.LoadURLWithParams(load_url_params
);
3352 entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3353 EXPECT_EQ(url0
, controller
.GetVisibleEntry()->GetURL());
3354 EXPECT_EQ(url1
, controller
.GetPendingEntry()->GetURL());
3355 EXPECT_TRUE(controller
.GetPendingEntry()->is_renderer_initiated());
3357 // After commit, both visible should be updated, there should be no pending
3358 // entry, and we should no longer treat the entry as renderer-initiated.
3359 main_test_rfh()->PrepareForCommit();
3360 main_test_rfh()->SendNavigate(1, entry_id
, true, url1
);
3361 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
3362 EXPECT_FALSE(controller
.GetPendingEntry());
3363 EXPECT_FALSE(controller
.GetLastCommittedEntry()->is_renderer_initiated());
3365 notifications
.Reset();
3368 // Tests that the URLs for renderer-initiated navigations in new tabs are
3369 // displayed to the user before commit, as long as the initial about:blank
3370 // page has not been modified. If so, we must revert to showing about:blank.
3371 // See http://crbug.com/9682.
3372 TEST_F(NavigationControllerTest
, ShowRendererURLInNewTabUntilModified
) {
3373 NavigationControllerImpl
& controller
= controller_impl();
3374 TestNotificationTracker notifications
;
3375 RegisterForAllNavNotifications(¬ifications
, &controller
);
3377 const GURL
url("http://foo");
3379 // For renderer-initiated navigations in new tabs (with no committed entries),
3380 // we show the pending entry's URL as long as the about:blank page is not
3382 NavigationController::LoadURLParams
load_url_params(url
);
3383 load_url_params
.transition_type
= ui::PAGE_TRANSITION_LINK
;
3384 load_url_params
.is_renderer_initiated
= true;
3385 controller
.LoadURLWithParams(load_url_params
);
3386 EXPECT_EQ(url
, controller
.GetVisibleEntry()->GetURL());
3387 EXPECT_EQ(url
, controller
.GetPendingEntry()->GetURL());
3388 EXPECT_TRUE(controller
.GetPendingEntry()->is_renderer_initiated());
3389 EXPECT_TRUE(controller
.IsInitialNavigation());
3390 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3392 // There should be no title yet.
3393 EXPECT_TRUE(contents()->GetTitle().empty());
3395 // If something else modifies the contents of the about:blank page, then
3396 // we must revert to showing about:blank to avoid a URL spoof.
3397 main_test_rfh()->OnMessageReceived(FrameHostMsg_DidAccessInitialDocument(0));
3398 EXPECT_TRUE(contents()->HasAccessedInitialDocument());
3399 EXPECT_FALSE(controller
.GetVisibleEntry());
3400 EXPECT_EQ(url
, controller
.GetPendingEntry()->GetURL());
3402 notifications
.Reset();
3405 // Tests that the URLs for browser-initiated navigations in new tabs are
3406 // displayed to the user even after they fail, as long as the initial
3407 // about:blank page has not been modified. If so, we must revert to showing
3408 // about:blank. See http://crbug.com/355537.
3409 TEST_F(NavigationControllerTest
, ShowBrowserURLAfterFailUntilModified
) {
3410 NavigationControllerImpl
& controller
= controller_impl();
3411 TestNotificationTracker notifications
;
3412 RegisterForAllNavNotifications(¬ifications
, &controller
);
3414 const GURL
url("http://foo");
3416 // For browser-initiated navigations in new tabs (with no committed entries),
3417 // we show the pending entry's URL as long as the about:blank page is not
3418 // modified. This is possible in cases that the user types a URL into a popup
3419 // tab created with a slow URL.
3420 NavigationController::LoadURLParams
load_url_params(url
);
3421 load_url_params
.transition_type
= ui::PAGE_TRANSITION_TYPED
;
3422 load_url_params
.is_renderer_initiated
= false;
3423 controller
.LoadURLWithParams(load_url_params
);
3424 EXPECT_EQ(url
, controller
.GetVisibleEntry()->GetURL());
3425 EXPECT_EQ(url
, controller
.GetPendingEntry()->GetURL());
3426 EXPECT_FALSE(controller
.GetPendingEntry()->is_renderer_initiated());
3427 EXPECT_TRUE(controller
.IsInitialNavigation());
3428 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3430 // There should be no title yet.
3431 EXPECT_TRUE(contents()->GetTitle().empty());
3433 // Suppose it aborts before committing, if it's a 204 or download or due to a
3434 // stop or a new navigation from the user. The URL should remain visible.
3435 FrameHostMsg_DidFailProvisionalLoadWithError_Params params
;
3436 params
.error_code
= net::ERR_ABORTED
;
3437 params
.error_description
= base::string16();
3439 params
.showing_repost_interstitial
= false;
3440 main_test_rfh()->OnMessageReceived(
3441 FrameHostMsg_DidFailProvisionalLoadWithError(0, params
));
3442 contents()->SetIsLoading(false, true, NULL
);
3443 EXPECT_EQ(url
, controller
.GetVisibleEntry()->GetURL());
3445 // If something else later modifies the contents of the about:blank page, then
3446 // we must revert to showing about:blank to avoid a URL spoof.
3447 main_test_rfh()->OnMessageReceived(FrameHostMsg_DidAccessInitialDocument(0));
3448 EXPECT_TRUE(contents()->HasAccessedInitialDocument());
3449 EXPECT_FALSE(controller
.GetVisibleEntry());
3450 EXPECT_FALSE(controller
.GetPendingEntry());
3452 notifications
.Reset();
3455 // Tests that the URLs for renderer-initiated navigations in new tabs are
3456 // displayed to the user even after they fail, as long as the initial
3457 // about:blank page has not been modified. If so, we must revert to showing
3458 // about:blank. See http://crbug.com/355537.
3459 TEST_F(NavigationControllerTest
, ShowRendererURLAfterFailUntilModified
) {
3460 NavigationControllerImpl
& controller
= controller_impl();
3461 TestNotificationTracker notifications
;
3462 RegisterForAllNavNotifications(¬ifications
, &controller
);
3464 const GURL
url("http://foo");
3466 // For renderer-initiated navigations in new tabs (with no committed entries),
3467 // we show the pending entry's URL as long as the about:blank page is not
3469 NavigationController::LoadURLParams
load_url_params(url
);
3470 load_url_params
.transition_type
= ui::PAGE_TRANSITION_LINK
;
3471 load_url_params
.is_renderer_initiated
= true;
3472 controller
.LoadURLWithParams(load_url_params
);
3473 EXPECT_EQ(url
, controller
.GetVisibleEntry()->GetURL());
3474 EXPECT_EQ(url
, controller
.GetPendingEntry()->GetURL());
3475 EXPECT_TRUE(controller
.GetPendingEntry()->is_renderer_initiated());
3476 EXPECT_TRUE(controller
.IsInitialNavigation());
3477 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3479 // There should be no title yet.
3480 EXPECT_TRUE(contents()->GetTitle().empty());
3482 // Suppose it aborts before committing, if it's a 204 or download or due to a
3483 // stop or a new navigation from the user. The URL should remain visible.
3484 FrameHostMsg_DidFailProvisionalLoadWithError_Params params
;
3485 params
.error_code
= net::ERR_ABORTED
;
3486 params
.error_description
= base::string16();
3488 params
.showing_repost_interstitial
= false;
3489 main_test_rfh()->OnMessageReceived(
3490 FrameHostMsg_DidFailProvisionalLoadWithError(0, params
));
3491 EXPECT_EQ(url
, controller
.GetVisibleEntry()->GetURL());
3493 // If something else later modifies the contents of the about:blank page, then
3494 // we must revert to showing about:blank to avoid a URL spoof.
3495 main_test_rfh()->OnMessageReceived(FrameHostMsg_DidAccessInitialDocument(0));
3496 EXPECT_TRUE(contents()->HasAccessedInitialDocument());
3497 EXPECT_FALSE(controller
.GetVisibleEntry());
3498 EXPECT_EQ(url
, controller
.GetPendingEntry()->GetURL());
3500 notifications
.Reset();
3503 TEST_F(NavigationControllerTest
, DontShowRendererURLInNewTabAfterCommit
) {
3504 NavigationControllerImpl
& controller
= controller_impl();
3505 TestNotificationTracker notifications
;
3506 RegisterForAllNavNotifications(¬ifications
, &controller
);
3508 const GURL
url1("http://foo/eh");
3509 const GURL
url2("http://foo/bee");
3511 // For renderer-initiated navigations in new tabs (with no committed entries),
3512 // we show the pending entry's URL as long as the about:blank page is not
3514 NavigationController::LoadURLParams
load_url_params(url1
);
3515 load_url_params
.transition_type
= ui::PAGE_TRANSITION_LINK
;
3516 load_url_params
.is_renderer_initiated
= true;
3517 controller
.LoadURLWithParams(load_url_params
);
3518 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
3519 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
3520 EXPECT_TRUE(controller
.GetPendingEntry()->is_renderer_initiated());
3521 EXPECT_TRUE(controller
.IsInitialNavigation());
3522 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3524 // Simulate a commit and then starting a new pending navigation.
3525 main_test_rfh()->PrepareForCommit();
3526 main_test_rfh()->SendNavigate(0, entry_id
, true, url1
);
3527 NavigationController::LoadURLParams
load_url2_params(url2
);
3528 load_url2_params
.transition_type
= ui::PAGE_TRANSITION_LINK
;
3529 load_url2_params
.is_renderer_initiated
= true;
3530 controller
.LoadURLWithParams(load_url2_params
);
3532 // We should not consider this an initial navigation, and thus should
3533 // not show the pending URL.
3534 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3535 EXPECT_FALSE(controller
.IsInitialNavigation());
3536 EXPECT_TRUE(controller
.GetVisibleEntry());
3537 EXPECT_EQ(url1
, controller
.GetVisibleEntry()->GetURL());
3539 notifications
.Reset();
3542 // Tests that IsInPageNavigation returns appropriate results. Prevents
3543 // regression for bug 1126349.
3544 TEST_F(NavigationControllerTest
, IsInPageNavigation
) {
3545 NavigationControllerImpl
& controller
= controller_impl();
3546 const GURL
url("http://www.google.com/home.html");
3548 // If the renderer claims it performed an in-page navigation from
3549 // about:blank, trust the renderer.
3550 // This can happen when an iframe is created and populated via
3551 // document.write(), then tries to perform a fragment navigation.
3552 // TODO(japhet): We should only trust the renderer if the about:blank
3553 // was the first document in the given frame, but we don't have enough
3554 // information to identify that case currently.
3555 const GURL
blank_url(url::kAboutBlankURL
);
3556 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, blank_url
);
3557 EXPECT_TRUE(controller
.IsURLInPageNavigation(url
, true,
3560 // Navigate to URL with no refs.
3561 main_test_rfh()->NavigateAndCommitRendererInitiated(0, false, url
);
3563 // Reloading the page is not an in-page navigation.
3564 EXPECT_FALSE(controller
.IsURLInPageNavigation(url
, false, main_test_rfh()));
3565 const GURL
other_url("http://www.google.com/add.html");
3566 EXPECT_FALSE(controller
.IsURLInPageNavigation(other_url
, false,
3568 const GURL
url_with_ref("http://www.google.com/home.html#my_ref");
3569 EXPECT_TRUE(controller
.IsURLInPageNavigation(url_with_ref
, true,
3572 // Navigate to URL with refs.
3573 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url_with_ref
);
3575 // Reloading the page is not an in-page navigation.
3576 EXPECT_FALSE(controller
.IsURLInPageNavigation(url_with_ref
, false,
3578 EXPECT_FALSE(controller
.IsURLInPageNavigation(url
, false,
3580 EXPECT_FALSE(controller
.IsURLInPageNavigation(other_url
, false,
3582 const GURL
other_url_with_ref("http://www.google.com/home.html#my_other_ref");
3583 EXPECT_TRUE(controller
.IsURLInPageNavigation(other_url_with_ref
, true,
3586 // Going to the same url again will be considered in-page
3587 // if the renderer says it is even if the navigation type isn't IN_PAGE.
3588 EXPECT_TRUE(controller
.IsURLInPageNavigation(url_with_ref
, true,
3591 // Going back to the non ref url will be considered in-page if the navigation
3593 EXPECT_TRUE(controller
.IsURLInPageNavigation(url
, true,
3596 // If the renderer says this is a same-origin in-page navigation, believe it.
3597 // This is the pushState/replaceState case.
3598 EXPECT_TRUE(controller
.IsURLInPageNavigation(other_url
, true,
3601 // Test allow_universal_access_from_file_urls flag.
3602 const GURL
different_origin_url("http://www.example.com");
3603 MockRenderProcessHost
* rph
= main_test_rfh()->GetProcess();
3604 WebPreferences prefs
= test_rvh()->GetWebkitPreferences();
3605 prefs
.allow_universal_access_from_file_urls
= true;
3606 test_rvh()->UpdateWebkitPreferences(prefs
);
3607 prefs
= test_rvh()->GetWebkitPreferences();
3608 EXPECT_TRUE(prefs
.allow_universal_access_from_file_urls
);
3609 // Allow in page navigation if existing URL is file scheme.
3610 const GURL
file_url("file:///foo/index.html");
3611 main_test_rfh()->NavigateAndCommitRendererInitiated(0, false, file_url
);
3612 EXPECT_EQ(0, rph
->bad_msg_count());
3613 EXPECT_TRUE(controller
.IsURLInPageNavigation(different_origin_url
, true,
3615 EXPECT_EQ(0, rph
->bad_msg_count());
3616 // Don't honor allow_universal_access_from_file_urls if existing URL is
3618 main_test_rfh()->NavigateAndCommitRendererInitiated(0, false, url
);
3619 EXPECT_FALSE(controller
.IsURLInPageNavigation(different_origin_url
, true,
3621 EXPECT_EQ(1, rph
->bad_msg_count());
3623 // Remove allow_universal_access_from_file_urls flag.
3624 prefs
.allow_universal_access_from_file_urls
= false;
3625 test_rvh()->UpdateWebkitPreferences(prefs
);
3626 prefs
= test_rvh()->GetWebkitPreferences();
3627 EXPECT_FALSE(prefs
.allow_universal_access_from_file_urls
);
3629 // Don't believe the renderer if it claims a cross-origin navigation is
3631 EXPECT_EQ(1, rph
->bad_msg_count());
3632 EXPECT_FALSE(controller
.IsURLInPageNavigation(different_origin_url
, true,
3634 EXPECT_EQ(2, rph
->bad_msg_count());
3637 // Some pages can have subframes with the same base URL (minus the reference) as
3638 // the main page. Even though this is hard, it can happen, and we don't want
3639 // these subframe navigations to affect the toplevel document. They should
3640 // instead be ignored. http://crbug.com/5585
3641 TEST_F(NavigationControllerTest
, SameSubframe
) {
3642 NavigationControllerImpl
& controller
= controller_impl();
3643 // Navigate the main frame.
3644 const GURL
url("http://www.google.com/");
3645 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url
);
3647 // We should be at the first navigation entry.
3648 EXPECT_EQ(controller
.GetEntryCount(), 1);
3649 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
3651 // Add and navigate a subframe that would normally count as in-page.
3652 main_test_rfh()->OnCreateChildFrame(
3653 MSG_ROUTING_NONE
, blink::WebTreeScopeType::Document
, std::string(),
3654 blink::WebSandboxFlags::None
);
3655 RenderFrameHostImpl
* subframe
=
3656 contents()->GetFrameTree()->root()->child_at(0)->current_frame_host();
3657 const GURL
subframe_url("http://www.google.com/#");
3658 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
3660 params
.nav_entry_id
= 0;
3661 params
.did_create_new_entry
= false;
3662 params
.url
= subframe_url
;
3663 params
.transition
= ui::PAGE_TRANSITION_AUTO_SUBFRAME
;
3664 params
.should_update_history
= false;
3665 params
.gesture
= NavigationGestureAuto
;
3666 params
.is_post
= false;
3667 params
.page_state
= PageState::CreateFromURL(subframe_url
);
3668 LoadCommittedDetails details
;
3669 EXPECT_FALSE(controller
.RendererDidNavigate(subframe
, params
, &details
));
3671 // Nothing should have changed.
3672 EXPECT_EQ(controller
.GetEntryCount(), 1);
3673 EXPECT_EQ(controller
.GetLastCommittedEntryIndex(), 0);
3676 // Make sure that on cloning a WebContentsImpl and going back needs_reload is
3678 TEST_F(NavigationControllerTest
, CloneAndGoBack
) {
3679 NavigationControllerImpl
& controller
= controller_impl();
3680 const GURL
url1("http://foo1");
3681 const GURL
url2("http://foo2");
3682 const base::string16
title(base::ASCIIToUTF16("Title"));
3684 NavigateAndCommit(url1
);
3685 controller
.GetVisibleEntry()->SetTitle(title
);
3686 NavigateAndCommit(url2
);
3688 scoped_ptr
<WebContents
> clone(controller
.GetWebContents()->Clone());
3690 ASSERT_EQ(2, clone
->GetController().GetEntryCount());
3691 EXPECT_TRUE(clone
->GetController().NeedsReload());
3692 clone
->GetController().GoBack();
3693 // Navigating back should have triggered needs_reload_ to go false.
3694 EXPECT_FALSE(clone
->GetController().NeedsReload());
3696 // Ensure that the pending URL and its title are visible.
3697 EXPECT_EQ(url1
, clone
->GetController().GetVisibleEntry()->GetURL());
3698 EXPECT_EQ(title
, clone
->GetTitle());
3701 // Make sure that reloading a cloned tab doesn't change its pending entry index.
3702 // See http://crbug.com/234491.
3703 TEST_F(NavigationControllerTest
, CloneAndReload
) {
3704 NavigationControllerImpl
& controller
= controller_impl();
3705 const GURL
url1("http://foo1");
3706 const GURL
url2("http://foo2");
3707 const base::string16
title(base::ASCIIToUTF16("Title"));
3709 NavigateAndCommit(url1
);
3710 controller
.GetVisibleEntry()->SetTitle(title
);
3711 NavigateAndCommit(url2
);
3713 scoped_ptr
<WebContents
> clone(controller
.GetWebContents()->Clone());
3714 clone
->GetController().LoadIfNecessary();
3716 ASSERT_EQ(2, clone
->GetController().GetEntryCount());
3717 EXPECT_EQ(1, clone
->GetController().GetPendingEntryIndex());
3719 clone
->GetController().Reload(true);
3720 EXPECT_EQ(1, clone
->GetController().GetPendingEntryIndex());
3723 // Make sure that cloning a WebContentsImpl doesn't copy interstitials.
3724 TEST_F(NavigationControllerTest
, CloneOmitsInterstitials
) {
3725 NavigationControllerImpl
& controller
= controller_impl();
3726 const GURL
url1("http://foo1");
3727 const GURL
url2("http://foo2");
3729 NavigateAndCommit(url1
);
3730 NavigateAndCommit(url2
);
3732 // Add an interstitial entry. Should be deleted with controller.
3733 NavigationEntryImpl
* interstitial_entry
= new NavigationEntryImpl();
3734 interstitial_entry
->set_page_type(PAGE_TYPE_INTERSTITIAL
);
3735 controller
.SetTransientEntry(make_scoped_ptr(interstitial_entry
));
3737 scoped_ptr
<WebContents
> clone(controller
.GetWebContents()->Clone());
3739 ASSERT_EQ(2, clone
->GetController().GetEntryCount());
3742 // Test requesting and triggering a lazy reload.
3743 TEST_F(NavigationControllerTest
, LazyReload
) {
3744 NavigationControllerImpl
& controller
= controller_impl();
3745 const GURL
url("http://foo");
3746 NavigateAndCommit(url
);
3747 ASSERT_FALSE(controller
.NeedsReload());
3748 EXPECT_NE(ui::PAGE_TRANSITION_RELOAD
,
3749 controller
.GetLastCommittedEntry()->GetTransitionType());
3751 // Request a reload to happen when the controller becomes active (e.g. after
3752 // the renderer gets killed in background on Android).
3753 controller
.SetNeedsReload();
3754 ASSERT_TRUE(controller
.NeedsReload());
3755 EXPECT_EQ(ui::PAGE_TRANSITION_RELOAD
,
3756 controller
.GetLastCommittedEntry()->GetTransitionType());
3758 // Set the controller as active, triggering the requested reload.
3759 controller
.SetActive(true);
3760 ASSERT_FALSE(controller
.NeedsReload());
3761 EXPECT_EQ(ui::PAGE_TRANSITION_RELOAD
,
3762 controller
.GetPendingEntry()->GetTransitionType());
3765 // Test requesting and triggering a lazy reload without any committed entry nor
3767 TEST_F(NavigationControllerTest
, LazyReloadWithoutCommittedEntry
) {
3768 NavigationControllerImpl
& controller
= controller_impl();
3769 ASSERT_EQ(-1, controller
.GetLastCommittedEntryIndex());
3770 EXPECT_FALSE(controller
.NeedsReload());
3771 controller
.SetNeedsReload();
3772 EXPECT_TRUE(controller
.NeedsReload());
3774 // Doing a "load if necessary" shouldn't DCHECK.
3775 controller
.LoadIfNecessary();
3776 ASSERT_FALSE(controller
.NeedsReload());
3779 // Test requesting and triggering a lazy reload without any committed entry and
3780 // only a pending entry.
3781 TEST_F(NavigationControllerTest
, LazyReloadWithOnlyPendingEntry
) {
3782 NavigationControllerImpl
& controller
= controller_impl();
3783 const GURL
url("http://foo");
3784 controller
.LoadURL(url
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3785 ASSERT_FALSE(controller
.NeedsReload());
3786 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED
,
3787 controller
.GetPendingEntry()->GetTransitionType());
3789 // Request a reload to happen when the controller becomes active (e.g. after
3790 // the renderer gets killed in background on Android).
3791 controller
.SetNeedsReload();
3792 ASSERT_TRUE(controller
.NeedsReload());
3793 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED
,
3794 controller
.GetPendingEntry()->GetTransitionType());
3796 // Set the controller as active, triggering the requested reload.
3797 controller
.SetActive(true);
3798 ASSERT_FALSE(controller
.NeedsReload());
3799 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED
,
3800 controller
.GetPendingEntry()->GetTransitionType());
3803 // Tests a subframe navigation while a toplevel navigation is pending.
3804 // http://crbug.com/43967
3805 TEST_F(NavigationControllerTest
, SubframeWhilePending
) {
3806 NavigationControllerImpl
& controller
= controller_impl();
3807 // Load the first page.
3808 const GURL
url1("http://foo/");
3809 NavigateAndCommit(url1
);
3811 // Now start a pending load to a totally different page, but don't commit it.
3812 const GURL
url2("http://bar/");
3814 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
3816 // Send a subframe update from the first page, as if one had just
3817 // automatically loaded. Auto subframes don't increment the page ID.
3818 main_test_rfh()->OnCreateChildFrame(
3819 MSG_ROUTING_NONE
, blink::WebTreeScopeType::Document
, std::string(),
3820 blink::WebSandboxFlags::None
);
3821 RenderFrameHostImpl
* subframe
=
3822 contents()->GetFrameTree()->root()->child_at(0)->current_frame_host();
3823 const GURL
url1_sub("http://foo/subframe");
3824 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
3825 params
.page_id
= controller
.GetLastCommittedEntry()->GetPageID();
3826 params
.nav_entry_id
= 0;
3827 params
.did_create_new_entry
= false;
3828 params
.url
= url1_sub
;
3829 params
.transition
= ui::PAGE_TRANSITION_AUTO_SUBFRAME
;
3830 params
.should_update_history
= false;
3831 params
.gesture
= NavigationGestureAuto
;
3832 params
.is_post
= false;
3833 params
.page_state
= PageState::CreateFromURL(url1_sub
);
3834 LoadCommittedDetails details
;
3836 // This should return false meaning that nothing was actually updated.
3837 EXPECT_FALSE(controller
.RendererDidNavigate(subframe
, params
, &details
));
3839 // The notification should have updated the last committed one, and not
3840 // the pending load.
3841 EXPECT_EQ(url1
, controller
.GetLastCommittedEntry()->GetURL());
3843 // The active entry should be unchanged by the subframe load.
3844 EXPECT_EQ(url2
, controller
.GetVisibleEntry()->GetURL());
3847 // Test CopyStateFrom with 2 urls, the first selected and nothing in the target.
3848 TEST_F(NavigationControllerTest
, CopyStateFrom
) {
3849 NavigationControllerImpl
& controller
= controller_impl();
3850 const GURL
url1("http://foo1");
3851 const GURL
url2("http://foo2");
3853 NavigateAndCommit(url1
);
3854 NavigateAndCommit(url2
);
3855 controller
.GoBack();
3856 contents()->CommitPendingNavigation();
3858 scoped_ptr
<TestWebContents
> other_contents(
3859 static_cast<TestWebContents
*>(CreateTestWebContents()));
3860 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
3861 other_controller
.CopyStateFrom(controller
);
3863 // other_controller should now contain 2 urls.
3864 ASSERT_EQ(2, other_controller
.GetEntryCount());
3865 // We should be looking at the first one.
3866 ASSERT_EQ(0, other_controller
.GetCurrentEntryIndex());
3868 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
3869 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(0)->GetPageID());
3870 EXPECT_EQ(url2
, other_controller
.GetEntryAtIndex(1)->GetURL());
3871 // This is a different site than url1, so the IDs start again at 0.
3872 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(1)->GetPageID());
3874 // The max page ID map should be copied over and updated with the max page ID
3875 // from the current tab.
3876 SiteInstance
* instance1
=
3877 other_controller
.GetEntryAtIndex(0)->site_instance();
3878 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
3880 // Ensure the SessionStorageNamespaceMaps are the same size and have
3881 // the same partitons loaded.
3883 // TODO(ajwong): We should load a url from a different partition earlier
3884 // to make sure this map has more than one entry.
3885 const SessionStorageNamespaceMap
& session_storage_namespace_map
=
3886 controller
.GetSessionStorageNamespaceMap();
3887 const SessionStorageNamespaceMap
& other_session_storage_namespace_map
=
3888 other_controller
.GetSessionStorageNamespaceMap();
3889 EXPECT_EQ(session_storage_namespace_map
.size(),
3890 other_session_storage_namespace_map
.size());
3891 for (SessionStorageNamespaceMap::const_iterator it
=
3892 session_storage_namespace_map
.begin();
3893 it
!= session_storage_namespace_map
.end();
3895 SessionStorageNamespaceMap::const_iterator other
=
3896 other_session_storage_namespace_map
.find(it
->first
);
3897 EXPECT_TRUE(other
!= other_session_storage_namespace_map
.end());
3901 // Tests CopyStateFromAndPrune with 2 urls in source, 1 in dest.
3902 TEST_F(NavigationControllerTest
, CopyStateFromAndPrune
) {
3903 NavigationControllerImpl
& controller
= controller_impl();
3904 const GURL
url1("http://foo/1");
3905 const GURL
url2("http://foo/2");
3906 const GURL
url3("http://foo/3");
3908 NavigateAndCommit(url1
);
3909 NavigateAndCommit(url2
);
3911 // First two entries should have the same SiteInstance.
3912 SiteInstance
* instance1
= controller
.GetEntryAtIndex(0)->site_instance();
3913 SiteInstance
* instance2
= controller
.GetEntryAtIndex(1)->site_instance();
3914 EXPECT_EQ(instance1
, instance2
);
3915 EXPECT_EQ(0, controller
.GetEntryAtIndex(0)->GetPageID());
3916 EXPECT_EQ(1, controller
.GetEntryAtIndex(1)->GetPageID());
3917 EXPECT_EQ(1, contents()->GetMaxPageIDForSiteInstance(instance1
));
3919 scoped_ptr
<TestWebContents
> other_contents(
3920 static_cast<TestWebContents
*>(CreateTestWebContents()));
3921 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
3922 other_contents
->NavigateAndCommit(url3
);
3923 other_contents
->ExpectSetHistoryOffsetAndLength(2, 3);
3924 other_controller
.CopyStateFromAndPrune(&controller
, false);
3926 // other_controller should now contain the 3 urls: url1, url2 and url3.
3928 ASSERT_EQ(3, other_controller
.GetEntryCount());
3930 ASSERT_EQ(2, other_controller
.GetCurrentEntryIndex());
3932 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
3933 EXPECT_EQ(url2
, other_controller
.GetEntryAtIndex(1)->GetURL());
3934 EXPECT_EQ(url3
, other_controller
.GetEntryAtIndex(2)->GetURL());
3935 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(0)->GetPageID());
3936 EXPECT_EQ(1, other_controller
.GetEntryAtIndex(1)->GetPageID());
3937 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(2)->GetPageID());
3939 // A new SiteInstance in a different BrowsingInstance should be used for the
3941 SiteInstance
* instance3
=
3942 other_controller
.GetEntryAtIndex(2)->site_instance();
3943 EXPECT_NE(instance3
, instance1
);
3944 EXPECT_FALSE(instance3
->IsRelatedSiteInstance(instance1
));
3946 // The max page ID map should be copied over and updated with the max page ID
3947 // from the current tab.
3948 EXPECT_EQ(1, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
3949 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance3
));
3952 // Test CopyStateFromAndPrune with 2 urls, the first selected and 1 entry in
3954 TEST_F(NavigationControllerTest
, CopyStateFromAndPrune2
) {
3955 NavigationControllerImpl
& controller
= controller_impl();
3956 const GURL
url1("http://foo1");
3957 const GURL
url2("http://foo2");
3958 const GURL
url3("http://foo3");
3960 NavigateAndCommit(url1
);
3961 NavigateAndCommit(url2
);
3962 controller
.GoBack();
3963 contents()->CommitPendingNavigation();
3965 scoped_ptr
<TestWebContents
> other_contents(
3966 static_cast<TestWebContents
*>(CreateTestWebContents()));
3967 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
3968 other_contents
->NavigateAndCommit(url3
);
3969 other_contents
->ExpectSetHistoryOffsetAndLength(1, 2);
3970 other_controller
.CopyStateFromAndPrune(&controller
, false);
3972 // other_controller should now contain: url1, url3
3974 ASSERT_EQ(2, other_controller
.GetEntryCount());
3975 ASSERT_EQ(1, other_controller
.GetCurrentEntryIndex());
3977 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
3978 EXPECT_EQ(url3
, other_controller
.GetEntryAtIndex(1)->GetURL());
3979 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(1)->GetPageID());
3981 // The max page ID map should be copied over and updated with the max page ID
3982 // from the current tab.
3983 SiteInstance
* instance1
=
3984 other_controller
.GetEntryAtIndex(1)->site_instance();
3985 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
3988 // Test CopyStateFromAndPrune with 2 urls, the last selected and 2 entries in
3990 TEST_F(NavigationControllerTest
, CopyStateFromAndPrune3
) {
3991 NavigationControllerImpl
& controller
= controller_impl();
3992 const GURL
url1("http://foo1");
3993 const GURL
url2("http://foo2");
3994 const GURL
url3("http://foo3");
3995 const GURL
url4("http://foo4");
3997 NavigateAndCommit(url1
);
3998 NavigateAndCommit(url2
);
4000 scoped_ptr
<TestWebContents
> other_contents(
4001 static_cast<TestWebContents
*>(CreateTestWebContents()));
4002 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
4003 other_contents
->NavigateAndCommit(url3
);
4004 other_contents
->NavigateAndCommit(url4
);
4005 other_contents
->ExpectSetHistoryOffsetAndLength(2, 3);
4006 other_controller
.CopyStateFromAndPrune(&controller
, false);
4008 // other_controller should now contain: url1, url2, url4
4010 ASSERT_EQ(3, other_controller
.GetEntryCount());
4011 ASSERT_EQ(2, other_controller
.GetCurrentEntryIndex());
4013 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
4014 EXPECT_EQ(url2
, other_controller
.GetEntryAtIndex(1)->GetURL());
4015 EXPECT_EQ(url4
, other_controller
.GetEntryAtIndex(2)->GetURL());
4017 // The max page ID map should be copied over and updated with the max page ID
4018 // from the current tab.
4019 SiteInstance
* instance1
=
4020 other_controller
.GetEntryAtIndex(2)->site_instance();
4021 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
4024 // Test CopyStateFromAndPrune with 2 urls, 2 entries in the target, with
4025 // not the last entry selected in the target.
4026 TEST_F(NavigationControllerTest
, CopyStateFromAndPruneNotLast
) {
4027 NavigationControllerImpl
& controller
= controller_impl();
4028 const GURL
url1("http://foo1");
4029 const GURL
url2("http://foo2");
4030 const GURL
url3("http://foo3");
4031 const GURL
url4("http://foo4");
4033 NavigateAndCommit(url1
);
4034 NavigateAndCommit(url2
);
4036 scoped_ptr
<TestWebContents
> other_contents(
4037 static_cast<TestWebContents
*>(CreateTestWebContents()));
4038 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
4039 other_contents
->NavigateAndCommit(url3
);
4040 other_contents
->NavigateAndCommit(url4
);
4041 other_controller
.GoBack();
4042 other_contents
->CommitPendingNavigation();
4043 other_contents
->ExpectSetHistoryOffsetAndLength(2, 3);
4044 other_controller
.CopyStateFromAndPrune(&controller
, false);
4046 // other_controller should now contain: url1, url2, url3
4048 ASSERT_EQ(3, other_controller
.GetEntryCount());
4049 ASSERT_EQ(2, other_controller
.GetCurrentEntryIndex());
4051 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
4052 EXPECT_EQ(url2
, other_controller
.GetEntryAtIndex(1)->GetURL());
4053 EXPECT_EQ(url3
, other_controller
.GetEntryAtIndex(2)->GetURL());
4055 // The max page ID map should be copied over and updated with the max page ID
4056 // from the current tab.
4057 SiteInstance
* instance1
=
4058 other_controller
.GetEntryAtIndex(2)->site_instance();
4059 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
4062 // Test CopyStateFromAndPrune with 2 urls, the first selected and 1 entry plus
4063 // a pending entry in the target.
4064 TEST_F(NavigationControllerTest
, CopyStateFromAndPruneTargetPending
) {
4065 NavigationControllerImpl
& controller
= controller_impl();
4066 const GURL
url1("http://foo1");
4067 const GURL
url2("http://foo2");
4068 const GURL
url3("http://foo3");
4069 const GURL
url4("http://foo4");
4071 NavigateAndCommit(url1
);
4072 NavigateAndCommit(url2
);
4073 controller
.GoBack();
4074 contents()->CommitPendingNavigation();
4076 scoped_ptr
<TestWebContents
> other_contents(
4077 static_cast<TestWebContents
*>(CreateTestWebContents()));
4078 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
4079 other_contents
->NavigateAndCommit(url3
);
4080 other_controller
.LoadURL(
4081 url4
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
4082 other_contents
->ExpectSetHistoryOffsetAndLength(1, 2);
4083 other_controller
.CopyStateFromAndPrune(&controller
, false);
4085 // other_controller should now contain url1, url3, and a pending entry
4088 ASSERT_EQ(2, other_controller
.GetEntryCount());
4089 EXPECT_EQ(1, other_controller
.GetCurrentEntryIndex());
4091 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
4092 EXPECT_EQ(url3
, other_controller
.GetEntryAtIndex(1)->GetURL());
4094 // And there should be a pending entry for url4.
4095 ASSERT_TRUE(other_controller
.GetPendingEntry());
4096 EXPECT_EQ(url4
, other_controller
.GetPendingEntry()->GetURL());
4098 // The max page ID map should be copied over and updated with the max page ID
4099 // from the current tab.
4100 SiteInstance
* instance1
=
4101 other_controller
.GetEntryAtIndex(0)->site_instance();
4102 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
4105 // Test CopyStateFromAndPrune with 1 url in the source, 1 entry and a pending
4106 // client redirect entry (with the same page ID) in the target. This used to
4107 // crash because the last committed entry would be pruned but max_page_id
4108 // remembered the page ID (http://crbug.com/234809).
4109 TEST_F(NavigationControllerTest
, CopyStateFromAndPruneTargetPending2
) {
4110 NavigationControllerImpl
& controller
= controller_impl();
4111 const GURL
url1("http://foo1");
4112 const GURL
url2a("http://foo2/a");
4113 const GURL
url2b("http://foo2/b");
4115 NavigateAndCommit(url1
);
4117 scoped_ptr
<TestWebContents
> other_contents(
4118 static_cast<TestWebContents
*>(CreateTestWebContents()));
4119 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
4120 other_contents
->NavigateAndCommit(url2a
);
4121 // Simulate a client redirect, which has the same page ID as entry 2a.
4122 other_controller
.LoadURL(
4123 url2b
, Referrer(), ui::PAGE_TRANSITION_LINK
, std::string());
4124 NavigationEntry
* entry
= other_controller
.GetPendingEntry();
4125 entry
->SetPageID(other_controller
.GetLastCommittedEntry()->GetPageID());
4127 other_contents
->ExpectSetHistoryOffsetAndLength(1, 2);
4128 other_controller
.CopyStateFromAndPrune(&controller
, false);
4130 // other_controller should now contain url1, url2a, and a pending entry
4133 ASSERT_EQ(2, other_controller
.GetEntryCount());
4134 EXPECT_EQ(1, other_controller
.GetCurrentEntryIndex());
4136 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
4137 EXPECT_EQ(url2a
, other_controller
.GetEntryAtIndex(1)->GetURL());
4139 // And there should be a pending entry for url4.
4140 ASSERT_TRUE(other_controller
.GetPendingEntry());
4141 EXPECT_EQ(url2b
, other_controller
.GetPendingEntry()->GetURL());
4143 // Let the pending entry commit.
4144 other_contents
->TestDidNavigate(other_contents
->GetMainFrame(),
4145 entry
->GetPageID(), 0, false, url2b
,
4146 ui::PAGE_TRANSITION_LINK
);
4148 // The max page ID map should be copied over and updated with the max page ID
4149 // from the current tab.
4150 SiteInstance
* instance1
=
4151 other_controller
.GetEntryAtIndex(1)->site_instance();
4152 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
4155 // Test CopyStateFromAndPrune with 2 urls, a back navigation pending in the
4156 // source, and 1 entry in the target. The back pending entry should be ignored.
4157 TEST_F(NavigationControllerTest
, CopyStateFromAndPruneSourcePending
) {
4158 NavigationControllerImpl
& controller
= controller_impl();
4159 const GURL
url1("http://foo1");
4160 const GURL
url2("http://foo2");
4161 const GURL
url3("http://foo3");
4163 NavigateAndCommit(url1
);
4164 NavigateAndCommit(url2
);
4165 controller
.GoBack();
4167 scoped_ptr
<TestWebContents
> other_contents(
4168 static_cast<TestWebContents
*>(CreateTestWebContents()));
4169 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
4170 other_contents
->NavigateAndCommit(url3
);
4171 other_contents
->ExpectSetHistoryOffsetAndLength(2, 3);
4172 other_controller
.CopyStateFromAndPrune(&controller
, false);
4174 // other_controller should now contain: url1, url2, url3
4176 ASSERT_EQ(3, other_controller
.GetEntryCount());
4177 ASSERT_EQ(2, other_controller
.GetCurrentEntryIndex());
4179 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
4180 EXPECT_EQ(url2
, other_controller
.GetEntryAtIndex(1)->GetURL());
4181 EXPECT_EQ(url3
, other_controller
.GetEntryAtIndex(2)->GetURL());
4182 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(2)->GetPageID());
4184 // The max page ID map should be copied over and updated with the max page ID
4185 // from the current tab.
4186 SiteInstance
* instance1
=
4187 other_controller
.GetEntryAtIndex(2)->site_instance();
4188 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
4191 // Tests CopyStateFromAndPrune with 3 urls in source, 1 in dest,
4192 // when the max entry count is 3. We should prune one entry.
4193 TEST_F(NavigationControllerTest
, CopyStateFromAndPruneMaxEntries
) {
4194 NavigationControllerImpl
& controller
= controller_impl();
4195 size_t original_count
= NavigationControllerImpl::max_entry_count();
4196 const int kMaxEntryCount
= 3;
4198 NavigationControllerImpl::set_max_entry_count_for_testing(kMaxEntryCount
);
4200 const GURL
url1("http://foo/1");
4201 const GURL
url2("http://foo/2");
4202 const GURL
url3("http://foo/3");
4203 const GURL
url4("http://foo/4");
4205 // Create a PrunedListener to observe prune notifications.
4206 PrunedListener
listener(&controller
);
4208 NavigateAndCommit(url1
);
4209 NavigateAndCommit(url2
);
4210 NavigateAndCommit(url3
);
4212 scoped_ptr
<TestWebContents
> other_contents(
4213 static_cast<TestWebContents
*>(CreateTestWebContents()));
4214 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
4215 other_contents
->NavigateAndCommit(url4
);
4216 other_contents
->ExpectSetHistoryOffsetAndLength(2, 3);
4217 other_controller
.CopyStateFromAndPrune(&controller
, false);
4219 // We should have received a pruned notification.
4220 EXPECT_EQ(1, listener
.notification_count_
);
4221 EXPECT_TRUE(listener
.details_
.from_front
);
4222 EXPECT_EQ(1, listener
.details_
.count
);
4224 // other_controller should now contain only 3 urls: url2, url3 and url4.
4226 ASSERT_EQ(3, other_controller
.GetEntryCount());
4228 ASSERT_EQ(2, other_controller
.GetCurrentEntryIndex());
4230 EXPECT_EQ(url2
, other_controller
.GetEntryAtIndex(0)->GetURL());
4231 EXPECT_EQ(url3
, other_controller
.GetEntryAtIndex(1)->GetURL());
4232 EXPECT_EQ(url4
, other_controller
.GetEntryAtIndex(2)->GetURL());
4233 EXPECT_EQ(1, other_controller
.GetEntryAtIndex(0)->GetPageID());
4234 EXPECT_EQ(2, other_controller
.GetEntryAtIndex(1)->GetPageID());
4235 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(2)->GetPageID());
4237 NavigationControllerImpl::set_max_entry_count_for_testing(original_count
);
4240 // Tests CopyStateFromAndPrune with 2 urls in source, 1 in dest, with
4241 // replace_entry set to true.
4242 TEST_F(NavigationControllerTest
, CopyStateFromAndPruneReplaceEntry
) {
4243 NavigationControllerImpl
& controller
= controller_impl();
4244 const GURL
url1("http://foo/1");
4245 const GURL
url2("http://foo/2");
4246 const GURL
url3("http://foo/3");
4248 NavigateAndCommit(url1
);
4249 NavigateAndCommit(url2
);
4251 // First two entries should have the same SiteInstance.
4252 SiteInstance
* instance1
= controller
.GetEntryAtIndex(0)->site_instance();
4253 SiteInstance
* instance2
= controller
.GetEntryAtIndex(1)->site_instance();
4254 EXPECT_EQ(instance1
, instance2
);
4255 EXPECT_EQ(0, controller
.GetEntryAtIndex(0)->GetPageID());
4256 EXPECT_EQ(1, controller
.GetEntryAtIndex(1)->GetPageID());
4257 EXPECT_EQ(1, contents()->GetMaxPageIDForSiteInstance(instance1
));
4259 scoped_ptr
<TestWebContents
> other_contents(
4260 static_cast<TestWebContents
*>(CreateTestWebContents()));
4261 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
4262 other_contents
->NavigateAndCommit(url3
);
4263 other_contents
->ExpectSetHistoryOffsetAndLength(1, 2);
4264 other_controller
.CopyStateFromAndPrune(&controller
, true);
4266 // other_controller should now contain the 2 urls: url1 and url3.
4268 ASSERT_EQ(2, other_controller
.GetEntryCount());
4270 ASSERT_EQ(1, other_controller
.GetCurrentEntryIndex());
4272 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
4273 EXPECT_EQ(url3
, other_controller
.GetEntryAtIndex(1)->GetURL());
4274 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(0)->GetPageID());
4275 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(1)->GetPageID());
4277 // A new SiteInstance in a different BrowsingInstance should be used for the
4279 SiteInstance
* instance3
=
4280 other_controller
.GetEntryAtIndex(1)->site_instance();
4281 EXPECT_NE(instance3
, instance1
);
4282 EXPECT_FALSE(instance3
->IsRelatedSiteInstance(instance1
));
4284 // The max page ID map should be copied over and updated with the max page ID
4285 // from the current tab.
4286 EXPECT_EQ(1, other_contents
->GetMaxPageIDForSiteInstance(instance1
));
4287 EXPECT_EQ(0, other_contents
->GetMaxPageIDForSiteInstance(instance3
));
4290 // Tests CopyStateFromAndPrune with 3 urls in source, 1 in dest, when the max
4291 // entry count is 3 and replace_entry is true. We should not prune entries.
4292 TEST_F(NavigationControllerTest
, CopyStateFromAndPruneMaxEntriesReplaceEntry
) {
4293 NavigationControllerImpl
& controller
= controller_impl();
4294 size_t original_count
= NavigationControllerImpl::max_entry_count();
4295 const int kMaxEntryCount
= 3;
4297 NavigationControllerImpl::set_max_entry_count_for_testing(kMaxEntryCount
);
4299 const GURL
url1("http://foo/1");
4300 const GURL
url2("http://foo/2");
4301 const GURL
url3("http://foo/3");
4302 const GURL
url4("http://foo/4");
4304 // Create a PrunedListener to observe prune notifications.
4305 PrunedListener
listener(&controller
);
4307 NavigateAndCommit(url1
);
4308 NavigateAndCommit(url2
);
4309 NavigateAndCommit(url3
);
4311 scoped_ptr
<TestWebContents
> other_contents(
4312 static_cast<TestWebContents
*>(CreateTestWebContents()));
4313 NavigationControllerImpl
& other_controller
= other_contents
->GetController();
4314 other_contents
->NavigateAndCommit(url4
);
4315 other_contents
->ExpectSetHistoryOffsetAndLength(2, 3);
4316 other_controller
.CopyStateFromAndPrune(&controller
, true);
4318 // We should have received no pruned notification.
4319 EXPECT_EQ(0, listener
.notification_count_
);
4321 // other_controller should now contain only 3 urls: url1, url2 and url4.
4323 ASSERT_EQ(3, other_controller
.GetEntryCount());
4325 ASSERT_EQ(2, other_controller
.GetCurrentEntryIndex());
4327 EXPECT_EQ(url1
, other_controller
.GetEntryAtIndex(0)->GetURL());
4328 EXPECT_EQ(url2
, other_controller
.GetEntryAtIndex(1)->GetURL());
4329 EXPECT_EQ(url4
, other_controller
.GetEntryAtIndex(2)->GetURL());
4330 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(0)->GetPageID());
4331 EXPECT_EQ(1, other_controller
.GetEntryAtIndex(1)->GetPageID());
4332 EXPECT_EQ(0, other_controller
.GetEntryAtIndex(2)->GetPageID());
4334 NavigationControllerImpl::set_max_entry_count_for_testing(original_count
);
4337 // Tests that we can navigate to the restored entries
4338 // imported by CopyStateFromAndPrune.
4339 TEST_F(NavigationControllerTest
, CopyRestoredStateAndNavigate
) {
4340 const GURL kRestoredUrls
[] = {
4341 GURL("http://site1.com"),
4342 GURL("http://site2.com"),
4344 const GURL
kInitialUrl("http://site3.com");
4346 ScopedVector
<NavigationEntry
> entries
;
4347 for (size_t i
= 0; i
< arraysize(kRestoredUrls
); ++i
) {
4348 scoped_ptr
<NavigationEntry
> entry
=
4349 NavigationControllerImpl::CreateNavigationEntry(
4350 kRestoredUrls
[i
], Referrer(), ui::PAGE_TRANSITION_RELOAD
, false,
4351 std::string(), browser_context());
4352 entry
->SetPageID(static_cast<int>(i
));
4353 entries
.push_back(entry
.Pass());
4356 // Create a WebContents with restored entries.
4357 scoped_ptr
<TestWebContents
> source_contents(
4358 static_cast<TestWebContents
*>(CreateTestWebContents()));
4359 NavigationControllerImpl
& source_controller
=
4360 source_contents
->GetController();
4361 source_controller
.Restore(
4363 NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY
,
4365 ASSERT_EQ(0u, entries
.size());
4366 source_controller
.LoadIfNecessary();
4367 source_contents
->CommitPendingNavigation();
4369 // Load a page, then copy state from |source_contents|.
4370 NavigateAndCommit(kInitialUrl
);
4371 contents()->ExpectSetHistoryOffsetAndLength(2, 3);
4372 controller_impl().CopyStateFromAndPrune(&source_controller
, false);
4373 ASSERT_EQ(3, controller_impl().GetEntryCount());
4375 // Go back to the first entry one at a time and
4376 // verify that it works as expected.
4377 EXPECT_EQ(2, controller_impl().GetCurrentEntryIndex());
4378 EXPECT_EQ(kInitialUrl
, controller_impl().GetActiveEntry()->GetURL());
4380 controller_impl().GoBack();
4381 contents()->CommitPendingNavigation();
4382 EXPECT_EQ(1, controller_impl().GetCurrentEntryIndex());
4383 EXPECT_EQ(kRestoredUrls
[1], controller_impl().GetActiveEntry()->GetURL());
4385 controller_impl().GoBack();
4386 contents()->CommitPendingNavigation();
4387 EXPECT_EQ(0, controller_impl().GetCurrentEntryIndex());
4388 EXPECT_EQ(kRestoredUrls
[0], controller_impl().GetActiveEntry()->GetURL());
4391 // Tests that navigations initiated from the page (with the history object)
4392 // work as expected, creating pending entries.
4393 TEST_F(NavigationControllerTest
, HistoryNavigate
) {
4394 NavigationControllerImpl
& controller
= controller_impl();
4395 const GURL
url1("http://foo/1");
4396 const GURL
url2("http://foo/2");
4397 const GURL
url3("http://foo/3");
4399 NavigateAndCommit(url1
);
4400 NavigateAndCommit(url2
);
4401 NavigateAndCommit(url3
);
4402 controller
.GoBack();
4403 contents()->CommitPendingNavigation();
4404 process()->sink().ClearMessages();
4406 // Simulate the page calling history.back(). It should create a pending entry.
4407 contents()->OnGoToEntryAtOffset(-1);
4408 EXPECT_EQ(0, controller
.GetPendingEntryIndex());
4409 // The actual cross-navigation is suspended until the current RVH tells us
4410 // it unloaded, simulate that.
4411 contents()->ProceedWithCrossSiteNavigation();
4412 // Also make sure we told the page to navigate.
4413 GURL nav_url
= GetLastNavigationURL();
4414 EXPECT_EQ(url1
, nav_url
);
4415 contents()->CommitPendingNavigation();
4416 process()->sink().ClearMessages();
4418 // Now test history.forward()
4419 contents()->OnGoToEntryAtOffset(2);
4420 EXPECT_EQ(2, controller
.GetPendingEntryIndex());
4421 // The actual cross-navigation is suspended until the current RVH tells us
4422 // it unloaded, simulate that.
4423 contents()->ProceedWithCrossSiteNavigation();
4424 nav_url
= GetLastNavigationURL();
4425 EXPECT_EQ(url3
, nav_url
);
4426 contents()->CommitPendingNavigation();
4427 process()->sink().ClearMessages();
4429 controller
.DiscardNonCommittedEntries();
4431 // Make sure an extravagant history.go() doesn't break.
4432 contents()->OnGoToEntryAtOffset(120); // Out of bounds.
4433 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4434 EXPECT_FALSE(HasNavigationRequest());
4437 // Test call to PruneAllButLastCommitted for the only entry.
4438 TEST_F(NavigationControllerTest
, PruneAllButLastCommittedForSingle
) {
4439 NavigationControllerImpl
& controller
= controller_impl();
4440 const GURL
url1("http://foo1");
4441 NavigateAndCommit(url1
);
4443 contents()->ExpectSetHistoryOffsetAndLength(0, 1);
4445 controller
.PruneAllButLastCommitted();
4447 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4448 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(), url1
);
4451 // Test call to PruneAllButLastCommitted for first entry.
4452 TEST_F(NavigationControllerTest
, PruneAllButLastCommittedForFirst
) {
4453 NavigationControllerImpl
& controller
= controller_impl();
4454 const GURL
url1("http://foo/1");
4455 const GURL
url2("http://foo/2");
4456 const GURL
url3("http://foo/3");
4458 NavigateAndCommit(url1
);
4459 NavigateAndCommit(url2
);
4460 NavigateAndCommit(url3
);
4461 controller
.GoBack();
4462 controller
.GoBack();
4463 contents()->CommitPendingNavigation();
4465 contents()->ExpectSetHistoryOffsetAndLength(0, 1);
4467 controller
.PruneAllButLastCommitted();
4469 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4470 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(), url1
);
4473 // Test call to PruneAllButLastCommitted for intermediate entry.
4474 TEST_F(NavigationControllerTest
, PruneAllButLastCommittedForIntermediate
) {
4475 NavigationControllerImpl
& controller
= controller_impl();
4476 const GURL
url1("http://foo/1");
4477 const GURL
url2("http://foo/2");
4478 const GURL
url3("http://foo/3");
4480 NavigateAndCommit(url1
);
4481 NavigateAndCommit(url2
);
4482 NavigateAndCommit(url3
);
4483 controller
.GoBack();
4484 contents()->CommitPendingNavigation();
4486 contents()->ExpectSetHistoryOffsetAndLength(0, 1);
4488 controller
.PruneAllButLastCommitted();
4490 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4491 EXPECT_EQ(controller
.GetEntryAtIndex(0)->GetURL(), url2
);
4494 // Test call to PruneAllButLastCommitted for a pending entry that is not yet in
4495 // the list of entries.
4496 TEST_F(NavigationControllerTest
, PruneAllButLastCommittedForPendingNotInList
) {
4497 NavigationControllerImpl
& controller
= controller_impl();
4498 const GURL
url1("http://foo/1");
4499 const GURL
url2("http://foo/2");
4500 const GURL
url3("http://foo/3");
4502 NavigateAndCommit(url1
);
4503 NavigateAndCommit(url2
);
4505 // Create a pending entry that is not in the entry list.
4507 url3
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
4508 int entry_id
= controller
.GetPendingEntry()->GetUniqueID();
4509 EXPECT_TRUE(controller
.GetPendingEntry());
4510 EXPECT_EQ(2, controller
.GetEntryCount());
4512 contents()->ExpectSetHistoryOffsetAndLength(0, 1);
4513 controller
.PruneAllButLastCommitted();
4515 // We should only have the last committed and pending entries at this point,
4516 // and the pending entry should still not be in the entry list.
4517 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
4518 EXPECT_EQ(url2
, controller
.GetEntryAtIndex(0)->GetURL());
4519 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4520 EXPECT_TRUE(controller
.GetPendingEntry());
4521 EXPECT_EQ(1, controller
.GetEntryCount());
4523 // Try to commit the pending entry.
4524 main_test_rfh()->PrepareForCommit();
4525 main_test_rfh()->SendNavigate(2, entry_id
, true, url3
);
4526 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4527 EXPECT_FALSE(controller
.GetPendingEntry());
4528 EXPECT_EQ(2, controller
.GetEntryCount());
4529 EXPECT_EQ(url3
, controller
.GetEntryAtIndex(1)->GetURL());
4532 // Test to ensure that when we do a history navigation back to the current
4533 // committed page (e.g., going forward to a slow-loading page, then pressing
4534 // the back button), we just stop the navigation to prevent the throbber from
4535 // running continuously. Otherwise, the RenderViewHost forces the throbber to
4536 // start, but WebKit essentially ignores the navigation and never sends a
4537 // message to stop the throbber.
4538 TEST_F(NavigationControllerTest
, StopOnHistoryNavigationToCurrentPage
) {
4539 NavigationControllerImpl
& controller
= controller_impl();
4540 const GURL
url0("http://foo/0");
4541 const GURL
url1("http://foo/1");
4543 NavigateAndCommit(url0
);
4544 NavigateAndCommit(url1
);
4546 // Go back to the original page, then forward to the slow page, then back
4547 controller
.GoBack();
4548 contents()->CommitPendingNavigation();
4550 controller
.GoForward();
4551 EXPECT_EQ(1, controller
.GetPendingEntryIndex());
4553 controller
.GoBack();
4554 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4557 TEST_F(NavigationControllerTest
, IsInitialNavigation
) {
4558 NavigationControllerImpl
& controller
= controller_impl();
4559 TestNotificationTracker notifications
;
4560 RegisterForAllNavNotifications(¬ifications
, &controller
);
4563 EXPECT_TRUE(controller
.IsInitialNavigation());
4565 // After commit, it stays false.
4566 const GURL
url1("http://foo1");
4567 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url1
);
4568 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4569 navigation_entry_committed_counter_
= 0;
4570 EXPECT_FALSE(controller
.IsInitialNavigation());
4572 // After starting a new navigation, it stays false.
4573 const GURL
url2("http://foo2");
4575 url2
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
4578 // Check that the favicon is not reused across a client redirect.
4579 // (crbug.com/28515)
4580 TEST_F(NavigationControllerTest
, ClearFaviconOnRedirect
) {
4581 const GURL
kPageWithFavicon("http://withfavicon.html");
4582 const GURL
kPageWithoutFavicon("http://withoutfavicon.html");
4583 const GURL
kIconURL("http://withfavicon.ico");
4584 const gfx::Image kDefaultFavicon
= FaviconStatus().image
;
4586 NavigationControllerImpl
& controller
= controller_impl();
4587 TestNotificationTracker notifications
;
4588 RegisterForAllNavNotifications(¬ifications
, &controller
);
4590 main_test_rfh()->NavigateAndCommitRendererInitiated(
4591 0, true, kPageWithFavicon
);
4592 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4593 navigation_entry_committed_counter_
= 0;
4595 NavigationEntry
* entry
= controller
.GetLastCommittedEntry();
4597 EXPECT_EQ(kPageWithFavicon
, entry
->GetURL());
4599 // Simulate Chromium having set the favicon for |kPageWithFavicon|.
4600 content::FaviconStatus
& favicon_status
= entry
->GetFavicon();
4601 favicon_status
.image
= CreateImage(SK_ColorWHITE
);
4602 favicon_status
.url
= kIconURL
;
4603 favicon_status
.valid
= true;
4604 EXPECT_FALSE(DoImagesMatch(kDefaultFavicon
, entry
->GetFavicon().image
));
4606 main_test_rfh()->SendRendererInitiatedNavigationRequest(kPageWithoutFavicon
,
4608 main_test_rfh()->PrepareForCommit();
4609 main_test_rfh()->SendNavigateWithTransition(
4612 false, // no new entry
4613 kPageWithoutFavicon
, ui::PAGE_TRANSITION_CLIENT_REDIRECT
);
4614 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4615 navigation_entry_committed_counter_
= 0;
4617 entry
= controller
.GetLastCommittedEntry();
4619 EXPECT_EQ(kPageWithoutFavicon
, entry
->GetURL());
4621 EXPECT_TRUE(DoImagesMatch(kDefaultFavicon
, entry
->GetFavicon().image
));
4624 // Check that the favicon is not cleared for NavigationEntries which were
4625 // previously navigated to.
4626 TEST_F(NavigationControllerTest
, BackNavigationDoesNotClearFavicon
) {
4627 const GURL
kUrl1("http://www.a.com/1");
4628 const GURL
kUrl2("http://www.a.com/2");
4629 const GURL
kIconURL("http://www.a.com/1/favicon.ico");
4631 NavigationControllerImpl
& controller
= controller_impl();
4632 TestNotificationTracker notifications
;
4633 RegisterForAllNavNotifications(¬ifications
, &controller
);
4635 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, kUrl1
);
4636 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4637 navigation_entry_committed_counter_
= 0;
4639 // Simulate Chromium having set the favicon for |kUrl1|.
4640 gfx::Image favicon_image
= CreateImage(SK_ColorWHITE
);
4641 content::NavigationEntry
* entry
= controller
.GetLastCommittedEntry();
4643 content::FaviconStatus
& favicon_status
= entry
->GetFavicon();
4644 favicon_status
.image
= favicon_image
;
4645 favicon_status
.url
= kIconURL
;
4646 favicon_status
.valid
= true;
4648 // Navigate to another page and go back to the original page.
4649 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, kUrl2
);
4650 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4651 navigation_entry_committed_counter_
= 0;
4652 main_test_rfh()->SendRendererInitiatedNavigationRequest(kUrl1
, false);
4653 main_test_rfh()->PrepareForCommit();
4654 main_test_rfh()->SendNavigateWithTransition(
4655 0, controller
.GetEntryAtIndex(0)->GetUniqueID(), false, kUrl1
,
4656 ui::PAGE_TRANSITION_FORWARD_BACK
);
4657 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4658 navigation_entry_committed_counter_
= 0;
4660 // Verify that the favicon for the page at |kUrl1| was not cleared.
4661 entry
= controller
.GetEntryAtIndex(0);
4663 EXPECT_EQ(kUrl1
, entry
->GetURL());
4664 EXPECT_TRUE(DoImagesMatch(favicon_image
, entry
->GetFavicon().image
));
4667 // The test crashes on android: http://crbug.com/170449
4668 #if defined(OS_ANDROID)
4669 #define MAYBE_PurgeScreenshot DISABLED_PurgeScreenshot
4671 #define MAYBE_PurgeScreenshot PurgeScreenshot
4673 // Tests that screenshot are purged correctly.
4674 TEST_F(NavigationControllerTest
, MAYBE_PurgeScreenshot
) {
4675 NavigationControllerImpl
& controller
= controller_impl();
4677 NavigationEntryImpl
* entry
;
4679 // Navigate enough times to make sure that some screenshots are purged.
4680 for (int i
= 0; i
< 12; ++i
) {
4681 const GURL
url(base::StringPrintf("http://foo%d/", i
));
4682 NavigateAndCommit(url
);
4683 EXPECT_EQ(i
, controller
.GetCurrentEntryIndex());
4686 MockScreenshotManager
* screenshot_manager
=
4687 new MockScreenshotManager(&controller
);
4688 controller
.SetScreenshotManager(make_scoped_ptr(screenshot_manager
));
4689 for (int i
= 0; i
< controller
.GetEntryCount(); ++i
) {
4690 entry
= controller
.GetEntryAtIndex(i
);
4691 screenshot_manager
->TakeScreenshotFor(entry
);
4692 EXPECT_TRUE(entry
->screenshot().get());
4695 NavigateAndCommit(GURL("https://foo/"));
4696 EXPECT_EQ(13, controller
.GetEntryCount());
4697 entry
= controller
.GetEntryAtIndex(11);
4698 screenshot_manager
->TakeScreenshotFor(entry
);
4700 for (int i
= 0; i
< 2; ++i
) {
4701 entry
= controller
.GetEntryAtIndex(i
);
4702 EXPECT_FALSE(entry
->screenshot().get()) << "Screenshot " << i
4706 for (int i
= 2; i
< controller
.GetEntryCount() - 1; ++i
) {
4707 entry
= controller
.GetEntryAtIndex(i
);
4708 EXPECT_TRUE(entry
->screenshot().get()) << "Screenshot not found for " << i
;
4711 // Navigate to index 5 and then try to assign screenshot to all entries.
4712 controller
.GoToIndex(5);
4713 contents()->CommitPendingNavigation();
4714 EXPECT_EQ(5, controller
.GetCurrentEntryIndex());
4715 for (int i
= 0; i
< controller
.GetEntryCount() - 1; ++i
) {
4716 entry
= controller
.GetEntryAtIndex(i
);
4717 screenshot_manager
->TakeScreenshotFor(entry
);
4720 for (int i
= 10; i
<= 12; ++i
) {
4721 entry
= controller
.GetEntryAtIndex(i
);
4722 EXPECT_FALSE(entry
->screenshot().get()) << "Screenshot " << i
4724 screenshot_manager
->TakeScreenshotFor(entry
);
4727 // Navigate to index 7 and assign screenshot to all entries.
4728 controller
.GoToIndex(7);
4729 contents()->CommitPendingNavigation();
4730 EXPECT_EQ(7, controller
.GetCurrentEntryIndex());
4731 for (int i
= 0; i
< controller
.GetEntryCount() - 1; ++i
) {
4732 entry
= controller
.GetEntryAtIndex(i
);
4733 screenshot_manager
->TakeScreenshotFor(entry
);
4736 for (int i
= 0; i
< 2; ++i
) {
4737 entry
= controller
.GetEntryAtIndex(i
);
4738 EXPECT_FALSE(entry
->screenshot().get()) << "Screenshot " << i
4742 // Clear all screenshots.
4743 EXPECT_EQ(13, controller
.GetEntryCount());
4744 EXPECT_EQ(10, screenshot_manager
->GetScreenshotCount());
4745 controller
.ClearAllScreenshots();
4746 EXPECT_EQ(0, screenshot_manager
->GetScreenshotCount());
4747 for (int i
= 0; i
< controller
.GetEntryCount(); ++i
) {
4748 entry
= controller
.GetEntryAtIndex(i
);
4749 EXPECT_FALSE(entry
->screenshot().get()) << "Screenshot " << i
4754 TEST_F(NavigationControllerTest
, PushStateUpdatesTitleAndFavicon
) {
4756 main_test_rfh()->NavigateAndCommitRendererInitiated(
4757 1, true, GURL("http://foo"));
4759 // Set title and favicon.
4760 base::string16
title(base::ASCIIToUTF16("Title"));
4761 FaviconStatus favicon
;
4762 favicon
.valid
= true;
4763 favicon
.url
= GURL("http://foo/favicon.ico");
4764 controller().GetLastCommittedEntry()->SetTitle(title
);
4765 controller().GetLastCommittedEntry()->GetFavicon() = favicon
;
4767 // history.pushState() is called.
4768 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
4769 GURL
kUrl2("http://foo#foo");
4771 params
.nav_entry_id
= 0;
4772 params
.did_create_new_entry
= true;
4774 params
.page_state
= PageState::CreateFromURL(kUrl2
);
4775 params
.was_within_same_page
= true;
4776 main_test_rfh()->SendRendererInitiatedNavigationRequest(kUrl2
, false);
4777 main_test_rfh()->PrepareForCommit();
4778 main_test_rfh()->SendNavigateWithParams(¶ms
);
4780 // The title should immediately be visible on the new NavigationEntry.
4781 base::string16 new_title
=
4782 controller().GetLastCommittedEntry()->GetTitleForDisplay(std::string());
4783 EXPECT_EQ(title
, new_title
);
4784 FaviconStatus new_favicon
=
4785 controller().GetLastCommittedEntry()->GetFavicon();
4786 EXPECT_EQ(favicon
.valid
, new_favicon
.valid
);
4787 EXPECT_EQ(favicon
.url
, new_favicon
.url
);
4790 // Test that the navigation controller clears its session history when a
4791 // navigation commits with the clear history list flag set.
4792 TEST_F(NavigationControllerTest
, ClearHistoryList
) {
4793 const GURL
url1("http://foo1");
4794 const GURL
url2("http://foo2");
4795 const GURL
url3("http://foo3");
4796 const GURL
url4("http://foo4");
4798 NavigationControllerImpl
& controller
= controller_impl();
4800 // Create a session history with three entries, second entry is active.
4801 NavigateAndCommit(url1
);
4802 NavigateAndCommit(url2
);
4803 NavigateAndCommit(url3
);
4804 controller
.GoBack();
4805 contents()->CommitPendingNavigation();
4806 EXPECT_EQ(3, controller
.GetEntryCount());
4807 EXPECT_EQ(1, controller
.GetCurrentEntryIndex());
4809 // Create a new pending navigation, and indicate that the session history
4810 // should be cleared.
4811 NavigationController::LoadURLParams
params(url4
);
4812 params
.should_clear_history_list
= true;
4813 controller
.LoadURLWithParams(params
);
4815 // Verify that the pending entry correctly indicates that the session history
4816 // should be cleared.
4817 NavigationEntryImpl
* entry
= controller
.GetPendingEntry();
4819 EXPECT_TRUE(entry
->should_clear_history_list());
4821 // Assume that the RenderFrame correctly cleared its history and commit the
4823 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
4824 switches::kEnableBrowserSideNavigation
)) {
4825 contents()->GetMainFrame()->SendBeforeUnloadACK(true);
4827 contents()->GetPendingMainFrame()->
4828 set_simulate_history_list_was_cleared(true);
4829 contents()->CommitPendingNavigation();
4831 // Verify that the NavigationController's session history was correctly
4833 EXPECT_EQ(1, controller
.GetEntryCount());
4834 EXPECT_EQ(0, controller
.GetCurrentEntryIndex());
4835 EXPECT_EQ(0, controller
.GetLastCommittedEntryIndex());
4836 EXPECT_EQ(-1, controller
.GetPendingEntryIndex());
4837 EXPECT_FALSE(controller
.CanGoBack());
4838 EXPECT_FALSE(controller
.CanGoForward());
4839 EXPECT_EQ(url4
, controller
.GetVisibleEntry()->GetURL());
4842 TEST_F(NavigationControllerTest
, PostThenReplaceStateThenReload
) {
4843 scoped_ptr
<TestWebContentsDelegate
> delegate(new TestWebContentsDelegate());
4844 EXPECT_FALSE(contents()->GetDelegate());
4845 contents()->SetDelegate(delegate
.get());
4848 GURL
url("http://foo");
4849 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
4851 params
.nav_entry_id
= 0;
4852 params
.did_create_new_entry
= true;
4854 params
.transition
= ui::PAGE_TRANSITION_FORM_SUBMIT
;
4855 params
.gesture
= NavigationGestureUser
;
4856 params
.page_state
= PageState::CreateFromURL(url
);
4857 params
.was_within_same_page
= false;
4858 params
.is_post
= true;
4860 main_test_rfh()->SendRendererInitiatedNavigationRequest(url
, false);
4861 main_test_rfh()->PrepareForCommit();
4862 contents()->GetMainFrame()->SendNavigateWithParams(¶ms
);
4864 // history.replaceState() is called.
4865 GURL
replace_url("http://foo#foo");
4867 params
.nav_entry_id
= 0;
4868 params
.did_create_new_entry
= false;
4869 params
.url
= replace_url
;
4870 params
.transition
= ui::PAGE_TRANSITION_LINK
;
4871 params
.gesture
= NavigationGestureUser
;
4872 params
.page_state
= PageState::CreateFromURL(replace_url
);
4873 params
.was_within_same_page
= true;
4874 params
.is_post
= false;
4875 params
.post_id
= -1;
4876 main_test_rfh()->SendRendererInitiatedNavigationRequest(replace_url
, false);
4877 main_test_rfh()->PrepareForCommit();
4878 contents()->GetMainFrame()->SendNavigateWithParams(¶ms
);
4880 // Now reload. replaceState overrides the POST, so we should not show a
4881 // repost warning dialog.
4882 controller_impl().Reload(true);
4883 EXPECT_EQ(0, delegate
->repost_form_warning_count());
4886 TEST_F(NavigationControllerTest
, UnreachableURLGivesErrorPage
) {
4887 GURL
url("http://foo");
4888 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
4890 params
.nav_entry_id
= 0;
4891 params
.did_create_new_entry
= true;
4893 params
.transition
= ui::PAGE_TRANSITION_LINK
;
4894 params
.gesture
= NavigationGestureUser
;
4895 params
.page_state
= PageState::CreateFromURL(url
);
4896 params
.was_within_same_page
= false;
4897 params
.is_post
= true;
4899 params
.url_is_unreachable
= true;
4901 // Navigate to new page.
4903 LoadCommittedDetails details
;
4904 controller_impl().RendererDidNavigate(main_test_rfh(), params
, &details
);
4905 EXPECT_EQ(PAGE_TYPE_ERROR
,
4906 controller_impl().GetLastCommittedEntry()->GetPageType());
4907 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE
, details
.type
);
4910 // Navigate to existing page.
4912 params
.did_create_new_entry
= false;
4913 LoadCommittedDetails details
;
4914 controller_impl().RendererDidNavigate(main_test_rfh(), params
, &details
);
4915 EXPECT_EQ(PAGE_TYPE_ERROR
,
4916 controller_impl().GetLastCommittedEntry()->GetPageType());
4917 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE
, details
.type
);
4920 // Navigate to same page.
4921 // Note: The call to LoadURL() creates a pending entry in order to trigger the
4922 // same-page transition.
4923 controller_impl().LoadURL(
4924 url
, Referrer(), ui::PAGE_TRANSITION_TYPED
, std::string());
4925 params
.nav_entry_id
= controller_impl().GetPendingEntry()->GetUniqueID();
4926 params
.transition
= ui::PAGE_TRANSITION_TYPED
;
4928 LoadCommittedDetails details
;
4929 controller_impl().RendererDidNavigate(main_test_rfh(), params
, &details
);
4930 EXPECT_EQ(PAGE_TYPE_ERROR
,
4931 controller_impl().GetLastCommittedEntry()->GetPageType());
4932 EXPECT_EQ(NAVIGATION_TYPE_SAME_PAGE
, details
.type
);
4935 // Navigate in page.
4936 params
.url
= GURL("http://foo#foo");
4937 params
.transition
= ui::PAGE_TRANSITION_LINK
;
4938 params
.was_within_same_page
= true;
4940 LoadCommittedDetails details
;
4941 controller_impl().RendererDidNavigate(main_test_rfh(), params
, &details
);
4942 EXPECT_EQ(PAGE_TYPE_ERROR
,
4943 controller_impl().GetLastCommittedEntry()->GetPageType());
4944 EXPECT_TRUE(details
.is_in_page
);
4948 // Tests that if a stale navigation comes back from the renderer, it is properly
4950 TEST_F(NavigationControllerTest
, StaleNavigationsResurrected
) {
4951 NavigationControllerImpl
& controller
= controller_impl();
4952 TestNotificationTracker notifications
;
4953 RegisterForAllNavNotifications(¬ifications
, &controller
);
4956 const GURL
url_a("http://foo.com/a");
4957 main_test_rfh()->NavigateAndCommitRendererInitiated(0, true, url_a
);
4958 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4959 navigation_entry_committed_counter_
= 0;
4960 EXPECT_EQ(1, controller
.GetEntryCount());
4961 EXPECT_EQ(0, controller
.GetCurrentEntryIndex());
4964 const GURL
url_b("http://foo.com/b");
4965 main_test_rfh()->NavigateAndCommitRendererInitiated(1, true, url_b
);
4966 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4967 navigation_entry_committed_counter_
= 0;
4968 EXPECT_EQ(2, controller
.GetEntryCount());
4969 EXPECT_EQ(1, controller
.GetCurrentEntryIndex());
4970 int b_entry_id
= controller
.GetLastCommittedEntry()->GetUniqueID();
4971 int b_page_id
= controller
.GetLastCommittedEntry()->GetPageID();
4974 controller
.GoBack();
4975 contents()->CommitPendingNavigation();
4976 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4977 navigation_entry_committed_counter_
= 0;
4978 EXPECT_EQ(2, controller
.GetEntryCount());
4979 EXPECT_EQ(0, controller
.GetCurrentEntryIndex());
4981 // Start going forward to page B.
4982 controller
.GoForward();
4984 // But the renderer unilaterally navigates to page C, pruning B.
4985 const GURL
url_c("http://foo.com/c");
4986 main_test_rfh()->NavigateAndCommitRendererInitiated(2, true, url_c
);
4987 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4988 navigation_entry_committed_counter_
= 0;
4989 EXPECT_EQ(2, controller
.GetEntryCount());
4990 EXPECT_EQ(1, controller
.GetCurrentEntryIndex());
4991 int c_entry_id
= controller
.GetLastCommittedEntry()->GetUniqueID();
4992 EXPECT_NE(c_entry_id
, b_entry_id
);
4994 // And then the navigation to B gets committed.
4995 main_test_rfh()->SendNavigate(b_page_id
, b_entry_id
, false, url_b
);
4996 EXPECT_EQ(1U, navigation_entry_committed_counter_
);
4997 navigation_entry_committed_counter_
= 0;
4999 // Even though we were doing a history navigation, because the entry was
5000 // pruned it will end up as a *new* entry at the end of the entry list. This
5001 // means that occasionally a navigation conflict will end up with one entry
5002 // bubbling to the end of the entry list, but that's the least-bad option.
5003 EXPECT_EQ(3, controller
.GetEntryCount());
5004 EXPECT_EQ(2, controller
.GetCurrentEntryIndex());
5005 EXPECT_EQ(url_a
, controller
.GetEntryAtIndex(0)->GetURL());
5006 EXPECT_EQ(url_c
, controller
.GetEntryAtIndex(1)->GetURL());
5007 EXPECT_EQ(url_b
, controller
.GetEntryAtIndex(2)->GetURL());
5010 // Test that if a renderer provides bogus security info (that fails to
5011 // deserialize properly) when reporting a navigation, the renderer gets
5013 TEST_F(NavigationControllerTest
, RendererNavigateBogusSecurityInfo
) {
5014 GURL
url("http://foo.test");
5015 FrameHostMsg_DidCommitProvisionalLoad_Params params
;
5017 params
.nav_entry_id
= 0;
5018 params
.did_create_new_entry
= true;
5020 params
.transition
= ui::PAGE_TRANSITION_LINK
;
5021 params
.should_update_history
= true;
5022 params
.gesture
= NavigationGestureUser
;
5023 params
.is_post
= false;
5024 params
.page_state
= PageState::CreateFromURL(url
);
5025 params
.was_within_same_page
= false;
5026 params
.security_info
= "bogus security info!";
5028 LoadCommittedDetails details
;
5029 EXPECT_EQ(0, main_test_rfh()->GetProcess()->bad_msg_count());
5031 controller_impl().RendererDidNavigate(main_test_rfh(), params
, &details
));
5033 SSLStatus default_ssl_status
;
5034 EXPECT_EQ(default_ssl_status
.security_style
,
5035 details
.ssl_status
.security_style
);
5036 EXPECT_EQ(default_ssl_status
.cert_id
, details
.ssl_status
.cert_id
);
5037 EXPECT_EQ(default_ssl_status
.cert_status
, details
.ssl_status
.cert_status
);
5038 EXPECT_EQ(default_ssl_status
.security_bits
, details
.ssl_status
.security_bits
);
5039 EXPECT_EQ(default_ssl_status
.connection_status
,
5040 details
.ssl_status
.connection_status
);
5041 EXPECT_EQ(default_ssl_status
.content_status
,
5042 details
.ssl_status
.content_status
);
5043 EXPECT_EQ(0u, details
.ssl_status
.signed_certificate_timestamp_ids
.size());
5045 EXPECT_EQ(1, main_test_rfh()->GetProcess()->bad_msg_count());
5048 } // namespace content