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