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