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