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