[Session restore] Rename group name Enabled to Restore.
[chromium-blink-merge.git] / content / browser / frame_host / navigation_controller_impl_unittest.cc
blobc1fce3c30fec4e115e8f926bf45b66b0ef7033ca
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/view_messages.h"
26 #include "content/public/browser/navigation_details.h"
27 #include "content/public/browser/notification_registrar.h"
28 #include "content/public/browser/notification_types.h"
29 #include "content/public/browser/render_view_host.h"
30 #include "content/public/browser/web_contents_delegate.h"
31 #include "content/public/browser/web_contents_observer.h"
32 #include "content/public/common/content_switches.h"
33 #include "content/public/common/page_state.h"
34 #include "content/public/common/page_type.h"
35 #include "content/public/common/url_constants.h"
36 #include "content/public/test/mock_render_process_host.h"
37 #include "content/public/test/test_notification_tracker.h"
38 #include "content/public/test/test_utils.h"
39 #include "content/test/test_render_frame_host.h"
40 #include "content/test/test_render_view_host.h"
41 #include "content/test/test_web_contents.h"
42 #include "net/base/net_util.h"
43 #include "skia/ext/platform_canvas.h"
44 #include "testing/gtest/include/gtest/gtest.h"
46 using base::Time;
48 namespace {
50 // Creates an image with a 1x1 SkBitmap of the specified |color|.
51 gfx::Image CreateImage(SkColor color) {
52 SkBitmap bitmap;
53 bitmap.allocN32Pixels(1, 1);
54 bitmap.eraseColor(color);
55 return gfx::Image::CreateFrom1xBitmap(bitmap);
58 // Returns true if images |a| and |b| have the same pixel data.
59 bool DoImagesMatch(const gfx::Image& a, const gfx::Image& b) {
60 // Assume that if the 1x bitmaps match, the images match.
61 SkBitmap a_bitmap = a.AsBitmap();
62 SkBitmap b_bitmap = b.AsBitmap();
64 if (a_bitmap.width() != b_bitmap.width() ||
65 a_bitmap.height() != b_bitmap.height()) {
66 return false;
68 SkAutoLockPixels a_bitmap_lock(a_bitmap);
69 SkAutoLockPixels b_bitmap_lock(b_bitmap);
70 return memcmp(a_bitmap.getPixels(),
71 b_bitmap.getPixels(),
72 a_bitmap.getSize()) == 0;
75 class MockScreenshotManager : public content::NavigationEntryScreenshotManager {
76 public:
77 explicit MockScreenshotManager(content::NavigationControllerImpl* owner)
78 : content::NavigationEntryScreenshotManager(owner),
79 encoding_screenshot_in_progress_(false) {
82 ~MockScreenshotManager() override {}
84 void TakeScreenshotFor(content::NavigationEntryImpl* entry) {
85 SkBitmap bitmap;
86 bitmap.allocPixels(SkImageInfo::Make(
87 1, 1, kAlpha_8_SkColorType, kPremul_SkAlphaType));
88 bitmap.eraseARGB(0, 0, 0, 0);
89 encoding_screenshot_in_progress_ = true;
90 OnScreenshotTaken(entry->GetUniqueID(), bitmap, content::READBACK_SUCCESS);
91 WaitUntilScreenshotIsReady();
94 int GetScreenshotCount() {
95 return content::NavigationEntryScreenshotManager::GetScreenshotCount();
98 void WaitUntilScreenshotIsReady() {
99 if (!encoding_screenshot_in_progress_)
100 return;
101 message_loop_runner_ = new content::MessageLoopRunner;
102 message_loop_runner_->Run();
105 private:
106 // Overridden from content::NavigationEntryScreenshotManager:
107 void TakeScreenshotImpl(content::RenderViewHost* host,
108 content::NavigationEntryImpl* entry) override {}
110 void OnScreenshotSet(content::NavigationEntryImpl* entry) override {
111 encoding_screenshot_in_progress_ = false;
112 NavigationEntryScreenshotManager::OnScreenshotSet(entry);
113 if (message_loop_runner_.get())
114 message_loop_runner_->Quit();
117 scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
118 bool encoding_screenshot_in_progress_;
120 DISALLOW_COPY_AND_ASSIGN(MockScreenshotManager);
123 } // namespace
125 namespace content {
127 // TimeSmoother tests ----------------------------------------------------------
129 // With no duplicates, GetSmoothedTime should be the identity
130 // function.
131 TEST(TimeSmoother, Basic) {
132 NavigationControllerImpl::TimeSmoother smoother;
133 for (int64 i = 1; i < 1000; ++i) {
134 base::Time t = base::Time::FromInternalValue(i);
135 EXPECT_EQ(t, smoother.GetSmoothedTime(t));
139 // With a single duplicate and timestamps thereafter increasing by one
140 // microsecond, the smoothed time should always be one behind.
141 TEST(TimeSmoother, SingleDuplicate) {
142 NavigationControllerImpl::TimeSmoother smoother;
143 base::Time t = base::Time::FromInternalValue(1);
144 EXPECT_EQ(t, smoother.GetSmoothedTime(t));
145 for (int64 i = 1; i < 1000; ++i) {
146 base::Time expected_t = base::Time::FromInternalValue(i + 1);
147 t = base::Time::FromInternalValue(i);
148 EXPECT_EQ(expected_t, smoother.GetSmoothedTime(t));
152 // With k duplicates and timestamps thereafter increasing by one
153 // microsecond, the smoothed time should always be k behind.
154 TEST(TimeSmoother, ManyDuplicates) {
155 const int64 kNumDuplicates = 100;
156 NavigationControllerImpl::TimeSmoother smoother;
157 base::Time t = base::Time::FromInternalValue(1);
158 for (int64 i = 0; i < kNumDuplicates; ++i) {
159 base::Time expected_t = base::Time::FromInternalValue(i + 1);
160 EXPECT_EQ(expected_t, smoother.GetSmoothedTime(t));
162 for (int64 i = 1; i < 1000; ++i) {
163 base::Time expected_t =
164 base::Time::FromInternalValue(i + kNumDuplicates);
165 t = base::Time::FromInternalValue(i);
166 EXPECT_EQ(expected_t, smoother.GetSmoothedTime(t));
170 // If the clock jumps far back enough after a run of duplicates, it
171 // should immediately jump to that value.
172 TEST(TimeSmoother, ClockBackwardsJump) {
173 const int64 kNumDuplicates = 100;
174 NavigationControllerImpl::TimeSmoother smoother;
175 base::Time t = base::Time::FromInternalValue(1000);
176 for (int64 i = 0; i < kNumDuplicates; ++i) {
177 base::Time expected_t = base::Time::FromInternalValue(i + 1000);
178 EXPECT_EQ(expected_t, smoother.GetSmoothedTime(t));
180 t = base::Time::FromInternalValue(500);
181 EXPECT_EQ(t, smoother.GetSmoothedTime(t));
184 // NavigationControllerTest ----------------------------------------------------
186 class NavigationControllerTest
187 : public RenderViewHostImplTestHarness,
188 public WebContentsObserver {
189 public:
190 NavigationControllerTest() : navigation_entry_committed_counter_(0) {
193 void SetUp() override {
194 RenderViewHostImplTestHarness::SetUp();
195 WebContents* web_contents = RenderViewHostImplTestHarness::web_contents();
196 ASSERT_TRUE(web_contents); // The WebContents should be created by now.
197 WebContentsObserver::Observe(web_contents);
200 // WebContentsObserver:
201 void DidStartNavigationToPendingEntry(
202 const GURL& url,
203 NavigationController::ReloadType reload_type) override {
204 navigated_url_ = url;
207 void NavigationEntryCommitted(
208 const LoadCommittedDetails& load_details) override {
209 navigation_entry_committed_counter_++;
212 const GURL& navigated_url() const {
213 return navigated_url_;
216 NavigationControllerImpl& controller_impl() {
217 return static_cast<NavigationControllerImpl&>(controller());
220 bool HasNavigationRequest() {
221 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
222 switches::kEnableBrowserSideNavigation)) {
223 FrameTreeNode* root = contents()->GetFrameTree()->root();
224 NavigationRequest* navigation_request = static_cast<NavigatorImpl*>(
225 root->navigator())->GetNavigationRequestForNodeForTesting(root);
226 return navigation_request != 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 FrameTreeNode* root = contents()->GetFrameTree()->root();
236 NavigationRequest* navigation_request = static_cast<NavigatorImpl*>(
237 root->navigator())->GetNavigationRequestForNodeForTesting(root);
238 CHECK(navigation_request);
239 return navigation_request->common_params().url;
241 const IPC::Message* message =
242 process()->sink().GetFirstMessageMatching(FrameMsg_Navigate::ID);
243 CHECK(message);
244 Tuple<CommonNavigationParams, StartNavigationParams,
245 RequestNavigationParams> nav_params;
246 FrameMsg_Navigate::Read(message, &nav_params);
247 return get<0>(nav_params).url;
250 protected:
251 GURL navigated_url_;
252 size_t navigation_entry_committed_counter_;
255 void RegisterForAllNavNotifications(TestNotificationTracker* tracker,
256 NavigationController* controller) {
257 tracker->ListenFor(NOTIFICATION_NAV_LIST_PRUNED,
258 Source<NavigationController>(controller));
259 tracker->ListenFor(NOTIFICATION_NAV_ENTRY_CHANGED,
260 Source<NavigationController>(controller));
263 class TestWebContentsDelegate : public WebContentsDelegate {
264 public:
265 explicit TestWebContentsDelegate() :
266 navigation_state_change_count_(0),
267 repost_form_warning_count_(0) {}
269 int navigation_state_change_count() {
270 return navigation_state_change_count_;
273 int repost_form_warning_count() {
274 return repost_form_warning_count_;
277 // Keep track of whether the tab has notified us of a navigation state change.
278 void NavigationStateChanged(WebContents* source,
279 InvalidateTypes changed_flags) override {
280 navigation_state_change_count_++;
283 void ShowRepostFormWarningDialog(WebContents* source) override {
284 repost_form_warning_count_++;
287 private:
288 // The number of times NavigationStateChanged has been called.
289 int navigation_state_change_count_;
291 // The number of times ShowRepostFormWarningDialog() was called.
292 int repost_form_warning_count_;
295 // -----------------------------------------------------------------------------
297 TEST_F(NavigationControllerTest, Defaults) {
298 NavigationControllerImpl& controller = controller_impl();
300 EXPECT_FALSE(controller.GetPendingEntry());
301 EXPECT_FALSE(controller.GetVisibleEntry());
302 EXPECT_FALSE(controller.GetLastCommittedEntry());
303 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
304 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), -1);
305 EXPECT_EQ(controller.GetEntryCount(), 0);
306 EXPECT_FALSE(controller.CanGoBack());
307 EXPECT_FALSE(controller.CanGoForward());
310 TEST_F(NavigationControllerTest, GoToOffset) {
311 NavigationControllerImpl& controller = controller_impl();
312 TestNotificationTracker notifications;
313 RegisterForAllNavNotifications(&notifications, &controller);
315 const int kNumUrls = 5;
316 std::vector<GURL> urls(kNumUrls);
317 for (int i = 0; i < kNumUrls; ++i) {
318 urls[i] = GURL(base::StringPrintf("http://www.a.com/%d", i));
321 main_test_rfh()->SendRendererInitiatedNavigationRequest(urls[0], true);
322 main_test_rfh()->PrepareForCommit();
323 main_test_rfh()->SendNavigate(0, urls[0]);
324 EXPECT_EQ(1U, navigation_entry_committed_counter_);
325 navigation_entry_committed_counter_ = 0;
326 EXPECT_EQ(urls[0], controller.GetVisibleEntry()->GetVirtualURL());
327 EXPECT_FALSE(controller.CanGoBack());
328 EXPECT_FALSE(controller.CanGoForward());
329 EXPECT_FALSE(controller.CanGoToOffset(1));
331 for (int i = 1; i <= 4; ++i) {
332 main_test_rfh()->SendRendererInitiatedNavigationRequest(urls[i], true);
333 main_test_rfh()->PrepareForCommit();
334 main_test_rfh()->SendNavigate(i, urls[i]);
335 EXPECT_EQ(1U, navigation_entry_committed_counter_);
336 navigation_entry_committed_counter_ = 0;
337 EXPECT_EQ(urls[i], controller.GetVisibleEntry()->GetVirtualURL());
338 EXPECT_TRUE(controller.CanGoToOffset(-i));
339 EXPECT_FALSE(controller.CanGoToOffset(-(i + 1)));
340 EXPECT_FALSE(controller.CanGoToOffset(1));
343 // We have loaded 5 pages, and are currently at the last-loaded page.
344 int url_index = 4;
346 enum Tests {
347 GO_TO_MIDDLE_PAGE = -2,
348 GO_FORWARDS = 1,
349 GO_BACKWARDS = -1,
350 GO_TO_BEGINNING = -2,
351 GO_TO_END = 4,
352 NUM_TESTS = 5,
355 const int test_offsets[NUM_TESTS] = {
356 GO_TO_MIDDLE_PAGE,
357 GO_FORWARDS,
358 GO_BACKWARDS,
359 GO_TO_BEGINNING,
360 GO_TO_END
363 for (int test = 0; test < NUM_TESTS; ++test) {
364 int offset = test_offsets[test];
365 controller.GoToOffset(offset);
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, 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 // Creating a pending notification should not have issued any of the
393 // notifications we're listening for.
394 EXPECT_EQ(0U, notifications.size());
396 // The load should now be pending.
397 EXPECT_EQ(controller.GetEntryCount(), 0);
398 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), -1);
399 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
400 EXPECT_FALSE(controller.GetLastCommittedEntry());
401 ASSERT_TRUE(controller.GetPendingEntry());
402 EXPECT_EQ(controller.GetPendingEntry(), controller.GetVisibleEntry());
403 EXPECT_FALSE(controller.CanGoBack());
404 EXPECT_FALSE(controller.CanGoForward());
405 EXPECT_EQ(contents()->GetMaxPageID(), -1);
407 // Neither the timestamp nor the status code should have been set yet.
408 EXPECT_TRUE(controller.GetPendingEntry()->GetTimestamp().is_null());
409 EXPECT_EQ(0, controller.GetPendingEntry()->GetHttpStatusCode());
411 // We should have gotten no notifications from the preceeding checks.
412 EXPECT_EQ(0U, notifications.size());
414 main_test_rfh()->PrepareForCommit();
415 main_test_rfh()->SendNavigate(0, url1);
416 EXPECT_EQ(1U, navigation_entry_committed_counter_);
417 navigation_entry_committed_counter_ = 0;
419 // The load should now be committed.
420 EXPECT_EQ(controller.GetEntryCount(), 1);
421 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
422 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
423 EXPECT_TRUE(controller.GetLastCommittedEntry());
424 EXPECT_FALSE(controller.GetPendingEntry());
425 ASSERT_TRUE(controller.GetVisibleEntry());
426 EXPECT_FALSE(controller.CanGoBack());
427 EXPECT_FALSE(controller.CanGoForward());
428 EXPECT_EQ(contents()->GetMaxPageID(), 0);
429 EXPECT_EQ(0, controller.GetLastCommittedEntry()->bindings());
431 // The timestamp should have been set.
432 EXPECT_FALSE(controller.GetVisibleEntry()->GetTimestamp().is_null());
434 // Load another...
435 controller.LoadURL(
436 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
438 // The load should now be pending.
439 EXPECT_EQ(controller.GetEntryCount(), 1);
440 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
441 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
442 EXPECT_TRUE(controller.GetLastCommittedEntry());
443 ASSERT_TRUE(controller.GetPendingEntry());
444 EXPECT_EQ(controller.GetPendingEntry(), controller.GetVisibleEntry());
445 // TODO(darin): maybe this should really be true?
446 EXPECT_FALSE(controller.CanGoBack());
447 EXPECT_FALSE(controller.CanGoForward());
448 EXPECT_EQ(contents()->GetMaxPageID(), 0);
450 EXPECT_TRUE(controller.GetPendingEntry()->GetTimestamp().is_null());
452 // Simulate the beforeunload ack for the cross-site transition, and then the
453 // commit.
454 main_test_rfh()->PrepareForCommit();
455 contents()->GetPendingMainFrame()->SendNavigate(1, url2);
456 EXPECT_EQ(1U, navigation_entry_committed_counter_);
457 navigation_entry_committed_counter_ = 0;
459 // The load should now be committed.
460 EXPECT_EQ(controller.GetEntryCount(), 2);
461 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
462 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
463 EXPECT_TRUE(controller.GetLastCommittedEntry());
464 EXPECT_FALSE(controller.GetPendingEntry());
465 ASSERT_TRUE(controller.GetVisibleEntry());
466 EXPECT_TRUE(controller.CanGoBack());
467 EXPECT_FALSE(controller.CanGoForward());
468 EXPECT_EQ(contents()->GetMaxPageID(), 1);
470 EXPECT_FALSE(controller.GetVisibleEntry()->GetTimestamp().is_null());
473 namespace {
475 base::Time GetFixedTime(base::Time time) {
476 return time;
479 } // namespace
481 TEST_F(NavigationControllerTest, LoadURLSameTime) {
482 NavigationControllerImpl& controller = controller_impl();
483 TestNotificationTracker notifications;
484 RegisterForAllNavNotifications(&notifications, &controller);
486 // Set the clock to always return a timestamp of 1.
487 controller.SetGetTimestampCallbackForTest(
488 base::Bind(&GetFixedTime, base::Time::FromInternalValue(1)));
490 const GURL url1("http://foo1");
491 const GURL url2("http://foo2");
493 controller.LoadURL(
494 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
496 main_test_rfh()->PrepareForCommit();
497 main_test_rfh()->SendNavigate(0, url1);
498 EXPECT_EQ(1U, navigation_entry_committed_counter_);
499 navigation_entry_committed_counter_ = 0;
501 // Load another...
502 controller.LoadURL(
503 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
505 // Simulate the beforeunload ack for the cross-site transition, and then the
506 // commit.
507 main_test_rfh()->PrepareForCommit();
508 contents()->GetPendingMainFrame()->SendNavigate(1, url2);
509 EXPECT_EQ(1U, navigation_entry_committed_counter_);
510 navigation_entry_committed_counter_ = 0;
512 // The two loads should now be committed.
513 ASSERT_EQ(controller.GetEntryCount(), 2);
515 // Timestamps should be distinct despite the clock returning the
516 // same value.
517 EXPECT_EQ(1u,
518 controller.GetEntryAtIndex(0)->GetTimestamp().ToInternalValue());
519 EXPECT_EQ(2u,
520 controller.GetEntryAtIndex(1)->GetTimestamp().ToInternalValue());
523 void CheckNavigationEntryMatchLoadParams(
524 NavigationController::LoadURLParams& load_params,
525 NavigationEntryImpl* entry) {
526 EXPECT_EQ(load_params.url, entry->GetURL());
527 EXPECT_EQ(load_params.referrer.url, entry->GetReferrer().url);
528 EXPECT_EQ(load_params.referrer.policy, entry->GetReferrer().policy);
529 EXPECT_EQ(load_params.transition_type, entry->GetTransitionType());
530 EXPECT_EQ(load_params.extra_headers, entry->extra_headers());
532 EXPECT_EQ(load_params.is_renderer_initiated, entry->is_renderer_initiated());
533 EXPECT_EQ(load_params.base_url_for_data_url, entry->GetBaseURLForDataURL());
534 if (!load_params.virtual_url_for_data_url.is_empty()) {
535 EXPECT_EQ(load_params.virtual_url_for_data_url, entry->GetVirtualURL());
537 if (NavigationController::UA_OVERRIDE_INHERIT !=
538 load_params.override_user_agent) {
539 bool should_override = (NavigationController::UA_OVERRIDE_TRUE ==
540 load_params.override_user_agent);
541 EXPECT_EQ(should_override, entry->GetIsOverridingUserAgent());
543 EXPECT_EQ(load_params.browser_initiated_post_data.get(),
544 entry->GetBrowserInitiatedPostData());
545 EXPECT_EQ(load_params.transferred_global_request_id,
546 entry->transferred_global_request_id());
549 TEST_F(NavigationControllerTest, LoadURLWithParams) {
550 NavigationControllerImpl& controller = controller_impl();
552 NavigationController::LoadURLParams load_params(GURL("http://foo"));
553 load_params.referrer =
554 Referrer(GURL("http://referrer"), blink::WebReferrerPolicyDefault);
555 load_params.transition_type = ui::PAGE_TRANSITION_GENERATED;
556 load_params.extra_headers = "content-type: text/plain";
557 load_params.load_type = NavigationController::LOAD_TYPE_DEFAULT;
558 load_params.is_renderer_initiated = true;
559 load_params.override_user_agent = NavigationController::UA_OVERRIDE_TRUE;
560 load_params.transferred_global_request_id = GlobalRequestID(2, 3);
562 controller.LoadURLWithParams(load_params);
563 NavigationEntryImpl* entry = controller.GetPendingEntry();
565 // The timestamp should not have been set yet.
566 ASSERT_TRUE(entry);
567 EXPECT_TRUE(entry->GetTimestamp().is_null());
569 CheckNavigationEntryMatchLoadParams(load_params, entry);
572 TEST_F(NavigationControllerTest, LoadURLWithExtraParams_Data) {
573 NavigationControllerImpl& controller = controller_impl();
575 NavigationController::LoadURLParams load_params(
576 GURL("data:text/html,dataurl"));
577 load_params.load_type = NavigationController::LOAD_TYPE_DATA;
578 load_params.base_url_for_data_url = GURL("http://foo");
579 load_params.virtual_url_for_data_url = GURL(url::kAboutBlankURL);
580 load_params.override_user_agent = NavigationController::UA_OVERRIDE_FALSE;
582 controller.LoadURLWithParams(load_params);
583 NavigationEntryImpl* entry = controller.GetPendingEntry();
585 CheckNavigationEntryMatchLoadParams(load_params, entry);
588 TEST_F(NavigationControllerTest, LoadURLWithExtraParams_HttpPost) {
589 NavigationControllerImpl& controller = controller_impl();
591 NavigationController::LoadURLParams load_params(GURL("https://posturl"));
592 load_params.transition_type = ui::PAGE_TRANSITION_TYPED;
593 load_params.load_type =
594 NavigationController::LOAD_TYPE_BROWSER_INITIATED_HTTP_POST;
595 load_params.override_user_agent = NavigationController::UA_OVERRIDE_TRUE;
598 const unsigned char* raw_data =
599 reinterpret_cast<const unsigned char*>("d\n\0a2");
600 const int length = 5;
601 std::vector<unsigned char> post_data_vector(raw_data, raw_data+length);
602 scoped_refptr<base::RefCountedBytes> data =
603 base::RefCountedBytes::TakeVector(&post_data_vector);
604 load_params.browser_initiated_post_data = data.get();
606 controller.LoadURLWithParams(load_params);
607 NavigationEntryImpl* entry = controller.GetPendingEntry();
609 CheckNavigationEntryMatchLoadParams(load_params, entry);
612 // Tests what happens when the same page is loaded again. Should not create a
613 // new session history entry. This is what happens when you press enter in the
614 // URL bar to reload: a pending entry is created and then it is discarded when
615 // the load commits (because WebCore didn't actually make a new entry).
616 TEST_F(NavigationControllerTest, LoadURL_SamePage) {
617 NavigationControllerImpl& controller = controller_impl();
618 TestNotificationTracker notifications;
619 RegisterForAllNavNotifications(&notifications, &controller);
621 const GURL url1("http://foo1");
623 controller.LoadURL(
624 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
625 EXPECT_EQ(0U, notifications.size());
626 main_test_rfh()->PrepareForCommit();
627 main_test_rfh()->SendNavigate(0, url1);
628 EXPECT_EQ(1U, navigation_entry_committed_counter_);
629 navigation_entry_committed_counter_ = 0;
631 ASSERT_TRUE(controller.GetVisibleEntry());
632 const base::Time timestamp = controller.GetVisibleEntry()->GetTimestamp();
633 EXPECT_FALSE(timestamp.is_null());
635 controller.LoadURL(
636 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
637 EXPECT_EQ(0U, notifications.size());
638 main_test_rfh()->PrepareForCommit();
639 main_test_rfh()->SendNavigate(0, url1);
640 EXPECT_EQ(1U, navigation_entry_committed_counter_);
641 navigation_entry_committed_counter_ = 0;
643 // We should not have produced a new session history entry.
644 EXPECT_EQ(controller.GetEntryCount(), 1);
645 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
646 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
647 EXPECT_TRUE(controller.GetLastCommittedEntry());
648 EXPECT_FALSE(controller.GetPendingEntry());
649 ASSERT_TRUE(controller.GetVisibleEntry());
650 EXPECT_FALSE(controller.CanGoBack());
651 EXPECT_FALSE(controller.CanGoForward());
653 // The timestamp should have been updated.
655 // TODO(akalin): Change this EXPECT_GE (and other similar ones) to
656 // EXPECT_GT once we guarantee that timestamps are unique.
657 EXPECT_GE(controller.GetVisibleEntry()->GetTimestamp(), timestamp);
660 // Load the same page twice, once as a GET and once as a POST.
661 // We should update the post state on the NavigationEntry.
662 TEST_F(NavigationControllerTest, LoadURL_SamePage_DifferentMethod) {
663 NavigationControllerImpl& controller = controller_impl();
664 TestNotificationTracker notifications;
665 RegisterForAllNavNotifications(&notifications, &controller);
667 const GURL url1("http://foo1");
669 controller.LoadURL(
670 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
671 FrameHostMsg_DidCommitProvisionalLoad_Params params;
672 params.page_id = 0;
673 params.url = url1;
674 params.transition = ui::PAGE_TRANSITION_TYPED;
675 params.is_post = true;
676 params.post_id = 123;
677 params.page_state = PageState::CreateForTesting(url1, false, 0, 0);
678 main_test_rfh()->PrepareForCommit();
679 main_test_rfh()->SendNavigateWithParams(&params);
681 // The post data should be visible.
682 NavigationEntry* entry = controller.GetVisibleEntry();
683 ASSERT_TRUE(entry);
684 EXPECT_TRUE(entry->GetHasPostData());
685 EXPECT_EQ(entry->GetPostID(), 123);
687 controller.LoadURL(
688 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
689 main_test_rfh()->PrepareForCommit();
690 main_test_rfh()->SendNavigate(0, url1);
692 // We should not have produced a new session history entry.
693 ASSERT_EQ(controller.GetVisibleEntry(), entry);
695 // The post data should have been cleared due to the GET.
696 EXPECT_FALSE(entry->GetHasPostData());
697 EXPECT_EQ(entry->GetPostID(), 0);
700 // Tests loading a URL but discarding it before the load commits.
701 TEST_F(NavigationControllerTest, LoadURL_Discarded) {
702 NavigationControllerImpl& controller = controller_impl();
703 TestNotificationTracker notifications;
704 RegisterForAllNavNotifications(&notifications, &controller);
706 const GURL url1("http://foo1");
707 const GURL url2("http://foo2");
709 controller.LoadURL(
710 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
711 EXPECT_EQ(0U, notifications.size());
712 main_test_rfh()->SendNavigate(0, url1);
713 EXPECT_EQ(1U, navigation_entry_committed_counter_);
714 navigation_entry_committed_counter_ = 0;
716 ASSERT_TRUE(controller.GetVisibleEntry());
717 const base::Time timestamp = controller.GetVisibleEntry()->GetTimestamp();
718 EXPECT_FALSE(timestamp.is_null());
720 controller.LoadURL(
721 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
722 controller.DiscardNonCommittedEntries();
723 EXPECT_EQ(0U, notifications.size());
725 // Should not have produced a new session history entry.
726 EXPECT_EQ(controller.GetEntryCount(), 1);
727 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
728 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
729 EXPECT_TRUE(controller.GetLastCommittedEntry());
730 EXPECT_FALSE(controller.GetPendingEntry());
731 ASSERT_TRUE(controller.GetVisibleEntry());
732 EXPECT_FALSE(controller.CanGoBack());
733 EXPECT_FALSE(controller.CanGoForward());
735 // Timestamp should not have changed.
736 EXPECT_EQ(timestamp, controller.GetVisibleEntry()->GetTimestamp());
739 // Tests navigations that come in unrequested. This happens when the user
740 // navigates from the web page, and here we test that there is no pending entry.
741 TEST_F(NavigationControllerTest, LoadURL_NoPending) {
742 NavigationControllerImpl& controller = controller_impl();
743 TestNotificationTracker notifications;
744 RegisterForAllNavNotifications(&notifications, &controller);
746 // First make an existing committed entry.
747 const GURL kExistingURL1("http://eh");
748 controller.LoadURL(
749 kExistingURL1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
750 main_test_rfh()->SendNavigate(0, kExistingURL1);
751 EXPECT_EQ(1U, navigation_entry_committed_counter_);
752 navigation_entry_committed_counter_ = 0;
754 // Do a new navigation without making a pending one.
755 const GURL kNewURL("http://see");
756 main_test_rfh()->SendNavigate(99, kNewURL);
758 // There should no longer be any pending entry, and the third navigation we
759 // just made should be committed.
760 EXPECT_EQ(1U, navigation_entry_committed_counter_);
761 navigation_entry_committed_counter_ = 0;
762 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
763 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
764 EXPECT_EQ(kNewURL, controller.GetVisibleEntry()->GetURL());
767 // Tests navigating to a new URL when there is a new pending navigation that is
768 // not the one that just loaded. This will happen if the user types in a URL to
769 // somewhere slow, and then navigates the current page before the typed URL
770 // commits.
771 TEST_F(NavigationControllerTest, LoadURL_NewPending) {
772 NavigationControllerImpl& controller = controller_impl();
773 TestNotificationTracker notifications;
774 RegisterForAllNavNotifications(&notifications, &controller);
776 // First make an existing committed entry.
777 const GURL kExistingURL1("http://eh");
778 controller.LoadURL(
779 kExistingURL1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
780 main_test_rfh()->PrepareForCommit();
781 main_test_rfh()->SendNavigate(0, kExistingURL1);
782 EXPECT_EQ(1U, navigation_entry_committed_counter_);
783 navigation_entry_committed_counter_ = 0;
785 // Make a pending entry to somewhere new.
786 const GURL kExistingURL2("http://bee");
787 controller.LoadURL(
788 kExistingURL2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
789 EXPECT_EQ(0U, notifications.size());
791 // After the beforeunload but before it commits...
792 main_test_rfh()->PrepareForCommit();
794 // ... Do a new navigation.
795 const GURL kNewURL("http://see");
796 main_test_rfh()->SendRendererInitiatedNavigationRequest(kNewURL, true);
797 main_test_rfh()->PrepareForCommit();
798 contents()->GetMainFrame()->SendNavigate(3, kNewURL);
800 // There should no longer be any pending entry, and the third navigation we
801 // just made should be committed.
802 EXPECT_EQ(1U, navigation_entry_committed_counter_);
803 navigation_entry_committed_counter_ = 0;
804 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
805 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
806 EXPECT_EQ(kNewURL, controller.GetVisibleEntry()->GetURL());
809 // Tests navigating to a new URL when there is a pending back/forward
810 // navigation. This will happen if the user hits back, but before that commits,
811 // they navigate somewhere new.
812 TEST_F(NavigationControllerTest, LoadURL_ExistingPending) {
813 NavigationControllerImpl& controller = controller_impl();
814 TestNotificationTracker notifications;
815 RegisterForAllNavNotifications(&notifications, &controller);
817 // First make some history.
818 const GURL kExistingURL1("http://foo/eh");
819 controller.LoadURL(
820 kExistingURL1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
821 main_test_rfh()->PrepareForCommit();
822 main_test_rfh()->SendNavigate(0, kExistingURL1);
823 EXPECT_EQ(1U, navigation_entry_committed_counter_);
824 navigation_entry_committed_counter_ = 0;
826 const GURL kExistingURL2("http://foo/bee");
827 controller.LoadURL(
828 kExistingURL2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
829 main_test_rfh()->PrepareForCommit();
830 main_test_rfh()->SendNavigate(1, kExistingURL2);
831 EXPECT_EQ(1U, navigation_entry_committed_counter_);
832 navigation_entry_committed_counter_ = 0;
834 // Now make a pending back/forward navigation. The zeroth entry should be
835 // pending.
836 controller.GoBack();
837 EXPECT_EQ(0U, notifications.size());
838 EXPECT_EQ(0, controller.GetPendingEntryIndex());
839 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
841 // Before that commits, do a new navigation.
842 const GURL kNewURL("http://foo/see");
843 main_test_rfh()->SendRendererInitiatedNavigationRequest(kNewURL, true);
844 main_test_rfh()->PrepareForCommit();
845 main_test_rfh()->SendNavigate(3, kNewURL);
847 // There should no longer be any pending entry, and the third navigation we
848 // just made should be committed.
849 EXPECT_EQ(1U, navigation_entry_committed_counter_);
850 navigation_entry_committed_counter_ = 0;
851 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
852 EXPECT_EQ(2, controller.GetLastCommittedEntryIndex());
853 EXPECT_EQ(kNewURL, controller.GetVisibleEntry()->GetURL());
856 // Tests navigating to a new URL when there is a pending back/forward
857 // navigation to a cross-process, privileged URL. This will happen if the user
858 // hits back, but before that commits, they navigate somewhere new.
859 TEST_F(NavigationControllerTest, LoadURL_PrivilegedPending) {
860 NavigationControllerImpl& controller = controller_impl();
861 TestNotificationTracker notifications;
862 RegisterForAllNavNotifications(&notifications, &controller);
864 // First make some history, starting with a privileged URL.
865 const GURL kExistingURL1("http://privileged");
866 controller.LoadURL(
867 kExistingURL1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
868 // Pretend it has bindings so we can tell if we incorrectly copy it.
869 main_test_rfh()->GetRenderViewHost()->AllowBindings(2);
870 main_test_rfh()->PrepareForCommit();
871 main_test_rfh()->SendNavigate(0, kExistingURL1);
872 EXPECT_EQ(1U, navigation_entry_committed_counter_);
873 navigation_entry_committed_counter_ = 0;
875 // Navigate cross-process to a second URL.
876 const GURL kExistingURL2("http://foo/eh");
877 controller.LoadURL(
878 kExistingURL2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
879 main_test_rfh()->PrepareForCommit();
880 TestRenderFrameHost* foo_rfh = contents()->GetPendingMainFrame();
881 foo_rfh->SendNavigate(1, kExistingURL2);
882 EXPECT_EQ(1U, navigation_entry_committed_counter_);
883 navigation_entry_committed_counter_ = 0;
885 // Now make a pending back/forward navigation to a privileged entry.
886 // The zeroth entry should be pending.
887 controller.GoBack();
888 foo_rfh->SendBeforeUnloadACK(true);
889 EXPECT_EQ(0U, notifications.size());
890 EXPECT_EQ(0, controller.GetPendingEntryIndex());
891 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
892 EXPECT_EQ(2, controller.GetPendingEntry()->bindings());
894 // Before that commits, do a new navigation.
895 const GURL kNewURL("http://foo/bee");
896 foo_rfh->SendRendererInitiatedNavigationRequest(kNewURL, true);
897 foo_rfh->PrepareForCommit();
898 foo_rfh->SendNavigate(3, kNewURL);
900 // There should no longer be any pending entry, and the third navigation we
901 // just made should be committed.
902 EXPECT_EQ(1U, navigation_entry_committed_counter_);
903 navigation_entry_committed_counter_ = 0;
904 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
905 EXPECT_EQ(2, controller.GetLastCommittedEntryIndex());
906 EXPECT_EQ(kNewURL, controller.GetVisibleEntry()->GetURL());
907 EXPECT_EQ(0, controller.GetLastCommittedEntry()->bindings());
910 // Tests navigating to an existing URL when there is a pending new navigation.
911 // This will happen if the user enters a URL, but before that commits, the
912 // current page fires history.back().
913 TEST_F(NavigationControllerTest, LoadURL_BackPreemptsPending) {
914 NavigationControllerImpl& controller = controller_impl();
915 TestNotificationTracker notifications;
916 RegisterForAllNavNotifications(&notifications, &controller);
918 // First make some history.
919 const GURL kExistingURL1("http://foo/eh");
920 controller.LoadURL(
921 kExistingURL1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
922 main_test_rfh()->PrepareForCommit();
923 main_test_rfh()->SendNavigate(0, kExistingURL1);
924 EXPECT_EQ(1U, navigation_entry_committed_counter_);
925 navigation_entry_committed_counter_ = 0;
927 const GURL kExistingURL2("http://foo/bee");
928 controller.LoadURL(
929 kExistingURL2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
930 main_test_rfh()->PrepareForCommit();
931 main_test_rfh()->SendNavigate(1, kExistingURL2);
932 EXPECT_EQ(1U, navigation_entry_committed_counter_);
933 navigation_entry_committed_counter_ = 0;
935 // A back navigation comes in from the renderer...
936 controller.GoToOffset(-1);
938 // ...while the user tries to navigate to a new page...
939 const GURL kNewURL("http://foo/see");
940 controller.LoadURL(
941 kNewURL, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
942 EXPECT_EQ(0U, notifications.size());
943 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
944 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
946 // ...and the back navigation commits.
947 main_test_rfh()->PrepareForCommit();
948 main_test_rfh()->SendNavigate(0, kExistingURL1);
950 // There should no longer be any pending entry, and the back navigation should
951 // be committed.
952 EXPECT_EQ(1U, navigation_entry_committed_counter_);
953 navigation_entry_committed_counter_ = 0;
954 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
955 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
956 EXPECT_EQ(kExistingURL1, controller.GetVisibleEntry()->GetURL());
959 // Tests an ignored navigation when there is a pending new navigation.
960 // This will happen if the user enters a URL, but before that commits, the
961 // current blank page reloads. See http://crbug.com/77507.
962 TEST_F(NavigationControllerTest, LoadURL_IgnorePreemptsPending) {
963 NavigationControllerImpl& controller = controller_impl();
964 TestNotificationTracker notifications;
965 RegisterForAllNavNotifications(&notifications, &controller);
967 // Set a WebContentsDelegate to listen for state changes.
968 scoped_ptr<TestWebContentsDelegate> delegate(new TestWebContentsDelegate());
969 EXPECT_FALSE(contents()->GetDelegate());
970 contents()->SetDelegate(delegate.get());
972 // Without any navigations, the renderer starts at about:blank.
973 const GURL kExistingURL(url::kAboutBlankURL);
975 // Now make a pending new navigation.
976 const GURL kNewURL("http://eh");
977 controller.LoadURL(
978 kNewURL, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
979 EXPECT_EQ(0U, notifications.size());
980 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
981 EXPECT_TRUE(controller.GetPendingEntry());
982 EXPECT_EQ(-1, controller.GetLastCommittedEntryIndex());
983 EXPECT_EQ(1, delegate->navigation_state_change_count());
985 // Before that commits, a document.write and location.reload can cause the
986 // renderer to send a FrameNavigate with page_id -1.
987 main_test_rfh()->SendRendererInitiatedNavigationRequest(kExistingURL, true);
988 main_test_rfh()->PrepareForCommit();
989 main_test_rfh()->SendNavigate(-1, kExistingURL);
991 // This should clear the pending entry and notify of a navigation state
992 // change, so that we do not keep displaying kNewURL.
993 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
994 EXPECT_FALSE(controller.GetPendingEntry());
995 EXPECT_EQ(-1, controller.GetLastCommittedEntryIndex());
996 EXPECT_EQ(2, delegate->navigation_state_change_count());
998 contents()->SetDelegate(NULL);
1001 // Tests that the pending entry state is correct after an abort.
1002 // We do not want to clear the pending entry, so that the user doesn't
1003 // lose a typed URL. (See http://crbug.com/9682.)
1004 TEST_F(NavigationControllerTest, LoadURL_AbortDoesntCancelPending) {
1005 NavigationControllerImpl& controller = controller_impl();
1006 TestNotificationTracker notifications;
1007 RegisterForAllNavNotifications(&notifications, &controller);
1009 // Set a WebContentsDelegate to listen for state changes.
1010 scoped_ptr<TestWebContentsDelegate> delegate(new TestWebContentsDelegate());
1011 EXPECT_FALSE(contents()->GetDelegate());
1012 contents()->SetDelegate(delegate.get());
1014 // Start with a pending new navigation.
1015 const GURL kNewURL("http://eh");
1016 controller.LoadURL(
1017 kNewURL, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1018 main_test_rfh()->PrepareForCommit();
1019 EXPECT_EQ(0U, notifications.size());
1020 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1021 EXPECT_TRUE(controller.GetPendingEntry());
1022 EXPECT_EQ(-1, controller.GetLastCommittedEntryIndex());
1023 EXPECT_EQ(1, delegate->navigation_state_change_count());
1025 // It may abort before committing, if it's a download or due to a stop or
1026 // a new navigation from the user.
1027 FrameHostMsg_DidFailProvisionalLoadWithError_Params params;
1028 params.error_code = net::ERR_ABORTED;
1029 params.error_description = base::string16();
1030 params.url = kNewURL;
1031 params.showing_repost_interstitial = false;
1032 main_test_rfh()->OnMessageReceived(
1033 FrameHostMsg_DidFailProvisionalLoadWithError(0, // routing_id
1034 params));
1036 // This should not clear the pending entry or notify of a navigation state
1037 // change, so that we keep displaying kNewURL (until the user clears it).
1038 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1039 EXPECT_TRUE(controller.GetPendingEntry());
1040 EXPECT_EQ(-1, controller.GetLastCommittedEntryIndex());
1041 EXPECT_EQ(1, delegate->navigation_state_change_count());
1042 NavigationEntry* pending_entry = controller.GetPendingEntry();
1044 // Ensure that a reload keeps the same pending entry.
1045 controller.Reload(true);
1046 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1047 EXPECT_TRUE(controller.GetPendingEntry());
1048 EXPECT_EQ(pending_entry, controller.GetPendingEntry());
1049 EXPECT_EQ(-1, controller.GetLastCommittedEntryIndex());
1051 contents()->SetDelegate(NULL);
1054 // Tests that the pending URL is not visible during a renderer-initiated
1055 // redirect and abort. See http://crbug.com/83031.
1056 TEST_F(NavigationControllerTest, LoadURL_RedirectAbortDoesntShowPendingURL) {
1057 NavigationControllerImpl& controller = controller_impl();
1058 TestNotificationTracker notifications;
1059 RegisterForAllNavNotifications(&notifications, &controller);
1061 // First make an existing committed entry.
1062 const GURL kExistingURL("http://foo/eh");
1063 controller.LoadURL(kExistingURL, content::Referrer(),
1064 ui::PAGE_TRANSITION_TYPED, std::string());
1065 main_test_rfh()->SendNavigate(1, kExistingURL);
1066 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1067 navigation_entry_committed_counter_ = 0;
1069 // Set a WebContentsDelegate to listen for state changes.
1070 scoped_ptr<TestWebContentsDelegate> delegate(new TestWebContentsDelegate());
1071 EXPECT_FALSE(contents()->GetDelegate());
1072 contents()->SetDelegate(delegate.get());
1074 // Now make a pending new navigation, initiated by the renderer.
1075 const GURL kNewURL("http://foo/bee");
1076 NavigationController::LoadURLParams load_url_params(kNewURL);
1077 load_url_params.transition_type = ui::PAGE_TRANSITION_TYPED;
1078 load_url_params.is_renderer_initiated = true;
1079 controller.LoadURLWithParams(load_url_params);
1080 EXPECT_EQ(0U, notifications.size());
1081 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1082 EXPECT_TRUE(controller.GetPendingEntry());
1083 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
1084 EXPECT_EQ(0, delegate->navigation_state_change_count());
1086 // The visible entry should be the last committed URL, not the pending one.
1087 EXPECT_EQ(kExistingURL, controller.GetVisibleEntry()->GetURL());
1089 // Now the navigation redirects. (There is no corresponding message here.)
1090 const GURL kRedirectURL("http://foo/see");
1092 // We don't want to change the NavigationEntry's url, in case it cancels.
1093 // Prevents regression of http://crbug.com/77786.
1094 EXPECT_EQ(kNewURL, controller.GetPendingEntry()->GetURL());
1096 // It may abort before committing, if it's a download or due to a stop or
1097 // a new navigation from the user.
1098 FrameHostMsg_DidFailProvisionalLoadWithError_Params params;
1099 params.error_code = net::ERR_ABORTED;
1100 params.error_description = base::string16();
1101 params.url = kRedirectURL;
1102 params.showing_repost_interstitial = false;
1103 main_test_rfh()->OnMessageReceived(
1104 FrameHostMsg_DidFailProvisionalLoadWithError(0, // routing_id
1105 params));
1107 // Because the pending entry is renderer initiated and not visible, we
1108 // clear it when it fails.
1109 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1110 EXPECT_FALSE(controller.GetPendingEntry());
1111 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
1112 EXPECT_EQ(1, delegate->navigation_state_change_count());
1114 // The visible entry should be the last committed URL, not the pending one,
1115 // so that no spoof is possible.
1116 EXPECT_EQ(kExistingURL, controller.GetVisibleEntry()->GetURL());
1118 contents()->SetDelegate(NULL);
1121 // Ensure that NavigationEntries track which bindings their RenderViewHost had
1122 // at the time they committed. http://crbug.com/173672.
1123 TEST_F(NavigationControllerTest, LoadURL_WithBindings) {
1124 NavigationControllerImpl& controller = controller_impl();
1125 TestNotificationTracker notifications;
1126 RegisterForAllNavNotifications(&notifications, &controller);
1127 std::vector<GURL> url_chain;
1129 const GURL url1("http://foo1");
1130 const GURL url2("http://foo2");
1132 // Navigate to a first, unprivileged URL.
1133 controller.LoadURL(
1134 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1135 EXPECT_EQ(NavigationEntryImpl::kInvalidBindings,
1136 controller.GetPendingEntry()->bindings());
1138 // Commit.
1139 TestRenderFrameHost* orig_rfh = contents()->GetMainFrame();
1140 orig_rfh->PrepareForCommit();
1141 orig_rfh->SendNavigate(0, url1);
1142 EXPECT_EQ(controller.GetEntryCount(), 1);
1143 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
1144 EXPECT_EQ(0, controller.GetLastCommittedEntry()->bindings());
1146 // Manually increase the number of active frames in the SiteInstance
1147 // that orig_rfh belongs to, to prevent it from being destroyed when
1148 // it gets swapped out, so that we can reuse orig_rfh when the
1149 // controller goes back.
1150 orig_rfh->GetSiteInstance()->increment_active_frame_count();
1152 // Navigate to a second URL, simulate the beforeunload ack for the cross-site
1153 // transition, and set bindings on the pending RenderViewHost to simulate a
1154 // privileged url.
1155 controller.LoadURL(
1156 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1157 orig_rfh->PrepareForCommit();
1158 TestRenderFrameHost* new_rfh = contents()->GetPendingMainFrame();
1159 new_rfh->GetRenderViewHost()->AllowBindings(1);
1160 new_rfh->SendNavigate(1, url2);
1162 // The second load should be committed, and bindings should be remembered.
1163 EXPECT_EQ(controller.GetEntryCount(), 2);
1164 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
1165 EXPECT_TRUE(controller.CanGoBack());
1166 EXPECT_EQ(1, controller.GetLastCommittedEntry()->bindings());
1168 // Going back, the first entry should still appear unprivileged.
1169 controller.GoBack();
1170 new_rfh->PrepareForCommit();
1171 orig_rfh->SendNavigate(0, url1);
1172 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
1173 EXPECT_EQ(0, controller.GetLastCommittedEntry()->bindings());
1176 TEST_F(NavigationControllerTest, Reload) {
1177 NavigationControllerImpl& controller = controller_impl();
1178 TestNotificationTracker notifications;
1179 RegisterForAllNavNotifications(&notifications, &controller);
1181 const GURL url1("http://foo1");
1183 controller.LoadURL(
1184 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1185 EXPECT_EQ(0U, notifications.size());
1186 main_test_rfh()->PrepareForCommit();
1187 main_test_rfh()->SendNavigate(0, url1);
1188 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1189 navigation_entry_committed_counter_ = 0;
1190 ASSERT_TRUE(controller.GetVisibleEntry());
1191 controller.GetVisibleEntry()->SetTitle(base::ASCIIToUTF16("Title"));
1192 controller.Reload(true);
1193 EXPECT_EQ(0U, notifications.size());
1195 const base::Time timestamp = controller.GetVisibleEntry()->GetTimestamp();
1196 EXPECT_FALSE(timestamp.is_null());
1198 // The reload is pending.
1199 EXPECT_EQ(controller.GetEntryCount(), 1);
1200 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1201 EXPECT_EQ(controller.GetPendingEntryIndex(), 0);
1202 EXPECT_TRUE(controller.GetLastCommittedEntry());
1203 EXPECT_TRUE(controller.GetPendingEntry());
1204 EXPECT_FALSE(controller.CanGoBack());
1205 EXPECT_FALSE(controller.CanGoForward());
1206 // Make sure the title has been cleared (will be redrawn just after reload).
1207 // Avoids a stale cached title when the new page being reloaded has no title.
1208 // See http://crbug.com/96041.
1209 EXPECT_TRUE(controller.GetVisibleEntry()->GetTitle().empty());
1211 main_test_rfh()->PrepareForCommit();
1212 main_test_rfh()->SendNavigate(0, url1);
1213 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1214 navigation_entry_committed_counter_ = 0;
1216 // Now the reload is committed.
1217 EXPECT_EQ(controller.GetEntryCount(), 1);
1218 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1219 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1220 EXPECT_TRUE(controller.GetLastCommittedEntry());
1221 EXPECT_FALSE(controller.GetPendingEntry());
1222 EXPECT_FALSE(controller.CanGoBack());
1223 EXPECT_FALSE(controller.CanGoForward());
1225 // The timestamp should have been updated.
1226 ASSERT_TRUE(controller.GetVisibleEntry());
1227 EXPECT_GE(controller.GetVisibleEntry()->GetTimestamp(), timestamp);
1230 // Tests what happens when a reload navigation produces a new page.
1231 TEST_F(NavigationControllerTest, Reload_GeneratesNewPage) {
1232 NavigationControllerImpl& controller = controller_impl();
1233 TestNotificationTracker notifications;
1234 RegisterForAllNavNotifications(&notifications, &controller);
1236 const GURL url1("http://foo1");
1237 const GURL url2("http://foo2");
1239 controller.LoadURL(
1240 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1241 main_test_rfh()->PrepareForCommit();
1242 main_test_rfh()->SendNavigate(0, url1);
1243 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1244 navigation_entry_committed_counter_ = 0;
1246 controller.Reload(true);
1247 EXPECT_EQ(0U, notifications.size());
1249 main_test_rfh()->PrepareForCommitWithServerRedirect(url2);
1250 main_test_rfh()->SendNavigate(1, url2);
1251 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1252 navigation_entry_committed_counter_ = 0;
1254 // Now the reload is committed.
1255 EXPECT_EQ(controller.GetEntryCount(), 2);
1256 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
1257 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1258 EXPECT_TRUE(controller.GetLastCommittedEntry());
1259 EXPECT_FALSE(controller.GetPendingEntry());
1260 EXPECT_TRUE(controller.CanGoBack());
1261 EXPECT_FALSE(controller.CanGoForward());
1264 // This test ensures that when a guest renderer reloads, the reload goes through
1265 // without ending up in the "we have a wrong process for the URL" branch in
1266 // NavigationControllerImpl::ReloadInternal.
1267 TEST_F(NavigationControllerTest, ReloadWithGuest) {
1268 NavigationControllerImpl& controller = controller_impl();
1270 const GURL url1("http://foo1");
1271 controller.LoadURL(
1272 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1273 main_test_rfh()->SendNavigate(0, url1);
1274 ASSERT_TRUE(controller.GetVisibleEntry());
1276 // Make the entry believe its RenderProcessHost is a guest.
1277 NavigationEntryImpl* entry1 = controller.GetVisibleEntry();
1278 reinterpret_cast<MockRenderProcessHost*>(
1279 entry1->site_instance()->GetProcess())->set_is_isolated_guest(true);
1281 // And reload.
1282 controller.Reload(true);
1284 // The reload is pending. Check that the NavigationEntry didn't get replaced
1285 // because of having the wrong process.
1286 EXPECT_EQ(controller.GetEntryCount(), 1);
1287 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1288 EXPECT_EQ(controller.GetPendingEntryIndex(), 0);
1290 NavigationEntryImpl* entry2 = controller.GetPendingEntry();
1291 EXPECT_EQ(entry1, entry2);
1294 #if !defined(OS_ANDROID) // http://crbug.com/157428
1295 TEST_F(NavigationControllerTest, ReloadOriginalRequestURL) {
1296 NavigationControllerImpl& controller = controller_impl();
1297 TestNotificationTracker notifications;
1298 RegisterForAllNavNotifications(&notifications, &controller);
1300 const GURL original_url("http://foo1");
1301 const GURL final_url("http://foo2");
1303 // Load up the original URL, but get redirected.
1304 controller.LoadURL(
1305 original_url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1306 EXPECT_EQ(0U, notifications.size());
1307 main_test_rfh()->PrepareForCommitWithServerRedirect(final_url);
1308 main_test_rfh()->SendNavigateWithOriginalRequestURL(
1309 0, final_url, original_url);
1310 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1311 navigation_entry_committed_counter_ = 0;
1313 // The NavigationEntry should save both the original URL and the final
1314 // redirected URL.
1315 EXPECT_EQ(
1316 original_url, controller.GetVisibleEntry()->GetOriginalRequestURL());
1317 EXPECT_EQ(final_url, controller.GetVisibleEntry()->GetURL());
1319 // Reload using the original URL.
1320 controller.GetVisibleEntry()->SetTitle(base::ASCIIToUTF16("Title"));
1321 controller.ReloadOriginalRequestURL(false);
1322 EXPECT_EQ(0U, notifications.size());
1324 // The reload is pending. The request should point to the original URL.
1325 EXPECT_EQ(original_url, navigated_url());
1326 EXPECT_EQ(controller.GetEntryCount(), 1);
1327 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1328 EXPECT_EQ(controller.GetPendingEntryIndex(), 0);
1329 EXPECT_TRUE(controller.GetLastCommittedEntry());
1330 EXPECT_TRUE(controller.GetPendingEntry());
1331 EXPECT_FALSE(controller.CanGoBack());
1332 EXPECT_FALSE(controller.CanGoForward());
1334 // Make sure the title has been cleared (will be redrawn just after reload).
1335 // Avoids a stale cached title when the new page being reloaded has no title.
1336 // See http://crbug.com/96041.
1337 EXPECT_TRUE(controller.GetVisibleEntry()->GetTitle().empty());
1339 // Send that the navigation has proceeded; say it got redirected again.
1340 main_test_rfh()->PrepareForCommitWithServerRedirect(final_url);
1341 main_test_rfh()->SendNavigate(0, final_url);
1342 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1343 navigation_entry_committed_counter_ = 0;
1345 // Now the reload is committed.
1346 EXPECT_EQ(controller.GetEntryCount(), 1);
1347 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1348 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1349 EXPECT_TRUE(controller.GetLastCommittedEntry());
1350 EXPECT_FALSE(controller.GetPendingEntry());
1351 EXPECT_FALSE(controller.CanGoBack());
1352 EXPECT_FALSE(controller.CanGoForward());
1355 #endif // !defined(OS_ANDROID)
1357 // Test that certain non-persisted NavigationEntryImpl values get reset after
1358 // commit.
1359 TEST_F(NavigationControllerTest, ResetEntryValuesAfterCommit) {
1360 NavigationControllerImpl& controller = controller_impl();
1362 // The value of "should replace entry" will be tested, but it's an error to
1363 // specify it when there are no entries. Create a simple entry to be replaced.
1364 const GURL url0("http://foo/0");
1365 controller.LoadURL(
1366 url0, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1367 main_test_rfh()->SendNavigate(0, url0);
1369 // Set up the pending entry.
1370 const GURL url1("http://foo/1");
1371 controller.LoadURL(
1372 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1374 // Set up some sample values.
1375 const unsigned char* raw_data =
1376 reinterpret_cast<const unsigned char*>("post\n\n\0data");
1377 const int length = 11;
1378 std::vector<unsigned char> post_data_vector(raw_data, raw_data+length);
1379 scoped_refptr<base::RefCountedBytes> post_data =
1380 base::RefCountedBytes::TakeVector(&post_data_vector);
1381 GlobalRequestID transfer_id(3, 4);
1383 // Set non-persisted values on the pending entry.
1384 NavigationEntryImpl* pending_entry = controller.GetPendingEntry();
1385 pending_entry->SetBrowserInitiatedPostData(post_data.get());
1386 pending_entry->set_is_renderer_initiated(true);
1387 pending_entry->set_transferred_global_request_id(transfer_id);
1388 pending_entry->set_should_replace_entry(true);
1389 pending_entry->set_should_clear_history_list(true);
1390 EXPECT_EQ(post_data.get(), pending_entry->GetBrowserInitiatedPostData());
1391 EXPECT_TRUE(pending_entry->is_renderer_initiated());
1392 EXPECT_EQ(transfer_id, pending_entry->transferred_global_request_id());
1393 EXPECT_TRUE(pending_entry->should_replace_entry());
1394 EXPECT_TRUE(pending_entry->should_clear_history_list());
1396 // Fake a commit response.
1397 main_test_rfh()->SendNavigate(1, url1);
1399 // Certain values that are only used for pending entries get reset after
1400 // commit.
1401 NavigationEntryImpl* committed_entry = controller.GetLastCommittedEntry();
1402 EXPECT_FALSE(committed_entry->GetBrowserInitiatedPostData());
1403 EXPECT_FALSE(committed_entry->is_renderer_initiated());
1404 EXPECT_EQ(GlobalRequestID(-1, -1),
1405 committed_entry->transferred_global_request_id());
1406 EXPECT_FALSE(committed_entry->should_replace_entry());
1407 EXPECT_FALSE(committed_entry->should_clear_history_list());
1410 // Test that Redirects are preserved after a commit.
1411 TEST_F(NavigationControllerTest, RedirectsAreNotResetByCommit) {
1412 NavigationControllerImpl& controller = controller_impl();
1413 const GURL url1("http://foo1");
1414 controller.LoadURL(
1415 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1417 // Set up some redirect values.
1418 std::vector<GURL> redirects;
1419 redirects.push_back(GURL("http://foo2"));
1421 // Set redirects on the pending entry.
1422 NavigationEntryImpl* pending_entry = controller.GetPendingEntry();
1423 pending_entry->SetRedirectChain(redirects);
1424 EXPECT_EQ(1U, pending_entry->GetRedirectChain().size());
1425 EXPECT_EQ(GURL("http://foo2"), pending_entry->GetRedirectChain()[0]);
1427 // Normal navigation will preserve redirects in the committed entry.
1428 main_test_rfh()->SendNavigateWithRedirects(0, url1, redirects);
1429 NavigationEntryImpl* committed_entry = controller.GetLastCommittedEntry();
1430 ASSERT_EQ(1U, committed_entry->GetRedirectChain().size());
1431 EXPECT_EQ(GURL("http://foo2"), committed_entry->GetRedirectChain()[0]);
1434 // Tests what happens when we navigate back successfully
1435 TEST_F(NavigationControllerTest, Back) {
1436 NavigationControllerImpl& controller = controller_impl();
1437 TestNotificationTracker notifications;
1438 RegisterForAllNavNotifications(&notifications, &controller);
1440 const GURL url1("http://foo1");
1441 main_test_rfh()->SendNavigate(0, url1);
1442 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1443 navigation_entry_committed_counter_ = 0;
1445 const GURL url2("http://foo2");
1446 main_test_rfh()->SendNavigate(1, url2);
1447 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1448 navigation_entry_committed_counter_ = 0;
1450 controller.GoBack();
1451 EXPECT_EQ(0U, notifications.size());
1453 // We should now have a pending navigation to go back.
1454 EXPECT_EQ(controller.GetEntryCount(), 2);
1455 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
1456 EXPECT_EQ(controller.GetPendingEntryIndex(), 0);
1457 EXPECT_TRUE(controller.GetLastCommittedEntry());
1458 EXPECT_TRUE(controller.GetPendingEntry());
1459 EXPECT_FALSE(controller.CanGoBack());
1460 EXPECT_FALSE(controller.CanGoToOffset(-1));
1461 EXPECT_TRUE(controller.CanGoForward());
1462 EXPECT_TRUE(controller.CanGoToOffset(1));
1463 EXPECT_FALSE(controller.CanGoToOffset(2)); // Cannot go foward 2 steps.
1465 // Timestamp for entry 1 should be on or after that of entry 0.
1466 EXPECT_FALSE(controller.GetEntryAtIndex(0)->GetTimestamp().is_null());
1467 EXPECT_GE(controller.GetEntryAtIndex(1)->GetTimestamp(),
1468 controller.GetEntryAtIndex(0)->GetTimestamp());
1470 main_test_rfh()->SendNavigate(0, url2);
1471 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1472 navigation_entry_committed_counter_ = 0;
1474 // The back navigation completed successfully.
1475 EXPECT_EQ(controller.GetEntryCount(), 2);
1476 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1477 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1478 EXPECT_TRUE(controller.GetLastCommittedEntry());
1479 EXPECT_FALSE(controller.GetPendingEntry());
1480 EXPECT_FALSE(controller.CanGoBack());
1481 EXPECT_FALSE(controller.CanGoToOffset(-1));
1482 EXPECT_TRUE(controller.CanGoForward());
1483 EXPECT_TRUE(controller.CanGoToOffset(1));
1484 EXPECT_FALSE(controller.CanGoToOffset(2)); // Cannot go foward 2 steps.
1486 // Timestamp for entry 0 should be on or after that of entry 1
1487 // (since we went back to it).
1488 EXPECT_GE(controller.GetEntryAtIndex(0)->GetTimestamp(),
1489 controller.GetEntryAtIndex(1)->GetTimestamp());
1492 // Tests what happens when a back navigation produces a new page.
1493 TEST_F(NavigationControllerTest, Back_GeneratesNewPage) {
1494 NavigationControllerImpl& controller = controller_impl();
1495 TestNotificationTracker notifications;
1496 RegisterForAllNavNotifications(&notifications, &controller);
1498 const GURL url1("http://foo/1");
1499 const GURL url2("http://foo/2");
1500 const GURL url3("http://foo/3");
1502 controller.LoadURL(
1503 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1504 main_test_rfh()->PrepareForCommit();
1505 main_test_rfh()->SendNavigate(0, url1);
1506 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1507 navigation_entry_committed_counter_ = 0;
1509 controller.LoadURL(
1510 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1511 main_test_rfh()->PrepareForCommit();
1512 main_test_rfh()->SendNavigate(1, url2);
1513 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1514 navigation_entry_committed_counter_ = 0;
1516 controller.GoBack();
1517 EXPECT_EQ(0U, notifications.size());
1519 // We should now have a pending navigation to go back.
1520 EXPECT_EQ(controller.GetEntryCount(), 2);
1521 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
1522 EXPECT_EQ(controller.GetPendingEntryIndex(), 0);
1523 EXPECT_TRUE(controller.GetLastCommittedEntry());
1524 EXPECT_TRUE(controller.GetPendingEntry());
1525 EXPECT_FALSE(controller.CanGoBack());
1526 EXPECT_TRUE(controller.CanGoForward());
1528 main_test_rfh()->PrepareForCommitWithServerRedirect(url3);
1529 main_test_rfh()->SendNavigate(2, url3);
1530 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1531 navigation_entry_committed_counter_ = 0;
1533 // The back navigation resulted in a completely new navigation.
1534 // TODO(darin): perhaps this behavior will be confusing to users?
1535 EXPECT_EQ(controller.GetEntryCount(), 3);
1536 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 2);
1537 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1538 EXPECT_TRUE(controller.GetLastCommittedEntry());
1539 EXPECT_FALSE(controller.GetPendingEntry());
1540 EXPECT_TRUE(controller.CanGoBack());
1541 EXPECT_FALSE(controller.CanGoForward());
1544 // Receives a back message when there is a new pending navigation entry.
1545 TEST_F(NavigationControllerTest, Back_NewPending) {
1546 NavigationControllerImpl& controller = controller_impl();
1547 TestNotificationTracker notifications;
1548 RegisterForAllNavNotifications(&notifications, &controller);
1550 const GURL kUrl1("http://foo1");
1551 const GURL kUrl2("http://foo2");
1552 const GURL kUrl3("http://foo3");
1554 // First navigate two places so we have some back history.
1555 main_test_rfh()->SendNavigate(0, kUrl1);
1556 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1557 navigation_entry_committed_counter_ = 0;
1559 // controller.LoadURL(kUrl2, ui::PAGE_TRANSITION_TYPED);
1560 main_test_rfh()->SendNavigate(1, kUrl2);
1561 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1562 navigation_entry_committed_counter_ = 0;
1564 // Now start a new pending navigation and go back before it commits.
1565 controller.LoadURL(
1566 kUrl3, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1567 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1568 EXPECT_EQ(kUrl3, controller.GetPendingEntry()->GetURL());
1569 controller.GoBack();
1571 // The pending navigation should now be the "back" item and the new one
1572 // should be gone.
1573 EXPECT_EQ(0, controller.GetPendingEntryIndex());
1574 EXPECT_EQ(kUrl1, controller.GetPendingEntry()->GetURL());
1577 // Receives a back message when there is a different renavigation already
1578 // pending.
1579 TEST_F(NavigationControllerTest, Back_OtherBackPending) {
1580 NavigationControllerImpl& controller = controller_impl();
1581 const GURL kUrl1("http://foo/1");
1582 const GURL kUrl2("http://foo/2");
1583 const GURL kUrl3("http://foo/3");
1585 // First navigate three places so we have some back history.
1586 main_test_rfh()->SendRendererInitiatedNavigationRequest(kUrl1, true);
1587 main_test_rfh()->PrepareForCommit();
1588 main_test_rfh()->SendNavigate(0, kUrl1);
1589 main_test_rfh()->SendRendererInitiatedNavigationRequest(kUrl2, true);
1590 main_test_rfh()->PrepareForCommit();
1591 main_test_rfh()->SendNavigate(1, kUrl2);
1592 main_test_rfh()->SendRendererInitiatedNavigationRequest(kUrl3, true);
1593 main_test_rfh()->PrepareForCommit();
1594 main_test_rfh()->SendNavigate(2, kUrl3);
1596 // With nothing pending, say we get a renderer back navigation request to the
1597 // second entry.
1598 controller.GoToOffset(-1);
1599 main_test_rfh()->PrepareForCommit();
1600 main_test_rfh()->SendNavigate(1, kUrl2);
1602 // We know all the entries have the same site instance, so we can just grab
1603 // a random one for looking up other entries.
1604 SiteInstance* site_instance =
1605 controller.GetLastCommittedEntry()->site_instance();
1607 // That second URL should be the last committed and it should have gotten the
1608 // new title.
1609 EXPECT_EQ(kUrl2, controller.GetEntryWithPageID(site_instance, 1)->GetURL());
1610 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
1611 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1613 // Now go forward to the last item again and say it was committed.
1614 controller.GoForward();
1615 main_test_rfh()->PrepareForCommit();
1616 main_test_rfh()->SendNavigate(2, kUrl3);
1618 // Now start going back one to the second page. It will be pending.
1619 controller.GoBack();
1620 EXPECT_EQ(1, controller.GetPendingEntryIndex());
1621 EXPECT_EQ(2, controller.GetLastCommittedEntryIndex());
1623 // Now have the renderer request a navigation back to the first page. This
1624 // will not match the pending one.
1625 controller.GoToOffset(-2);
1626 main_test_rfh()->PrepareForCommit();
1627 main_test_rfh()->SendNavigate(0, kUrl1);
1629 // The committed navigation should clear the pending entry.
1630 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1632 // But the navigated entry should be the last committed.
1633 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
1634 EXPECT_EQ(kUrl1, controller.GetLastCommittedEntry()->GetURL());
1637 // Tests what happens when we navigate forward successfully.
1638 TEST_F(NavigationControllerTest, Forward) {
1639 NavigationControllerImpl& controller = controller_impl();
1640 TestNotificationTracker notifications;
1641 RegisterForAllNavNotifications(&notifications, &controller);
1643 const GURL url1("http://foo1");
1644 const GURL url2("http://foo2");
1646 main_test_rfh()->SendRendererInitiatedNavigationRequest(url1, true);
1647 main_test_rfh()->PrepareForCommit();
1648 main_test_rfh()->SendNavigate(0, url1);
1649 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1650 navigation_entry_committed_counter_ = 0;
1652 main_test_rfh()->SendRendererInitiatedNavigationRequest(url2, true);
1653 main_test_rfh()->PrepareForCommit();
1654 main_test_rfh()->SendNavigate(1, url2);
1655 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1656 navigation_entry_committed_counter_ = 0;
1658 controller.GoBack();
1659 main_test_rfh()->PrepareForCommit();
1660 main_test_rfh()->SendNavigate(0, url1);
1661 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1662 navigation_entry_committed_counter_ = 0;
1664 controller.GoForward();
1666 // We should now have a pending navigation to go forward.
1667 EXPECT_EQ(controller.GetEntryCount(), 2);
1668 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1669 EXPECT_EQ(controller.GetPendingEntryIndex(), 1);
1670 EXPECT_TRUE(controller.GetLastCommittedEntry());
1671 EXPECT_TRUE(controller.GetPendingEntry());
1672 EXPECT_TRUE(controller.CanGoBack());
1673 EXPECT_TRUE(controller.CanGoToOffset(-1));
1674 EXPECT_FALSE(controller.CanGoToOffset(-2)); // Cannot go back 2 steps.
1675 EXPECT_FALSE(controller.CanGoForward());
1676 EXPECT_FALSE(controller.CanGoToOffset(1));
1678 // Timestamp for entry 0 should be on or after that of entry 1
1679 // (since we went back to it).
1680 EXPECT_FALSE(controller.GetEntryAtIndex(0)->GetTimestamp().is_null());
1681 EXPECT_GE(controller.GetEntryAtIndex(0)->GetTimestamp(),
1682 controller.GetEntryAtIndex(1)->GetTimestamp());
1684 main_test_rfh()->PrepareForCommit();
1685 main_test_rfh()->SendNavigate(1, url2);
1686 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1687 navigation_entry_committed_counter_ = 0;
1689 // The forward navigation completed successfully.
1690 EXPECT_EQ(controller.GetEntryCount(), 2);
1691 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
1692 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1693 EXPECT_TRUE(controller.GetLastCommittedEntry());
1694 EXPECT_FALSE(controller.GetPendingEntry());
1695 EXPECT_TRUE(controller.CanGoBack());
1696 EXPECT_TRUE(controller.CanGoToOffset(-1));
1697 EXPECT_FALSE(controller.CanGoToOffset(-2)); // Cannot go back 2 steps.
1698 EXPECT_FALSE(controller.CanGoForward());
1699 EXPECT_FALSE(controller.CanGoToOffset(1));
1701 // Timestamp for entry 1 should be on or after that of entry 0
1702 // (since we went forward to it).
1703 EXPECT_GE(controller.GetEntryAtIndex(1)->GetTimestamp(),
1704 controller.GetEntryAtIndex(0)->GetTimestamp());
1707 // Tests what happens when a forward navigation produces a new page.
1708 TEST_F(NavigationControllerTest, Forward_GeneratesNewPage) {
1709 NavigationControllerImpl& controller = controller_impl();
1710 TestNotificationTracker notifications;
1711 RegisterForAllNavNotifications(&notifications, &controller);
1713 const GURL url1("http://foo1");
1714 const GURL url2("http://foo2");
1715 const GURL url3("http://foo3");
1717 main_test_rfh()->SendRendererInitiatedNavigationRequest(url1, true);
1718 main_test_rfh()->PrepareForCommit();
1719 main_test_rfh()->SendNavigate(0, url1);
1720 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1721 navigation_entry_committed_counter_ = 0;
1722 main_test_rfh()->SendRendererInitiatedNavigationRequest(url2, true);
1723 main_test_rfh()->PrepareForCommit();
1724 main_test_rfh()->SendNavigate(1, url2);
1725 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1726 navigation_entry_committed_counter_ = 0;
1728 controller.GoBack();
1729 main_test_rfh()->PrepareForCommit();
1730 main_test_rfh()->SendNavigate(0, url1);
1731 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1732 navigation_entry_committed_counter_ = 0;
1734 controller.GoForward();
1735 EXPECT_EQ(0U, notifications.size());
1737 // Should now have a pending navigation to go forward.
1738 EXPECT_EQ(controller.GetEntryCount(), 2);
1739 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1740 EXPECT_EQ(controller.GetPendingEntryIndex(), 1);
1741 EXPECT_TRUE(controller.GetLastCommittedEntry());
1742 EXPECT_TRUE(controller.GetPendingEntry());
1743 EXPECT_TRUE(controller.CanGoBack());
1744 EXPECT_FALSE(controller.CanGoForward());
1746 main_test_rfh()->PrepareForCommit();
1747 main_test_rfh()->SendNavigate(2, url3);
1748 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1749 navigation_entry_committed_counter_ = 0;
1750 EXPECT_TRUE(notifications.Check1AndReset(NOTIFICATION_NAV_LIST_PRUNED));
1752 EXPECT_EQ(controller.GetEntryCount(), 2);
1753 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
1754 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1755 EXPECT_TRUE(controller.GetLastCommittedEntry());
1756 EXPECT_FALSE(controller.GetPendingEntry());
1757 EXPECT_TRUE(controller.CanGoBack());
1758 EXPECT_FALSE(controller.CanGoForward());
1761 // Two consequent navigation for the same URL entered in should be considered
1762 // as SAME_PAGE navigation even when we are redirected to some other page.
1763 TEST_F(NavigationControllerTest, Redirect) {
1764 NavigationControllerImpl& controller = controller_impl();
1765 TestNotificationTracker notifications;
1766 RegisterForAllNavNotifications(&notifications, &controller);
1768 const GURL url1("http://foo1");
1769 const GURL url2("http://foo2"); // Redirection target
1771 // First request
1772 controller.LoadURL(
1773 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1775 EXPECT_EQ(0U, notifications.size());
1776 main_test_rfh()->SendNavigate(0, url2);
1777 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1778 navigation_entry_committed_counter_ = 0;
1780 // Second request
1781 controller.LoadURL(
1782 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1784 EXPECT_TRUE(controller.GetPendingEntry());
1785 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1786 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
1788 FrameHostMsg_DidCommitProvisionalLoad_Params params;
1789 params.page_id = 0;
1790 params.url = url2;
1791 params.transition = ui::PAGE_TRANSITION_SERVER_REDIRECT;
1792 params.redirects.push_back(GURL("http://foo1"));
1793 params.redirects.push_back(GURL("http://foo2"));
1794 params.should_update_history = false;
1795 params.gesture = NavigationGestureAuto;
1796 params.is_post = false;
1797 params.page_state = PageState::CreateFromURL(url2);
1799 LoadCommittedDetails details;
1801 EXPECT_EQ(0U, notifications.size());
1802 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
1803 &details));
1804 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1805 navigation_entry_committed_counter_ = 0;
1807 EXPECT_TRUE(details.type == NAVIGATION_TYPE_SAME_PAGE);
1808 EXPECT_EQ(controller.GetEntryCount(), 1);
1809 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1810 EXPECT_TRUE(controller.GetLastCommittedEntry());
1811 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1812 EXPECT_FALSE(controller.GetPendingEntry());
1813 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
1815 EXPECT_FALSE(controller.CanGoBack());
1816 EXPECT_FALSE(controller.CanGoForward());
1819 // Similar to Redirect above, but the first URL is requested by POST,
1820 // the second URL is requested by GET. NavigationEntry::has_post_data_
1821 // must be cleared. http://crbug.com/21245
1822 TEST_F(NavigationControllerTest, PostThenRedirect) {
1823 NavigationControllerImpl& controller = controller_impl();
1824 TestNotificationTracker notifications;
1825 RegisterForAllNavNotifications(&notifications, &controller);
1827 const GURL url1("http://foo1");
1828 const GURL url2("http://foo2"); // Redirection target
1830 // First request as POST
1831 controller.LoadURL(
1832 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1833 controller.GetVisibleEntry()->SetHasPostData(true);
1835 EXPECT_EQ(0U, notifications.size());
1836 main_test_rfh()->SendNavigate(0, url2);
1837 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1838 navigation_entry_committed_counter_ = 0;
1840 // Second request
1841 controller.LoadURL(
1842 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1844 EXPECT_TRUE(controller.GetPendingEntry());
1845 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1846 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
1848 FrameHostMsg_DidCommitProvisionalLoad_Params params;
1849 params.page_id = 0;
1850 params.url = url2;
1851 params.transition = ui::PAGE_TRANSITION_SERVER_REDIRECT;
1852 params.redirects.push_back(GURL("http://foo1"));
1853 params.redirects.push_back(GURL("http://foo2"));
1854 params.should_update_history = false;
1855 params.gesture = NavigationGestureAuto;
1856 params.is_post = false;
1857 params.page_state = PageState::CreateFromURL(url2);
1859 LoadCommittedDetails details;
1861 EXPECT_EQ(0U, notifications.size());
1862 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
1863 &details));
1864 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1865 navigation_entry_committed_counter_ = 0;
1867 EXPECT_TRUE(details.type == NAVIGATION_TYPE_SAME_PAGE);
1868 EXPECT_EQ(controller.GetEntryCount(), 1);
1869 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1870 EXPECT_TRUE(controller.GetLastCommittedEntry());
1871 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1872 EXPECT_FALSE(controller.GetPendingEntry());
1873 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
1874 EXPECT_FALSE(controller.GetVisibleEntry()->GetHasPostData());
1876 EXPECT_FALSE(controller.CanGoBack());
1877 EXPECT_FALSE(controller.CanGoForward());
1880 // A redirect right off the bat should be a NEW_PAGE.
1881 TEST_F(NavigationControllerTest, ImmediateRedirect) {
1882 NavigationControllerImpl& controller = controller_impl();
1883 TestNotificationTracker notifications;
1884 RegisterForAllNavNotifications(&notifications, &controller);
1886 const GURL url1("http://foo1");
1887 const GURL url2("http://foo2"); // Redirection target
1889 // First request
1890 controller.LoadURL(
1891 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
1893 EXPECT_TRUE(controller.GetPendingEntry());
1894 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1895 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
1897 FrameHostMsg_DidCommitProvisionalLoad_Params params;
1898 params.page_id = 0;
1899 params.url = url2;
1900 params.transition = ui::PAGE_TRANSITION_SERVER_REDIRECT;
1901 params.redirects.push_back(GURL("http://foo1"));
1902 params.redirects.push_back(GURL("http://foo2"));
1903 params.should_update_history = false;
1904 params.gesture = NavigationGestureAuto;
1905 params.is_post = false;
1906 params.page_state = PageState::CreateFromURL(url2);
1908 LoadCommittedDetails details;
1910 EXPECT_EQ(0U, notifications.size());
1911 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
1912 &details));
1913 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1914 navigation_entry_committed_counter_ = 0;
1916 EXPECT_TRUE(details.type == NAVIGATION_TYPE_NEW_PAGE);
1917 EXPECT_EQ(controller.GetEntryCount(), 1);
1918 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1919 EXPECT_TRUE(controller.GetLastCommittedEntry());
1920 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1921 EXPECT_FALSE(controller.GetPendingEntry());
1922 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
1924 EXPECT_FALSE(controller.CanGoBack());
1925 EXPECT_FALSE(controller.CanGoForward());
1928 // Tests navigation via link click within a subframe. A new navigation entry
1929 // should be created.
1930 TEST_F(NavigationControllerTest, NewSubframe) {
1931 NavigationControllerImpl& controller = controller_impl();
1932 TestNotificationTracker notifications;
1933 RegisterForAllNavNotifications(&notifications, &controller);
1935 const GURL url1("http://foo1");
1936 main_test_rfh()->SendNavigate(0, url1);
1937 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1938 navigation_entry_committed_counter_ = 0;
1940 const GURL url2("http://foo2");
1941 FrameHostMsg_DidCommitProvisionalLoad_Params params;
1942 params.page_id = 1;
1943 params.url = url2;
1944 params.transition = ui::PAGE_TRANSITION_MANUAL_SUBFRAME;
1945 params.should_update_history = false;
1946 params.gesture = NavigationGestureUser;
1947 params.is_post = false;
1948 params.page_state = PageState::CreateFromURL(url2);
1950 LoadCommittedDetails details;
1951 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
1952 &details));
1953 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1954 navigation_entry_committed_counter_ = 0;
1955 EXPECT_EQ(url1, details.previous_url);
1956 EXPECT_FALSE(details.is_in_page);
1957 EXPECT_FALSE(details.is_main_frame);
1959 // The new entry should be appended.
1960 EXPECT_EQ(2, controller.GetEntryCount());
1962 // New entry should refer to the new page, but the old URL (entries only
1963 // reflect the toplevel URL).
1964 EXPECT_EQ(url1, details.entry->GetURL());
1965 EXPECT_EQ(params.page_id, details.entry->GetPageID());
1968 // Auto subframes are ones the page loads automatically like ads. They should
1969 // not create new navigation entries.
1970 // TODO(creis): Test cross-site and nested iframes.
1971 // TODO(creis): Test updating entries for history auto subframe navigations.
1972 TEST_F(NavigationControllerTest, AutoSubframe) {
1973 NavigationControllerImpl& controller = controller_impl();
1974 TestNotificationTracker notifications;
1975 RegisterForAllNavNotifications(&notifications, &controller);
1977 const GURL url1("http://foo/1");
1978 main_test_rfh()->SendNavigate(1, url1);
1979 EXPECT_EQ(1U, navigation_entry_committed_counter_);
1980 navigation_entry_committed_counter_ = 0;
1982 // Add a subframe and navigate it.
1983 main_test_rfh()->OnCreateChildFrame(MSG_ROUTING_NONE, std::string(),
1984 SandboxFlags::NONE);
1985 RenderFrameHostImpl* subframe =
1986 contents()->GetFrameTree()->root()->child_at(0)->current_frame_host();
1987 const GURL url2("http://foo/2");
1989 FrameHostMsg_DidCommitProvisionalLoad_Params params;
1990 params.page_id = 1;
1991 params.url = url2;
1992 params.transition = ui::PAGE_TRANSITION_AUTO_SUBFRAME;
1993 params.should_update_history = false;
1994 params.gesture = NavigationGestureUser;
1995 params.is_post = false;
1996 params.page_state = PageState::CreateFromURL(url2);
1998 // Navigating should do nothing.
1999 LoadCommittedDetails details;
2000 EXPECT_FALSE(controller.RendererDidNavigate(subframe, params, &details));
2001 EXPECT_EQ(0U, notifications.size());
2004 // There should still be only one entry.
2005 EXPECT_EQ(1, controller.GetEntryCount());
2006 NavigationEntryImpl* entry = controller.GetLastCommittedEntry();
2007 EXPECT_EQ(url1, entry->GetURL());
2008 EXPECT_EQ(1, entry->GetPageID());
2009 FrameNavigationEntry* root_entry = entry->root_node()->frame_entry.get();
2010 EXPECT_EQ(url1, root_entry->url());
2012 // Verify subframe entries if we're in --site-per-process mode.
2013 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
2014 switches::kSitePerProcess)) {
2015 // The entry should now have a subframe FrameNavigationEntry.
2016 ASSERT_EQ(1U, entry->root_node()->children.size());
2017 FrameNavigationEntry* frame_entry =
2018 entry->root_node()->children[0]->frame_entry.get();
2019 EXPECT_EQ(url2, frame_entry->url());
2020 } else {
2021 // There are no subframe FrameNavigationEntries by default.
2022 EXPECT_EQ(0U, entry->root_node()->children.size());
2025 // Add a second subframe and navigate.
2026 main_test_rfh()->OnCreateChildFrame(MSG_ROUTING_NONE, std::string(),
2027 SandboxFlags::NONE);
2028 RenderFrameHostImpl* subframe2 =
2029 contents()->GetFrameTree()->root()->child_at(1)->current_frame_host();
2030 const GURL url3("http://foo/3");
2032 FrameHostMsg_DidCommitProvisionalLoad_Params params;
2033 params.page_id = 1;
2034 params.url = url3;
2035 params.transition = ui::PAGE_TRANSITION_AUTO_SUBFRAME;
2036 params.should_update_history = false;
2037 params.gesture = NavigationGestureUser;
2038 params.is_post = false;
2039 params.page_state = PageState::CreateFromURL(url3);
2041 // Navigating should do nothing.
2042 LoadCommittedDetails details;
2043 EXPECT_FALSE(controller.RendererDidNavigate(subframe2, params, &details));
2044 EXPECT_EQ(0U, notifications.size());
2047 // There should still be only one entry, mostly unchanged.
2048 EXPECT_EQ(1, controller.GetEntryCount());
2049 EXPECT_EQ(entry, controller.GetLastCommittedEntry());
2050 EXPECT_EQ(url1, entry->GetURL());
2051 EXPECT_EQ(1, entry->GetPageID());
2052 EXPECT_EQ(root_entry, entry->root_node()->frame_entry.get());
2053 EXPECT_EQ(url1, root_entry->url());
2055 // Verify subframe entries if we're in --site-per-process mode.
2056 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
2057 switches::kSitePerProcess)) {
2058 // The entry should now have 2 subframe FrameNavigationEntries.
2059 ASSERT_EQ(2U, entry->root_node()->children.size());
2060 FrameNavigationEntry* new_frame_entry =
2061 entry->root_node()->children[1]->frame_entry.get();
2062 EXPECT_EQ(url3, new_frame_entry->url());
2063 } else {
2064 // There are no subframe FrameNavigationEntries by default.
2065 EXPECT_EQ(0U, entry->root_node()->children.size());
2069 // Tests navigation and then going back to a subframe navigation.
2070 TEST_F(NavigationControllerTest, BackSubframe) {
2071 NavigationControllerImpl& controller = controller_impl();
2072 TestNotificationTracker notifications;
2073 RegisterForAllNavNotifications(&notifications, &controller);
2075 // Main page.
2076 const GURL url1("http://foo1");
2077 main_test_rfh()->SendNavigate(0, url1);
2078 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2079 navigation_entry_committed_counter_ = 0;
2081 // First manual subframe navigation.
2082 const GURL url2("http://foo2");
2083 FrameHostMsg_DidCommitProvisionalLoad_Params params;
2084 params.page_id = 1;
2085 params.url = url2;
2086 params.transition = ui::PAGE_TRANSITION_MANUAL_SUBFRAME;
2087 params.should_update_history = false;
2088 params.gesture = NavigationGestureUser;
2089 params.is_post = false;
2090 params.page_state = PageState::CreateFromURL(url2);
2092 // This should generate a new entry.
2093 LoadCommittedDetails details;
2094 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
2095 &details));
2096 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2097 navigation_entry_committed_counter_ = 0;
2098 EXPECT_EQ(2, controller.GetEntryCount());
2100 // Second manual subframe navigation should also make a new entry.
2101 const GURL url3("http://foo3");
2102 params.page_id = 2;
2103 params.url = url3;
2104 params.transition = ui::PAGE_TRANSITION_MANUAL_SUBFRAME;
2105 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
2106 &details));
2107 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2108 navigation_entry_committed_counter_ = 0;
2109 EXPECT_EQ(3, controller.GetEntryCount());
2110 EXPECT_EQ(2, controller.GetCurrentEntryIndex());
2112 // Go back one.
2113 controller.GoBack();
2114 params.page_id = 1;
2115 params.url = url2;
2116 params.transition = ui::PAGE_TRANSITION_AUTO_SUBFRAME;
2117 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
2118 &details));
2119 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2120 navigation_entry_committed_counter_ = 0;
2121 EXPECT_EQ(3, controller.GetEntryCount());
2122 EXPECT_EQ(1, controller.GetCurrentEntryIndex());
2123 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
2124 EXPECT_FALSE(controller.GetPendingEntry());
2126 // Go back one more.
2127 controller.GoBack();
2128 params.page_id = 0;
2129 params.url = url1;
2130 params.transition = ui::PAGE_TRANSITION_AUTO_SUBFRAME;
2131 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
2132 &details));
2133 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2134 navigation_entry_committed_counter_ = 0;
2135 EXPECT_EQ(3, controller.GetEntryCount());
2136 EXPECT_EQ(0, controller.GetCurrentEntryIndex());
2137 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
2138 EXPECT_FALSE(controller.GetPendingEntry());
2141 TEST_F(NavigationControllerTest, LinkClick) {
2142 NavigationControllerImpl& controller = controller_impl();
2143 TestNotificationTracker notifications;
2144 RegisterForAllNavNotifications(&notifications, &controller);
2146 const GURL url1("http://foo1");
2147 const GURL url2("http://foo2");
2149 main_test_rfh()->SendNavigate(0, url1);
2150 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2151 navigation_entry_committed_counter_ = 0;
2153 main_test_rfh()->SendNavigate(1, url2);
2154 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2155 navigation_entry_committed_counter_ = 0;
2157 // Should not have produced a new session history entry.
2158 EXPECT_EQ(controller.GetEntryCount(), 2);
2159 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
2160 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
2161 EXPECT_TRUE(controller.GetLastCommittedEntry());
2162 EXPECT_FALSE(controller.GetPendingEntry());
2163 EXPECT_TRUE(controller.CanGoBack());
2164 EXPECT_FALSE(controller.CanGoForward());
2167 TEST_F(NavigationControllerTest, InPage) {
2168 NavigationControllerImpl& controller = controller_impl();
2169 TestNotificationTracker notifications;
2170 RegisterForAllNavNotifications(&notifications, &controller);
2172 // Main page.
2173 const GURL url1("http://foo");
2174 main_test_rfh()->SendNavigate(0, url1);
2175 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2176 navigation_entry_committed_counter_ = 0;
2178 // Ensure main page navigation to same url respects the was_within_same_page
2179 // hint provided in the params.
2180 FrameHostMsg_DidCommitProvisionalLoad_Params self_params;
2181 self_params.page_id = 0;
2182 self_params.url = url1;
2183 self_params.transition = ui::PAGE_TRANSITION_LINK;
2184 self_params.should_update_history = false;
2185 self_params.gesture = NavigationGestureUser;
2186 self_params.is_post = false;
2187 self_params.page_state = PageState::CreateFromURL(url1);
2188 self_params.was_within_same_page = true;
2190 LoadCommittedDetails details;
2191 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), self_params,
2192 &details));
2193 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2194 navigation_entry_committed_counter_ = 0;
2195 EXPECT_TRUE(details.is_in_page);
2196 EXPECT_TRUE(details.did_replace_entry);
2197 EXPECT_EQ(1, controller.GetEntryCount());
2199 // Fragment navigation to a new page_id.
2200 const GURL url2("http://foo#a");
2201 FrameHostMsg_DidCommitProvisionalLoad_Params params;
2202 params.page_id = 1;
2203 params.url = url2;
2204 params.transition = ui::PAGE_TRANSITION_LINK;
2205 params.should_update_history = false;
2206 params.gesture = NavigationGestureUser;
2207 params.is_post = false;
2208 params.page_state = PageState::CreateFromURL(url2);
2209 params.was_within_same_page = true;
2211 // This should generate a new entry.
2212 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
2213 &details));
2214 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2215 navigation_entry_committed_counter_ = 0;
2216 EXPECT_TRUE(details.is_in_page);
2217 EXPECT_FALSE(details.did_replace_entry);
2218 EXPECT_EQ(2, controller.GetEntryCount());
2220 // Go back one.
2221 FrameHostMsg_DidCommitProvisionalLoad_Params back_params(params);
2222 controller.GoBack();
2223 back_params.url = url1;
2224 back_params.page_id = 0;
2225 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), back_params,
2226 &details));
2227 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2228 navigation_entry_committed_counter_ = 0;
2229 EXPECT_TRUE(details.is_in_page);
2230 EXPECT_EQ(2, controller.GetEntryCount());
2231 EXPECT_EQ(0, controller.GetCurrentEntryIndex());
2232 EXPECT_EQ(back_params.url, controller.GetVisibleEntry()->GetURL());
2234 // Go forward
2235 FrameHostMsg_DidCommitProvisionalLoad_Params forward_params(params);
2236 controller.GoForward();
2237 forward_params.url = url2;
2238 forward_params.page_id = 1;
2239 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), forward_params,
2240 &details));
2241 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2242 navigation_entry_committed_counter_ = 0;
2243 EXPECT_TRUE(details.is_in_page);
2244 EXPECT_EQ(2, controller.GetEntryCount());
2245 EXPECT_EQ(1, controller.GetCurrentEntryIndex());
2246 EXPECT_EQ(forward_params.url,
2247 controller.GetVisibleEntry()->GetURL());
2249 // Now go back and forward again. This is to work around a bug where we would
2250 // compare the incoming URL with the last committed entry rather than the
2251 // one identified by an existing page ID. This would result in the second URL
2252 // losing the reference fragment when you navigate away from it and then back.
2253 controller.GoBack();
2254 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), back_params,
2255 &details));
2256 controller.GoForward();
2257 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), forward_params,
2258 &details));
2259 EXPECT_EQ(forward_params.url,
2260 controller.GetVisibleEntry()->GetURL());
2262 // Finally, navigate to an unrelated URL to make sure in_page is not sticky.
2263 const GURL url3("http://bar");
2264 params.page_id = 2;
2265 params.url = url3;
2266 navigation_entry_committed_counter_ = 0;
2267 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
2268 &details));
2269 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2270 navigation_entry_committed_counter_ = 0;
2271 EXPECT_FALSE(details.is_in_page);
2272 EXPECT_EQ(3, controller.GetEntryCount());
2273 EXPECT_EQ(2, controller.GetCurrentEntryIndex());
2276 TEST_F(NavigationControllerTest, InPage_Replace) {
2277 NavigationControllerImpl& controller = controller_impl();
2278 TestNotificationTracker notifications;
2279 RegisterForAllNavNotifications(&notifications, &controller);
2281 // Main page.
2282 const GURL url1("http://foo");
2283 main_test_rfh()->SendNavigate(0, url1);
2284 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2285 navigation_entry_committed_counter_ = 0;
2287 // First navigation.
2288 const GURL url2("http://foo#a");
2289 FrameHostMsg_DidCommitProvisionalLoad_Params params;
2290 params.page_id = 0; // Same page_id
2291 params.url = url2;
2292 params.transition = ui::PAGE_TRANSITION_LINK;
2293 params.should_update_history = false;
2294 params.gesture = NavigationGestureUser;
2295 params.is_post = false;
2296 params.page_state = PageState::CreateFromURL(url2);
2297 params.was_within_same_page = true;
2299 // This should NOT generate a new entry, nor prune the list.
2300 LoadCommittedDetails details;
2301 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
2302 &details));
2303 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2304 navigation_entry_committed_counter_ = 0;
2305 EXPECT_TRUE(details.is_in_page);
2306 EXPECT_TRUE(details.did_replace_entry);
2307 EXPECT_EQ(1, controller.GetEntryCount());
2310 // Tests for http://crbug.com/40395
2311 // Simulates this:
2312 // <script>
2313 // window.location.replace("#a");
2314 // window.location='http://foo3/';
2315 // </script>
2316 TEST_F(NavigationControllerTest, ClientRedirectAfterInPageNavigation) {
2317 NavigationControllerImpl& controller = controller_impl();
2318 TestNotificationTracker notifications;
2319 RegisterForAllNavNotifications(&notifications, &controller);
2321 // Load an initial page.
2323 const GURL url("http://foo/");
2324 main_test_rfh()->SendNavigate(0, url);
2325 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2326 navigation_entry_committed_counter_ = 0;
2329 // Navigate to a new page.
2331 const GURL url("http://foo2/");
2332 main_test_rfh()->SendNavigate(1, url);
2333 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2334 navigation_entry_committed_counter_ = 0;
2337 // Navigate within the page.
2339 const GURL url("http://foo2/#a");
2340 FrameHostMsg_DidCommitProvisionalLoad_Params params;
2341 params.page_id = 1; // Same page_id
2342 params.url = url;
2343 params.transition = ui::PAGE_TRANSITION_LINK;
2344 params.redirects.push_back(url);
2345 params.should_update_history = true;
2346 params.gesture = NavigationGestureUnknown;
2347 params.is_post = false;
2348 params.page_state = PageState::CreateFromURL(url);
2349 params.was_within_same_page = true;
2351 // This should NOT generate a new entry, nor prune the list.
2352 LoadCommittedDetails details;
2353 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
2354 &details));
2355 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2356 navigation_entry_committed_counter_ = 0;
2357 EXPECT_TRUE(details.is_in_page);
2358 EXPECT_TRUE(details.did_replace_entry);
2359 EXPECT_EQ(2, controller.GetEntryCount());
2362 // Perform a client redirect to a new page.
2364 const GURL url("http://foo3/");
2365 FrameHostMsg_DidCommitProvisionalLoad_Params params;
2366 params.page_id = 2; // New page_id
2367 params.url = url;
2368 params.transition = ui::PAGE_TRANSITION_CLIENT_REDIRECT;
2369 params.redirects.push_back(GURL("http://foo2/#a"));
2370 params.redirects.push_back(url);
2371 params.should_update_history = true;
2372 params.gesture = NavigationGestureUnknown;
2373 params.is_post = false;
2374 params.page_state = PageState::CreateFromURL(url);
2376 // This SHOULD generate a new entry.
2377 LoadCommittedDetails details;
2378 EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
2379 &details));
2380 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2381 navigation_entry_committed_counter_ = 0;
2382 EXPECT_FALSE(details.is_in_page);
2383 EXPECT_EQ(3, controller.GetEntryCount());
2386 // Verify that BACK brings us back to http://foo2/.
2388 const GURL url("http://foo2/");
2389 controller.GoBack();
2390 main_test_rfh()->SendNavigate(1, url);
2391 EXPECT_EQ(1U, navigation_entry_committed_counter_);
2392 navigation_entry_committed_counter_ = 0;
2393 EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
2397 TEST_F(NavigationControllerTest, PushStateWithoutPreviousEntry)
2399 ASSERT_FALSE(controller_impl().GetLastCommittedEntry());
2400 FrameHostMsg_DidCommitProvisionalLoad_Params params;
2401 GURL url("http://foo");
2402 params.page_id = 1;
2403 params.url = url;
2404 params.page_state = PageState::CreateFromURL(url);
2405 params.was_within_same_page = true;
2406 contents()->GetMainFrame()->SendNavigateWithParams(&params);
2407 // We pass if we don't crash.
2410 // NotificationObserver implementation used in verifying we've received the
2411 // NOTIFICATION_NAV_LIST_PRUNED method.
2412 class PrunedListener : public NotificationObserver {
2413 public:
2414 explicit PrunedListener(NavigationControllerImpl* controller)
2415 : notification_count_(0) {
2416 registrar_.Add(this, NOTIFICATION_NAV_LIST_PRUNED,
2417 Source<NavigationController>(controller));
2420 void Observe(int type,
2421 const NotificationSource& source,
2422 const NotificationDetails& details) override {
2423 if (type == NOTIFICATION_NAV_LIST_PRUNED) {
2424 notification_count_++;
2425 details_ = *(Details<PrunedDetails>(details).ptr());
2429 // Number of times NAV_LIST_PRUNED has been observed.
2430 int notification_count_;
2432 // Details from the last NAV_LIST_PRUNED.
2433 PrunedDetails details_;
2435 private:
2436 NotificationRegistrar registrar_;
2438 DISALLOW_COPY_AND_ASSIGN(PrunedListener);
2441 // Tests that we limit the number of navigation entries created correctly.
2442 TEST_F(NavigationControllerTest, EnforceMaxNavigationCount) {
2443 NavigationControllerImpl& controller = controller_impl();
2444 size_t original_count = NavigationControllerImpl::max_entry_count();
2445 const int kMaxEntryCount = 5;
2447 NavigationControllerImpl::set_max_entry_count_for_testing(kMaxEntryCount);
2449 int url_index;
2450 // Load up to the max count, all entries should be there.
2451 for (url_index = 0; url_index < kMaxEntryCount; url_index++) {
2452 GURL url(base::StringPrintf("http://www.a.com/%d", url_index));
2453 controller.LoadURL(
2454 url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2455 main_test_rfh()->PrepareForCommit();
2456 main_test_rfh()->SendNavigate(url_index, url);
2459 EXPECT_EQ(controller.GetEntryCount(), kMaxEntryCount);
2461 // Created a PrunedListener to observe prune notifications.
2462 PrunedListener listener(&controller);
2464 // Navigate some more.
2465 GURL url(base::StringPrintf("http://www.a.com/%d", url_index));
2466 controller.LoadURL(
2467 url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2468 main_test_rfh()->PrepareForCommit();
2469 main_test_rfh()->SendNavigate(url_index, url);
2470 url_index++;
2472 // We should have got a pruned navigation.
2473 EXPECT_EQ(1, listener.notification_count_);
2474 EXPECT_TRUE(listener.details_.from_front);
2475 EXPECT_EQ(1, listener.details_.count);
2477 // We expect http://www.a.com/0 to be gone.
2478 EXPECT_EQ(controller.GetEntryCount(), kMaxEntryCount);
2479 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(),
2480 GURL("http:////www.a.com/1"));
2482 // More navigations.
2483 for (int i = 0; i < 3; i++) {
2484 url = GURL(base::StringPrintf("http:////www.a.com/%d", url_index));
2485 controller.LoadURL(
2486 url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2487 main_test_rfh()->PrepareForCommit();
2488 main_test_rfh()->SendNavigate(url_index, url);
2489 url_index++;
2491 EXPECT_EQ(controller.GetEntryCount(), kMaxEntryCount);
2492 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(),
2493 GURL("http:////www.a.com/4"));
2495 NavigationControllerImpl::set_max_entry_count_for_testing(original_count);
2498 // Tests that we can do a restore and navigate to the restored entries and
2499 // everything is updated properly. This can be tricky since there is no
2500 // SiteInstance for the entries created initially.
2501 TEST_F(NavigationControllerTest, RestoreNavigate) {
2502 // Create a NavigationController with a restored set of tabs.
2503 GURL url("http://foo");
2504 std::vector<NavigationEntry*> entries;
2505 NavigationEntry* entry = NavigationControllerImpl::CreateNavigationEntry(
2506 url, Referrer(), ui::PAGE_TRANSITION_RELOAD, false, std::string(),
2507 browser_context());
2508 entry->SetPageID(0);
2509 entry->SetTitle(base::ASCIIToUTF16("Title"));
2510 entry->SetPageState(PageState::CreateFromEncodedData("state"));
2511 const base::Time timestamp = base::Time::Now();
2512 entry->SetTimestamp(timestamp);
2513 entries.push_back(entry);
2514 scoped_ptr<WebContentsImpl> our_contents(static_cast<WebContentsImpl*>(
2515 WebContents::Create(WebContents::CreateParams(browser_context()))));
2516 NavigationControllerImpl& our_controller = our_contents->GetController();
2517 our_controller.Restore(
2519 NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY,
2520 &entries);
2521 ASSERT_EQ(0u, entries.size());
2523 // Before navigating to the restored entry, it should have a restore_type
2524 // and no SiteInstance.
2525 ASSERT_EQ(1, our_controller.GetEntryCount());
2526 EXPECT_EQ(NavigationEntryImpl::RESTORE_LAST_SESSION_EXITED_CLEANLY,
2527 our_controller.GetEntryAtIndex(0)->restore_type());
2528 EXPECT_FALSE(our_controller.GetEntryAtIndex(0)->site_instance());
2530 // After navigating, we should have one entry, and it should be "pending".
2531 // It should now have a SiteInstance and no restore_type.
2532 our_controller.GoToIndex(0);
2533 EXPECT_EQ(1, our_controller.GetEntryCount());
2534 EXPECT_EQ(our_controller.GetEntryAtIndex(0),
2535 our_controller.GetPendingEntry());
2536 EXPECT_EQ(0, our_controller.GetEntryAtIndex(0)->GetPageID());
2537 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE,
2538 our_controller.GetEntryAtIndex(0)->restore_type());
2539 EXPECT_TRUE(our_controller.GetEntryAtIndex(0)->site_instance());
2541 // Timestamp should remain the same before the navigation finishes.
2542 EXPECT_EQ(timestamp, our_controller.GetEntryAtIndex(0)->GetTimestamp());
2544 // Say we navigated to that entry.
2545 FrameHostMsg_DidCommitProvisionalLoad_Params params;
2546 params.page_id = 0;
2547 params.url = url;
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(url);
2553 LoadCommittedDetails details;
2554 our_controller.RendererDidNavigate(our_contents->GetMainFrame(), params,
2555 &details);
2557 // There should be no longer any pending entry and one committed one. This
2558 // means that we were able to locate the entry, assign its site instance, and
2559 // commit it properly.
2560 EXPECT_EQ(1, our_controller.GetEntryCount());
2561 EXPECT_EQ(0, our_controller.GetLastCommittedEntryIndex());
2562 EXPECT_FALSE(our_controller.GetPendingEntry());
2563 EXPECT_EQ(
2564 url,
2565 our_controller.GetLastCommittedEntry()->site_instance()->GetSiteURL());
2566 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE,
2567 our_controller.GetEntryAtIndex(0)->restore_type());
2569 // Timestamp should have been updated.
2570 EXPECT_GE(our_controller.GetEntryAtIndex(0)->GetTimestamp(), timestamp);
2573 // Tests that we can still navigate to a restored entry after a different
2574 // navigation fails and clears the pending entry. http://crbug.com/90085
2575 TEST_F(NavigationControllerTest, RestoreNavigateAfterFailure) {
2576 // Create a NavigationController with a restored set of tabs.
2577 GURL url("http://foo");
2578 std::vector<NavigationEntry*> entries;
2579 NavigationEntry* entry = NavigationControllerImpl::CreateNavigationEntry(
2580 url, Referrer(), ui::PAGE_TRANSITION_RELOAD, false, std::string(),
2581 browser_context());
2582 entry->SetPageID(0);
2583 entry->SetTitle(base::ASCIIToUTF16("Title"));
2584 entry->SetPageState(PageState::CreateFromEncodedData("state"));
2585 entries.push_back(entry);
2586 scoped_ptr<WebContentsImpl> our_contents(static_cast<WebContentsImpl*>(
2587 WebContents::Create(WebContents::CreateParams(browser_context()))));
2588 NavigationControllerImpl& our_controller = our_contents->GetController();
2589 our_controller.Restore(
2590 0, NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY, &entries);
2591 ASSERT_EQ(0u, entries.size());
2593 // Before navigating to the restored entry, it should have a restore_type
2594 // and no SiteInstance.
2595 EXPECT_EQ(NavigationEntryImpl::RESTORE_LAST_SESSION_EXITED_CLEANLY,
2596 our_controller.GetEntryAtIndex(0)->restore_type());
2597 EXPECT_FALSE(our_controller.GetEntryAtIndex(0)->site_instance());
2599 // After navigating, we should have one entry, and it should be "pending".
2600 // It should now have a SiteInstance and no restore_type.
2601 our_controller.GoToIndex(0);
2602 EXPECT_EQ(1, our_controller.GetEntryCount());
2603 EXPECT_EQ(our_controller.GetEntryAtIndex(0),
2604 our_controller.GetPendingEntry());
2605 EXPECT_EQ(0, our_controller.GetEntryAtIndex(0)->GetPageID());
2606 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE,
2607 our_controller.GetEntryAtIndex(0)->restore_type());
2608 EXPECT_TRUE(our_controller.GetEntryAtIndex(0)->site_instance());
2610 // This pending navigation may have caused a different navigation to fail,
2611 // which causes the pending entry to be cleared.
2612 FrameHostMsg_DidFailProvisionalLoadWithError_Params fail_load_params;
2613 fail_load_params.error_code = net::ERR_ABORTED;
2614 fail_load_params.error_description = base::string16();
2615 fail_load_params.url = url;
2616 fail_load_params.showing_repost_interstitial = false;
2617 main_test_rfh()->OnMessageReceived(
2618 FrameHostMsg_DidFailProvisionalLoadWithError(0, // routing_id
2619 fail_load_params));
2621 // Now the pending restored entry commits.
2622 FrameHostMsg_DidCommitProvisionalLoad_Params params;
2623 params.page_id = 0;
2624 params.url = url;
2625 params.transition = ui::PAGE_TRANSITION_LINK;
2626 params.should_update_history = false;
2627 params.gesture = NavigationGestureUser;
2628 params.is_post = false;
2629 params.page_state = PageState::CreateFromURL(url);
2630 LoadCommittedDetails details;
2631 our_controller.RendererDidNavigate(our_contents->GetMainFrame(), params,
2632 &details);
2634 // There should be no pending entry and one committed one.
2635 EXPECT_EQ(1, our_controller.GetEntryCount());
2636 EXPECT_EQ(0, our_controller.GetLastCommittedEntryIndex());
2637 EXPECT_FALSE(our_controller.GetPendingEntry());
2638 EXPECT_EQ(
2639 url,
2640 our_controller.GetLastCommittedEntry()->site_instance()->GetSiteURL());
2641 EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE,
2642 our_controller.GetEntryAtIndex(0)->restore_type());
2645 // Make sure that the page type and stuff is correct after an interstitial.
2646 TEST_F(NavigationControllerTest, Interstitial) {
2647 NavigationControllerImpl& controller = controller_impl();
2648 // First navigate somewhere normal.
2649 const GURL url1("http://foo");
2650 controller.LoadURL(
2651 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2652 main_test_rfh()->PrepareForCommit();
2653 main_test_rfh()->SendNavigate(0, url1);
2655 // Now navigate somewhere with an interstitial.
2656 const GURL url2("http://bar");
2657 controller.LoadURL(url2, Referrer(), ui::PAGE_TRANSITION_TYPED,
2658 std::string());
2659 controller.GetPendingEntry()->set_page_type(PAGE_TYPE_INTERSTITIAL);
2661 // At this point the interstitial will be displayed and the load will still
2662 // be pending. If the user continues, the load will commit.
2663 main_test_rfh()->PrepareForCommit();
2664 main_test_rfh()->SendNavigate(1, url2);
2666 // The page should be a normal page again.
2667 EXPECT_EQ(url2, controller.GetLastCommittedEntry()->GetURL());
2668 EXPECT_EQ(PAGE_TYPE_NORMAL,
2669 controller.GetLastCommittedEntry()->GetPageType());
2672 TEST_F(NavigationControllerTest, RemoveEntry) {
2673 NavigationControllerImpl& controller = controller_impl();
2674 const GURL url1("http://foo/1");
2675 const GURL url2("http://foo/2");
2676 const GURL url3("http://foo/3");
2677 const GURL url4("http://foo/4");
2678 const GURL url5("http://foo/5");
2679 const GURL pending_url("http://foo/pending");
2680 const GURL default_url("http://foo/default");
2682 controller.LoadURL(
2683 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2684 main_test_rfh()->PrepareForCommit();
2685 main_test_rfh()->SendNavigate(0, url1);
2686 controller.LoadURL(
2687 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2688 main_test_rfh()->PrepareForCommit();
2689 main_test_rfh()->SendNavigate(1, url2);
2690 controller.LoadURL(
2691 url3, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2692 main_test_rfh()->PrepareForCommit();
2693 main_test_rfh()->SendNavigate(2, url3);
2694 controller.LoadURL(
2695 url4, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2696 main_test_rfh()->PrepareForCommit();
2697 main_test_rfh()->SendNavigate(3, url4);
2698 controller.LoadURL(
2699 url5, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2700 main_test_rfh()->PrepareForCommit();
2701 main_test_rfh()->SendNavigate(4, url5);
2703 // Try to remove the last entry. Will fail because it is the current entry.
2704 EXPECT_FALSE(controller.RemoveEntryAtIndex(controller.GetEntryCount() - 1));
2705 EXPECT_EQ(5, controller.GetEntryCount());
2706 EXPECT_EQ(4, controller.GetLastCommittedEntryIndex());
2708 // Go back, but don't commit yet. Check that we can't delete the current
2709 // and pending entries.
2710 controller.GoBack();
2711 EXPECT_FALSE(controller.RemoveEntryAtIndex(controller.GetEntryCount() - 1));
2712 EXPECT_FALSE(controller.RemoveEntryAtIndex(controller.GetEntryCount() - 2));
2714 // Now commit and delete the last entry.
2715 main_test_rfh()->PrepareForCommit();
2716 main_test_rfh()->SendNavigate(3, url4);
2717 EXPECT_TRUE(controller.RemoveEntryAtIndex(controller.GetEntryCount() - 1));
2718 EXPECT_EQ(4, controller.GetEntryCount());
2719 EXPECT_EQ(3, controller.GetLastCommittedEntryIndex());
2720 EXPECT_FALSE(controller.GetPendingEntry());
2722 // Remove an entry which is not the last committed one.
2723 EXPECT_TRUE(controller.RemoveEntryAtIndex(0));
2724 EXPECT_EQ(3, controller.GetEntryCount());
2725 EXPECT_EQ(2, controller.GetLastCommittedEntryIndex());
2726 EXPECT_FALSE(controller.GetPendingEntry());
2728 // Remove the 2 remaining entries.
2729 controller.RemoveEntryAtIndex(1);
2730 controller.RemoveEntryAtIndex(0);
2732 // This should leave us with only the last committed entry.
2733 EXPECT_EQ(1, controller.GetEntryCount());
2734 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
2737 TEST_F(NavigationControllerTest, RemoveEntryWithPending) {
2738 NavigationControllerImpl& controller = controller_impl();
2739 const GURL url1("http://foo/1");
2740 const GURL url2("http://foo/2");
2741 const GURL url3("http://foo/3");
2742 const GURL default_url("http://foo/default");
2744 controller.LoadURL(
2745 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2746 main_test_rfh()->PrepareForCommit();
2747 main_test_rfh()->SendNavigate(0, url1);
2748 controller.LoadURL(
2749 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2750 main_test_rfh()->PrepareForCommit();
2751 main_test_rfh()->SendNavigate(1, url2);
2752 controller.LoadURL(
2753 url3, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2754 main_test_rfh()->PrepareForCommit();
2755 main_test_rfh()->SendNavigate(2, url3);
2757 // Go back, but don't commit yet. Check that we can't delete the current
2758 // and pending entries.
2759 controller.GoBack();
2760 EXPECT_FALSE(controller.RemoveEntryAtIndex(2));
2761 EXPECT_FALSE(controller.RemoveEntryAtIndex(1));
2763 // Remove the first entry, while there is a pending entry. This is expected
2764 // to discard the pending entry.
2765 EXPECT_TRUE(controller.RemoveEntryAtIndex(0));
2766 EXPECT_FALSE(controller.GetPendingEntry());
2767 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
2769 // We should update the last committed entry index.
2770 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
2772 // Now commit and ensure we land on the right entry.
2773 main_test_rfh()->PrepareForCommit();
2774 main_test_rfh()->SendNavigate(1, url2);
2775 EXPECT_EQ(2, controller.GetEntryCount());
2776 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
2777 EXPECT_FALSE(controller.GetPendingEntry());
2780 // Tests the transient entry, making sure it goes away with all navigations.
2781 TEST_F(NavigationControllerTest, TransientEntry) {
2782 NavigationControllerImpl& controller = controller_impl();
2783 TestNotificationTracker notifications;
2784 RegisterForAllNavNotifications(&notifications, &controller);
2786 const GURL url0("http://foo/0");
2787 const GURL url1("http://foo/1");
2788 const GURL url2("http://foo/2");
2789 const GURL url3("http://foo/3");
2790 const GURL url3_ref("http://foo/3#bar");
2791 const GURL url4("http://foo/4");
2792 const GURL transient_url("http://foo/transient");
2794 controller.LoadURL(
2795 url0, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2796 main_test_rfh()->PrepareForCommit();
2797 main_test_rfh()->SendNavigate(0, url0);
2798 controller.LoadURL(
2799 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2800 main_test_rfh()->PrepareForCommit();
2801 main_test_rfh()->SendNavigate(1, url1);
2803 notifications.Reset();
2805 // Adding a transient with no pending entry.
2806 NavigationEntryImpl* transient_entry = new NavigationEntryImpl;
2807 transient_entry->SetURL(transient_url);
2808 controller.SetTransientEntry(transient_entry);
2810 // We should not have received any notifications.
2811 EXPECT_EQ(0U, notifications.size());
2813 // Check our state.
2814 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2815 EXPECT_EQ(controller.GetEntryCount(), 3);
2816 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
2817 EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
2818 EXPECT_TRUE(controller.GetLastCommittedEntry());
2819 EXPECT_FALSE(controller.GetPendingEntry());
2820 EXPECT_TRUE(controller.CanGoBack());
2821 EXPECT_FALSE(controller.CanGoForward());
2822 EXPECT_EQ(contents()->GetMaxPageID(), 1);
2824 // Navigate.
2825 controller.LoadURL(
2826 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2827 main_test_rfh()->PrepareForCommit();
2828 main_test_rfh()->SendNavigate(2, url2);
2830 // We should have navigated, transient entry should be gone.
2831 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
2832 EXPECT_EQ(controller.GetEntryCount(), 3);
2834 // Add a transient again, then navigate with no pending entry this time.
2835 transient_entry = new NavigationEntryImpl;
2836 transient_entry->SetURL(transient_url);
2837 controller.SetTransientEntry(transient_entry);
2838 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2839 main_test_rfh()->SendRendererInitiatedNavigationRequest(url3, true);
2840 main_test_rfh()->PrepareForCommit();
2841 main_test_rfh()->SendNavigate(3, url3);
2842 // Transient entry should be gone.
2843 EXPECT_EQ(url3, controller.GetVisibleEntry()->GetURL());
2844 EXPECT_EQ(controller.GetEntryCount(), 4);
2846 // Initiate a navigation, add a transient then commit navigation.
2847 controller.LoadURL(
2848 url4, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2849 transient_entry = new NavigationEntryImpl;
2850 transient_entry->SetURL(transient_url);
2851 controller.SetTransientEntry(transient_entry);
2852 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2853 main_test_rfh()->PrepareForCommit();
2854 main_test_rfh()->SendNavigate(4, url4);
2855 EXPECT_EQ(url4, controller.GetVisibleEntry()->GetURL());
2856 EXPECT_EQ(controller.GetEntryCount(), 5);
2858 // Add a transient and go back. This should simply remove the transient.
2859 transient_entry = new NavigationEntryImpl;
2860 transient_entry->SetURL(transient_url);
2861 controller.SetTransientEntry(transient_entry);
2862 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2863 EXPECT_TRUE(controller.CanGoBack());
2864 EXPECT_FALSE(controller.CanGoForward());
2865 controller.GoBack();
2866 // Transient entry should be gone.
2867 EXPECT_EQ(url4, controller.GetVisibleEntry()->GetURL());
2868 EXPECT_EQ(controller.GetEntryCount(), 5);
2870 // Suppose the page requested a history navigation backward.
2871 controller.GoToOffset(-1);
2872 main_test_rfh()->PrepareForCommit();
2873 main_test_rfh()->SendNavigate(3, url3);
2875 // Add a transient and go to an entry before the current one.
2876 transient_entry = new NavigationEntryImpl;
2877 transient_entry->SetURL(transient_url);
2878 controller.SetTransientEntry(transient_entry);
2879 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2880 controller.GoToIndex(1);
2881 // The navigation should have been initiated, transient entry should be gone.
2882 EXPECT_FALSE(controller.GetTransientEntry());
2883 EXPECT_EQ(url1, controller.GetPendingEntry()->GetURL());
2884 // Visible entry does not update for history navigations until commit.
2885 EXPECT_EQ(url3, controller.GetVisibleEntry()->GetURL());
2886 main_test_rfh()->PrepareForCommit();
2887 main_test_rfh()->SendNavigate(1, url1);
2888 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
2890 // Add a transient and go to an entry after the current one.
2891 transient_entry = new NavigationEntryImpl;
2892 transient_entry->SetURL(transient_url);
2893 controller.SetTransientEntry(transient_entry);
2894 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2895 controller.GoToIndex(3);
2896 // The navigation should have been initiated, transient entry should be gone.
2897 // Because of the transient entry that is removed, going to index 3 makes us
2898 // land on url2 (which is visible after the commit).
2899 EXPECT_EQ(url2, controller.GetPendingEntry()->GetURL());
2900 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
2901 main_test_rfh()->PrepareForCommit();
2902 main_test_rfh()->SendNavigate(2, url2);
2903 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
2905 // Add a transient and go forward.
2906 transient_entry = new NavigationEntryImpl;
2907 transient_entry->SetURL(transient_url);
2908 controller.SetTransientEntry(transient_entry);
2909 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2910 EXPECT_TRUE(controller.CanGoForward());
2911 controller.GoForward();
2912 // We should have navigated, transient entry should be gone.
2913 EXPECT_FALSE(controller.GetTransientEntry());
2914 EXPECT_EQ(url3, controller.GetPendingEntry()->GetURL());
2915 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
2916 main_test_rfh()->PrepareForCommit();
2917 main_test_rfh()->SendNavigate(3, url3);
2918 EXPECT_EQ(url3, controller.GetVisibleEntry()->GetURL());
2920 // Add a transient and do an in-page navigation, replacing the current entry.
2921 transient_entry = new NavigationEntryImpl;
2922 transient_entry->SetURL(transient_url);
2923 controller.SetTransientEntry(transient_entry);
2924 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2926 main_test_rfh()->SendRendererInitiatedNavigationRequest(url3_ref, false);
2927 main_test_rfh()->PrepareForCommit();
2928 main_test_rfh()->SendNavigate(3, url3_ref);
2929 // Transient entry should be gone.
2930 EXPECT_FALSE(controller.GetTransientEntry());
2931 EXPECT_EQ(url3_ref, controller.GetVisibleEntry()->GetURL());
2933 // Ensure the URLs are correct.
2934 EXPECT_EQ(controller.GetEntryCount(), 5);
2935 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url0);
2936 EXPECT_EQ(controller.GetEntryAtIndex(1)->GetURL(), url1);
2937 EXPECT_EQ(controller.GetEntryAtIndex(2)->GetURL(), url2);
2938 EXPECT_EQ(controller.GetEntryAtIndex(3)->GetURL(), url3_ref);
2939 EXPECT_EQ(controller.GetEntryAtIndex(4)->GetURL(), url4);
2942 // Test that Reload initiates a new navigation to a transient entry's URL.
2943 TEST_F(NavigationControllerTest, ReloadTransient) {
2944 NavigationControllerImpl& controller = controller_impl();
2945 const GURL url0("http://foo/0");
2946 const GURL url1("http://foo/1");
2947 const GURL transient_url("http://foo/transient");
2949 // Load |url0|, and start a pending navigation to |url1|.
2950 controller.LoadURL(
2951 url0, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2952 main_test_rfh()->PrepareForCommit();
2953 main_test_rfh()->SendNavigate(0, url0);
2954 controller.LoadURL(
2955 url1, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
2957 // A transient entry is added, interrupting the navigation.
2958 NavigationEntryImpl* transient_entry = new NavigationEntryImpl;
2959 transient_entry->SetURL(transient_url);
2960 controller.SetTransientEntry(transient_entry);
2961 EXPECT_TRUE(controller.GetTransientEntry());
2962 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2964 // The page is reloaded, which should remove the pending entry for |url1| and
2965 // the transient entry for |transient_url|, and start a navigation to
2966 // |transient_url|.
2967 controller.Reload(true);
2968 EXPECT_FALSE(controller.GetTransientEntry());
2969 EXPECT_TRUE(controller.GetPendingEntry());
2970 EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2971 ASSERT_EQ(controller.GetEntryCount(), 1);
2972 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url0);
2974 // Load of |transient_url| completes.
2975 main_test_rfh()->PrepareForCommit();
2976 main_test_rfh()->SendNavigate(1, transient_url);
2977 ASSERT_EQ(controller.GetEntryCount(), 2);
2978 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url0);
2979 EXPECT_EQ(controller.GetEntryAtIndex(1)->GetURL(), transient_url);
2982 // Ensure that renderer initiated pending entries get replaced, so that we
2983 // don't show a stale virtual URL when a navigation commits.
2984 // See http://crbug.com/266922.
2985 TEST_F(NavigationControllerTest, RendererInitiatedPendingEntries) {
2986 NavigationControllerImpl& controller = controller_impl();
2987 Navigator* navigator =
2988 contents()->GetFrameTree()->root()->navigator();
2990 const GURL url1("nonexistent:12121");
2991 const GURL url1_fixed("http://nonexistent:12121/");
2992 const GURL url2("http://foo");
2994 // We create pending entries for renderer-initiated navigations so that we
2995 // can show them in new tabs when it is safe.
2996 navigator->DidStartProvisionalLoad(main_test_rfh(), url1, false);
2998 // Simulate what happens if a BrowserURLHandler rewrites the URL, causing
2999 // the virtual URL to differ from the URL.
3000 controller.GetPendingEntry()->SetURL(url1_fixed);
3001 controller.GetPendingEntry()->SetVirtualURL(url1);
3003 EXPECT_EQ(url1_fixed, controller.GetPendingEntry()->GetURL());
3004 EXPECT_EQ(url1, controller.GetPendingEntry()->GetVirtualURL());
3005 EXPECT_TRUE(controller.GetPendingEntry()->is_renderer_initiated());
3007 // If the user clicks another link, we should replace the pending entry.
3008 navigator->DidStartProvisionalLoad(main_test_rfh(), url2, false);
3009 EXPECT_EQ(url2, controller.GetPendingEntry()->GetURL());
3010 EXPECT_EQ(url2, controller.GetPendingEntry()->GetVirtualURL());
3012 // Once it commits, the URL and virtual URL should reflect the actual page.
3013 main_test_rfh()->SendNavigate(0, url2);
3014 EXPECT_EQ(url2, controller.GetLastCommittedEntry()->GetURL());
3015 EXPECT_EQ(url2, controller.GetLastCommittedEntry()->GetVirtualURL());
3017 // We should not replace the pending entry for an error URL.
3018 navigator->DidStartProvisionalLoad(main_test_rfh(), url1, false);
3019 EXPECT_EQ(url1, controller.GetPendingEntry()->GetURL());
3020 navigator->DidStartProvisionalLoad(main_test_rfh(),
3021 GURL(kUnreachableWebDataURL), false);
3022 EXPECT_EQ(url1, controller.GetPendingEntry()->GetURL());
3024 // We should remember if the pending entry will replace the current one.
3025 // http://crbug.com/308444.
3026 navigator->DidStartProvisionalLoad(main_test_rfh(), url1, false);
3027 controller.GetPendingEntry()->set_should_replace_entry(true);
3028 navigator->DidStartProvisionalLoad(main_test_rfh(), url2, false);
3029 EXPECT_TRUE(controller.GetPendingEntry()->should_replace_entry());
3030 // TODO(nasko): Until OnNavigate is moved to RenderFrameHost, we need
3031 // to go through the RenderViewHost. The TestRenderViewHost routes navigations
3032 // to the main frame.
3033 main_test_rfh()->SendNavigate(0, url2);
3034 EXPECT_EQ(url2, controller.GetLastCommittedEntry()->GetURL());
3037 // Tests that the URLs for renderer-initiated navigations are not displayed to
3038 // the user until the navigation commits, to prevent URL spoof attacks.
3039 // See http://crbug.com/99016.
3040 TEST_F(NavigationControllerTest, DontShowRendererURLUntilCommit) {
3041 NavigationControllerImpl& controller = controller_impl();
3042 TestNotificationTracker notifications;
3043 RegisterForAllNavNotifications(&notifications, &controller);
3045 const GURL url0("http://foo/0");
3046 const GURL url1("http://foo/1");
3048 // For typed navigations (browser-initiated), both pending and visible entries
3049 // should update before commit.
3050 controller.LoadURL(
3051 url0, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
3052 EXPECT_EQ(url0, controller.GetPendingEntry()->GetURL());
3053 EXPECT_EQ(url0, controller.GetVisibleEntry()->GetURL());
3054 main_test_rfh()->PrepareForCommit();
3055 main_test_rfh()->SendNavigate(0, url0);
3057 // For link clicks (renderer-initiated navigations), the pending entry should
3058 // update before commit but the visible should not.
3059 NavigationController::LoadURLParams load_url_params(url1);
3060 load_url_params.is_renderer_initiated = true;
3061 controller.LoadURLWithParams(load_url_params);
3062 EXPECT_EQ(url0, controller.GetVisibleEntry()->GetURL());
3063 EXPECT_EQ(url1, controller.GetPendingEntry()->GetURL());
3064 EXPECT_TRUE(controller.GetPendingEntry()->is_renderer_initiated());
3066 // After commit, both visible should be updated, there should be no pending
3067 // entry, and we should no longer treat the entry as renderer-initiated.
3068 main_test_rfh()->PrepareForCommit();
3069 main_test_rfh()->SendNavigate(1, url1);
3070 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
3071 EXPECT_FALSE(controller.GetPendingEntry());
3072 EXPECT_FALSE(controller.GetLastCommittedEntry()->is_renderer_initiated());
3074 notifications.Reset();
3077 // Tests that the URLs for renderer-initiated navigations in new tabs are
3078 // displayed to the user before commit, as long as the initial about:blank
3079 // page has not been modified. If so, we must revert to showing about:blank.
3080 // See http://crbug.com/9682.
3081 TEST_F(NavigationControllerTest, ShowRendererURLInNewTabUntilModified) {
3082 NavigationControllerImpl& controller = controller_impl();
3083 TestNotificationTracker notifications;
3084 RegisterForAllNavNotifications(&notifications, &controller);
3086 const GURL url("http://foo");
3088 // For renderer-initiated navigations in new tabs (with no committed entries),
3089 // we show the pending entry's URL as long as the about:blank page is not
3090 // modified.
3091 NavigationController::LoadURLParams load_url_params(url);
3092 load_url_params.transition_type = ui::PAGE_TRANSITION_LINK;
3093 load_url_params.is_renderer_initiated = true;
3094 controller.LoadURLWithParams(load_url_params);
3095 EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
3096 EXPECT_EQ(url, controller.GetPendingEntry()->GetURL());
3097 EXPECT_TRUE(controller.GetPendingEntry()->is_renderer_initiated());
3098 EXPECT_TRUE(controller.IsInitialNavigation());
3099 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3101 // There should be no title yet.
3102 EXPECT_TRUE(contents()->GetTitle().empty());
3104 // If something else modifies the contents of the about:blank page, then
3105 // we must revert to showing about:blank to avoid a URL spoof.
3106 main_test_rfh()->OnMessageReceived(FrameHostMsg_DidAccessInitialDocument(0));
3107 EXPECT_TRUE(contents()->HasAccessedInitialDocument());
3108 EXPECT_FALSE(controller.GetVisibleEntry());
3109 EXPECT_EQ(url, controller.GetPendingEntry()->GetURL());
3111 notifications.Reset();
3114 // Tests that the URLs for browser-initiated navigations in new tabs are
3115 // displayed to the user even after they fail, as long as the initial
3116 // about:blank page has not been modified. If so, we must revert to showing
3117 // about:blank. See http://crbug.com/355537.
3118 TEST_F(NavigationControllerTest, ShowBrowserURLAfterFailUntilModified) {
3119 NavigationControllerImpl& controller = controller_impl();
3120 TestNotificationTracker notifications;
3121 RegisterForAllNavNotifications(&notifications, &controller);
3123 const GURL url("http://foo");
3125 // For browser-initiated navigations in new tabs (with no committed entries),
3126 // we show the pending entry's URL as long as the about:blank page is not
3127 // modified. This is possible in cases that the user types a URL into a popup
3128 // tab created with a slow URL.
3129 NavigationController::LoadURLParams load_url_params(url);
3130 load_url_params.transition_type = ui::PAGE_TRANSITION_TYPED;
3131 load_url_params.is_renderer_initiated = false;
3132 controller.LoadURLWithParams(load_url_params);
3133 EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
3134 EXPECT_EQ(url, controller.GetPendingEntry()->GetURL());
3135 EXPECT_FALSE(controller.GetPendingEntry()->is_renderer_initiated());
3136 EXPECT_TRUE(controller.IsInitialNavigation());
3137 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3139 // There should be no title yet.
3140 EXPECT_TRUE(contents()->GetTitle().empty());
3142 // Suppose it aborts before committing, if it's a 204 or download or due to a
3143 // stop or a new navigation from the user. The URL should remain visible.
3144 FrameHostMsg_DidFailProvisionalLoadWithError_Params params;
3145 params.error_code = net::ERR_ABORTED;
3146 params.error_description = base::string16();
3147 params.url = url;
3148 params.showing_repost_interstitial = false;
3149 main_test_rfh()->OnMessageReceived(
3150 FrameHostMsg_DidFailProvisionalLoadWithError(0, params));
3151 contents()->SetIsLoading(false, true, NULL);
3152 EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
3154 // If something else later modifies the contents of the about:blank page, then
3155 // we must revert to showing about:blank to avoid a URL spoof.
3156 main_test_rfh()->OnMessageReceived(FrameHostMsg_DidAccessInitialDocument(0));
3157 EXPECT_TRUE(contents()->HasAccessedInitialDocument());
3158 EXPECT_FALSE(controller.GetVisibleEntry());
3159 EXPECT_FALSE(controller.GetPendingEntry());
3161 notifications.Reset();
3164 // Tests that the URLs for renderer-initiated navigations in new tabs are
3165 // displayed to the user even after they fail, as long as the initial
3166 // about:blank page has not been modified. If so, we must revert to showing
3167 // about:blank. See http://crbug.com/355537.
3168 TEST_F(NavigationControllerTest, ShowRendererURLAfterFailUntilModified) {
3169 NavigationControllerImpl& controller = controller_impl();
3170 TestNotificationTracker notifications;
3171 RegisterForAllNavNotifications(&notifications, &controller);
3173 const GURL url("http://foo");
3175 // For renderer-initiated navigations in new tabs (with no committed entries),
3176 // we show the pending entry's URL as long as the about:blank page is not
3177 // modified.
3178 NavigationController::LoadURLParams load_url_params(url);
3179 load_url_params.transition_type = ui::PAGE_TRANSITION_LINK;
3180 load_url_params.is_renderer_initiated = true;
3181 controller.LoadURLWithParams(load_url_params);
3182 EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
3183 EXPECT_EQ(url, controller.GetPendingEntry()->GetURL());
3184 EXPECT_TRUE(controller.GetPendingEntry()->is_renderer_initiated());
3185 EXPECT_TRUE(controller.IsInitialNavigation());
3186 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3188 // There should be no title yet.
3189 EXPECT_TRUE(contents()->GetTitle().empty());
3191 // Suppose it aborts before committing, if it's a 204 or download or due to a
3192 // stop or a new navigation from the user. The URL should remain visible.
3193 FrameHostMsg_DidFailProvisionalLoadWithError_Params params;
3194 params.error_code = net::ERR_ABORTED;
3195 params.error_description = base::string16();
3196 params.url = url;
3197 params.showing_repost_interstitial = false;
3198 main_test_rfh()->OnMessageReceived(
3199 FrameHostMsg_DidFailProvisionalLoadWithError(0, params));
3200 EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
3202 // If something else later modifies the contents of the about:blank page, then
3203 // we must revert to showing about:blank to avoid a URL spoof.
3204 main_test_rfh()->OnMessageReceived(FrameHostMsg_DidAccessInitialDocument(0));
3205 EXPECT_TRUE(contents()->HasAccessedInitialDocument());
3206 EXPECT_FALSE(controller.GetVisibleEntry());
3207 EXPECT_EQ(url, controller.GetPendingEntry()->GetURL());
3209 notifications.Reset();
3212 TEST_F(NavigationControllerTest, DontShowRendererURLInNewTabAfterCommit) {
3213 NavigationControllerImpl& controller = controller_impl();
3214 TestNotificationTracker notifications;
3215 RegisterForAllNavNotifications(&notifications, &controller);
3217 const GURL url1("http://foo/eh");
3218 const GURL url2("http://foo/bee");
3220 // For renderer-initiated navigations in new tabs (with no committed entries),
3221 // we show the pending entry's URL as long as the about:blank page is not
3222 // modified.
3223 NavigationController::LoadURLParams load_url_params(url1);
3224 load_url_params.transition_type = ui::PAGE_TRANSITION_LINK;
3225 load_url_params.is_renderer_initiated = true;
3226 controller.LoadURLWithParams(load_url_params);
3227 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
3228 EXPECT_TRUE(controller.GetPendingEntry()->is_renderer_initiated());
3229 EXPECT_TRUE(controller.IsInitialNavigation());
3230 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3232 // Simulate a commit and then starting a new pending navigation.
3233 main_test_rfh()->SendNavigate(0, url1);
3234 NavigationController::LoadURLParams load_url2_params(url2);
3235 load_url2_params.transition_type = ui::PAGE_TRANSITION_LINK;
3236 load_url2_params.is_renderer_initiated = true;
3237 controller.LoadURLWithParams(load_url2_params);
3239 // We should not consider this an initial navigation, and thus should
3240 // not show the pending URL.
3241 EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3242 EXPECT_FALSE(controller.IsInitialNavigation());
3243 EXPECT_TRUE(controller.GetVisibleEntry());
3244 EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
3246 notifications.Reset();
3249 // Tests that IsInPageNavigation returns appropriate results. Prevents
3250 // regression for bug 1126349.
3251 TEST_F(NavigationControllerTest, IsInPageNavigation) {
3252 NavigationControllerImpl& controller = controller_impl();
3253 const GURL url("http://www.google.com/home.html");
3255 // If the renderer claims it performed an in-page navigation from
3256 // about:blank, trust the renderer.
3257 // This can happen when an iframe is created and populated via
3258 // document.write(), then tries to perform a fragment navigation.
3259 // TODO(japhet): We should only trust the renderer if the about:blank
3260 // was the first document in the given frame, but we don't have enough
3261 // information to identify that case currently.
3262 const GURL blank_url(url::kAboutBlankURL);
3263 main_test_rfh()->SendNavigate(0, blank_url);
3264 EXPECT_TRUE(controller.IsURLInPageNavigation(url, true,
3265 main_test_rfh()));
3267 // Navigate to URL with no refs.
3268 main_test_rfh()->SendNavigate(0, url);
3270 // Reloading the page is not an in-page navigation.
3271 EXPECT_FALSE(controller.IsURLInPageNavigation(url, false,
3272 main_test_rfh()));
3273 const GURL other_url("http://www.google.com/add.html");
3274 EXPECT_FALSE(controller.IsURLInPageNavigation(other_url, false,
3275 main_test_rfh()));
3276 const GURL url_with_ref("http://www.google.com/home.html#my_ref");
3277 EXPECT_TRUE(controller.IsURLInPageNavigation(url_with_ref, true,
3278 main_test_rfh()));
3280 // Navigate to URL with refs.
3281 main_test_rfh()->SendNavigate(1, url_with_ref);
3283 // Reloading the page is not an in-page navigation.
3284 EXPECT_FALSE(controller.IsURLInPageNavigation(url_with_ref, false,
3285 main_test_rfh()));
3286 EXPECT_FALSE(controller.IsURLInPageNavigation(url, false,
3287 main_test_rfh()));
3288 EXPECT_FALSE(controller.IsURLInPageNavigation(other_url, false,
3289 main_test_rfh()));
3290 const GURL other_url_with_ref("http://www.google.com/home.html#my_other_ref");
3291 EXPECT_TRUE(controller.IsURLInPageNavigation(other_url_with_ref, true,
3292 main_test_rfh()));
3294 // Going to the same url again will be considered in-page
3295 // if the renderer says it is even if the navigation type isn't IN_PAGE.
3296 EXPECT_TRUE(controller.IsURLInPageNavigation(url_with_ref, true,
3297 main_test_rfh()));
3299 // Going back to the non ref url will be considered in-page if the navigation
3300 // type is IN_PAGE.
3301 EXPECT_TRUE(controller.IsURLInPageNavigation(url, true,
3302 main_test_rfh()));
3304 // If the renderer says this is a same-origin in-page navigation, believe it.
3305 // This is the pushState/replaceState case.
3306 EXPECT_TRUE(controller.IsURLInPageNavigation(other_url, true,
3307 main_test_rfh()));
3309 // Test allow_universal_access_from_file_urls flag.
3310 const GURL different_origin_url("http://www.example.com");
3311 MockRenderProcessHost* rph =
3312 static_cast<MockRenderProcessHost*>(main_test_rfh()->GetProcess());
3313 WebPreferences prefs = test_rvh()->GetWebkitPreferences();
3314 prefs.allow_universal_access_from_file_urls = true;
3315 test_rvh()->UpdateWebkitPreferences(prefs);
3316 prefs = test_rvh()->GetWebkitPreferences();
3317 EXPECT_TRUE(prefs.allow_universal_access_from_file_urls);
3318 // Allow in page navigation if existing URL is file scheme.
3319 const GURL file_url("file:///foo/index.html");
3320 main_test_rfh()->SendNavigate(0, file_url);
3321 EXPECT_EQ(0, rph->bad_msg_count());
3322 EXPECT_TRUE(controller.IsURLInPageNavigation(different_origin_url, true,
3323 main_test_rfh()));
3324 EXPECT_EQ(0, rph->bad_msg_count());
3325 // Don't honor allow_universal_access_from_file_urls if existing URL is
3326 // not file scheme.
3327 main_test_rfh()->SendNavigate(0, url);
3328 EXPECT_FALSE(controller.IsURLInPageNavigation(different_origin_url, true,
3329 main_test_rfh()));
3330 EXPECT_EQ(1, rph->bad_msg_count());
3332 // Remove allow_universal_access_from_file_urls flag.
3333 prefs.allow_universal_access_from_file_urls = false;
3334 test_rvh()->UpdateWebkitPreferences(prefs);
3335 prefs = test_rvh()->GetWebkitPreferences();
3336 EXPECT_FALSE(prefs.allow_universal_access_from_file_urls);
3338 // Don't believe the renderer if it claims a cross-origin navigation is
3339 // in-page.
3340 EXPECT_EQ(1, rph->bad_msg_count());
3341 EXPECT_FALSE(controller.IsURLInPageNavigation(different_origin_url, true,
3342 main_test_rfh()));
3343 EXPECT_EQ(2, rph->bad_msg_count());
3346 // Some pages can have subframes with the same base URL (minus the reference) as
3347 // the main page. Even though this is hard, it can happen, and we don't want
3348 // these subframe navigations to affect the toplevel document. They should
3349 // instead be ignored. http://crbug.com/5585
3350 TEST_F(NavigationControllerTest, SameSubframe) {
3351 NavigationControllerImpl& controller = controller_impl();
3352 // Navigate the main frame.
3353 const GURL url("http://www.google.com/");
3354 main_test_rfh()->SendNavigate(0, url);
3356 // We should be at the first navigation entry.
3357 EXPECT_EQ(controller.GetEntryCount(), 1);
3358 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
3360 // Navigate a subframe that would normally count as in-page.
3361 const GURL subframe("http://www.google.com/#");
3362 FrameHostMsg_DidCommitProvisionalLoad_Params params;
3363 params.page_id = 0;
3364 params.url = subframe;
3365 params.transition = ui::PAGE_TRANSITION_AUTO_SUBFRAME;
3366 params.should_update_history = false;
3367 params.gesture = NavigationGestureAuto;
3368 params.is_post = false;
3369 params.page_state = PageState::CreateFromURL(subframe);
3370 LoadCommittedDetails details;
3371 EXPECT_FALSE(controller.RendererDidNavigate(main_test_rfh(), params,
3372 &details));
3374 // Nothing should have changed.
3375 EXPECT_EQ(controller.GetEntryCount(), 1);
3376 EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
3379 // Make sure that on cloning a WebContentsImpl and going back needs_reload is
3380 // false.
3381 TEST_F(NavigationControllerTest, CloneAndGoBack) {
3382 NavigationControllerImpl& controller = controller_impl();
3383 const GURL url1("http://foo1");
3384 const GURL url2("http://foo2");
3385 const base::string16 title(base::ASCIIToUTF16("Title"));
3387 NavigateAndCommit(url1);
3388 controller.GetVisibleEntry()->SetTitle(title);
3389 NavigateAndCommit(url2);
3391 scoped_ptr<WebContents> clone(controller.GetWebContents()->Clone());
3393 ASSERT_EQ(2, clone->GetController().GetEntryCount());
3394 EXPECT_TRUE(clone->GetController().NeedsReload());
3395 clone->GetController().GoBack();
3396 // Navigating back should have triggered needs_reload_ to go false.
3397 EXPECT_FALSE(clone->GetController().NeedsReload());
3399 // Ensure that the pending URL and its title are visible.
3400 EXPECT_EQ(url1, clone->GetController().GetVisibleEntry()->GetURL());
3401 EXPECT_EQ(title, clone->GetTitle());
3404 // Make sure that reloading a cloned tab doesn't change its pending entry index.
3405 // See http://crbug.com/234491.
3406 TEST_F(NavigationControllerTest, CloneAndReload) {
3407 NavigationControllerImpl& controller = controller_impl();
3408 const GURL url1("http://foo1");
3409 const GURL url2("http://foo2");
3410 const base::string16 title(base::ASCIIToUTF16("Title"));
3412 NavigateAndCommit(url1);
3413 controller.GetVisibleEntry()->SetTitle(title);
3414 NavigateAndCommit(url2);
3416 scoped_ptr<WebContents> clone(controller.GetWebContents()->Clone());
3417 clone->GetController().LoadIfNecessary();
3419 ASSERT_EQ(2, clone->GetController().GetEntryCount());
3420 EXPECT_EQ(1, clone->GetController().GetPendingEntryIndex());
3422 clone->GetController().Reload(true);
3423 EXPECT_EQ(1, clone->GetController().GetPendingEntryIndex());
3426 // Make sure that cloning a WebContentsImpl doesn't copy interstitials.
3427 TEST_F(NavigationControllerTest, CloneOmitsInterstitials) {
3428 NavigationControllerImpl& controller = controller_impl();
3429 const GURL url1("http://foo1");
3430 const GURL url2("http://foo2");
3432 NavigateAndCommit(url1);
3433 NavigateAndCommit(url2);
3435 // Add an interstitial entry. Should be deleted with controller.
3436 NavigationEntryImpl* interstitial_entry = new NavigationEntryImpl();
3437 interstitial_entry->set_page_type(PAGE_TYPE_INTERSTITIAL);
3438 controller.SetTransientEntry(interstitial_entry);
3440 scoped_ptr<WebContents> clone(controller.GetWebContents()->Clone());
3442 ASSERT_EQ(2, clone->GetController().GetEntryCount());
3445 // Test requesting and triggering a lazy reload.
3446 TEST_F(NavigationControllerTest, LazyReload) {
3447 NavigationControllerImpl& controller = controller_impl();
3448 const GURL url("http://foo");
3449 NavigateAndCommit(url);
3450 ASSERT_FALSE(controller.NeedsReload());
3451 EXPECT_NE(ui::PAGE_TRANSITION_RELOAD,
3452 controller.GetLastCommittedEntry()->GetTransitionType());
3454 // Request a reload to happen when the controller becomes active (e.g. after
3455 // the renderer gets killed in background on Android).
3456 controller.SetNeedsReload();
3457 ASSERT_TRUE(controller.NeedsReload());
3458 EXPECT_EQ(ui::PAGE_TRANSITION_RELOAD,
3459 controller.GetLastCommittedEntry()->GetTransitionType());
3461 // Set the controller as active, triggering the requested reload.
3462 controller.SetActive(true);
3463 ASSERT_FALSE(controller.NeedsReload());
3464 EXPECT_EQ(ui::PAGE_TRANSITION_RELOAD,
3465 controller.GetPendingEntry()->GetTransitionType());
3468 // Test requesting and triggering a lazy reload without any committed entry.
3469 TEST_F(NavigationControllerTest, LazyReloadWithoutCommittedEntry) {
3470 NavigationControllerImpl& controller = controller_impl();
3471 const GURL url("http://foo");
3472 controller.LoadURL(url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
3473 ASSERT_FALSE(controller.NeedsReload());
3474 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED,
3475 controller.GetPendingEntry()->GetTransitionType());
3477 // Request a reload to happen when the controller becomes active (e.g. after
3478 // the renderer gets killed in background on Android).
3479 controller.SetNeedsReload();
3480 ASSERT_TRUE(controller.NeedsReload());
3481 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED,
3482 controller.GetPendingEntry()->GetTransitionType());
3484 // Set the controller as active, triggering the requested reload.
3485 controller.SetActive(true);
3486 ASSERT_FALSE(controller.NeedsReload());
3487 EXPECT_EQ(ui::PAGE_TRANSITION_TYPED,
3488 controller.GetPendingEntry()->GetTransitionType());
3491 // Tests a subframe navigation while a toplevel navigation is pending.
3492 // http://crbug.com/43967
3493 TEST_F(NavigationControllerTest, SubframeWhilePending) {
3494 NavigationControllerImpl& controller = controller_impl();
3495 // Load the first page.
3496 const GURL url1("http://foo/");
3497 NavigateAndCommit(url1);
3499 // Now start a pending load to a totally different page, but don't commit it.
3500 const GURL url2("http://bar/");
3501 controller.LoadURL(
3502 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
3504 // Send a subframe update from the first page, as if one had just
3505 // automatically loaded. Auto subframes don't increment the page ID.
3506 const GURL url1_sub("http://foo/subframe");
3507 FrameHostMsg_DidCommitProvisionalLoad_Params params;
3508 params.page_id = controller.GetLastCommittedEntry()->GetPageID();
3509 params.url = url1_sub;
3510 params.transition = ui::PAGE_TRANSITION_AUTO_SUBFRAME;
3511 params.should_update_history = false;
3512 params.gesture = NavigationGestureAuto;
3513 params.is_post = false;
3514 params.page_state = PageState::CreateFromURL(url1_sub);
3515 LoadCommittedDetails details;
3517 // This should return false meaning that nothing was actually updated.
3518 EXPECT_FALSE(controller.RendererDidNavigate(main_test_rfh(), params,
3519 &details));
3521 // The notification should have updated the last committed one, and not
3522 // the pending load.
3523 EXPECT_EQ(url1, controller.GetLastCommittedEntry()->GetURL());
3525 // The active entry should be unchanged by the subframe load.
3526 EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
3529 // Test CopyStateFrom with 2 urls, the first selected and nothing in the target.
3530 TEST_F(NavigationControllerTest, CopyStateFrom) {
3531 NavigationControllerImpl& controller = controller_impl();
3532 const GURL url1("http://foo1");
3533 const GURL url2("http://foo2");
3535 NavigateAndCommit(url1);
3536 NavigateAndCommit(url2);
3537 controller.GoBack();
3538 contents()->CommitPendingNavigation();
3540 scoped_ptr<TestWebContents> other_contents(
3541 static_cast<TestWebContents*>(CreateTestWebContents()));
3542 NavigationControllerImpl& other_controller = other_contents->GetController();
3543 other_controller.CopyStateFrom(controller);
3545 // other_controller should now contain 2 urls.
3546 ASSERT_EQ(2, other_controller.GetEntryCount());
3547 // We should be looking at the first one.
3548 ASSERT_EQ(0, other_controller.GetCurrentEntryIndex());
3550 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3551 EXPECT_EQ(0, other_controller.GetEntryAtIndex(0)->GetPageID());
3552 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(1)->GetURL());
3553 // This is a different site than url1, so the IDs start again at 0.
3554 EXPECT_EQ(0, other_controller.GetEntryAtIndex(1)->GetPageID());
3556 // The max page ID map should be copied over and updated with the max page ID
3557 // from the current tab.
3558 SiteInstance* instance1 =
3559 other_controller.GetEntryAtIndex(0)->site_instance();
3560 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
3562 // Ensure the SessionStorageNamespaceMaps are the same size and have
3563 // the same partitons loaded.
3565 // TODO(ajwong): We should load a url from a different partition earlier
3566 // to make sure this map has more than one entry.
3567 const SessionStorageNamespaceMap& session_storage_namespace_map =
3568 controller.GetSessionStorageNamespaceMap();
3569 const SessionStorageNamespaceMap& other_session_storage_namespace_map =
3570 other_controller.GetSessionStorageNamespaceMap();
3571 EXPECT_EQ(session_storage_namespace_map.size(),
3572 other_session_storage_namespace_map.size());
3573 for (SessionStorageNamespaceMap::const_iterator it =
3574 session_storage_namespace_map.begin();
3575 it != session_storage_namespace_map.end();
3576 ++it) {
3577 SessionStorageNamespaceMap::const_iterator other =
3578 other_session_storage_namespace_map.find(it->first);
3579 EXPECT_TRUE(other != other_session_storage_namespace_map.end());
3583 // Tests CopyStateFromAndPrune with 2 urls in source, 1 in dest.
3584 TEST_F(NavigationControllerTest, CopyStateFromAndPrune) {
3585 NavigationControllerImpl& controller = controller_impl();
3586 const GURL url1("http://foo/1");
3587 const GURL url2("http://foo/2");
3588 const GURL url3("http://foo/3");
3590 NavigateAndCommit(url1);
3591 NavigateAndCommit(url2);
3593 // First two entries should have the same SiteInstance.
3594 SiteInstance* instance1 = controller.GetEntryAtIndex(0)->site_instance();
3595 SiteInstance* instance2 = controller.GetEntryAtIndex(1)->site_instance();
3596 EXPECT_EQ(instance1, instance2);
3597 EXPECT_EQ(0, controller.GetEntryAtIndex(0)->GetPageID());
3598 EXPECT_EQ(1, controller.GetEntryAtIndex(1)->GetPageID());
3599 EXPECT_EQ(1, contents()->GetMaxPageIDForSiteInstance(instance1));
3601 scoped_ptr<TestWebContents> other_contents(
3602 static_cast<TestWebContents*>(CreateTestWebContents()));
3603 NavigationControllerImpl& other_controller = other_contents->GetController();
3604 other_contents->NavigateAndCommit(url3);
3605 other_contents->ExpectSetHistoryOffsetAndLength(2, 3);
3606 other_controller.CopyStateFromAndPrune(&controller, false);
3608 // other_controller should now contain the 3 urls: url1, url2 and url3.
3610 ASSERT_EQ(3, other_controller.GetEntryCount());
3612 ASSERT_EQ(2, other_controller.GetCurrentEntryIndex());
3614 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3615 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(1)->GetURL());
3616 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(2)->GetURL());
3617 EXPECT_EQ(0, other_controller.GetEntryAtIndex(0)->GetPageID());
3618 EXPECT_EQ(1, other_controller.GetEntryAtIndex(1)->GetPageID());
3619 EXPECT_EQ(0, other_controller.GetEntryAtIndex(2)->GetPageID());
3621 // A new SiteInstance in a different BrowsingInstance should be used for the
3622 // new tab.
3623 SiteInstance* instance3 =
3624 other_controller.GetEntryAtIndex(2)->site_instance();
3625 EXPECT_NE(instance3, instance1);
3626 EXPECT_FALSE(instance3->IsRelatedSiteInstance(instance1));
3628 // The max page ID map should be copied over and updated with the max page ID
3629 // from the current tab.
3630 EXPECT_EQ(1, other_contents->GetMaxPageIDForSiteInstance(instance1));
3631 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance3));
3634 // Test CopyStateFromAndPrune with 2 urls, the first selected and 1 entry in
3635 // the target.
3636 TEST_F(NavigationControllerTest, CopyStateFromAndPrune2) {
3637 NavigationControllerImpl& controller = controller_impl();
3638 const GURL url1("http://foo1");
3639 const GURL url2("http://foo2");
3640 const GURL url3("http://foo3");
3642 NavigateAndCommit(url1);
3643 NavigateAndCommit(url2);
3644 controller.GoBack();
3645 contents()->CommitPendingNavigation();
3647 scoped_ptr<TestWebContents> other_contents(
3648 static_cast<TestWebContents*>(CreateTestWebContents()));
3649 NavigationControllerImpl& other_controller = other_contents->GetController();
3650 other_contents->NavigateAndCommit(url3);
3651 other_contents->ExpectSetHistoryOffsetAndLength(1, 2);
3652 other_controller.CopyStateFromAndPrune(&controller, false);
3654 // other_controller should now contain: url1, url3
3656 ASSERT_EQ(2, other_controller.GetEntryCount());
3657 ASSERT_EQ(1, other_controller.GetCurrentEntryIndex());
3659 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3660 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(1)->GetURL());
3661 EXPECT_EQ(0, other_controller.GetEntryAtIndex(1)->GetPageID());
3663 // The max page ID map should be copied over and updated with the max page ID
3664 // from the current tab.
3665 SiteInstance* instance1 =
3666 other_controller.GetEntryAtIndex(1)->site_instance();
3667 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
3670 // Test CopyStateFromAndPrune with 2 urls, the last selected and 2 entries in
3671 // the target.
3672 TEST_F(NavigationControllerTest, CopyStateFromAndPrune3) {
3673 NavigationControllerImpl& controller = controller_impl();
3674 const GURL url1("http://foo1");
3675 const GURL url2("http://foo2");
3676 const GURL url3("http://foo3");
3677 const GURL url4("http://foo4");
3679 NavigateAndCommit(url1);
3680 NavigateAndCommit(url2);
3682 scoped_ptr<TestWebContents> other_contents(
3683 static_cast<TestWebContents*>(CreateTestWebContents()));
3684 NavigationControllerImpl& other_controller = other_contents->GetController();
3685 other_contents->NavigateAndCommit(url3);
3686 other_contents->NavigateAndCommit(url4);
3687 other_contents->ExpectSetHistoryOffsetAndLength(2, 3);
3688 other_controller.CopyStateFromAndPrune(&controller, false);
3690 // other_controller should now contain: url1, url2, url4
3692 ASSERT_EQ(3, other_controller.GetEntryCount());
3693 ASSERT_EQ(2, other_controller.GetCurrentEntryIndex());
3695 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3696 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(1)->GetURL());
3697 EXPECT_EQ(url4, other_controller.GetEntryAtIndex(2)->GetURL());
3699 // The max page ID map should be copied over and updated with the max page ID
3700 // from the current tab.
3701 SiteInstance* instance1 =
3702 other_controller.GetEntryAtIndex(2)->site_instance();
3703 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
3706 // Test CopyStateFromAndPrune with 2 urls, 2 entries in the target, with
3707 // not the last entry selected in the target.
3708 TEST_F(NavigationControllerTest, CopyStateFromAndPruneNotLast) {
3709 NavigationControllerImpl& controller = controller_impl();
3710 const GURL url1("http://foo1");
3711 const GURL url2("http://foo2");
3712 const GURL url3("http://foo3");
3713 const GURL url4("http://foo4");
3715 NavigateAndCommit(url1);
3716 NavigateAndCommit(url2);
3718 scoped_ptr<TestWebContents> other_contents(
3719 static_cast<TestWebContents*>(CreateTestWebContents()));
3720 NavigationControllerImpl& other_controller = other_contents->GetController();
3721 other_contents->NavigateAndCommit(url3);
3722 other_contents->NavigateAndCommit(url4);
3723 other_controller.GoBack();
3724 other_contents->CommitPendingNavigation();
3725 other_contents->ExpectSetHistoryOffsetAndLength(2, 3);
3726 other_controller.CopyStateFromAndPrune(&controller, false);
3728 // other_controller should now contain: url1, url2, url3
3730 ASSERT_EQ(3, other_controller.GetEntryCount());
3731 ASSERT_EQ(2, other_controller.GetCurrentEntryIndex());
3733 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3734 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(1)->GetURL());
3735 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(2)->GetURL());
3737 // The max page ID map should be copied over and updated with the max page ID
3738 // from the current tab.
3739 SiteInstance* instance1 =
3740 other_controller.GetEntryAtIndex(2)->site_instance();
3741 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
3744 // Test CopyStateFromAndPrune with 2 urls, the first selected and 1 entry plus
3745 // a pending entry in the target.
3746 TEST_F(NavigationControllerTest, CopyStateFromAndPruneTargetPending) {
3747 NavigationControllerImpl& controller = controller_impl();
3748 const GURL url1("http://foo1");
3749 const GURL url2("http://foo2");
3750 const GURL url3("http://foo3");
3751 const GURL url4("http://foo4");
3753 NavigateAndCommit(url1);
3754 NavigateAndCommit(url2);
3755 controller.GoBack();
3756 contents()->CommitPendingNavigation();
3758 scoped_ptr<TestWebContents> other_contents(
3759 static_cast<TestWebContents*>(CreateTestWebContents()));
3760 NavigationControllerImpl& other_controller = other_contents->GetController();
3761 other_contents->NavigateAndCommit(url3);
3762 other_controller.LoadURL(
3763 url4, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
3764 other_contents->ExpectSetHistoryOffsetAndLength(1, 2);
3765 other_controller.CopyStateFromAndPrune(&controller, false);
3767 // other_controller should now contain url1, url3, and a pending entry
3768 // for url4.
3770 ASSERT_EQ(2, other_controller.GetEntryCount());
3771 EXPECT_EQ(1, other_controller.GetCurrentEntryIndex());
3773 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3774 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(1)->GetURL());
3776 // And there should be a pending entry for url4.
3777 ASSERT_TRUE(other_controller.GetPendingEntry());
3778 EXPECT_EQ(url4, other_controller.GetPendingEntry()->GetURL());
3780 // The max page ID map should be copied over and updated with the max page ID
3781 // from the current tab.
3782 SiteInstance* instance1 =
3783 other_controller.GetEntryAtIndex(0)->site_instance();
3784 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
3787 // Test CopyStateFromAndPrune with 1 url in the source, 1 entry and a pending
3788 // client redirect entry (with the same page ID) in the target. This used to
3789 // crash because the last committed entry would be pruned but max_page_id
3790 // remembered the page ID (http://crbug.com/234809).
3791 TEST_F(NavigationControllerTest, CopyStateFromAndPruneTargetPending2) {
3792 NavigationControllerImpl& controller = controller_impl();
3793 const GURL url1("http://foo1");
3794 const GURL url2a("http://foo2/a");
3795 const GURL url2b("http://foo2/b");
3797 NavigateAndCommit(url1);
3799 scoped_ptr<TestWebContents> other_contents(
3800 static_cast<TestWebContents*>(CreateTestWebContents()));
3801 NavigationControllerImpl& other_controller = other_contents->GetController();
3802 other_contents->NavigateAndCommit(url2a);
3803 // Simulate a client redirect, which has the same page ID as entry 2a.
3804 other_controller.LoadURL(
3805 url2b, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
3806 other_controller.GetPendingEntry()->SetPageID(
3807 other_controller.GetLastCommittedEntry()->GetPageID());
3809 other_contents->ExpectSetHistoryOffsetAndLength(1, 2);
3810 other_controller.CopyStateFromAndPrune(&controller, false);
3812 // other_controller should now contain url1, url2a, and a pending entry
3813 // for url2b.
3815 ASSERT_EQ(2, other_controller.GetEntryCount());
3816 EXPECT_EQ(1, other_controller.GetCurrentEntryIndex());
3818 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3819 EXPECT_EQ(url2a, other_controller.GetEntryAtIndex(1)->GetURL());
3821 // And there should be a pending entry for url4.
3822 ASSERT_TRUE(other_controller.GetPendingEntry());
3823 EXPECT_EQ(url2b, other_controller.GetPendingEntry()->GetURL());
3825 // Let the pending entry commit.
3826 other_contents->CommitPendingNavigation();
3828 // The max page ID map should be copied over and updated with the max page ID
3829 // from the current tab.
3830 SiteInstance* instance1 =
3831 other_controller.GetEntryAtIndex(1)->site_instance();
3832 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
3835 // Test CopyStateFromAndPrune with 2 urls, a back navigation pending in the
3836 // source, and 1 entry in the target. The back pending entry should be ignored.
3837 TEST_F(NavigationControllerTest, CopyStateFromAndPruneSourcePending) {
3838 NavigationControllerImpl& controller = controller_impl();
3839 const GURL url1("http://foo1");
3840 const GURL url2("http://foo2");
3841 const GURL url3("http://foo3");
3843 NavigateAndCommit(url1);
3844 NavigateAndCommit(url2);
3845 controller.GoBack();
3847 scoped_ptr<TestWebContents> other_contents(
3848 static_cast<TestWebContents*>(CreateTestWebContents()));
3849 NavigationControllerImpl& other_controller = other_contents->GetController();
3850 other_contents->NavigateAndCommit(url3);
3851 other_contents->ExpectSetHistoryOffsetAndLength(2, 3);
3852 other_controller.CopyStateFromAndPrune(&controller, false);
3854 // other_controller should now contain: url1, url2, url3
3856 ASSERT_EQ(3, other_controller.GetEntryCount());
3857 ASSERT_EQ(2, other_controller.GetCurrentEntryIndex());
3859 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3860 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(1)->GetURL());
3861 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(2)->GetURL());
3862 EXPECT_EQ(0, other_controller.GetEntryAtIndex(2)->GetPageID());
3864 // The max page ID map should be copied over and updated with the max page ID
3865 // from the current tab.
3866 SiteInstance* instance1 =
3867 other_controller.GetEntryAtIndex(2)->site_instance();
3868 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
3871 // Tests CopyStateFromAndPrune with 3 urls in source, 1 in dest,
3872 // when the max entry count is 3. We should prune one entry.
3873 TEST_F(NavigationControllerTest, CopyStateFromAndPruneMaxEntries) {
3874 NavigationControllerImpl& controller = controller_impl();
3875 size_t original_count = NavigationControllerImpl::max_entry_count();
3876 const int kMaxEntryCount = 3;
3878 NavigationControllerImpl::set_max_entry_count_for_testing(kMaxEntryCount);
3880 const GURL url1("http://foo/1");
3881 const GURL url2("http://foo/2");
3882 const GURL url3("http://foo/3");
3883 const GURL url4("http://foo/4");
3885 // Create a PrunedListener to observe prune notifications.
3886 PrunedListener listener(&controller);
3888 NavigateAndCommit(url1);
3889 NavigateAndCommit(url2);
3890 NavigateAndCommit(url3);
3892 scoped_ptr<TestWebContents> other_contents(
3893 static_cast<TestWebContents*>(CreateTestWebContents()));
3894 NavigationControllerImpl& other_controller = other_contents->GetController();
3895 other_contents->NavigateAndCommit(url4);
3896 other_contents->ExpectSetHistoryOffsetAndLength(2, 3);
3897 other_controller.CopyStateFromAndPrune(&controller, false);
3899 // We should have received a pruned notification.
3900 EXPECT_EQ(1, listener.notification_count_);
3901 EXPECT_TRUE(listener.details_.from_front);
3902 EXPECT_EQ(1, listener.details_.count);
3904 // other_controller should now contain only 3 urls: url2, url3 and url4.
3906 ASSERT_EQ(3, other_controller.GetEntryCount());
3908 ASSERT_EQ(2, other_controller.GetCurrentEntryIndex());
3910 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(0)->GetURL());
3911 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(1)->GetURL());
3912 EXPECT_EQ(url4, other_controller.GetEntryAtIndex(2)->GetURL());
3913 EXPECT_EQ(1, other_controller.GetEntryAtIndex(0)->GetPageID());
3914 EXPECT_EQ(2, other_controller.GetEntryAtIndex(1)->GetPageID());
3915 EXPECT_EQ(0, other_controller.GetEntryAtIndex(2)->GetPageID());
3917 NavigationControllerImpl::set_max_entry_count_for_testing(original_count);
3920 // Tests CopyStateFromAndPrune with 2 urls in source, 1 in dest, with
3921 // replace_entry set to true.
3922 TEST_F(NavigationControllerTest, CopyStateFromAndPruneReplaceEntry) {
3923 NavigationControllerImpl& controller = controller_impl();
3924 const GURL url1("http://foo/1");
3925 const GURL url2("http://foo/2");
3926 const GURL url3("http://foo/3");
3928 NavigateAndCommit(url1);
3929 NavigateAndCommit(url2);
3931 // First two entries should have the same SiteInstance.
3932 SiteInstance* instance1 = controller.GetEntryAtIndex(0)->site_instance();
3933 SiteInstance* instance2 = controller.GetEntryAtIndex(1)->site_instance();
3934 EXPECT_EQ(instance1, instance2);
3935 EXPECT_EQ(0, controller.GetEntryAtIndex(0)->GetPageID());
3936 EXPECT_EQ(1, controller.GetEntryAtIndex(1)->GetPageID());
3937 EXPECT_EQ(1, contents()->GetMaxPageIDForSiteInstance(instance1));
3939 scoped_ptr<TestWebContents> other_contents(
3940 static_cast<TestWebContents*>(CreateTestWebContents()));
3941 NavigationControllerImpl& other_controller = other_contents->GetController();
3942 other_contents->NavigateAndCommit(url3);
3943 other_contents->ExpectSetHistoryOffsetAndLength(1, 2);
3944 other_controller.CopyStateFromAndPrune(&controller, true);
3946 // other_controller should now contain the 2 urls: url1 and url3.
3948 ASSERT_EQ(2, other_controller.GetEntryCount());
3950 ASSERT_EQ(1, other_controller.GetCurrentEntryIndex());
3952 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3953 EXPECT_EQ(url3, other_controller.GetEntryAtIndex(1)->GetURL());
3954 EXPECT_EQ(0, other_controller.GetEntryAtIndex(0)->GetPageID());
3955 EXPECT_EQ(0, other_controller.GetEntryAtIndex(1)->GetPageID());
3957 // A new SiteInstance in a different BrowsingInstance should be used for the
3958 // new tab.
3959 SiteInstance* instance3 =
3960 other_controller.GetEntryAtIndex(1)->site_instance();
3961 EXPECT_NE(instance3, instance1);
3962 EXPECT_FALSE(instance3->IsRelatedSiteInstance(instance1));
3964 // The max page ID map should be copied over and updated with the max page ID
3965 // from the current tab.
3966 EXPECT_EQ(1, other_contents->GetMaxPageIDForSiteInstance(instance1));
3967 EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance3));
3970 // Tests CopyStateFromAndPrune with 3 urls in source, 1 in dest, when the max
3971 // entry count is 3 and replace_entry is true. We should not prune entries.
3972 TEST_F(NavigationControllerTest, CopyStateFromAndPruneMaxEntriesReplaceEntry) {
3973 NavigationControllerImpl& controller = controller_impl();
3974 size_t original_count = NavigationControllerImpl::max_entry_count();
3975 const int kMaxEntryCount = 3;
3977 NavigationControllerImpl::set_max_entry_count_for_testing(kMaxEntryCount);
3979 const GURL url1("http://foo/1");
3980 const GURL url2("http://foo/2");
3981 const GURL url3("http://foo/3");
3982 const GURL url4("http://foo/4");
3984 // Create a PrunedListener to observe prune notifications.
3985 PrunedListener listener(&controller);
3987 NavigateAndCommit(url1);
3988 NavigateAndCommit(url2);
3989 NavigateAndCommit(url3);
3991 scoped_ptr<TestWebContents> other_contents(
3992 static_cast<TestWebContents*>(CreateTestWebContents()));
3993 NavigationControllerImpl& other_controller = other_contents->GetController();
3994 other_contents->NavigateAndCommit(url4);
3995 other_contents->ExpectSetHistoryOffsetAndLength(2, 3);
3996 other_controller.CopyStateFromAndPrune(&controller, true);
3998 // We should have received no pruned notification.
3999 EXPECT_EQ(0, listener.notification_count_);
4001 // other_controller should now contain only 3 urls: url1, url2 and url4.
4003 ASSERT_EQ(3, other_controller.GetEntryCount());
4005 ASSERT_EQ(2, other_controller.GetCurrentEntryIndex());
4007 EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
4008 EXPECT_EQ(url2, other_controller.GetEntryAtIndex(1)->GetURL());
4009 EXPECT_EQ(url4, other_controller.GetEntryAtIndex(2)->GetURL());
4010 EXPECT_EQ(0, other_controller.GetEntryAtIndex(0)->GetPageID());
4011 EXPECT_EQ(1, other_controller.GetEntryAtIndex(1)->GetPageID());
4012 EXPECT_EQ(0, other_controller.GetEntryAtIndex(2)->GetPageID());
4014 NavigationControllerImpl::set_max_entry_count_for_testing(original_count);
4017 // Tests that we can navigate to the restored entries
4018 // imported by CopyStateFromAndPrune.
4019 TEST_F(NavigationControllerTest, CopyRestoredStateAndNavigate) {
4020 const GURL kRestoredUrls[] = {
4021 GURL("http://site1.com"),
4022 GURL("http://site2.com"),
4024 const GURL kInitialUrl("http://site3.com");
4026 std::vector<NavigationEntry*> entries;
4027 for (size_t i = 0; i < arraysize(kRestoredUrls); ++i) {
4028 NavigationEntry* entry = NavigationControllerImpl::CreateNavigationEntry(
4029 kRestoredUrls[i], Referrer(), ui::PAGE_TRANSITION_RELOAD, false,
4030 std::string(), browser_context());
4031 entry->SetPageID(static_cast<int>(i));
4032 entries.push_back(entry);
4035 // Create a WebContents with restored entries.
4036 scoped_ptr<TestWebContents> source_contents(
4037 static_cast<TestWebContents*>(CreateTestWebContents()));
4038 NavigationControllerImpl& source_controller =
4039 source_contents->GetController();
4040 source_controller.Restore(
4041 entries.size() - 1,
4042 NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY,
4043 &entries);
4044 ASSERT_EQ(0u, entries.size());
4045 source_controller.LoadIfNecessary();
4046 source_contents->CommitPendingNavigation();
4048 // Load a page, then copy state from |source_contents|.
4049 NavigateAndCommit(kInitialUrl);
4050 contents()->ExpectSetHistoryOffsetAndLength(2, 3);
4051 controller_impl().CopyStateFromAndPrune(&source_controller, false);
4052 ASSERT_EQ(3, controller_impl().GetEntryCount());
4054 // Go back to the first entry one at a time and
4055 // verify that it works as expected.
4056 EXPECT_EQ(2, controller_impl().GetCurrentEntryIndex());
4057 EXPECT_EQ(kInitialUrl, controller_impl().GetActiveEntry()->GetURL());
4059 controller_impl().GoBack();
4060 contents()->CommitPendingNavigation();
4061 EXPECT_EQ(1, controller_impl().GetCurrentEntryIndex());
4062 EXPECT_EQ(kRestoredUrls[1], controller_impl().GetActiveEntry()->GetURL());
4064 controller_impl().GoBack();
4065 contents()->CommitPendingNavigation();
4066 EXPECT_EQ(0, controller_impl().GetCurrentEntryIndex());
4067 EXPECT_EQ(kRestoredUrls[0], controller_impl().GetActiveEntry()->GetURL());
4070 // Tests that navigations initiated from the page (with the history object)
4071 // work as expected, creating pending entries.
4072 TEST_F(NavigationControllerTest, HistoryNavigate) {
4073 NavigationControllerImpl& controller = controller_impl();
4074 const GURL url1("http://foo/1");
4075 const GURL url2("http://foo/2");
4076 const GURL url3("http://foo/3");
4078 NavigateAndCommit(url1);
4079 NavigateAndCommit(url2);
4080 NavigateAndCommit(url3);
4081 controller.GoBack();
4082 contents()->CommitPendingNavigation();
4083 process()->sink().ClearMessages();
4085 // Simulate the page calling history.back(). It should create a pending entry.
4086 contents()->OnGoToEntryAtOffset(-1);
4087 EXPECT_EQ(0, controller.GetPendingEntryIndex());
4088 // The actual cross-navigation is suspended until the current RVH tells us
4089 // it unloaded, simulate that.
4090 contents()->ProceedWithCrossSiteNavigation();
4091 // Also make sure we told the page to navigate.
4092 GURL nav_url = GetLastNavigationURL();
4093 EXPECT_EQ(url1, nav_url);
4094 contents()->CommitPendingNavigation();
4095 process()->sink().ClearMessages();
4097 // Now test history.forward()
4098 contents()->OnGoToEntryAtOffset(2);
4099 EXPECT_EQ(2, controller.GetPendingEntryIndex());
4100 // The actual cross-navigation is suspended until the current RVH tells us
4101 // it unloaded, simulate that.
4102 contents()->ProceedWithCrossSiteNavigation();
4103 nav_url = GetLastNavigationURL();
4104 EXPECT_EQ(url3, nav_url);
4105 contents()->CommitPendingNavigation();
4106 process()->sink().ClearMessages();
4108 controller.DiscardNonCommittedEntries();
4110 // Make sure an extravagant history.go() doesn't break.
4111 contents()->OnGoToEntryAtOffset(120); // Out of bounds.
4112 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
4113 EXPECT_FALSE(HasNavigationRequest());
4116 // Test call to PruneAllButLastCommitted for the only entry.
4117 TEST_F(NavigationControllerTest, PruneAllButLastCommittedForSingle) {
4118 NavigationControllerImpl& controller = controller_impl();
4119 const GURL url1("http://foo1");
4120 NavigateAndCommit(url1);
4122 contents()->ExpectSetHistoryOffsetAndLength(0, 1);
4124 controller.PruneAllButLastCommitted();
4126 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
4127 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url1);
4130 // Test call to PruneAllButLastCommitted for first entry.
4131 TEST_F(NavigationControllerTest, PruneAllButLastCommittedForFirst) {
4132 NavigationControllerImpl& controller = controller_impl();
4133 const GURL url1("http://foo/1");
4134 const GURL url2("http://foo/2");
4135 const GURL url3("http://foo/3");
4137 NavigateAndCommit(url1);
4138 NavigateAndCommit(url2);
4139 NavigateAndCommit(url3);
4140 controller.GoBack();
4141 controller.GoBack();
4142 contents()->CommitPendingNavigation();
4144 contents()->ExpectSetHistoryOffsetAndLength(0, 1);
4146 controller.PruneAllButLastCommitted();
4148 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
4149 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url1);
4152 // Test call to PruneAllButLastCommitted for intermediate entry.
4153 TEST_F(NavigationControllerTest, PruneAllButLastCommittedForIntermediate) {
4154 NavigationControllerImpl& controller = controller_impl();
4155 const GURL url1("http://foo/1");
4156 const GURL url2("http://foo/2");
4157 const GURL url3("http://foo/3");
4159 NavigateAndCommit(url1);
4160 NavigateAndCommit(url2);
4161 NavigateAndCommit(url3);
4162 controller.GoBack();
4163 contents()->CommitPendingNavigation();
4165 contents()->ExpectSetHistoryOffsetAndLength(0, 1);
4167 controller.PruneAllButLastCommitted();
4169 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
4170 EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url2);
4173 // Test call to PruneAllButLastCommitted for a pending entry that is not yet in
4174 // the list of entries.
4175 TEST_F(NavigationControllerTest, PruneAllButLastCommittedForPendingNotInList) {
4176 NavigationControllerImpl& controller = controller_impl();
4177 const GURL url1("http://foo/1");
4178 const GURL url2("http://foo/2");
4179 const GURL url3("http://foo/3");
4181 NavigateAndCommit(url1);
4182 NavigateAndCommit(url2);
4184 // Create a pending entry that is not in the entry list.
4185 controller.LoadURL(
4186 url3, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
4187 EXPECT_TRUE(controller.GetPendingEntry());
4188 EXPECT_EQ(2, controller.GetEntryCount());
4190 contents()->ExpectSetHistoryOffsetAndLength(0, 1);
4191 controller.PruneAllButLastCommitted();
4193 // We should only have the last committed and pending entries at this point,
4194 // and the pending entry should still not be in the entry list.
4195 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
4196 EXPECT_EQ(url2, controller.GetEntryAtIndex(0)->GetURL());
4197 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
4198 EXPECT_TRUE(controller.GetPendingEntry());
4199 EXPECT_EQ(1, controller.GetEntryCount());
4201 // Try to commit the pending entry.
4202 main_test_rfh()->PrepareForCommit();
4203 main_test_rfh()->SendNavigate(2, url3);
4204 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
4205 EXPECT_FALSE(controller.GetPendingEntry());
4206 EXPECT_EQ(2, controller.GetEntryCount());
4207 EXPECT_EQ(url3, controller.GetEntryAtIndex(1)->GetURL());
4210 // Test to ensure that when we do a history navigation back to the current
4211 // committed page (e.g., going forward to a slow-loading page, then pressing
4212 // the back button), we just stop the navigation to prevent the throbber from
4213 // running continuously. Otherwise, the RenderViewHost forces the throbber to
4214 // start, but WebKit essentially ignores the navigation and never sends a
4215 // message to stop the throbber.
4216 TEST_F(NavigationControllerTest, StopOnHistoryNavigationToCurrentPage) {
4217 NavigationControllerImpl& controller = controller_impl();
4218 const GURL url0("http://foo/0");
4219 const GURL url1("http://foo/1");
4221 NavigateAndCommit(url0);
4222 NavigateAndCommit(url1);
4224 // Go back to the original page, then forward to the slow page, then back
4225 controller.GoBack();
4226 contents()->CommitPendingNavigation();
4228 controller.GoForward();
4229 EXPECT_EQ(1, controller.GetPendingEntryIndex());
4231 controller.GoBack();
4232 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
4235 TEST_F(NavigationControllerTest, IsInitialNavigation) {
4236 NavigationControllerImpl& controller = controller_impl();
4237 TestNotificationTracker notifications;
4238 RegisterForAllNavNotifications(&notifications, &controller);
4240 // Initial state.
4241 EXPECT_TRUE(controller.IsInitialNavigation());
4243 // After commit, it stays false.
4244 const GURL url1("http://foo1");
4245 main_test_rfh()->SendNavigate(0, url1);
4246 EXPECT_EQ(1U, navigation_entry_committed_counter_);
4247 navigation_entry_committed_counter_ = 0;
4248 EXPECT_FALSE(controller.IsInitialNavigation());
4250 // After starting a new navigation, it stays false.
4251 const GURL url2("http://foo2");
4252 controller.LoadURL(
4253 url2, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
4256 // Check that the favicon is not reused across a client redirect.
4257 // (crbug.com/28515)
4258 TEST_F(NavigationControllerTest, ClearFaviconOnRedirect) {
4259 const GURL kPageWithFavicon("http://withfavicon.html");
4260 const GURL kPageWithoutFavicon("http://withoutfavicon.html");
4261 const GURL kIconURL("http://withfavicon.ico");
4262 const gfx::Image kDefaultFavicon = FaviconStatus().image;
4264 NavigationControllerImpl& controller = controller_impl();
4265 TestNotificationTracker notifications;
4266 RegisterForAllNavNotifications(&notifications, &controller);
4268 main_test_rfh()->SendNavigate(0, kPageWithFavicon);
4269 EXPECT_EQ(1U, navigation_entry_committed_counter_);
4270 navigation_entry_committed_counter_ = 0;
4272 NavigationEntry* entry = controller.GetLastCommittedEntry();
4273 EXPECT_TRUE(entry);
4274 EXPECT_EQ(kPageWithFavicon, entry->GetURL());
4276 // Simulate Chromium having set the favicon for |kPageWithFavicon|.
4277 content::FaviconStatus& favicon_status = entry->GetFavicon();
4278 favicon_status.image = CreateImage(SK_ColorWHITE);
4279 favicon_status.url = kIconURL;
4280 favicon_status.valid = true;
4281 EXPECT_FALSE(DoImagesMatch(kDefaultFavicon, entry->GetFavicon().image));
4283 main_test_rfh()->SendNavigateWithTransition(
4284 0, // same page ID.
4285 kPageWithoutFavicon,
4286 ui::PAGE_TRANSITION_CLIENT_REDIRECT);
4287 EXPECT_EQ(1U, navigation_entry_committed_counter_);
4288 navigation_entry_committed_counter_ = 0;
4290 entry = controller.GetLastCommittedEntry();
4291 EXPECT_TRUE(entry);
4292 EXPECT_EQ(kPageWithoutFavicon, entry->GetURL());
4294 EXPECT_TRUE(DoImagesMatch(kDefaultFavicon, entry->GetFavicon().image));
4297 // Check that the favicon is not cleared for NavigationEntries which were
4298 // previously navigated to.
4299 TEST_F(NavigationControllerTest, BackNavigationDoesNotClearFavicon) {
4300 const GURL kUrl1("http://www.a.com/1");
4301 const GURL kUrl2("http://www.a.com/2");
4302 const GURL kIconURL("http://www.a.com/1/favicon.ico");
4304 NavigationControllerImpl& controller = controller_impl();
4305 TestNotificationTracker notifications;
4306 RegisterForAllNavNotifications(&notifications, &controller);
4308 main_test_rfh()->SendNavigate(0, kUrl1);
4309 EXPECT_EQ(1U, navigation_entry_committed_counter_);
4310 navigation_entry_committed_counter_ = 0;
4312 // Simulate Chromium having set the favicon for |kUrl1|.
4313 gfx::Image favicon_image = CreateImage(SK_ColorWHITE);
4314 content::NavigationEntry* entry = controller.GetLastCommittedEntry();
4315 EXPECT_TRUE(entry);
4316 content::FaviconStatus& favicon_status = entry->GetFavicon();
4317 favicon_status.image = favicon_image;
4318 favicon_status.url = kIconURL;
4319 favicon_status.valid = true;
4321 // Navigate to another page and go back to the original page.
4322 main_test_rfh()->SendNavigate(1, kUrl2);
4323 EXPECT_EQ(1U, navigation_entry_committed_counter_);
4324 navigation_entry_committed_counter_ = 0;
4325 main_test_rfh()->SendNavigateWithTransition(
4327 kUrl1,
4328 ui::PAGE_TRANSITION_FORWARD_BACK);
4329 EXPECT_EQ(1U, navigation_entry_committed_counter_);
4330 navigation_entry_committed_counter_ = 0;
4332 // Verify that the favicon for the page at |kUrl1| was not cleared.
4333 entry = controller.GetEntryAtIndex(0);
4334 EXPECT_TRUE(entry);
4335 EXPECT_EQ(kUrl1, entry->GetURL());
4336 EXPECT_TRUE(DoImagesMatch(favicon_image, entry->GetFavicon().image));
4339 // The test crashes on android: http://crbug.com/170449
4340 #if defined(OS_ANDROID)
4341 #define MAYBE_PurgeScreenshot DISABLED_PurgeScreenshot
4342 #else
4343 #define MAYBE_PurgeScreenshot PurgeScreenshot
4344 #endif
4345 // Tests that screenshot are purged correctly.
4346 TEST_F(NavigationControllerTest, MAYBE_PurgeScreenshot) {
4347 NavigationControllerImpl& controller = controller_impl();
4349 NavigationEntryImpl* entry;
4351 // Navigate enough times to make sure that some screenshots are purged.
4352 for (int i = 0; i < 12; ++i) {
4353 const GURL url(base::StringPrintf("http://foo%d/", i));
4354 NavigateAndCommit(url);
4355 EXPECT_EQ(i, controller.GetCurrentEntryIndex());
4358 MockScreenshotManager* screenshot_manager =
4359 new MockScreenshotManager(&controller);
4360 controller.SetScreenshotManager(screenshot_manager);
4361 for (int i = 0; i < controller.GetEntryCount(); ++i) {
4362 entry = controller.GetEntryAtIndex(i);
4363 screenshot_manager->TakeScreenshotFor(entry);
4364 EXPECT_TRUE(entry->screenshot().get());
4367 NavigateAndCommit(GURL("https://foo/"));
4368 EXPECT_EQ(13, controller.GetEntryCount());
4369 entry = controller.GetEntryAtIndex(11);
4370 screenshot_manager->TakeScreenshotFor(entry);
4372 for (int i = 0; i < 2; ++i) {
4373 entry = controller.GetEntryAtIndex(i);
4374 EXPECT_FALSE(entry->screenshot().get()) << "Screenshot " << i
4375 << " not purged";
4378 for (int i = 2; i < controller.GetEntryCount() - 1; ++i) {
4379 entry = controller.GetEntryAtIndex(i);
4380 EXPECT_TRUE(entry->screenshot().get()) << "Screenshot not found for " << i;
4383 // Navigate to index 5 and then try to assign screenshot to all entries.
4384 controller.GoToIndex(5);
4385 contents()->CommitPendingNavigation();
4386 EXPECT_EQ(5, controller.GetCurrentEntryIndex());
4387 for (int i = 0; i < controller.GetEntryCount() - 1; ++i) {
4388 entry = controller.GetEntryAtIndex(i);
4389 screenshot_manager->TakeScreenshotFor(entry);
4392 for (int i = 10; i <= 12; ++i) {
4393 entry = controller.GetEntryAtIndex(i);
4394 EXPECT_FALSE(entry->screenshot().get()) << "Screenshot " << i
4395 << " not purged";
4396 screenshot_manager->TakeScreenshotFor(entry);
4399 // Navigate to index 7 and assign screenshot to all entries.
4400 controller.GoToIndex(7);
4401 contents()->CommitPendingNavigation();
4402 EXPECT_EQ(7, controller.GetCurrentEntryIndex());
4403 for (int i = 0; i < controller.GetEntryCount() - 1; ++i) {
4404 entry = controller.GetEntryAtIndex(i);
4405 screenshot_manager->TakeScreenshotFor(entry);
4408 for (int i = 0; i < 2; ++i) {
4409 entry = controller.GetEntryAtIndex(i);
4410 EXPECT_FALSE(entry->screenshot().get()) << "Screenshot " << i
4411 << " not purged";
4414 // Clear all screenshots.
4415 EXPECT_EQ(13, controller.GetEntryCount());
4416 EXPECT_EQ(10, screenshot_manager->GetScreenshotCount());
4417 controller.ClearAllScreenshots();
4418 EXPECT_EQ(0, screenshot_manager->GetScreenshotCount());
4419 for (int i = 0; i < controller.GetEntryCount(); ++i) {
4420 entry = controller.GetEntryAtIndex(i);
4421 EXPECT_FALSE(entry->screenshot().get()) << "Screenshot " << i
4422 << " not cleared";
4426 TEST_F(NavigationControllerTest, PushStateUpdatesTitleAndFavicon) {
4427 // Navigate.
4428 contents()->GetMainFrame()->SendNavigate(1, GURL("http://foo"));
4430 // Set title and favicon.
4431 base::string16 title(base::ASCIIToUTF16("Title"));
4432 FaviconStatus favicon;
4433 favicon.valid = true;
4434 favicon.url = GURL("http://foo/favicon.ico");
4435 controller().GetLastCommittedEntry()->SetTitle(title);
4436 controller().GetLastCommittedEntry()->GetFavicon() = favicon;
4438 // history.pushState() is called.
4439 FrameHostMsg_DidCommitProvisionalLoad_Params params;
4440 GURL url("http://foo#foo");
4441 params.page_id = 2;
4442 params.url = url;
4443 params.page_state = PageState::CreateFromURL(url);
4444 params.was_within_same_page = true;
4445 contents()->GetMainFrame()->SendNavigateWithParams(&params);
4447 // The title should immediately be visible on the new NavigationEntry.
4448 base::string16 new_title =
4449 controller().GetLastCommittedEntry()->GetTitleForDisplay(std::string());
4450 EXPECT_EQ(title, new_title);
4451 FaviconStatus new_favicon =
4452 controller().GetLastCommittedEntry()->GetFavicon();
4453 EXPECT_EQ(favicon.valid, new_favicon.valid);
4454 EXPECT_EQ(favicon.url, new_favicon.url);
4457 // Test that the navigation controller clears its session history when a
4458 // navigation commits with the clear history list flag set.
4459 TEST_F(NavigationControllerTest, ClearHistoryList) {
4460 const GURL url1("http://foo1");
4461 const GURL url2("http://foo2");
4462 const GURL url3("http://foo3");
4463 const GURL url4("http://foo4");
4465 NavigationControllerImpl& controller = controller_impl();
4467 // Create a session history with three entries, second entry is active.
4468 NavigateAndCommit(url1);
4469 NavigateAndCommit(url2);
4470 NavigateAndCommit(url3);
4471 controller.GoBack();
4472 contents()->CommitPendingNavigation();
4473 EXPECT_EQ(3, controller.GetEntryCount());
4474 EXPECT_EQ(1, controller.GetCurrentEntryIndex());
4476 // Create a new pending navigation, and indicate that the session history
4477 // should be cleared.
4478 NavigationController::LoadURLParams params(url4);
4479 params.should_clear_history_list = true;
4480 controller.LoadURLWithParams(params);
4482 // Verify that the pending entry correctly indicates that the session history
4483 // should be cleared.
4484 NavigationEntryImpl* entry = controller.GetPendingEntry();
4485 ASSERT_TRUE(entry);
4486 EXPECT_TRUE(entry->should_clear_history_list());
4488 // Assume that the RenderFrame correctly cleared its history and commit the
4489 // navigation.
4490 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
4491 switches::kEnableBrowserSideNavigation)) {
4492 contents()->GetMainFrame()->SendBeforeUnloadACK(true);
4494 contents()->GetPendingMainFrame()->
4495 set_simulate_history_list_was_cleared(true);
4496 contents()->CommitPendingNavigation();
4498 // Verify that the NavigationController's session history was correctly
4499 // cleared.
4500 EXPECT_EQ(1, controller.GetEntryCount());
4501 EXPECT_EQ(0, controller.GetCurrentEntryIndex());
4502 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
4503 EXPECT_EQ(-1, controller.GetPendingEntryIndex());
4504 EXPECT_FALSE(controller.CanGoBack());
4505 EXPECT_FALSE(controller.CanGoForward());
4506 EXPECT_EQ(url4, controller.GetVisibleEntry()->GetURL());
4509 TEST_F(NavigationControllerTest, PostThenReplaceStateThenReload) {
4510 scoped_ptr<TestWebContentsDelegate> delegate(new TestWebContentsDelegate());
4511 EXPECT_FALSE(contents()->GetDelegate());
4512 contents()->SetDelegate(delegate.get());
4514 // Submit a form.
4515 GURL url("http://foo");
4516 FrameHostMsg_DidCommitProvisionalLoad_Params params;
4517 params.page_id = 1;
4518 params.url = url;
4519 params.transition = ui::PAGE_TRANSITION_FORM_SUBMIT;
4520 params.gesture = NavigationGestureUser;
4521 params.page_state = PageState::CreateFromURL(url);
4522 params.was_within_same_page = false;
4523 params.is_post = true;
4524 params.post_id = 2;
4525 contents()->GetMainFrame()->SendNavigateWithParams(&params);
4527 // history.replaceState() is called.
4528 GURL replace_url("http://foo#foo");
4529 params.page_id = 1;
4530 params.url = replace_url;
4531 params.transition = ui::PAGE_TRANSITION_LINK;
4532 params.gesture = NavigationGestureUser;
4533 params.page_state = PageState::CreateFromURL(replace_url);
4534 params.was_within_same_page = true;
4535 params.is_post = false;
4536 params.post_id = -1;
4537 contents()->GetMainFrame()->SendNavigateWithParams(&params);
4539 // Now reload. replaceState overrides the POST, so we should not show a
4540 // repost warning dialog.
4541 controller_impl().Reload(true);
4542 EXPECT_EQ(0, delegate->repost_form_warning_count());
4545 TEST_F(NavigationControllerTest, UnreachableURLGivesErrorPage) {
4546 GURL url("http://foo");
4547 FrameHostMsg_DidCommitProvisionalLoad_Params params;
4548 params.page_id = 1;
4549 params.url = url;
4550 params.transition = ui::PAGE_TRANSITION_LINK;
4551 params.gesture = NavigationGestureUser;
4552 params.page_state = PageState::CreateFromURL(url);
4553 params.was_within_same_page = false;
4554 params.is_post = true;
4555 params.post_id = 2;
4556 params.url_is_unreachable = true;
4557 // Navigate to new page
4559 LoadCommittedDetails details;
4560 controller_impl().RendererDidNavigate(main_test_rfh(), params, &details);
4561 EXPECT_EQ(PAGE_TYPE_ERROR,
4562 controller_impl().GetLastCommittedEntry()->GetPageType());
4563 EXPECT_EQ(NAVIGATION_TYPE_NEW_PAGE, details.type);
4566 // Navigate to existing page.
4568 LoadCommittedDetails details;
4569 controller_impl().RendererDidNavigate(main_test_rfh(), params, &details);
4570 EXPECT_EQ(PAGE_TYPE_ERROR,
4571 controller_impl().GetLastCommittedEntry()->GetPageType());
4572 EXPECT_EQ(NAVIGATION_TYPE_EXISTING_PAGE, details.type);
4575 // Navigate to same page.
4576 // Note: The call to LoadURL() creates a pending entry in order to trigger the
4577 // same-page transition.
4578 controller_impl().LoadURL(
4579 url, Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
4580 params.transition = ui::PAGE_TRANSITION_TYPED;
4582 LoadCommittedDetails details;
4583 controller_impl().RendererDidNavigate(main_test_rfh(), params, &details);
4584 EXPECT_EQ(PAGE_TYPE_ERROR,
4585 controller_impl().GetLastCommittedEntry()->GetPageType());
4586 EXPECT_EQ(NAVIGATION_TYPE_SAME_PAGE, details.type);
4589 // Navigate in page.
4590 params.url = GURL("http://foo#foo");
4591 params.transition = ui::PAGE_TRANSITION_LINK;
4592 params.was_within_same_page = true;
4594 LoadCommittedDetails details;
4595 controller_impl().RendererDidNavigate(main_test_rfh(), params, &details);
4596 EXPECT_EQ(PAGE_TYPE_ERROR,
4597 controller_impl().GetLastCommittedEntry()->GetPageType());
4598 EXPECT_EQ(NAVIGATION_TYPE_IN_PAGE, details.type);
4602 } // namespace content