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