navigation_controller_impl_unittest.cc revision cedac228d2dd51db4b79ea1e72c7f249408ee061
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(1, 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())->set_is_isolated_guest(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_should_clear_history_list(true);
1324  EXPECT_EQ(post_data.get(), pending_entry->GetBrowserInitiatedPostData());
1325  EXPECT_TRUE(pending_entry->is_renderer_initiated());
1326  EXPECT_EQ(transfer_id, pending_entry->transferred_global_request_id());
1327  EXPECT_TRUE(pending_entry->should_replace_entry());
1328  EXPECT_TRUE(pending_entry->should_clear_history_list());
1329
1330  main_test_rfh()->SendNavigate(0, url1);
1331
1332  // Certain values that are only used for pending entries get reset after
1333  // commit.
1334  NavigationEntryImpl* committed_entry =
1335      NavigationEntryImpl::FromNavigationEntry(
1336          controller.GetLastCommittedEntry());
1337  EXPECT_FALSE(committed_entry->GetBrowserInitiatedPostData());
1338  EXPECT_FALSE(committed_entry->is_renderer_initiated());
1339  EXPECT_EQ(GlobalRequestID(-1, -1),
1340            committed_entry->transferred_global_request_id());
1341  EXPECT_FALSE(committed_entry->should_replace_entry());
1342  EXPECT_FALSE(committed_entry->should_clear_history_list());
1343}
1344
1345// Test that Redirects are preserved after a commit.
1346TEST_F(NavigationControllerTest, RedirectsAreNotResetByCommit) {
1347  NavigationControllerImpl& controller = controller_impl();
1348  const GURL url1("http://foo1");
1349  controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1350
1351  // Set up some redirect values.
1352  std::vector<GURL> redirects;
1353  redirects.push_back(GURL("http://foo2"));
1354
1355  // Set redirects on the pending entry.
1356  NavigationEntryImpl* pending_entry =
1357      NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry());
1358  pending_entry->SetRedirectChain(redirects);
1359  EXPECT_EQ(1U, pending_entry->GetRedirectChain().size());
1360  EXPECT_EQ(GURL("http://foo2"), pending_entry->GetRedirectChain()[0]);
1361
1362  // Normal navigation will preserve redirects in the committed entry.
1363  main_test_rfh()->SendNavigateWithRedirects(0, url1, redirects);
1364  NavigationEntryImpl* committed_entry =
1365      NavigationEntryImpl::FromNavigationEntry(
1366          controller.GetLastCommittedEntry());
1367  ASSERT_EQ(1U, committed_entry->GetRedirectChain().size());
1368  EXPECT_EQ(GURL("http://foo2"), committed_entry->GetRedirectChain()[0]);
1369}
1370
1371// Tests what happens when we navigate back successfully
1372TEST_F(NavigationControllerTest, Back) {
1373  NavigationControllerImpl& controller = controller_impl();
1374  TestNotificationTracker notifications;
1375  RegisterForAllNavNotifications(&notifications, &controller);
1376
1377  const GURL url1("http://foo1");
1378  main_test_rfh()->SendNavigate(0, url1);
1379  EXPECT_EQ(1U, navigation_entry_committed_counter_);
1380  navigation_entry_committed_counter_ = 0;
1381
1382  const GURL url2("http://foo2");
1383  main_test_rfh()->SendNavigate(1, url2);
1384  EXPECT_EQ(1U, navigation_entry_committed_counter_);
1385  navigation_entry_committed_counter_ = 0;
1386
1387  controller.GoBack();
1388  EXPECT_EQ(0U, notifications.size());
1389
1390  // We should now have a pending navigation to go back.
1391  EXPECT_EQ(controller.GetEntryCount(), 2);
1392  EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
1393  EXPECT_EQ(controller.GetPendingEntryIndex(), 0);
1394  EXPECT_TRUE(controller.GetLastCommittedEntry());
1395  EXPECT_TRUE(controller.GetPendingEntry());
1396  EXPECT_FALSE(controller.CanGoBack());
1397  EXPECT_FALSE(controller.CanGoToOffset(-1));
1398  EXPECT_TRUE(controller.CanGoForward());
1399  EXPECT_TRUE(controller.CanGoToOffset(1));
1400  EXPECT_FALSE(controller.CanGoToOffset(2));  // Cannot go foward 2 steps.
1401
1402  // Timestamp for entry 1 should be on or after that of entry 0.
1403  EXPECT_FALSE(controller.GetEntryAtIndex(0)->GetTimestamp().is_null());
1404  EXPECT_GE(controller.GetEntryAtIndex(1)->GetTimestamp(),
1405            controller.GetEntryAtIndex(0)->GetTimestamp());
1406
1407  main_test_rfh()->SendNavigate(0, url2);
1408  EXPECT_EQ(1U, navigation_entry_committed_counter_);
1409  navigation_entry_committed_counter_ = 0;
1410
1411  // The back navigation completed successfully.
1412  EXPECT_EQ(controller.GetEntryCount(), 2);
1413  EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1414  EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1415  EXPECT_TRUE(controller.GetLastCommittedEntry());
1416  EXPECT_FALSE(controller.GetPendingEntry());
1417  EXPECT_FALSE(controller.CanGoBack());
1418  EXPECT_FALSE(controller.CanGoToOffset(-1));
1419  EXPECT_TRUE(controller.CanGoForward());
1420  EXPECT_TRUE(controller.CanGoToOffset(1));
1421  EXPECT_FALSE(controller.CanGoToOffset(2));  // Cannot go foward 2 steps.
1422
1423  // Timestamp for entry 0 should be on or after that of entry 1
1424  // (since we went back to it).
1425  EXPECT_GE(controller.GetEntryAtIndex(0)->GetTimestamp(),
1426            controller.GetEntryAtIndex(1)->GetTimestamp());
1427}
1428
1429// Tests what happens when a back navigation produces a new page.
1430TEST_F(NavigationControllerTest, Back_GeneratesNewPage) {
1431  NavigationControllerImpl& controller = controller_impl();
1432  TestNotificationTracker notifications;
1433  RegisterForAllNavNotifications(&notifications, &controller);
1434
1435  const GURL url1("http://foo/1");
1436  const GURL url2("http://foo/2");
1437  const GURL url3("http://foo/3");
1438
1439  controller.LoadURL(
1440      url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1441  main_test_rfh()->SendNavigate(0, url1);
1442  EXPECT_EQ(1U, navigation_entry_committed_counter_);
1443  navigation_entry_committed_counter_ = 0;
1444
1445  controller.LoadURL(url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1446  main_test_rfh()->SendNavigate(1, url2);
1447  EXPECT_EQ(1U, navigation_entry_committed_counter_);
1448  navigation_entry_committed_counter_ = 0;
1449
1450  controller.GoBack();
1451  EXPECT_EQ(0U, notifications.size());
1452
1453  // We should now have a pending navigation to go back.
1454  EXPECT_EQ(controller.GetEntryCount(), 2);
1455  EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
1456  EXPECT_EQ(controller.GetPendingEntryIndex(), 0);
1457  EXPECT_TRUE(controller.GetLastCommittedEntry());
1458  EXPECT_TRUE(controller.GetPendingEntry());
1459  EXPECT_FALSE(controller.CanGoBack());
1460  EXPECT_TRUE(controller.CanGoForward());
1461
1462  main_test_rfh()->SendNavigate(2, url3);
1463  EXPECT_EQ(1U, navigation_entry_committed_counter_);
1464  navigation_entry_committed_counter_ = 0;
1465
1466  // The back navigation resulted in a completely new navigation.
1467  // TODO(darin): perhaps this behavior will be confusing to users?
1468  EXPECT_EQ(controller.GetEntryCount(), 3);
1469  EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 2);
1470  EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1471  EXPECT_TRUE(controller.GetLastCommittedEntry());
1472  EXPECT_FALSE(controller.GetPendingEntry());
1473  EXPECT_TRUE(controller.CanGoBack());
1474  EXPECT_FALSE(controller.CanGoForward());
1475}
1476
1477// Receives a back message when there is a new pending navigation entry.
1478TEST_F(NavigationControllerTest, Back_NewPending) {
1479  NavigationControllerImpl& controller = controller_impl();
1480  TestNotificationTracker notifications;
1481  RegisterForAllNavNotifications(&notifications, &controller);
1482
1483  const GURL kUrl1("http://foo1");
1484  const GURL kUrl2("http://foo2");
1485  const GURL kUrl3("http://foo3");
1486
1487  // First navigate two places so we have some back history.
1488  main_test_rfh()->SendNavigate(0, kUrl1);
1489  EXPECT_EQ(1U, navigation_entry_committed_counter_);
1490  navigation_entry_committed_counter_ = 0;
1491
1492  // controller.LoadURL(kUrl2, PAGE_TRANSITION_TYPED);
1493  main_test_rfh()->SendNavigate(1, kUrl2);
1494  EXPECT_EQ(1U, navigation_entry_committed_counter_);
1495  navigation_entry_committed_counter_ = 0;
1496
1497  // Now start a new pending navigation and go back before it commits.
1498  controller.LoadURL(kUrl3, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1499  EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1500  EXPECT_EQ(kUrl3, controller.GetPendingEntry()->GetURL());
1501  controller.GoBack();
1502
1503  // The pending navigation should now be the "back" item and the new one
1504  // should be gone.
1505  EXPECT_EQ(0, controller.GetPendingEntryIndex());
1506  EXPECT_EQ(kUrl1, controller.GetPendingEntry()->GetURL());
1507}
1508
1509// Receives a back message when there is a different renavigation already
1510// pending.
1511TEST_F(NavigationControllerTest, Back_OtherBackPending) {
1512  NavigationControllerImpl& controller = controller_impl();
1513  const GURL kUrl1("http://foo/1");
1514  const GURL kUrl2("http://foo/2");
1515  const GURL kUrl3("http://foo/3");
1516
1517  // First navigate three places so we have some back history.
1518  main_test_rfh()->SendNavigate(0, kUrl1);
1519  main_test_rfh()->SendNavigate(1, kUrl2);
1520  main_test_rfh()->SendNavigate(2, kUrl3);
1521
1522  // With nothing pending, say we get a navigation to the second entry.
1523  main_test_rfh()->SendNavigate(1, kUrl2);
1524
1525  // We know all the entries have the same site instance, so we can just grab
1526  // a random one for looking up other entries.
1527  SiteInstance* site_instance =
1528      NavigationEntryImpl::FromNavigationEntry(
1529          controller.GetLastCommittedEntry())->site_instance();
1530
1531  // That second URL should be the last committed and it should have gotten the
1532  // new title.
1533  EXPECT_EQ(kUrl2, controller.GetEntryWithPageID(site_instance, 1)->GetURL());
1534  EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
1535  EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1536
1537  // Now go forward to the last item again and say it was committed.
1538  controller.GoForward();
1539  main_test_rfh()->SendNavigate(2, kUrl3);
1540
1541  // Now start going back one to the second page. It will be pending.
1542  controller.GoBack();
1543  EXPECT_EQ(1, controller.GetPendingEntryIndex());
1544  EXPECT_EQ(2, controller.GetLastCommittedEntryIndex());
1545
1546  // Not synthesize a totally new back event to the first page. This will not
1547  // match the pending one.
1548  main_test_rfh()->SendNavigate(0, kUrl1);
1549
1550  // The committed navigation should clear the pending entry.
1551  EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1552
1553  // But the navigated entry should be the last committed.
1554  EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
1555  EXPECT_EQ(kUrl1, controller.GetLastCommittedEntry()->GetURL());
1556}
1557
1558// Tests what happens when we navigate forward successfully.
1559TEST_F(NavigationControllerTest, Forward) {
1560  NavigationControllerImpl& controller = controller_impl();
1561  TestNotificationTracker notifications;
1562  RegisterForAllNavNotifications(&notifications, &controller);
1563
1564  const GURL url1("http://foo1");
1565  const GURL url2("http://foo2");
1566
1567  main_test_rfh()->SendNavigate(0, url1);
1568  EXPECT_EQ(1U, navigation_entry_committed_counter_);
1569  navigation_entry_committed_counter_ = 0;
1570
1571  main_test_rfh()->SendNavigate(1, url2);
1572  EXPECT_EQ(1U, navigation_entry_committed_counter_);
1573  navigation_entry_committed_counter_ = 0;
1574
1575  controller.GoBack();
1576  main_test_rfh()->SendNavigate(0, url1);
1577  EXPECT_EQ(1U, navigation_entry_committed_counter_);
1578  navigation_entry_committed_counter_ = 0;
1579
1580  controller.GoForward();
1581
1582  // We should now have a pending navigation to go forward.
1583  EXPECT_EQ(controller.GetEntryCount(), 2);
1584  EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1585  EXPECT_EQ(controller.GetPendingEntryIndex(), 1);
1586  EXPECT_TRUE(controller.GetLastCommittedEntry());
1587  EXPECT_TRUE(controller.GetPendingEntry());
1588  EXPECT_TRUE(controller.CanGoBack());
1589  EXPECT_TRUE(controller.CanGoToOffset(-1));
1590  EXPECT_FALSE(controller.CanGoToOffset(-2));  // Cannot go back 2 steps.
1591  EXPECT_FALSE(controller.CanGoForward());
1592  EXPECT_FALSE(controller.CanGoToOffset(1));
1593
1594  // Timestamp for entry 0 should be on or after that of entry 1
1595  // (since we went back to it).
1596  EXPECT_FALSE(controller.GetEntryAtIndex(0)->GetTimestamp().is_null());
1597  EXPECT_GE(controller.GetEntryAtIndex(0)->GetTimestamp(),
1598            controller.GetEntryAtIndex(1)->GetTimestamp());
1599
1600  main_test_rfh()->SendNavigate(1, url2);
1601  EXPECT_EQ(1U, navigation_entry_committed_counter_);
1602  navigation_entry_committed_counter_ = 0;
1603
1604  // The forward navigation completed successfully.
1605  EXPECT_EQ(controller.GetEntryCount(), 2);
1606  EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
1607  EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1608  EXPECT_TRUE(controller.GetLastCommittedEntry());
1609  EXPECT_FALSE(controller.GetPendingEntry());
1610  EXPECT_TRUE(controller.CanGoBack());
1611  EXPECT_TRUE(controller.CanGoToOffset(-1));
1612  EXPECT_FALSE(controller.CanGoToOffset(-2));  // Cannot go back 2 steps.
1613  EXPECT_FALSE(controller.CanGoForward());
1614  EXPECT_FALSE(controller.CanGoToOffset(1));
1615
1616  // Timestamp for entry 1 should be on or after that of entry 0
1617  // (since we went forward to it).
1618  EXPECT_GE(controller.GetEntryAtIndex(1)->GetTimestamp(),
1619            controller.GetEntryAtIndex(0)->GetTimestamp());
1620}
1621
1622// Tests what happens when a forward navigation produces a new page.
1623TEST_F(NavigationControllerTest, Forward_GeneratesNewPage) {
1624  NavigationControllerImpl& controller = controller_impl();
1625  TestNotificationTracker notifications;
1626  RegisterForAllNavNotifications(&notifications, &controller);
1627
1628  const GURL url1("http://foo1");
1629  const GURL url2("http://foo2");
1630  const GURL url3("http://foo3");
1631
1632  main_test_rfh()->SendNavigate(0, url1);
1633  EXPECT_EQ(1U, navigation_entry_committed_counter_);
1634  navigation_entry_committed_counter_ = 0;
1635  main_test_rfh()->SendNavigate(1, url2);
1636  EXPECT_EQ(1U, navigation_entry_committed_counter_);
1637  navigation_entry_committed_counter_ = 0;
1638
1639  controller.GoBack();
1640  main_test_rfh()->SendNavigate(0, url1);
1641  EXPECT_EQ(1U, navigation_entry_committed_counter_);
1642  navigation_entry_committed_counter_ = 0;
1643
1644  controller.GoForward();
1645  EXPECT_EQ(0U, notifications.size());
1646
1647  // Should now have a pending navigation to go forward.
1648  EXPECT_EQ(controller.GetEntryCount(), 2);
1649  EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1650  EXPECT_EQ(controller.GetPendingEntryIndex(), 1);
1651  EXPECT_TRUE(controller.GetLastCommittedEntry());
1652  EXPECT_TRUE(controller.GetPendingEntry());
1653  EXPECT_TRUE(controller.CanGoBack());
1654  EXPECT_FALSE(controller.CanGoForward());
1655
1656  main_test_rfh()->SendNavigate(2, url3);
1657  EXPECT_EQ(1U, navigation_entry_committed_counter_);
1658  navigation_entry_committed_counter_ = 0;
1659  EXPECT_TRUE(notifications.Check1AndReset(NOTIFICATION_NAV_LIST_PRUNED));
1660
1661  EXPECT_EQ(controller.GetEntryCount(), 2);
1662  EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
1663  EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1664  EXPECT_TRUE(controller.GetLastCommittedEntry());
1665  EXPECT_FALSE(controller.GetPendingEntry());
1666  EXPECT_TRUE(controller.CanGoBack());
1667  EXPECT_FALSE(controller.CanGoForward());
1668}
1669
1670// Two consequent navigation for the same URL entered in should be considered
1671// as SAME_PAGE navigation even when we are redirected to some other page.
1672TEST_F(NavigationControllerTest, Redirect) {
1673  NavigationControllerImpl& controller = controller_impl();
1674  TestNotificationTracker notifications;
1675  RegisterForAllNavNotifications(&notifications, &controller);
1676
1677  const GURL url1("http://foo1");
1678  const GURL url2("http://foo2");  // Redirection target
1679
1680  // First request
1681  controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1682
1683  EXPECT_EQ(0U, notifications.size());
1684  main_test_rfh()->SendNavigate(0, url2);
1685  EXPECT_EQ(1U, navigation_entry_committed_counter_);
1686  navigation_entry_committed_counter_ = 0;
1687
1688  // Second request
1689  controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1690
1691  EXPECT_TRUE(controller.GetPendingEntry());
1692  EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1693  EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
1694
1695  FrameHostMsg_DidCommitProvisionalLoad_Params params;
1696  params.page_id = 0;
1697  params.url = url2;
1698  params.transition = PAGE_TRANSITION_SERVER_REDIRECT;
1699  params.redirects.push_back(GURL("http://foo1"));
1700  params.redirects.push_back(GURL("http://foo2"));
1701  params.should_update_history = false;
1702  params.gesture = NavigationGestureAuto;
1703  params.is_post = false;
1704  params.page_state = PageState::CreateFromURL(url2);
1705
1706  LoadCommittedDetails details;
1707
1708  EXPECT_EQ(0U, notifications.size());
1709  EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
1710                                             &details));
1711  EXPECT_EQ(1U, navigation_entry_committed_counter_);
1712  navigation_entry_committed_counter_ = 0;
1713
1714  EXPECT_TRUE(details.type == NAVIGATION_TYPE_SAME_PAGE);
1715  EXPECT_EQ(controller.GetEntryCount(), 1);
1716  EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1717  EXPECT_TRUE(controller.GetLastCommittedEntry());
1718  EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1719  EXPECT_FALSE(controller.GetPendingEntry());
1720  EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
1721
1722  EXPECT_FALSE(controller.CanGoBack());
1723  EXPECT_FALSE(controller.CanGoForward());
1724}
1725
1726// Similar to Redirect above, but the first URL is requested by POST,
1727// the second URL is requested by GET. NavigationEntry::has_post_data_
1728// must be cleared. http://crbug.com/21245
1729TEST_F(NavigationControllerTest, PostThenRedirect) {
1730  NavigationControllerImpl& controller = controller_impl();
1731  TestNotificationTracker notifications;
1732  RegisterForAllNavNotifications(&notifications, &controller);
1733
1734  const GURL url1("http://foo1");
1735  const GURL url2("http://foo2");  // Redirection target
1736
1737  // First request as POST
1738  controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1739  controller.GetVisibleEntry()->SetHasPostData(true);
1740
1741  EXPECT_EQ(0U, notifications.size());
1742  main_test_rfh()->SendNavigate(0, url2);
1743  EXPECT_EQ(1U, navigation_entry_committed_counter_);
1744  navigation_entry_committed_counter_ = 0;
1745
1746  // Second request
1747  controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1748
1749  EXPECT_TRUE(controller.GetPendingEntry());
1750  EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1751  EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
1752
1753  FrameHostMsg_DidCommitProvisionalLoad_Params params;
1754  params.page_id = 0;
1755  params.url = url2;
1756  params.transition = PAGE_TRANSITION_SERVER_REDIRECT;
1757  params.redirects.push_back(GURL("http://foo1"));
1758  params.redirects.push_back(GURL("http://foo2"));
1759  params.should_update_history = false;
1760  params.gesture = NavigationGestureAuto;
1761  params.is_post = false;
1762  params.page_state = PageState::CreateFromURL(url2);
1763
1764  LoadCommittedDetails details;
1765
1766  EXPECT_EQ(0U, notifications.size());
1767  EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
1768                                             &details));
1769  EXPECT_EQ(1U, navigation_entry_committed_counter_);
1770  navigation_entry_committed_counter_ = 0;
1771
1772  EXPECT_TRUE(details.type == NAVIGATION_TYPE_SAME_PAGE);
1773  EXPECT_EQ(controller.GetEntryCount(), 1);
1774  EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1775  EXPECT_TRUE(controller.GetLastCommittedEntry());
1776  EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1777  EXPECT_FALSE(controller.GetPendingEntry());
1778  EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
1779  EXPECT_FALSE(controller.GetVisibleEntry()->GetHasPostData());
1780
1781  EXPECT_FALSE(controller.CanGoBack());
1782  EXPECT_FALSE(controller.CanGoForward());
1783}
1784
1785// A redirect right off the bat should be a NEW_PAGE.
1786TEST_F(NavigationControllerTest, ImmediateRedirect) {
1787  NavigationControllerImpl& controller = controller_impl();
1788  TestNotificationTracker notifications;
1789  RegisterForAllNavNotifications(&notifications, &controller);
1790
1791  const GURL url1("http://foo1");
1792  const GURL url2("http://foo2");  // Redirection target
1793
1794  // First request
1795  controller.LoadURL(url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
1796
1797  EXPECT_TRUE(controller.GetPendingEntry());
1798  EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1799  EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
1800
1801  FrameHostMsg_DidCommitProvisionalLoad_Params params;
1802  params.page_id = 0;
1803  params.url = url2;
1804  params.transition = PAGE_TRANSITION_SERVER_REDIRECT;
1805  params.redirects.push_back(GURL("http://foo1"));
1806  params.redirects.push_back(GURL("http://foo2"));
1807  params.should_update_history = false;
1808  params.gesture = NavigationGestureAuto;
1809  params.is_post = false;
1810  params.page_state = PageState::CreateFromURL(url2);
1811
1812  LoadCommittedDetails details;
1813
1814  EXPECT_EQ(0U, notifications.size());
1815  EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
1816                                             &details));
1817  EXPECT_EQ(1U, navigation_entry_committed_counter_);
1818  navigation_entry_committed_counter_ = 0;
1819
1820  EXPECT_TRUE(details.type == NAVIGATION_TYPE_NEW_PAGE);
1821  EXPECT_EQ(controller.GetEntryCount(), 1);
1822  EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
1823  EXPECT_TRUE(controller.GetLastCommittedEntry());
1824  EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
1825  EXPECT_FALSE(controller.GetPendingEntry());
1826  EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
1827
1828  EXPECT_FALSE(controller.CanGoBack());
1829  EXPECT_FALSE(controller.CanGoForward());
1830}
1831
1832// Tests navigation via link click within a subframe. A new navigation entry
1833// should be created.
1834TEST_F(NavigationControllerTest, NewSubframe) {
1835  NavigationControllerImpl& controller = controller_impl();
1836  TestNotificationTracker notifications;
1837  RegisterForAllNavNotifications(&notifications, &controller);
1838
1839  const GURL url1("http://foo1");
1840  main_test_rfh()->SendNavigate(0, url1);
1841  EXPECT_EQ(1U, navigation_entry_committed_counter_);
1842  navigation_entry_committed_counter_ = 0;
1843
1844  const GURL url2("http://foo2");
1845  FrameHostMsg_DidCommitProvisionalLoad_Params params;
1846  params.page_id = 1;
1847  params.url = url2;
1848  params.transition = PAGE_TRANSITION_MANUAL_SUBFRAME;
1849  params.should_update_history = false;
1850  params.gesture = NavigationGestureUser;
1851  params.is_post = false;
1852  params.page_state = PageState::CreateFromURL(url2);
1853
1854  LoadCommittedDetails details;
1855  EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
1856                                             &details));
1857  EXPECT_EQ(1U, navigation_entry_committed_counter_);
1858  navigation_entry_committed_counter_ = 0;
1859  EXPECT_EQ(url1, details.previous_url);
1860  EXPECT_FALSE(details.is_in_page);
1861  EXPECT_FALSE(details.is_main_frame);
1862
1863  // The new entry should be appended.
1864  EXPECT_EQ(2, controller.GetEntryCount());
1865
1866  // New entry should refer to the new page, but the old URL (entries only
1867  // reflect the toplevel URL).
1868  EXPECT_EQ(url1, details.entry->GetURL());
1869  EXPECT_EQ(params.page_id, details.entry->GetPageID());
1870}
1871
1872// Some pages create a popup, then write an iframe into it. This causes a
1873// subframe navigation without having any committed entry. Such navigations
1874// just get thrown on the ground, but we shouldn't crash.
1875TEST_F(NavigationControllerTest, SubframeOnEmptyPage) {
1876  NavigationControllerImpl& controller = controller_impl();
1877  TestNotificationTracker notifications;
1878  RegisterForAllNavNotifications(&notifications, &controller);
1879
1880  // Navigation controller currently has no entries.
1881  const GURL url("http://foo2");
1882  FrameHostMsg_DidCommitProvisionalLoad_Params params;
1883  params.page_id = 1;
1884  params.url = url;
1885  params.transition = PAGE_TRANSITION_AUTO_SUBFRAME;
1886  params.should_update_history = false;
1887  params.gesture = NavigationGestureAuto;
1888  params.is_post = false;
1889  params.page_state = PageState::CreateFromURL(url);
1890
1891  LoadCommittedDetails details;
1892  EXPECT_FALSE(controller.RendererDidNavigate(main_test_rfh(), params,
1893                                              &details));
1894  EXPECT_EQ(0U, notifications.size());
1895}
1896
1897// Auto subframes are ones the page loads automatically like ads. They should
1898// not create new navigation entries.
1899TEST_F(NavigationControllerTest, AutoSubframe) {
1900  NavigationControllerImpl& controller = controller_impl();
1901  TestNotificationTracker notifications;
1902  RegisterForAllNavNotifications(&notifications, &controller);
1903
1904  const GURL url1("http://foo1");
1905  main_test_rfh()->SendNavigate(0, url1);
1906  EXPECT_EQ(1U, navigation_entry_committed_counter_);
1907  navigation_entry_committed_counter_ = 0;
1908
1909  const GURL url2("http://foo2");
1910  FrameHostMsg_DidCommitProvisionalLoad_Params params;
1911  params.page_id = 0;
1912  params.url = url2;
1913  params.transition = PAGE_TRANSITION_AUTO_SUBFRAME;
1914  params.should_update_history = false;
1915  params.gesture = NavigationGestureUser;
1916  params.is_post = false;
1917  params.page_state = PageState::CreateFromURL(url2);
1918
1919  // Navigating should do nothing.
1920  LoadCommittedDetails details;
1921  EXPECT_FALSE(controller.RendererDidNavigate(main_test_rfh(), params,
1922                                              &details));
1923  EXPECT_EQ(0U, notifications.size());
1924
1925  // There should still be only one entry.
1926  EXPECT_EQ(1, controller.GetEntryCount());
1927}
1928
1929// Tests navigation and then going back to a subframe navigation.
1930TEST_F(NavigationControllerTest, BackSubframe) {
1931  NavigationControllerImpl& controller = controller_impl();
1932  TestNotificationTracker notifications;
1933  RegisterForAllNavNotifications(&notifications, &controller);
1934
1935  // Main page.
1936  const GURL url1("http://foo1");
1937  main_test_rfh()->SendNavigate(0, url1);
1938  EXPECT_EQ(1U, navigation_entry_committed_counter_);
1939  navigation_entry_committed_counter_ = 0;
1940
1941  // First manual subframe navigation.
1942  const GURL url2("http://foo2");
1943  FrameHostMsg_DidCommitProvisionalLoad_Params params;
1944  params.page_id = 1;
1945  params.url = url2;
1946  params.transition = PAGE_TRANSITION_MANUAL_SUBFRAME;
1947  params.should_update_history = false;
1948  params.gesture = NavigationGestureUser;
1949  params.is_post = false;
1950  params.page_state = PageState::CreateFromURL(url2);
1951
1952  // This should generate a new entry.
1953  LoadCommittedDetails details;
1954  EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
1955                                             &details));
1956  EXPECT_EQ(1U, navigation_entry_committed_counter_);
1957  navigation_entry_committed_counter_ = 0;
1958  EXPECT_EQ(2, controller.GetEntryCount());
1959
1960  // Second manual subframe navigation should also make a new entry.
1961  const GURL url3("http://foo3");
1962  params.page_id = 2;
1963  params.url = url3;
1964  EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
1965                                             &details));
1966  EXPECT_EQ(1U, navigation_entry_committed_counter_);
1967  navigation_entry_committed_counter_ = 0;
1968  EXPECT_EQ(3, controller.GetEntryCount());
1969  EXPECT_EQ(2, controller.GetCurrentEntryIndex());
1970
1971  // Go back one.
1972  controller.GoBack();
1973  params.url = url2;
1974  params.page_id = 1;
1975  EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
1976                                             &details));
1977  EXPECT_EQ(1U, navigation_entry_committed_counter_);
1978  navigation_entry_committed_counter_ = 0;
1979  EXPECT_EQ(3, controller.GetEntryCount());
1980  EXPECT_EQ(1, controller.GetCurrentEntryIndex());
1981  EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1982  EXPECT_FALSE(controller.GetPendingEntry());
1983
1984  // Go back one more.
1985  controller.GoBack();
1986  params.url = url1;
1987  params.page_id = 0;
1988  EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
1989                                             &details));
1990  EXPECT_EQ(1U, navigation_entry_committed_counter_);
1991  navigation_entry_committed_counter_ = 0;
1992  EXPECT_EQ(3, controller.GetEntryCount());
1993  EXPECT_EQ(0, controller.GetCurrentEntryIndex());
1994  EXPECT_EQ(-1, controller.GetPendingEntryIndex());
1995  EXPECT_FALSE(controller.GetPendingEntry());
1996}
1997
1998TEST_F(NavigationControllerTest, LinkClick) {
1999  NavigationControllerImpl& controller = controller_impl();
2000  TestNotificationTracker notifications;
2001  RegisterForAllNavNotifications(&notifications, &controller);
2002
2003  const GURL url1("http://foo1");
2004  const GURL url2("http://foo2");
2005
2006  main_test_rfh()->SendNavigate(0, url1);
2007  EXPECT_EQ(1U, navigation_entry_committed_counter_);
2008  navigation_entry_committed_counter_ = 0;
2009
2010  main_test_rfh()->SendNavigate(1, url2);
2011  EXPECT_EQ(1U, navigation_entry_committed_counter_);
2012  navigation_entry_committed_counter_ = 0;
2013
2014  // Should not have produced a new session history entry.
2015  EXPECT_EQ(controller.GetEntryCount(), 2);
2016  EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
2017  EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
2018  EXPECT_TRUE(controller.GetLastCommittedEntry());
2019  EXPECT_FALSE(controller.GetPendingEntry());
2020  EXPECT_TRUE(controller.CanGoBack());
2021  EXPECT_FALSE(controller.CanGoForward());
2022}
2023
2024TEST_F(NavigationControllerTest, InPage) {
2025  NavigationControllerImpl& controller = controller_impl();
2026  TestNotificationTracker notifications;
2027  RegisterForAllNavNotifications(&notifications, &controller);
2028
2029  // Main page.
2030  const GURL url1("http://foo");
2031  main_test_rfh()->SendNavigate(0, url1);
2032  EXPECT_EQ(1U, navigation_entry_committed_counter_);
2033  navigation_entry_committed_counter_ = 0;
2034
2035  // Ensure main page navigation to same url respects the was_within_same_page
2036  // hint provided in the params.
2037  FrameHostMsg_DidCommitProvisionalLoad_Params self_params;
2038  self_params.page_id = 0;
2039  self_params.url = url1;
2040  self_params.transition = PAGE_TRANSITION_LINK;
2041  self_params.should_update_history = false;
2042  self_params.gesture = NavigationGestureUser;
2043  self_params.is_post = false;
2044  self_params.page_state = PageState::CreateFromURL(url1);
2045  self_params.was_within_same_page = true;
2046
2047  LoadCommittedDetails details;
2048  EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), self_params,
2049                                             &details));
2050  EXPECT_EQ(1U, navigation_entry_committed_counter_);
2051  navigation_entry_committed_counter_ = 0;
2052  EXPECT_TRUE(details.is_in_page);
2053  EXPECT_TRUE(details.did_replace_entry);
2054  EXPECT_EQ(1, controller.GetEntryCount());
2055
2056  // Fragment navigation to a new page_id.
2057  const GURL url2("http://foo#a");
2058  FrameHostMsg_DidCommitProvisionalLoad_Params params;
2059  params.page_id = 1;
2060  params.url = url2;
2061  params.transition = PAGE_TRANSITION_LINK;
2062  params.should_update_history = false;
2063  params.gesture = NavigationGestureUser;
2064  params.is_post = false;
2065  params.page_state = PageState::CreateFromURL(url2);
2066  params.was_within_same_page = true;
2067
2068  // This should generate a new entry.
2069  EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
2070                                             &details));
2071  EXPECT_EQ(1U, navigation_entry_committed_counter_);
2072  navigation_entry_committed_counter_ = 0;
2073  EXPECT_TRUE(details.is_in_page);
2074  EXPECT_FALSE(details.did_replace_entry);
2075  EXPECT_EQ(2, controller.GetEntryCount());
2076
2077  // Go back one.
2078  FrameHostMsg_DidCommitProvisionalLoad_Params back_params(params);
2079  controller.GoBack();
2080  back_params.url = url1;
2081  back_params.page_id = 0;
2082  EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), back_params,
2083                                             &details));
2084  EXPECT_EQ(1U, navigation_entry_committed_counter_);
2085  navigation_entry_committed_counter_ = 0;
2086  EXPECT_TRUE(details.is_in_page);
2087  EXPECT_EQ(2, controller.GetEntryCount());
2088  EXPECT_EQ(0, controller.GetCurrentEntryIndex());
2089  EXPECT_EQ(back_params.url, controller.GetVisibleEntry()->GetURL());
2090
2091  // Go forward
2092  FrameHostMsg_DidCommitProvisionalLoad_Params forward_params(params);
2093  controller.GoForward();
2094  forward_params.url = url2;
2095  forward_params.page_id = 1;
2096  EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), forward_params,
2097                                             &details));
2098  EXPECT_EQ(1U, navigation_entry_committed_counter_);
2099  navigation_entry_committed_counter_ = 0;
2100  EXPECT_TRUE(details.is_in_page);
2101  EXPECT_EQ(2, controller.GetEntryCount());
2102  EXPECT_EQ(1, controller.GetCurrentEntryIndex());
2103  EXPECT_EQ(forward_params.url,
2104            controller.GetVisibleEntry()->GetURL());
2105
2106  // Now go back and forward again. This is to work around a bug where we would
2107  // compare the incoming URL with the last committed entry rather than the
2108  // one identified by an existing page ID. This would result in the second URL
2109  // losing the reference fragment when you navigate away from it and then back.
2110  controller.GoBack();
2111  EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), back_params,
2112                                             &details));
2113  controller.GoForward();
2114  EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), forward_params,
2115                                             &details));
2116  EXPECT_EQ(forward_params.url,
2117            controller.GetVisibleEntry()->GetURL());
2118
2119  // Finally, navigate to an unrelated URL to make sure in_page is not sticky.
2120  const GURL url3("http://bar");
2121  params.page_id = 2;
2122  params.url = url3;
2123  navigation_entry_committed_counter_ = 0;
2124  EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
2125                                             &details));
2126  EXPECT_EQ(1U, navigation_entry_committed_counter_);
2127  navigation_entry_committed_counter_ = 0;
2128  EXPECT_FALSE(details.is_in_page);
2129  EXPECT_EQ(3, controller.GetEntryCount());
2130  EXPECT_EQ(2, controller.GetCurrentEntryIndex());
2131}
2132
2133TEST_F(NavigationControllerTest, InPage_Replace) {
2134  NavigationControllerImpl& controller = controller_impl();
2135  TestNotificationTracker notifications;
2136  RegisterForAllNavNotifications(&notifications, &controller);
2137
2138  // Main page.
2139  const GURL url1("http://foo");
2140  main_test_rfh()->SendNavigate(0, url1);
2141  EXPECT_EQ(1U, navigation_entry_committed_counter_);
2142  navigation_entry_committed_counter_ = 0;
2143
2144  // First navigation.
2145  const GURL url2("http://foo#a");
2146  FrameHostMsg_DidCommitProvisionalLoad_Params params;
2147  params.page_id = 0;  // Same page_id
2148  params.url = url2;
2149  params.transition = PAGE_TRANSITION_LINK;
2150  params.should_update_history = false;
2151  params.gesture = NavigationGestureUser;
2152  params.is_post = false;
2153  params.page_state = PageState::CreateFromURL(url2);
2154
2155  // This should NOT generate a new entry, nor prune the list.
2156  LoadCommittedDetails details;
2157  EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
2158                                             &details));
2159  EXPECT_EQ(1U, navigation_entry_committed_counter_);
2160  navigation_entry_committed_counter_ = 0;
2161  EXPECT_TRUE(details.is_in_page);
2162  EXPECT_TRUE(details.did_replace_entry);
2163  EXPECT_EQ(1, controller.GetEntryCount());
2164}
2165
2166// Tests for http://crbug.com/40395
2167// Simulates this:
2168//   <script>
2169//     window.location.replace("#a");
2170//     window.location='http://foo3/';
2171//   </script>
2172TEST_F(NavigationControllerTest, ClientRedirectAfterInPageNavigation) {
2173  NavigationControllerImpl& controller = controller_impl();
2174  TestNotificationTracker notifications;
2175  RegisterForAllNavNotifications(&notifications, &controller);
2176
2177  // Load an initial page.
2178  {
2179    const GURL url("http://foo/");
2180    main_test_rfh()->SendNavigate(0, url);
2181    EXPECT_EQ(1U, navigation_entry_committed_counter_);
2182    navigation_entry_committed_counter_ = 0;
2183  }
2184
2185  // Navigate to a new page.
2186  {
2187    const GURL url("http://foo2/");
2188    main_test_rfh()->SendNavigate(1, url);
2189    EXPECT_EQ(1U, navigation_entry_committed_counter_);
2190    navigation_entry_committed_counter_ = 0;
2191  }
2192
2193  // Navigate within the page.
2194  {
2195    const GURL url("http://foo2/#a");
2196    FrameHostMsg_DidCommitProvisionalLoad_Params params;
2197    params.page_id = 1;  // Same page_id
2198    params.url = url;
2199    params.transition = PAGE_TRANSITION_LINK;
2200    params.redirects.push_back(url);
2201    params.should_update_history = true;
2202    params.gesture = NavigationGestureUnknown;
2203    params.is_post = false;
2204    params.page_state = PageState::CreateFromURL(url);
2205
2206    // This should NOT generate a new entry, nor prune the list.
2207    LoadCommittedDetails details;
2208    EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
2209                                               &details));
2210    EXPECT_EQ(1U, navigation_entry_committed_counter_);
2211    navigation_entry_committed_counter_ = 0;
2212    EXPECT_TRUE(details.is_in_page);
2213    EXPECT_TRUE(details.did_replace_entry);
2214    EXPECT_EQ(2, controller.GetEntryCount());
2215  }
2216
2217  // Perform a client redirect to a new page.
2218  {
2219    const GURL url("http://foo3/");
2220    FrameHostMsg_DidCommitProvisionalLoad_Params params;
2221    params.page_id = 2;  // New page_id
2222    params.url = url;
2223    params.transition = PAGE_TRANSITION_CLIENT_REDIRECT;
2224    params.redirects.push_back(GURL("http://foo2/#a"));
2225    params.redirects.push_back(url);
2226    params.should_update_history = true;
2227    params.gesture = NavigationGestureUnknown;
2228    params.is_post = false;
2229    params.page_state = PageState::CreateFromURL(url);
2230
2231    // This SHOULD generate a new entry.
2232    LoadCommittedDetails details;
2233    EXPECT_TRUE(controller.RendererDidNavigate(main_test_rfh(), params,
2234                                               &details));
2235    EXPECT_EQ(1U, navigation_entry_committed_counter_);
2236    navigation_entry_committed_counter_ = 0;
2237    EXPECT_FALSE(details.is_in_page);
2238    EXPECT_EQ(3, controller.GetEntryCount());
2239  }
2240
2241  // Verify that BACK brings us back to http://foo2/.
2242  {
2243    const GURL url("http://foo2/");
2244    controller.GoBack();
2245    main_test_rfh()->SendNavigate(1, url);
2246    EXPECT_EQ(1U, navigation_entry_committed_counter_);
2247    navigation_entry_committed_counter_ = 0;
2248    EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
2249  }
2250}
2251
2252// NotificationObserver implementation used in verifying we've received the
2253// NOTIFICATION_NAV_LIST_PRUNED method.
2254class PrunedListener : public NotificationObserver {
2255 public:
2256  explicit PrunedListener(NavigationControllerImpl* controller)
2257      : notification_count_(0) {
2258    registrar_.Add(this, NOTIFICATION_NAV_LIST_PRUNED,
2259                   Source<NavigationController>(controller));
2260  }
2261
2262  virtual void Observe(int type,
2263                       const NotificationSource& source,
2264                       const NotificationDetails& details) OVERRIDE {
2265    if (type == NOTIFICATION_NAV_LIST_PRUNED) {
2266      notification_count_++;
2267      details_ = *(Details<PrunedDetails>(details).ptr());
2268    }
2269  }
2270
2271  // Number of times NAV_LIST_PRUNED has been observed.
2272  int notification_count_;
2273
2274  // Details from the last NAV_LIST_PRUNED.
2275  PrunedDetails details_;
2276
2277 private:
2278  NotificationRegistrar registrar_;
2279
2280  DISALLOW_COPY_AND_ASSIGN(PrunedListener);
2281};
2282
2283// Tests that we limit the number of navigation entries created correctly.
2284TEST_F(NavigationControllerTest, EnforceMaxNavigationCount) {
2285  NavigationControllerImpl& controller = controller_impl();
2286  size_t original_count = NavigationControllerImpl::max_entry_count();
2287  const int kMaxEntryCount = 5;
2288
2289  NavigationControllerImpl::set_max_entry_count_for_testing(kMaxEntryCount);
2290
2291  int url_index;
2292  // Load up to the max count, all entries should be there.
2293  for (url_index = 0; url_index < kMaxEntryCount; url_index++) {
2294    GURL url(base::StringPrintf("http://www.a.com/%d", url_index));
2295    controller.LoadURL(
2296        url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2297    main_test_rfh()->SendNavigate(url_index, url);
2298  }
2299
2300  EXPECT_EQ(controller.GetEntryCount(), kMaxEntryCount);
2301
2302  // Created a PrunedListener to observe prune notifications.
2303  PrunedListener listener(&controller);
2304
2305  // Navigate some more.
2306  GURL url(base::StringPrintf("http://www.a.com/%d", url_index));
2307  controller.LoadURL(
2308      url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2309  main_test_rfh()->SendNavigate(url_index, url);
2310  url_index++;
2311
2312  // We should have got a pruned navigation.
2313  EXPECT_EQ(1, listener.notification_count_);
2314  EXPECT_TRUE(listener.details_.from_front);
2315  EXPECT_EQ(1, listener.details_.count);
2316
2317  // We expect http://www.a.com/0 to be gone.
2318  EXPECT_EQ(controller.GetEntryCount(), kMaxEntryCount);
2319  EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(),
2320            GURL("http:////www.a.com/1"));
2321
2322  // More navigations.
2323  for (int i = 0; i < 3; i++) {
2324    url = GURL(base::StringPrintf("http:////www.a.com/%d", url_index));
2325    controller.LoadURL(
2326        url, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2327    main_test_rfh()->SendNavigate(url_index, url);
2328    url_index++;
2329  }
2330  EXPECT_EQ(controller.GetEntryCount(), kMaxEntryCount);
2331  EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(),
2332            GURL("http:////www.a.com/4"));
2333
2334  NavigationControllerImpl::set_max_entry_count_for_testing(original_count);
2335}
2336
2337// Tests that we can do a restore and navigate to the restored entries and
2338// everything is updated properly. This can be tricky since there is no
2339// SiteInstance for the entries created initially.
2340TEST_F(NavigationControllerTest, RestoreNavigate) {
2341  // Create a NavigationController with a restored set of tabs.
2342  GURL url("http://foo");
2343  std::vector<NavigationEntry*> entries;
2344  NavigationEntry* entry = NavigationControllerImpl::CreateNavigationEntry(
2345      url, Referrer(), PAGE_TRANSITION_RELOAD, false, std::string(),
2346      browser_context());
2347  entry->SetPageID(0);
2348  entry->SetTitle(base::ASCIIToUTF16("Title"));
2349  entry->SetPageState(PageState::CreateFromEncodedData("state"));
2350  const base::Time timestamp = base::Time::Now();
2351  entry->SetTimestamp(timestamp);
2352  entries.push_back(entry);
2353  scoped_ptr<WebContentsImpl> our_contents(static_cast<WebContentsImpl*>(
2354      WebContents::Create(WebContents::CreateParams(browser_context()))));
2355  NavigationControllerImpl& our_controller = our_contents->GetController();
2356  our_controller.Restore(
2357      0,
2358      NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY,
2359      &entries);
2360  ASSERT_EQ(0u, entries.size());
2361
2362  // Before navigating to the restored entry, it should have a restore_type
2363  // and no SiteInstance.
2364  ASSERT_EQ(1, our_controller.GetEntryCount());
2365  EXPECT_EQ(NavigationEntryImpl::RESTORE_LAST_SESSION_EXITED_CLEANLY,
2366            NavigationEntryImpl::FromNavigationEntry(
2367                our_controller.GetEntryAtIndex(0))->restore_type());
2368  EXPECT_FALSE(NavigationEntryImpl::FromNavigationEntry(
2369      our_controller.GetEntryAtIndex(0))->site_instance());
2370
2371  // After navigating, we should have one entry, and it should be "pending".
2372  // It should now have a SiteInstance and no restore_type.
2373  our_controller.GoToIndex(0);
2374  EXPECT_EQ(1, our_controller.GetEntryCount());
2375  EXPECT_EQ(our_controller.GetEntryAtIndex(0),
2376            our_controller.GetPendingEntry());
2377  EXPECT_EQ(0, our_controller.GetEntryAtIndex(0)->GetPageID());
2378  EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE,
2379            NavigationEntryImpl::FromNavigationEntry
2380                (our_controller.GetEntryAtIndex(0))->restore_type());
2381  EXPECT_TRUE(NavigationEntryImpl::FromNavigationEntry(
2382      our_controller.GetEntryAtIndex(0))->site_instance());
2383
2384  // Timestamp should remain the same before the navigation finishes.
2385  EXPECT_EQ(timestamp, our_controller.GetEntryAtIndex(0)->GetTimestamp());
2386
2387  // Say we navigated to that entry.
2388  FrameHostMsg_DidCommitProvisionalLoad_Params params;
2389  params.page_id = 0;
2390  params.url = url;
2391  params.transition = PAGE_TRANSITION_LINK;
2392  params.should_update_history = false;
2393  params.gesture = NavigationGestureUser;
2394  params.is_post = false;
2395  params.page_state = PageState::CreateFromURL(url);
2396  LoadCommittedDetails details;
2397  our_controller.RendererDidNavigate(our_contents->GetMainFrame(), params,
2398                                     &details);
2399
2400  // There should be no longer any pending entry and one committed one. This
2401  // means that we were able to locate the entry, assign its site instance, and
2402  // commit it properly.
2403  EXPECT_EQ(1, our_controller.GetEntryCount());
2404  EXPECT_EQ(0, our_controller.GetLastCommittedEntryIndex());
2405  EXPECT_FALSE(our_controller.GetPendingEntry());
2406  EXPECT_EQ(url,
2407            NavigationEntryImpl::FromNavigationEntry(
2408                our_controller.GetLastCommittedEntry())->site_instance()->
2409                    GetSiteURL());
2410  EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE,
2411            NavigationEntryImpl::FromNavigationEntry(
2412                our_controller.GetEntryAtIndex(0))->restore_type());
2413
2414  // Timestamp should have been updated.
2415  EXPECT_GE(our_controller.GetEntryAtIndex(0)->GetTimestamp(), timestamp);
2416}
2417
2418// Tests that we can still navigate to a restored entry after a different
2419// navigation fails and clears the pending entry.  http://crbug.com/90085
2420TEST_F(NavigationControllerTest, RestoreNavigateAfterFailure) {
2421  // Create a NavigationController with a restored set of tabs.
2422  GURL url("http://foo");
2423  std::vector<NavigationEntry*> entries;
2424  NavigationEntry* entry = NavigationControllerImpl::CreateNavigationEntry(
2425      url, Referrer(), PAGE_TRANSITION_RELOAD, false, std::string(),
2426      browser_context());
2427  entry->SetPageID(0);
2428  entry->SetTitle(base::ASCIIToUTF16("Title"));
2429  entry->SetPageState(PageState::CreateFromEncodedData("state"));
2430  entries.push_back(entry);
2431  scoped_ptr<WebContentsImpl> our_contents(static_cast<WebContentsImpl*>(
2432      WebContents::Create(WebContents::CreateParams(browser_context()))));
2433  NavigationControllerImpl& our_controller = our_contents->GetController();
2434  our_controller.Restore(
2435      0, NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY, &entries);
2436  ASSERT_EQ(0u, entries.size());
2437
2438  // Before navigating to the restored entry, it should have a restore_type
2439  // and no SiteInstance.
2440  EXPECT_EQ(NavigationEntryImpl::RESTORE_LAST_SESSION_EXITED_CLEANLY,
2441            NavigationEntryImpl::FromNavigationEntry(
2442                our_controller.GetEntryAtIndex(0))->restore_type());
2443  EXPECT_FALSE(NavigationEntryImpl::FromNavigationEntry(
2444      our_controller.GetEntryAtIndex(0))->site_instance());
2445
2446  // After navigating, we should have one entry, and it should be "pending".
2447  // It should now have a SiteInstance and no restore_type.
2448  our_controller.GoToIndex(0);
2449  EXPECT_EQ(1, our_controller.GetEntryCount());
2450  EXPECT_EQ(our_controller.GetEntryAtIndex(0),
2451            our_controller.GetPendingEntry());
2452  EXPECT_EQ(0, our_controller.GetEntryAtIndex(0)->GetPageID());
2453  EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE,
2454            NavigationEntryImpl::FromNavigationEntry(
2455                our_controller.GetEntryAtIndex(0))->restore_type());
2456  EXPECT_TRUE(NavigationEntryImpl::FromNavigationEntry(
2457      our_controller.GetEntryAtIndex(0))->site_instance());
2458
2459  // This pending navigation may have caused a different navigation to fail,
2460  // which causes the pending entry to be cleared.
2461  FrameHostMsg_DidFailProvisionalLoadWithError_Params fail_load_params;
2462  fail_load_params.error_code = net::ERR_ABORTED;
2463  fail_load_params.error_description = base::string16();
2464  fail_load_params.url = url;
2465  fail_load_params.showing_repost_interstitial = false;
2466  main_test_rfh()->OnMessageReceived(
2467      FrameHostMsg_DidFailProvisionalLoadWithError(0,  // routing_id
2468                                                  fail_load_params));
2469
2470  // Now the pending restored entry commits.
2471  FrameHostMsg_DidCommitProvisionalLoad_Params params;
2472  params.page_id = 0;
2473  params.url = url;
2474  params.transition = PAGE_TRANSITION_LINK;
2475  params.should_update_history = false;
2476  params.gesture = NavigationGestureUser;
2477  params.is_post = false;
2478  params.page_state = PageState::CreateFromURL(url);
2479  LoadCommittedDetails details;
2480  our_controller.RendererDidNavigate(our_contents->GetMainFrame(), params,
2481                                     &details);
2482
2483  // There should be no pending entry and one committed one.
2484  EXPECT_EQ(1, our_controller.GetEntryCount());
2485  EXPECT_EQ(0, our_controller.GetLastCommittedEntryIndex());
2486  EXPECT_FALSE(our_controller.GetPendingEntry());
2487  EXPECT_EQ(url,
2488            NavigationEntryImpl::FromNavigationEntry(
2489                our_controller.GetLastCommittedEntry())->site_instance()->
2490                    GetSiteURL());
2491  EXPECT_EQ(NavigationEntryImpl::RESTORE_NONE,
2492            NavigationEntryImpl::FromNavigationEntry(
2493                our_controller.GetEntryAtIndex(0))->restore_type());
2494}
2495
2496// Make sure that the page type and stuff is correct after an interstitial.
2497TEST_F(NavigationControllerTest, Interstitial) {
2498  NavigationControllerImpl& controller = controller_impl();
2499  // First navigate somewhere normal.
2500  const GURL url1("http://foo");
2501  controller.LoadURL(
2502      url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2503  main_test_rfh()->SendNavigate(0, url1);
2504
2505  // Now navigate somewhere with an interstitial.
2506  const GURL url2("http://bar");
2507  controller.LoadURL(
2508      url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2509  NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())->
2510      set_page_type(PAGE_TYPE_INTERSTITIAL);
2511
2512  // At this point the interstitial will be displayed and the load will still
2513  // be pending. If the user continues, the load will commit.
2514  main_test_rfh()->SendNavigate(1, url2);
2515
2516  // The page should be a normal page again.
2517  EXPECT_EQ(url2, controller.GetLastCommittedEntry()->GetURL());
2518  EXPECT_EQ(PAGE_TYPE_NORMAL,
2519            controller.GetLastCommittedEntry()->GetPageType());
2520}
2521
2522TEST_F(NavigationControllerTest, RemoveEntry) {
2523  NavigationControllerImpl& controller = controller_impl();
2524  const GURL url1("http://foo/1");
2525  const GURL url2("http://foo/2");
2526  const GURL url3("http://foo/3");
2527  const GURL url4("http://foo/4");
2528  const GURL url5("http://foo/5");
2529  const GURL pending_url("http://foo/pending");
2530  const GURL default_url("http://foo/default");
2531
2532  controller.LoadURL(
2533      url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2534  main_test_rfh()->SendNavigate(0, url1);
2535  controller.LoadURL(
2536      url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2537  main_test_rfh()->SendNavigate(1, url2);
2538  controller.LoadURL(
2539      url3, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2540  main_test_rfh()->SendNavigate(2, url3);
2541  controller.LoadURL(
2542      url4, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2543  main_test_rfh()->SendNavigate(3, url4);
2544  controller.LoadURL(
2545      url5, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2546  main_test_rfh()->SendNavigate(4, url5);
2547
2548  // Try to remove the last entry.  Will fail because it is the current entry.
2549  EXPECT_FALSE(controller.RemoveEntryAtIndex(controller.GetEntryCount() - 1));
2550  EXPECT_EQ(5, controller.GetEntryCount());
2551  EXPECT_EQ(4, controller.GetLastCommittedEntryIndex());
2552
2553  // Go back, but don't commit yet. Check that we can't delete the current
2554  // and pending entries.
2555  controller.GoBack();
2556  EXPECT_FALSE(controller.RemoveEntryAtIndex(controller.GetEntryCount() - 1));
2557  EXPECT_FALSE(controller.RemoveEntryAtIndex(controller.GetEntryCount() - 2));
2558
2559  // Now commit and delete the last entry.
2560  main_test_rfh()->SendNavigate(3, url4);
2561  EXPECT_TRUE(controller.RemoveEntryAtIndex(controller.GetEntryCount() - 1));
2562  EXPECT_EQ(4, controller.GetEntryCount());
2563  EXPECT_EQ(3, controller.GetLastCommittedEntryIndex());
2564  EXPECT_FALSE(controller.GetPendingEntry());
2565
2566  // Remove an entry which is not the last committed one.
2567  EXPECT_TRUE(controller.RemoveEntryAtIndex(0));
2568  EXPECT_EQ(3, controller.GetEntryCount());
2569  EXPECT_EQ(2, controller.GetLastCommittedEntryIndex());
2570  EXPECT_FALSE(controller.GetPendingEntry());
2571
2572  // Remove the 2 remaining entries.
2573  controller.RemoveEntryAtIndex(1);
2574  controller.RemoveEntryAtIndex(0);
2575
2576  // This should leave us with only the last committed entry.
2577  EXPECT_EQ(1, controller.GetEntryCount());
2578  EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
2579}
2580
2581// Tests the transient entry, making sure it goes away with all navigations.
2582TEST_F(NavigationControllerTest, TransientEntry) {
2583  NavigationControllerImpl& controller = controller_impl();
2584  TestNotificationTracker notifications;
2585  RegisterForAllNavNotifications(&notifications, &controller);
2586
2587  const GURL url0("http://foo/0");
2588  const GURL url1("http://foo/1");
2589  const GURL url2("http://foo/2");
2590  const GURL url3("http://foo/3");
2591  const GURL url3_ref("http://foo/3#bar");
2592  const GURL url4("http://foo/4");
2593  const GURL transient_url("http://foo/transient");
2594
2595  controller.LoadURL(
2596      url0, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2597  main_test_rfh()->SendNavigate(0, url0);
2598  controller.LoadURL(
2599      url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2600  main_test_rfh()->SendNavigate(1, url1);
2601
2602  notifications.Reset();
2603
2604  // Adding a transient with no pending entry.
2605  NavigationEntryImpl* transient_entry = new NavigationEntryImpl;
2606  transient_entry->SetURL(transient_url);
2607  controller.SetTransientEntry(transient_entry);
2608
2609  // We should not have received any notifications.
2610  EXPECT_EQ(0U, notifications.size());
2611
2612  // Check our state.
2613  EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2614  EXPECT_EQ(controller.GetEntryCount(), 3);
2615  EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 1);
2616  EXPECT_EQ(controller.GetPendingEntryIndex(), -1);
2617  EXPECT_TRUE(controller.GetLastCommittedEntry());
2618  EXPECT_FALSE(controller.GetPendingEntry());
2619  EXPECT_TRUE(controller.CanGoBack());
2620  EXPECT_FALSE(controller.CanGoForward());
2621  EXPECT_EQ(contents()->GetMaxPageID(), 1);
2622
2623  // Navigate.
2624  controller.LoadURL(
2625      url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2626  main_test_rfh()->SendNavigate(2, url2);
2627
2628  // We should have navigated, transient entry should be gone.
2629  EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
2630  EXPECT_EQ(controller.GetEntryCount(), 3);
2631
2632  // Add a transient again, then navigate with no pending entry this time.
2633  transient_entry = new NavigationEntryImpl;
2634  transient_entry->SetURL(transient_url);
2635  controller.SetTransientEntry(transient_entry);
2636  EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2637  main_test_rfh()->SendNavigate(3, url3);
2638  // Transient entry should be gone.
2639  EXPECT_EQ(url3, controller.GetVisibleEntry()->GetURL());
2640  EXPECT_EQ(controller.GetEntryCount(), 4);
2641
2642  // Initiate a navigation, add a transient then commit navigation.
2643  controller.LoadURL(
2644      url4, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2645  transient_entry = new NavigationEntryImpl;
2646  transient_entry->SetURL(transient_url);
2647  controller.SetTransientEntry(transient_entry);
2648  EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2649  main_test_rfh()->SendNavigate(4, url4);
2650  EXPECT_EQ(url4, controller.GetVisibleEntry()->GetURL());
2651  EXPECT_EQ(controller.GetEntryCount(), 5);
2652
2653  // Add a transient and go back.  This should simply remove the transient.
2654  transient_entry = new NavigationEntryImpl;
2655  transient_entry->SetURL(transient_url);
2656  controller.SetTransientEntry(transient_entry);
2657  EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2658  EXPECT_TRUE(controller.CanGoBack());
2659  EXPECT_FALSE(controller.CanGoForward());
2660  controller.GoBack();
2661  // Transient entry should be gone.
2662  EXPECT_EQ(url4, controller.GetVisibleEntry()->GetURL());
2663  EXPECT_EQ(controller.GetEntryCount(), 5);
2664  main_test_rfh()->SendNavigate(3, url3);
2665
2666  // Add a transient and go to an entry before the current one.
2667  transient_entry = new NavigationEntryImpl;
2668  transient_entry->SetURL(transient_url);
2669  controller.SetTransientEntry(transient_entry);
2670  EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2671  controller.GoToIndex(1);
2672  // The navigation should have been initiated, transient entry should be gone.
2673  EXPECT_FALSE(controller.GetTransientEntry());
2674  EXPECT_EQ(url1, controller.GetPendingEntry()->GetURL());
2675  // Visible entry does not update for history navigations until commit.
2676  EXPECT_EQ(url3, controller.GetVisibleEntry()->GetURL());
2677  main_test_rfh()->SendNavigate(1, url1);
2678  EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
2679
2680  // Add a transient and go to an entry after the current one.
2681  transient_entry = new NavigationEntryImpl;
2682  transient_entry->SetURL(transient_url);
2683  controller.SetTransientEntry(transient_entry);
2684  EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2685  controller.GoToIndex(3);
2686  // The navigation should have been initiated, transient entry should be gone.
2687  // Because of the transient entry that is removed, going to index 3 makes us
2688  // land on url2 (which is visible after the commit).
2689  EXPECT_EQ(url2, controller.GetPendingEntry()->GetURL());
2690  EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
2691  main_test_rfh()->SendNavigate(2, url2);
2692  EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
2693
2694  // Add a transient and go forward.
2695  transient_entry = new NavigationEntryImpl;
2696  transient_entry->SetURL(transient_url);
2697  controller.SetTransientEntry(transient_entry);
2698  EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2699  EXPECT_TRUE(controller.CanGoForward());
2700  controller.GoForward();
2701  // We should have navigated, transient entry should be gone.
2702  EXPECT_FALSE(controller.GetTransientEntry());
2703  EXPECT_EQ(url3, controller.GetPendingEntry()->GetURL());
2704  EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
2705  main_test_rfh()->SendNavigate(3, url3);
2706  EXPECT_EQ(url3, controller.GetVisibleEntry()->GetURL());
2707
2708  // Add a transient and do an in-page navigation, replacing the current entry.
2709  transient_entry = new NavigationEntryImpl;
2710  transient_entry->SetURL(transient_url);
2711  controller.SetTransientEntry(transient_entry);
2712  EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2713  main_test_rfh()->SendNavigate(3, url3_ref);
2714  // Transient entry should be gone.
2715  EXPECT_FALSE(controller.GetTransientEntry());
2716  EXPECT_EQ(url3_ref, controller.GetVisibleEntry()->GetURL());
2717
2718  // Ensure the URLs are correct.
2719  EXPECT_EQ(controller.GetEntryCount(), 5);
2720  EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url0);
2721  EXPECT_EQ(controller.GetEntryAtIndex(1)->GetURL(), url1);
2722  EXPECT_EQ(controller.GetEntryAtIndex(2)->GetURL(), url2);
2723  EXPECT_EQ(controller.GetEntryAtIndex(3)->GetURL(), url3_ref);
2724  EXPECT_EQ(controller.GetEntryAtIndex(4)->GetURL(), url4);
2725}
2726
2727// Test that Reload initiates a new navigation to a transient entry's URL.
2728TEST_F(NavigationControllerTest, ReloadTransient) {
2729  NavigationControllerImpl& controller = controller_impl();
2730  const GURL url0("http://foo/0");
2731  const GURL url1("http://foo/1");
2732  const GURL transient_url("http://foo/transient");
2733
2734  // Load |url0|, and start a pending navigation to |url1|.
2735  controller.LoadURL(
2736      url0, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2737  main_test_rfh()->SendNavigate(0, url0);
2738  controller.LoadURL(
2739      url1, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2740
2741  // A transient entry is added, interrupting the navigation.
2742  NavigationEntryImpl* transient_entry = new NavigationEntryImpl;
2743  transient_entry->SetURL(transient_url);
2744  controller.SetTransientEntry(transient_entry);
2745  EXPECT_TRUE(controller.GetTransientEntry());
2746  EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2747
2748  // The page is reloaded, which should remove the pending entry for |url1| and
2749  // the transient entry for |transient_url|, and start a navigation to
2750  // |transient_url|.
2751  controller.Reload(true);
2752  EXPECT_FALSE(controller.GetTransientEntry());
2753  EXPECT_TRUE(controller.GetPendingEntry());
2754  EXPECT_EQ(transient_url, controller.GetVisibleEntry()->GetURL());
2755  ASSERT_EQ(controller.GetEntryCount(), 1);
2756  EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url0);
2757
2758  // Load of |transient_url| completes.
2759  main_test_rfh()->SendNavigate(1, transient_url);
2760  ASSERT_EQ(controller.GetEntryCount(), 2);
2761  EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url0);
2762  EXPECT_EQ(controller.GetEntryAtIndex(1)->GetURL(), transient_url);
2763}
2764
2765// Ensure that renderer initiated pending entries get replaced, so that we
2766// don't show a stale virtual URL when a navigation commits.
2767// See http://crbug.com/266922.
2768TEST_F(NavigationControllerTest, RendererInitiatedPendingEntries) {
2769  NavigationControllerImpl& controller = controller_impl();
2770  Navigator* navigator =
2771      contents()->GetFrameTree()->root()->navigator();
2772
2773  const GURL url1("nonexistent:12121");
2774  const GURL url1_fixed("http://nonexistent:12121/");
2775  const GURL url2("http://foo");
2776
2777  // We create pending entries for renderer-initiated navigations so that we
2778  // can show them in new tabs when it is safe.
2779  navigator->DidStartProvisionalLoad(main_test_rfh(), -1, url1);
2780
2781  // Simulate what happens if a BrowserURLHandler rewrites the URL, causing
2782  // the virtual URL to differ from the URL.
2783  controller.GetPendingEntry()->SetURL(url1_fixed);
2784  controller.GetPendingEntry()->SetVirtualURL(url1);
2785
2786  EXPECT_EQ(url1_fixed, controller.GetPendingEntry()->GetURL());
2787  EXPECT_EQ(url1, controller.GetPendingEntry()->GetVirtualURL());
2788  EXPECT_TRUE(
2789      NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())->
2790          is_renderer_initiated());
2791
2792  // If the user clicks another link, we should replace the pending entry.
2793  navigator->DidStartProvisionalLoad(main_test_rfh(), -1, url2);
2794  EXPECT_EQ(url2, controller.GetPendingEntry()->GetURL());
2795  EXPECT_EQ(url2, controller.GetPendingEntry()->GetVirtualURL());
2796
2797  // Once it commits, the URL and virtual URL should reflect the actual page.
2798  main_test_rfh()->SendNavigate(0, url2);
2799  EXPECT_EQ(url2, controller.GetLastCommittedEntry()->GetURL());
2800  EXPECT_EQ(url2, controller.GetLastCommittedEntry()->GetVirtualURL());
2801
2802  // We should not replace the pending entry for an error URL.
2803  navigator->DidStartProvisionalLoad(main_test_rfh(), -1, url1);
2804  EXPECT_EQ(url1, controller.GetPendingEntry()->GetURL());
2805  navigator->DidStartProvisionalLoad(
2806      main_test_rfh(), -1, GURL(kUnreachableWebDataURL));
2807  EXPECT_EQ(url1, controller.GetPendingEntry()->GetURL());
2808
2809  // We should remember if the pending entry will replace the current one.
2810  // http://crbug.com/308444.
2811  navigator->DidStartProvisionalLoad(main_test_rfh(), -1, url1);
2812  NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())->
2813      set_should_replace_entry(true);
2814  navigator->DidStartProvisionalLoad(main_test_rfh(), -1, url2);
2815  EXPECT_TRUE(
2816      NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())->
2817          should_replace_entry());
2818  // TODO(nasko): Until OnNavigate is moved to RenderFrameHost, we need
2819  // to go through the RenderViewHost. The TestRenderViewHost routes navigations
2820  // to the main frame.
2821  main_test_rfh()->SendNavigate(0, url2);
2822  EXPECT_EQ(url2, controller.GetLastCommittedEntry()->GetURL());
2823}
2824
2825// Tests that the URLs for renderer-initiated navigations are not displayed to
2826// the user until the navigation commits, to prevent URL spoof attacks.
2827// See http://crbug.com/99016.
2828TEST_F(NavigationControllerTest, DontShowRendererURLUntilCommit) {
2829  NavigationControllerImpl& controller = controller_impl();
2830  TestNotificationTracker notifications;
2831  RegisterForAllNavNotifications(&notifications, &controller);
2832
2833  const GURL url0("http://foo/0");
2834  const GURL url1("http://foo/1");
2835
2836  // For typed navigations (browser-initiated), both pending and visible entries
2837  // should update before commit.
2838  controller.LoadURL(url0, Referrer(), PAGE_TRANSITION_TYPED, std::string());
2839  EXPECT_EQ(url0, controller.GetPendingEntry()->GetURL());
2840  EXPECT_EQ(url0, controller.GetVisibleEntry()->GetURL());
2841  main_test_rfh()->SendNavigate(0, url0);
2842
2843  // For link clicks (renderer-initiated navigations), the pending entry should
2844  // update before commit but the visible should not.
2845  NavigationController::LoadURLParams load_url_params(url1);
2846  load_url_params.is_renderer_initiated = true;
2847  controller.LoadURLWithParams(load_url_params);
2848  EXPECT_EQ(url0, controller.GetVisibleEntry()->GetURL());
2849  EXPECT_EQ(url1, controller.GetPendingEntry()->GetURL());
2850  EXPECT_TRUE(
2851      NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())->
2852          is_renderer_initiated());
2853
2854  // After commit, both visible should be updated, there should be no pending
2855  // entry, and we should no longer treat the entry as renderer-initiated.
2856  main_test_rfh()->SendNavigate(1, url1);
2857  EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
2858  EXPECT_FALSE(controller.GetPendingEntry());
2859  EXPECT_FALSE(
2860      NavigationEntryImpl::FromNavigationEntry(
2861          controller.GetLastCommittedEntry())->is_renderer_initiated());
2862
2863  notifications.Reset();
2864}
2865
2866// Tests that the URLs for renderer-initiated navigations in new tabs are
2867// displayed to the user before commit, as long as the initial about:blank
2868// page has not been modified.  If so, we must revert to showing about:blank.
2869// See http://crbug.com/9682.
2870TEST_F(NavigationControllerTest, ShowRendererURLInNewTabUntilModified) {
2871  NavigationControllerImpl& controller = controller_impl();
2872  TestNotificationTracker notifications;
2873  RegisterForAllNavNotifications(&notifications, &controller);
2874
2875  const GURL url("http://foo");
2876
2877  // For renderer-initiated navigations in new tabs (with no committed entries),
2878  // we show the pending entry's URL as long as the about:blank page is not
2879  // modified.
2880  NavigationController::LoadURLParams load_url_params(url);
2881  load_url_params.transition_type = PAGE_TRANSITION_LINK;
2882  load_url_params.is_renderer_initiated = true;
2883  controller.LoadURLWithParams(load_url_params);
2884  EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
2885  EXPECT_EQ(url, controller.GetPendingEntry()->GetURL());
2886  EXPECT_TRUE(
2887      NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())->
2888          is_renderer_initiated());
2889  EXPECT_TRUE(controller.IsInitialNavigation());
2890  EXPECT_FALSE(contents()->HasAccessedInitialDocument());
2891
2892  // There should be no title yet.
2893  EXPECT_TRUE(contents()->GetTitle().empty());
2894
2895  // If something else modifies the contents of the about:blank page, then
2896  // we must revert to showing about:blank to avoid a URL spoof.
2897  main_test_rfh()->OnMessageReceived(FrameHostMsg_DidAccessInitialDocument(0));
2898  EXPECT_TRUE(contents()->HasAccessedInitialDocument());
2899  EXPECT_FALSE(controller.GetVisibleEntry());
2900  EXPECT_EQ(url, controller.GetPendingEntry()->GetURL());
2901
2902  notifications.Reset();
2903}
2904
2905// Tests that the URLs for browser-initiated navigations in new tabs are
2906// displayed to the user even after they fail, as long as the initial
2907// about:blank page has not been modified.  If so, we must revert to showing
2908// about:blank. See http://crbug.com/355537.
2909TEST_F(NavigationControllerTest, ShowBrowserURLAfterFailUntilModified) {
2910  NavigationControllerImpl& controller = controller_impl();
2911  TestNotificationTracker notifications;
2912  RegisterForAllNavNotifications(&notifications, &controller);
2913
2914  const GURL url("http://foo");
2915
2916  // For browser-initiated navigations in new tabs (with no committed entries),
2917  // we show the pending entry's URL as long as the about:blank page is not
2918  // modified.  This is possible in cases that the user types a URL into a popup
2919  // tab created with a slow URL.
2920  NavigationController::LoadURLParams load_url_params(url);
2921  load_url_params.transition_type = PAGE_TRANSITION_TYPED;
2922  load_url_params.is_renderer_initiated = false;
2923  controller.LoadURLWithParams(load_url_params);
2924  EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
2925  EXPECT_EQ(url, controller.GetPendingEntry()->GetURL());
2926  EXPECT_FALSE(
2927      NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())->
2928          is_renderer_initiated());
2929  EXPECT_TRUE(controller.IsInitialNavigation());
2930  EXPECT_FALSE(contents()->HasAccessedInitialDocument());
2931
2932  // There should be no title yet.
2933  EXPECT_TRUE(contents()->GetTitle().empty());
2934
2935  // Suppose it aborts before committing, if it's a 204 or download or due to a
2936  // stop or a new navigation from the user.  The URL should remain visible.
2937  FrameHostMsg_DidFailProvisionalLoadWithError_Params params;
2938  params.error_code = net::ERR_ABORTED;
2939  params.error_description = base::string16();
2940  params.url = url;
2941  params.showing_repost_interstitial = false;
2942  main_test_rfh()->OnMessageReceived(
2943      FrameHostMsg_DidFailProvisionalLoadWithError(0, params));
2944  contents()->SetIsLoading(test_rvh(), false, true, NULL);
2945  EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
2946
2947  // If something else later modifies the contents of the about:blank page, then
2948  // we must revert to showing about:blank to avoid a URL spoof.
2949  main_test_rfh()->OnMessageReceived(FrameHostMsg_DidAccessInitialDocument(0));
2950  EXPECT_TRUE(contents()->HasAccessedInitialDocument());
2951  EXPECT_FALSE(controller.GetVisibleEntry());
2952  EXPECT_FALSE(controller.GetPendingEntry());
2953
2954  notifications.Reset();
2955}
2956
2957// Tests that the URLs for renderer-initiated navigations in new tabs are
2958// displayed to the user even after they fail, as long as the initial
2959// about:blank page has not been modified.  If so, we must revert to showing
2960// about:blank. See http://crbug.com/355537.
2961TEST_F(NavigationControllerTest, ShowRendererURLAfterFailUntilModified) {
2962  NavigationControllerImpl& controller = controller_impl();
2963  TestNotificationTracker notifications;
2964  RegisterForAllNavNotifications(&notifications, &controller);
2965
2966  const GURL url("http://foo");
2967
2968  // For renderer-initiated navigations in new tabs (with no committed entries),
2969  // we show the pending entry's URL as long as the about:blank page is not
2970  // modified.
2971  NavigationController::LoadURLParams load_url_params(url);
2972  load_url_params.transition_type = PAGE_TRANSITION_LINK;
2973  load_url_params.is_renderer_initiated = true;
2974  controller.LoadURLWithParams(load_url_params);
2975  EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
2976  EXPECT_EQ(url, controller.GetPendingEntry()->GetURL());
2977  EXPECT_TRUE(
2978      NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())->
2979          is_renderer_initiated());
2980  EXPECT_TRUE(controller.IsInitialNavigation());
2981  EXPECT_FALSE(contents()->HasAccessedInitialDocument());
2982
2983  // There should be no title yet.
2984  EXPECT_TRUE(contents()->GetTitle().empty());
2985
2986  // Suppose it aborts before committing, if it's a 204 or download or due to a
2987  // stop or a new navigation from the user.  The URL should remain visible.
2988  FrameHostMsg_DidFailProvisionalLoadWithError_Params params;
2989  params.error_code = net::ERR_ABORTED;
2990  params.error_description = base::string16();
2991  params.url = url;
2992  params.showing_repost_interstitial = false;
2993  main_test_rfh()->OnMessageReceived(
2994      FrameHostMsg_DidFailProvisionalLoadWithError(0, params));
2995  EXPECT_EQ(url, controller.GetVisibleEntry()->GetURL());
2996
2997  // If something else later modifies the contents of the about:blank page, then
2998  // we must revert to showing about:blank to avoid a URL spoof.
2999  main_test_rfh()->OnMessageReceived(FrameHostMsg_DidAccessInitialDocument(0));
3000  EXPECT_TRUE(contents()->HasAccessedInitialDocument());
3001  EXPECT_FALSE(controller.GetVisibleEntry());
3002  EXPECT_EQ(url, controller.GetPendingEntry()->GetURL());
3003
3004  notifications.Reset();
3005}
3006
3007TEST_F(NavigationControllerTest, DontShowRendererURLInNewTabAfterCommit) {
3008  NavigationControllerImpl& controller = controller_impl();
3009  TestNotificationTracker notifications;
3010  RegisterForAllNavNotifications(&notifications, &controller);
3011
3012  const GURL url1("http://foo/eh");
3013  const GURL url2("http://foo/bee");
3014
3015  // For renderer-initiated navigations in new tabs (with no committed entries),
3016  // we show the pending entry's URL as long as the about:blank page is not
3017  // modified.
3018  NavigationController::LoadURLParams load_url_params(url1);
3019  load_url_params.transition_type = PAGE_TRANSITION_LINK;
3020  load_url_params.is_renderer_initiated = true;
3021  controller.LoadURLWithParams(load_url_params);
3022  EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
3023  EXPECT_TRUE(
3024      NavigationEntryImpl::FromNavigationEntry(controller.GetPendingEntry())->
3025          is_renderer_initiated());
3026  EXPECT_TRUE(controller.IsInitialNavigation());
3027  EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3028
3029  // Simulate a commit and then starting a new pending navigation.
3030  main_test_rfh()->SendNavigate(0, url1);
3031  NavigationController::LoadURLParams load_url2_params(url2);
3032  load_url2_params.transition_type = PAGE_TRANSITION_LINK;
3033  load_url2_params.is_renderer_initiated = true;
3034  controller.LoadURLWithParams(load_url2_params);
3035
3036  // We should not consider this an initial navigation, and thus should
3037  // not show the pending URL.
3038  EXPECT_FALSE(contents()->HasAccessedInitialDocument());
3039  EXPECT_FALSE(controller.IsInitialNavigation());
3040  EXPECT_TRUE(controller.GetVisibleEntry());
3041  EXPECT_EQ(url1, controller.GetVisibleEntry()->GetURL());
3042
3043  notifications.Reset();
3044}
3045
3046// Tests that IsInPageNavigation returns appropriate results.  Prevents
3047// regression for bug 1126349.
3048TEST_F(NavigationControllerTest, IsInPageNavigation) {
3049  NavigationControllerImpl& controller = controller_impl();
3050  // Navigate to URL with no refs.
3051  const GURL url("http://www.google.com/home.html");
3052  main_test_rfh()->SendNavigate(0, url);
3053
3054  // Reloading the page is not an in-page navigation.
3055  EXPECT_FALSE(controller.IsURLInPageNavigation(url));
3056  const GURL other_url("http://www.google.com/add.html");
3057  EXPECT_FALSE(controller.IsURLInPageNavigation(other_url));
3058  const GURL url_with_ref("http://www.google.com/home.html#my_ref");
3059  EXPECT_TRUE(controller.IsURLInPageNavigation(url_with_ref));
3060
3061  // Navigate to URL with refs.
3062  main_test_rfh()->SendNavigate(1, url_with_ref);
3063
3064  // Reloading the page is not an in-page navigation.
3065  EXPECT_FALSE(controller.IsURLInPageNavigation(url_with_ref));
3066  EXPECT_FALSE(controller.IsURLInPageNavigation(url));
3067  EXPECT_FALSE(controller.IsURLInPageNavigation(other_url));
3068  const GURL other_url_with_ref("http://www.google.com/home.html#my_other_ref");
3069  EXPECT_TRUE(controller.IsURLInPageNavigation(other_url_with_ref));
3070
3071  // Going to the same url again will be considered in-page
3072  // if the renderer says it is even if the navigation type isn't IN_PAGE.
3073  EXPECT_TRUE(controller.IsURLInPageNavigation(url_with_ref, true,
3074      NAVIGATION_TYPE_UNKNOWN));
3075
3076  // Going back to the non ref url will be considered in-page if the navigation
3077  // type is IN_PAGE.
3078  EXPECT_TRUE(controller.IsURLInPageNavigation(url, true,
3079      NAVIGATION_TYPE_IN_PAGE));
3080}
3081
3082// Some pages can have subframes with the same base URL (minus the reference) as
3083// the main page. Even though this is hard, it can happen, and we don't want
3084// these subframe navigations to affect the toplevel document. They should
3085// instead be ignored.  http://crbug.com/5585
3086TEST_F(NavigationControllerTest, SameSubframe) {
3087  NavigationControllerImpl& controller = controller_impl();
3088  // Navigate the main frame.
3089  const GURL url("http://www.google.com/");
3090  main_test_rfh()->SendNavigate(0, url);
3091
3092  // We should be at the first navigation entry.
3093  EXPECT_EQ(controller.GetEntryCount(), 1);
3094  EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
3095
3096  // Navigate a subframe that would normally count as in-page.
3097  const GURL subframe("http://www.google.com/#");
3098  FrameHostMsg_DidCommitProvisionalLoad_Params params;
3099  params.page_id = 0;
3100  params.url = subframe;
3101  params.transition = PAGE_TRANSITION_AUTO_SUBFRAME;
3102  params.should_update_history = false;
3103  params.gesture = NavigationGestureAuto;
3104  params.is_post = false;
3105  params.page_state = PageState::CreateFromURL(subframe);
3106  LoadCommittedDetails details;
3107  EXPECT_FALSE(controller.RendererDidNavigate(main_test_rfh(), params,
3108                                              &details));
3109
3110  // Nothing should have changed.
3111  EXPECT_EQ(controller.GetEntryCount(), 1);
3112  EXPECT_EQ(controller.GetLastCommittedEntryIndex(), 0);
3113}
3114
3115// Make sure that on cloning a WebContentsImpl and going back needs_reload is
3116// false.
3117TEST_F(NavigationControllerTest, CloneAndGoBack) {
3118  NavigationControllerImpl& controller = controller_impl();
3119  const GURL url1("http://foo1");
3120  const GURL url2("http://foo2");
3121  const base::string16 title(base::ASCIIToUTF16("Title"));
3122
3123  NavigateAndCommit(url1);
3124  controller.GetVisibleEntry()->SetTitle(title);
3125  NavigateAndCommit(url2);
3126
3127  scoped_ptr<WebContents> clone(controller.GetWebContents()->Clone());
3128
3129  ASSERT_EQ(2, clone->GetController().GetEntryCount());
3130  EXPECT_TRUE(clone->GetController().NeedsReload());
3131  clone->GetController().GoBack();
3132  // Navigating back should have triggered needs_reload_ to go false.
3133  EXPECT_FALSE(clone->GetController().NeedsReload());
3134
3135  // Ensure that the pending URL and its title are visible.
3136  EXPECT_EQ(url1, clone->GetController().GetVisibleEntry()->GetURL());
3137  EXPECT_EQ(title, clone->GetTitle());
3138}
3139
3140// Make sure that reloading a cloned tab doesn't change its pending entry index.
3141// See http://crbug.com/234491.
3142TEST_F(NavigationControllerTest, CloneAndReload) {
3143  NavigationControllerImpl& controller = controller_impl();
3144  const GURL url1("http://foo1");
3145  const GURL url2("http://foo2");
3146  const base::string16 title(base::ASCIIToUTF16("Title"));
3147
3148  NavigateAndCommit(url1);
3149  controller.GetVisibleEntry()->SetTitle(title);
3150  NavigateAndCommit(url2);
3151
3152  scoped_ptr<WebContents> clone(controller.GetWebContents()->Clone());
3153  clone->GetController().LoadIfNecessary();
3154
3155  ASSERT_EQ(2, clone->GetController().GetEntryCount());
3156  EXPECT_EQ(1, clone->GetController().GetPendingEntryIndex());
3157
3158  clone->GetController().Reload(true);
3159  EXPECT_EQ(1, clone->GetController().GetPendingEntryIndex());
3160}
3161
3162// Make sure that cloning a WebContentsImpl doesn't copy interstitials.
3163TEST_F(NavigationControllerTest, CloneOmitsInterstitials) {
3164  NavigationControllerImpl& controller = controller_impl();
3165  const GURL url1("http://foo1");
3166  const GURL url2("http://foo2");
3167
3168  NavigateAndCommit(url1);
3169  NavigateAndCommit(url2);
3170
3171  // Add an interstitial entry.  Should be deleted with controller.
3172  NavigationEntryImpl* interstitial_entry = new NavigationEntryImpl();
3173  interstitial_entry->set_page_type(PAGE_TYPE_INTERSTITIAL);
3174  controller.SetTransientEntry(interstitial_entry);
3175
3176  scoped_ptr<WebContents> clone(controller.GetWebContents()->Clone());
3177
3178  ASSERT_EQ(2, clone->GetController().GetEntryCount());
3179}
3180
3181// Test requesting and triggering a lazy reload.
3182TEST_F(NavigationControllerTest, LazyReload) {
3183  NavigationControllerImpl& controller = controller_impl();
3184  const GURL url("http://foo");
3185  NavigateAndCommit(url);
3186  ASSERT_FALSE(controller.NeedsReload());
3187
3188  // Request a reload to happen when the controller becomes active (e.g. after
3189  // the renderer gets killed in background on Android).
3190  controller.SetNeedsReload();
3191  ASSERT_TRUE(controller.NeedsReload());
3192
3193  // Set the controller as active, triggering the requested reload.
3194  controller.SetActive(true);
3195  ASSERT_FALSE(controller.NeedsReload());
3196}
3197
3198// Tests a subframe navigation while a toplevel navigation is pending.
3199// http://crbug.com/43967
3200TEST_F(NavigationControllerTest, SubframeWhilePending) {
3201  NavigationControllerImpl& controller = controller_impl();
3202  // Load the first page.
3203  const GURL url1("http://foo/");
3204  NavigateAndCommit(url1);
3205
3206  // Now start a pending load to a totally different page, but don't commit it.
3207  const GURL url2("http://bar/");
3208  controller.LoadURL(
3209      url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
3210
3211  // Send a subframe update from the first page, as if one had just
3212  // automatically loaded. Auto subframes don't increment the page ID.
3213  const GURL url1_sub("http://foo/subframe");
3214  FrameHostMsg_DidCommitProvisionalLoad_Params params;
3215  params.page_id = controller.GetLastCommittedEntry()->GetPageID();
3216  params.url = url1_sub;
3217  params.transition = PAGE_TRANSITION_AUTO_SUBFRAME;
3218  params.should_update_history = false;
3219  params.gesture = NavigationGestureAuto;
3220  params.is_post = false;
3221  params.page_state = PageState::CreateFromURL(url1_sub);
3222  LoadCommittedDetails details;
3223
3224  // This should return false meaning that nothing was actually updated.
3225  EXPECT_FALSE(controller.RendererDidNavigate(main_test_rfh(), params,
3226                                              &details));
3227
3228  // The notification should have updated the last committed one, and not
3229  // the pending load.
3230  EXPECT_EQ(url1, controller.GetLastCommittedEntry()->GetURL());
3231
3232  // The active entry should be unchanged by the subframe load.
3233  EXPECT_EQ(url2, controller.GetVisibleEntry()->GetURL());
3234}
3235
3236// Test CopyStateFrom with 2 urls, the first selected and nothing in the target.
3237TEST_F(NavigationControllerTest, CopyStateFrom) {
3238  NavigationControllerImpl& controller = controller_impl();
3239  const GURL url1("http://foo1");
3240  const GURL url2("http://foo2");
3241
3242  NavigateAndCommit(url1);
3243  NavigateAndCommit(url2);
3244  controller.GoBack();
3245  contents()->CommitPendingNavigation();
3246
3247  scoped_ptr<TestWebContents> other_contents(
3248      static_cast<TestWebContents*>(CreateTestWebContents()));
3249  NavigationControllerImpl& other_controller = other_contents->GetController();
3250  other_controller.CopyStateFrom(controller);
3251
3252  // other_controller should now contain 2 urls.
3253  ASSERT_EQ(2, other_controller.GetEntryCount());
3254  // We should be looking at the first one.
3255  ASSERT_EQ(0, other_controller.GetCurrentEntryIndex());
3256
3257  EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3258  EXPECT_EQ(0, other_controller.GetEntryAtIndex(0)->GetPageID());
3259  EXPECT_EQ(url2, other_controller.GetEntryAtIndex(1)->GetURL());
3260  // This is a different site than url1, so the IDs start again at 0.
3261  EXPECT_EQ(0, other_controller.GetEntryAtIndex(1)->GetPageID());
3262
3263  // The max page ID map should be copied over and updated with the max page ID
3264  // from the current tab.
3265  SiteInstance* instance1 =
3266      GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0));
3267  EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
3268
3269  // Ensure the SessionStorageNamespaceMaps are the same size and have
3270  // the same partitons loaded.
3271  //
3272  // TODO(ajwong): We should load a url from a different partition earlier
3273  // to make sure this map has more than one entry.
3274  const SessionStorageNamespaceMap& session_storage_namespace_map =
3275      controller.GetSessionStorageNamespaceMap();
3276  const SessionStorageNamespaceMap& other_session_storage_namespace_map =
3277      other_controller.GetSessionStorageNamespaceMap();
3278  EXPECT_EQ(session_storage_namespace_map.size(),
3279            other_session_storage_namespace_map.size());
3280  for (SessionStorageNamespaceMap::const_iterator it =
3281           session_storage_namespace_map.begin();
3282       it != session_storage_namespace_map.end();
3283       ++it) {
3284    SessionStorageNamespaceMap::const_iterator other =
3285        other_session_storage_namespace_map.find(it->first);
3286    EXPECT_TRUE(other != other_session_storage_namespace_map.end());
3287  }
3288}
3289
3290// Tests CopyStateFromAndPrune with 2 urls in source, 1 in dest.
3291TEST_F(NavigationControllerTest, CopyStateFromAndPrune) {
3292  NavigationControllerImpl& controller = controller_impl();
3293  const GURL url1("http://foo/1");
3294  const GURL url2("http://foo/2");
3295  const GURL url3("http://foo/3");
3296
3297  NavigateAndCommit(url1);
3298  NavigateAndCommit(url2);
3299
3300  // First two entries should have the same SiteInstance.
3301  SiteInstance* instance1 =
3302      GetSiteInstanceFromEntry(controller.GetEntryAtIndex(0));
3303  SiteInstance* instance2 =
3304      GetSiteInstanceFromEntry(controller.GetEntryAtIndex(1));
3305  EXPECT_EQ(instance1, instance2);
3306  EXPECT_EQ(0, controller.GetEntryAtIndex(0)->GetPageID());
3307  EXPECT_EQ(1, controller.GetEntryAtIndex(1)->GetPageID());
3308  EXPECT_EQ(1, contents()->GetMaxPageIDForSiteInstance(instance1));
3309
3310  scoped_ptr<TestWebContents> other_contents(
3311      static_cast<TestWebContents*>(CreateTestWebContents()));
3312  NavigationControllerImpl& other_controller = other_contents->GetController();
3313  other_contents->NavigateAndCommit(url3);
3314  other_contents->ExpectSetHistoryLengthAndPrune(
3315      GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0)), 2,
3316      other_controller.GetEntryAtIndex(0)->GetPageID());
3317  other_controller.CopyStateFromAndPrune(&controller, false);
3318
3319  // other_controller should now contain the 3 urls: url1, url2 and url3.
3320
3321  ASSERT_EQ(3, other_controller.GetEntryCount());
3322
3323  ASSERT_EQ(2, other_controller.GetCurrentEntryIndex());
3324
3325  EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3326  EXPECT_EQ(url2, other_controller.GetEntryAtIndex(1)->GetURL());
3327  EXPECT_EQ(url3, other_controller.GetEntryAtIndex(2)->GetURL());
3328  EXPECT_EQ(0, other_controller.GetEntryAtIndex(0)->GetPageID());
3329  EXPECT_EQ(1, other_controller.GetEntryAtIndex(1)->GetPageID());
3330  EXPECT_EQ(0, other_controller.GetEntryAtIndex(2)->GetPageID());
3331
3332  // A new SiteInstance in a different BrowsingInstance should be used for the
3333  // new tab.
3334  SiteInstance* instance3 =
3335      GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(2));
3336  EXPECT_NE(instance3, instance1);
3337  EXPECT_FALSE(instance3->IsRelatedSiteInstance(instance1));
3338
3339  // The max page ID map should be copied over and updated with the max page ID
3340  // from the current tab.
3341  EXPECT_EQ(1, other_contents->GetMaxPageIDForSiteInstance(instance1));
3342  EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance3));
3343}
3344
3345// Test CopyStateFromAndPrune with 2 urls, the first selected and 1 entry in
3346// the target.
3347TEST_F(NavigationControllerTest, CopyStateFromAndPrune2) {
3348  NavigationControllerImpl& controller = controller_impl();
3349  const GURL url1("http://foo1");
3350  const GURL url2("http://foo2");
3351  const GURL url3("http://foo3");
3352
3353  NavigateAndCommit(url1);
3354  NavigateAndCommit(url2);
3355  controller.GoBack();
3356  contents()->CommitPendingNavigation();
3357
3358  scoped_ptr<TestWebContents> other_contents(
3359      static_cast<TestWebContents*>(CreateTestWebContents()));
3360  NavigationControllerImpl& other_controller = other_contents->GetController();
3361  other_contents->NavigateAndCommit(url3);
3362  other_contents->ExpectSetHistoryLengthAndPrune(
3363      GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0)), 1,
3364      other_controller.GetEntryAtIndex(0)->GetPageID());
3365  other_controller.CopyStateFromAndPrune(&controller, false);
3366
3367  // other_controller should now contain: url1, url3
3368
3369  ASSERT_EQ(2, other_controller.GetEntryCount());
3370  ASSERT_EQ(1, other_controller.GetCurrentEntryIndex());
3371
3372  EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3373  EXPECT_EQ(url3, other_controller.GetEntryAtIndex(1)->GetURL());
3374  EXPECT_EQ(0, other_controller.GetEntryAtIndex(1)->GetPageID());
3375
3376  // The max page ID map should be copied over and updated with the max page ID
3377  // from the current tab.
3378  SiteInstance* instance1 =
3379      GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(1));
3380  EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
3381}
3382
3383// Test CopyStateFromAndPrune with 2 urls, the last selected and 2 entries in
3384// the target.
3385TEST_F(NavigationControllerTest, CopyStateFromAndPrune3) {
3386  NavigationControllerImpl& controller = controller_impl();
3387  const GURL url1("http://foo1");
3388  const GURL url2("http://foo2");
3389  const GURL url3("http://foo3");
3390  const GURL url4("http://foo4");
3391
3392  NavigateAndCommit(url1);
3393  NavigateAndCommit(url2);
3394
3395  scoped_ptr<TestWebContents> other_contents(
3396      static_cast<TestWebContents*>(CreateTestWebContents()));
3397  NavigationControllerImpl& other_controller = other_contents->GetController();
3398  other_contents->NavigateAndCommit(url3);
3399  other_contents->NavigateAndCommit(url4);
3400  other_contents->ExpectSetHistoryLengthAndPrune(
3401      GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(1)), 2,
3402      other_controller.GetEntryAtIndex(0)->GetPageID());
3403  other_controller.CopyStateFromAndPrune(&controller, false);
3404
3405  // other_controller should now contain: url1, url2, url4
3406
3407  ASSERT_EQ(3, other_controller.GetEntryCount());
3408  ASSERT_EQ(2, other_controller.GetCurrentEntryIndex());
3409
3410  EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3411  EXPECT_EQ(url2, other_controller.GetEntryAtIndex(1)->GetURL());
3412  EXPECT_EQ(url4, other_controller.GetEntryAtIndex(2)->GetURL());
3413
3414  // The max page ID map should be copied over and updated with the max page ID
3415  // from the current tab.
3416  SiteInstance* instance1 =
3417      GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(2));
3418  EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
3419}
3420
3421// Test CopyStateFromAndPrune with 2 urls, 2 entries in the target, with
3422// not the last entry selected in the target.
3423TEST_F(NavigationControllerTest, CopyStateFromAndPruneNotLast) {
3424  NavigationControllerImpl& controller = controller_impl();
3425  const GURL url1("http://foo1");
3426  const GURL url2("http://foo2");
3427  const GURL url3("http://foo3");
3428  const GURL url4("http://foo4");
3429
3430  NavigateAndCommit(url1);
3431  NavigateAndCommit(url2);
3432
3433  scoped_ptr<TestWebContents> other_contents(
3434      static_cast<TestWebContents*>(CreateTestWebContents()));
3435  NavigationControllerImpl& other_controller = other_contents->GetController();
3436  other_contents->NavigateAndCommit(url3);
3437  other_contents->NavigateAndCommit(url4);
3438  other_controller.GoBack();
3439  other_contents->CommitPendingNavigation();
3440  other_contents->ExpectSetHistoryLengthAndPrune(
3441      GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0)), 2,
3442      other_controller.GetEntryAtIndex(0)->GetPageID());
3443  other_controller.CopyStateFromAndPrune(&controller, false);
3444
3445  // other_controller should now contain: url1, url2, url3
3446
3447  ASSERT_EQ(3, other_controller.GetEntryCount());
3448  ASSERT_EQ(2, other_controller.GetCurrentEntryIndex());
3449
3450  EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3451  EXPECT_EQ(url2, other_controller.GetEntryAtIndex(1)->GetURL());
3452  EXPECT_EQ(url3, other_controller.GetEntryAtIndex(2)->GetURL());
3453
3454  // The max page ID map should be copied over and updated with the max page ID
3455  // from the current tab.
3456  SiteInstance* instance1 =
3457      GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(2));
3458  EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
3459}
3460
3461// Test CopyStateFromAndPrune with 2 urls, the first selected and 1 entry plus
3462// a pending entry in the target.
3463TEST_F(NavigationControllerTest, CopyStateFromAndPruneTargetPending) {
3464  NavigationControllerImpl& controller = controller_impl();
3465  const GURL url1("http://foo1");
3466  const GURL url2("http://foo2");
3467  const GURL url3("http://foo3");
3468  const GURL url4("http://foo4");
3469
3470  NavigateAndCommit(url1);
3471  NavigateAndCommit(url2);
3472  controller.GoBack();
3473  contents()->CommitPendingNavigation();
3474
3475  scoped_ptr<TestWebContents> other_contents(
3476      static_cast<TestWebContents*>(CreateTestWebContents()));
3477  NavigationControllerImpl& other_controller = other_contents->GetController();
3478  other_contents->NavigateAndCommit(url3);
3479  other_controller.LoadURL(
3480      url4, Referrer(), PAGE_TRANSITION_TYPED, std::string());
3481  other_contents->ExpectSetHistoryLengthAndPrune(
3482      GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0)), 1,
3483      other_controller.GetEntryAtIndex(0)->GetPageID());
3484  other_controller.CopyStateFromAndPrune(&controller, false);
3485
3486  // other_controller should now contain url1, url3, and a pending entry
3487  // for url4.
3488
3489  ASSERT_EQ(2, other_controller.GetEntryCount());
3490  EXPECT_EQ(1, other_controller.GetCurrentEntryIndex());
3491
3492  EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3493  EXPECT_EQ(url3, other_controller.GetEntryAtIndex(1)->GetURL());
3494
3495  // And there should be a pending entry for url4.
3496  ASSERT_TRUE(other_controller.GetPendingEntry());
3497  EXPECT_EQ(url4, other_controller.GetPendingEntry()->GetURL());
3498
3499  // The max page ID map should be copied over and updated with the max page ID
3500  // from the current tab.
3501  SiteInstance* instance1 =
3502      GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0));
3503  EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
3504}
3505
3506// Test CopyStateFromAndPrune with 1 url in the source, 1 entry and a pending
3507// client redirect entry (with the same page ID) in the target.  This used to
3508// crash because the last committed entry would be pruned but max_page_id
3509// remembered the page ID (http://crbug.com/234809).
3510TEST_F(NavigationControllerTest, CopyStateFromAndPruneTargetPending2) {
3511  NavigationControllerImpl& controller = controller_impl();
3512  const GURL url1("http://foo1");
3513  const GURL url2a("http://foo2/a");
3514  const GURL url2b("http://foo2/b");
3515
3516  NavigateAndCommit(url1);
3517
3518  scoped_ptr<TestWebContents> other_contents(
3519      static_cast<TestWebContents*>(CreateTestWebContents()));
3520  NavigationControllerImpl& other_controller = other_contents->GetController();
3521  other_contents->NavigateAndCommit(url2a);
3522  // Simulate a client redirect, which has the same page ID as entry 2a.
3523  other_controller.LoadURL(
3524      url2b, Referrer(), PAGE_TRANSITION_LINK, std::string());
3525  other_controller.GetPendingEntry()->SetPageID(
3526      other_controller.GetLastCommittedEntry()->GetPageID());
3527
3528  other_contents->ExpectSetHistoryLengthAndPrune(
3529      GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0)), 1,
3530      other_controller.GetEntryAtIndex(0)->GetPageID());
3531  other_controller.CopyStateFromAndPrune(&controller, false);
3532
3533  // other_controller should now contain url1, url2a, and a pending entry
3534  // for url2b.
3535
3536  ASSERT_EQ(2, other_controller.GetEntryCount());
3537  EXPECT_EQ(1, other_controller.GetCurrentEntryIndex());
3538
3539  EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3540  EXPECT_EQ(url2a, other_controller.GetEntryAtIndex(1)->GetURL());
3541
3542  // And there should be a pending entry for url4.
3543  ASSERT_TRUE(other_controller.GetPendingEntry());
3544  EXPECT_EQ(url2b, other_controller.GetPendingEntry()->GetURL());
3545
3546  // Let the pending entry commit.
3547  other_contents->CommitPendingNavigation();
3548
3549  // The max page ID map should be copied over and updated with the max page ID
3550  // from the current tab.
3551  SiteInstance* instance1 =
3552      GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(1));
3553  EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
3554}
3555
3556// Test CopyStateFromAndPrune with 2 urls, a back navigation pending in the
3557// source, and 1 entry in the target. The back pending entry should be ignored.
3558TEST_F(NavigationControllerTest, CopyStateFromAndPruneSourcePending) {
3559  NavigationControllerImpl& controller = controller_impl();
3560  const GURL url1("http://foo1");
3561  const GURL url2("http://foo2");
3562  const GURL url3("http://foo3");
3563
3564  NavigateAndCommit(url1);
3565  NavigateAndCommit(url2);
3566  controller.GoBack();
3567
3568  scoped_ptr<TestWebContents> other_contents(
3569      static_cast<TestWebContents*>(CreateTestWebContents()));
3570  NavigationControllerImpl& other_controller = other_contents->GetController();
3571  other_contents->NavigateAndCommit(url3);
3572  other_contents->ExpectSetHistoryLengthAndPrune(
3573      GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0)), 2,
3574      other_controller.GetEntryAtIndex(0)->GetPageID());
3575  other_controller.CopyStateFromAndPrune(&controller, false);
3576
3577  // other_controller should now contain: url1, url2, url3
3578
3579  ASSERT_EQ(3, other_controller.GetEntryCount());
3580  ASSERT_EQ(2, other_controller.GetCurrentEntryIndex());
3581
3582  EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3583  EXPECT_EQ(url2, other_controller.GetEntryAtIndex(1)->GetURL());
3584  EXPECT_EQ(url3, other_controller.GetEntryAtIndex(2)->GetURL());
3585  EXPECT_EQ(0, other_controller.GetEntryAtIndex(2)->GetPageID());
3586
3587  // The max page ID map should be copied over and updated with the max page ID
3588  // from the current tab.
3589  SiteInstance* instance1 =
3590      GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(2));
3591  EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance1));
3592}
3593
3594// Tests CopyStateFromAndPrune with 3 urls in source, 1 in dest,
3595// when the max entry count is 3.  We should prune one entry.
3596TEST_F(NavigationControllerTest, CopyStateFromAndPruneMaxEntries) {
3597  NavigationControllerImpl& controller = controller_impl();
3598  size_t original_count = NavigationControllerImpl::max_entry_count();
3599  const int kMaxEntryCount = 3;
3600
3601  NavigationControllerImpl::set_max_entry_count_for_testing(kMaxEntryCount);
3602
3603  const GURL url1("http://foo/1");
3604  const GURL url2("http://foo/2");
3605  const GURL url3("http://foo/3");
3606  const GURL url4("http://foo/4");
3607
3608  // Create a PrunedListener to observe prune notifications.
3609  PrunedListener listener(&controller);
3610
3611  NavigateAndCommit(url1);
3612  NavigateAndCommit(url2);
3613  NavigateAndCommit(url3);
3614
3615  scoped_ptr<TestWebContents> other_contents(
3616      static_cast<TestWebContents*>(CreateTestWebContents()));
3617  NavigationControllerImpl& other_controller = other_contents->GetController();
3618  other_contents->NavigateAndCommit(url4);
3619  other_contents->ExpectSetHistoryLengthAndPrune(
3620      GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0)), 2,
3621      other_controller.GetEntryAtIndex(0)->GetPageID());
3622  other_controller.CopyStateFromAndPrune(&controller, false);
3623
3624  // We should have received a pruned notification.
3625  EXPECT_EQ(1, listener.notification_count_);
3626  EXPECT_TRUE(listener.details_.from_front);
3627  EXPECT_EQ(1, listener.details_.count);
3628
3629  // other_controller should now contain only 3 urls: url2, url3 and url4.
3630
3631  ASSERT_EQ(3, other_controller.GetEntryCount());
3632
3633  ASSERT_EQ(2, other_controller.GetCurrentEntryIndex());
3634
3635  EXPECT_EQ(url2, other_controller.GetEntryAtIndex(0)->GetURL());
3636  EXPECT_EQ(url3, other_controller.GetEntryAtIndex(1)->GetURL());
3637  EXPECT_EQ(url4, other_controller.GetEntryAtIndex(2)->GetURL());
3638  EXPECT_EQ(1, other_controller.GetEntryAtIndex(0)->GetPageID());
3639  EXPECT_EQ(2, other_controller.GetEntryAtIndex(1)->GetPageID());
3640  EXPECT_EQ(0, other_controller.GetEntryAtIndex(2)->GetPageID());
3641
3642  NavigationControllerImpl::set_max_entry_count_for_testing(original_count);
3643}
3644
3645// Tests CopyStateFromAndPrune with 2 urls in source, 1 in dest, with
3646// replace_entry set to true.
3647TEST_F(NavigationControllerTest, CopyStateFromAndPruneReplaceEntry) {
3648  NavigationControllerImpl& controller = controller_impl();
3649  const GURL url1("http://foo/1");
3650  const GURL url2("http://foo/2");
3651  const GURL url3("http://foo/3");
3652
3653  NavigateAndCommit(url1);
3654  NavigateAndCommit(url2);
3655
3656  // First two entries should have the same SiteInstance.
3657  SiteInstance* instance1 =
3658      GetSiteInstanceFromEntry(controller.GetEntryAtIndex(0));
3659  SiteInstance* instance2 =
3660      GetSiteInstanceFromEntry(controller.GetEntryAtIndex(1));
3661  EXPECT_EQ(instance1, instance2);
3662  EXPECT_EQ(0, controller.GetEntryAtIndex(0)->GetPageID());
3663  EXPECT_EQ(1, controller.GetEntryAtIndex(1)->GetPageID());
3664  EXPECT_EQ(1, contents()->GetMaxPageIDForSiteInstance(instance1));
3665
3666  scoped_ptr<TestWebContents> other_contents(
3667      static_cast<TestWebContents*>(CreateTestWebContents()));
3668  NavigationControllerImpl& other_controller = other_contents->GetController();
3669  other_contents->NavigateAndCommit(url3);
3670  other_contents->ExpectSetHistoryLengthAndPrune(
3671      GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0)), 1,
3672      other_controller.GetEntryAtIndex(0)->GetPageID());
3673  other_controller.CopyStateFromAndPrune(&controller, true);
3674
3675  // other_controller should now contain the 2 urls: url1 and url3.
3676
3677  ASSERT_EQ(2, other_controller.GetEntryCount());
3678
3679  ASSERT_EQ(1, other_controller.GetCurrentEntryIndex());
3680
3681  EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3682  EXPECT_EQ(url3, other_controller.GetEntryAtIndex(1)->GetURL());
3683  EXPECT_EQ(0, other_controller.GetEntryAtIndex(0)->GetPageID());
3684  EXPECT_EQ(0, other_controller.GetEntryAtIndex(1)->GetPageID());
3685
3686  // A new SiteInstance in a different BrowsingInstance should be used for the
3687  // new tab.
3688  SiteInstance* instance3 =
3689      GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(1));
3690  EXPECT_NE(instance3, instance1);
3691  EXPECT_FALSE(instance3->IsRelatedSiteInstance(instance1));
3692
3693  // The max page ID map should be copied over and updated with the max page ID
3694  // from the current tab.
3695  EXPECT_EQ(1, other_contents->GetMaxPageIDForSiteInstance(instance1));
3696  EXPECT_EQ(0, other_contents->GetMaxPageIDForSiteInstance(instance3));
3697}
3698
3699// Tests CopyStateFromAndPrune with 3 urls in source, 1 in dest, when the max
3700// entry count is 3 and replace_entry is true.  We should not prune entries.
3701TEST_F(NavigationControllerTest, CopyStateFromAndPruneMaxEntriesReplaceEntry) {
3702  NavigationControllerImpl& controller = controller_impl();
3703  size_t original_count = NavigationControllerImpl::max_entry_count();
3704  const int kMaxEntryCount = 3;
3705
3706  NavigationControllerImpl::set_max_entry_count_for_testing(kMaxEntryCount);
3707
3708  const GURL url1("http://foo/1");
3709  const GURL url2("http://foo/2");
3710  const GURL url3("http://foo/3");
3711  const GURL url4("http://foo/4");
3712
3713  // Create a PrunedListener to observe prune notifications.
3714  PrunedListener listener(&controller);
3715
3716  NavigateAndCommit(url1);
3717  NavigateAndCommit(url2);
3718  NavigateAndCommit(url3);
3719
3720  scoped_ptr<TestWebContents> other_contents(
3721      static_cast<TestWebContents*>(CreateTestWebContents()));
3722  NavigationControllerImpl& other_controller = other_contents->GetController();
3723  other_contents->NavigateAndCommit(url4);
3724  other_contents->ExpectSetHistoryLengthAndPrune(
3725      GetSiteInstanceFromEntry(other_controller.GetEntryAtIndex(0)), 2,
3726      other_controller.GetEntryAtIndex(0)->GetPageID());
3727  other_controller.CopyStateFromAndPrune(&controller, true);
3728
3729  // We should have received no pruned notification.
3730  EXPECT_EQ(0, listener.notification_count_);
3731
3732  // other_controller should now contain only 3 urls: url1, url2 and url4.
3733
3734  ASSERT_EQ(3, other_controller.GetEntryCount());
3735
3736  ASSERT_EQ(2, other_controller.GetCurrentEntryIndex());
3737
3738  EXPECT_EQ(url1, other_controller.GetEntryAtIndex(0)->GetURL());
3739  EXPECT_EQ(url2, other_controller.GetEntryAtIndex(1)->GetURL());
3740  EXPECT_EQ(url4, other_controller.GetEntryAtIndex(2)->GetURL());
3741  EXPECT_EQ(0, other_controller.GetEntryAtIndex(0)->GetPageID());
3742  EXPECT_EQ(1, other_controller.GetEntryAtIndex(1)->GetPageID());
3743  EXPECT_EQ(0, other_controller.GetEntryAtIndex(2)->GetPageID());
3744
3745  NavigationControllerImpl::set_max_entry_count_for_testing(original_count);
3746}
3747
3748// Tests that we can navigate to the restored entries
3749// imported by CopyStateFromAndPrune.
3750TEST_F(NavigationControllerTest, CopyRestoredStateAndNavigate) {
3751  const GURL kRestoredUrls[] = {
3752    GURL("http://site1.com"),
3753    GURL("http://site2.com"),
3754  };
3755  const GURL kInitialUrl("http://site3.com");
3756
3757  std::vector<NavigationEntry*> entries;
3758  for (size_t i = 0; i < arraysize(kRestoredUrls); ++i) {
3759    NavigationEntry* entry = NavigationControllerImpl::CreateNavigationEntry(
3760        kRestoredUrls[i], Referrer(), PAGE_TRANSITION_RELOAD, false,
3761        std::string(), browser_context());
3762    entry->SetPageID(static_cast<int>(i));
3763    entries.push_back(entry);
3764  }
3765
3766  // Create a WebContents with restored entries.
3767  scoped_ptr<TestWebContents> source_contents(
3768      static_cast<TestWebContents*>(CreateTestWebContents()));
3769  NavigationControllerImpl& source_controller =
3770      source_contents->GetController();
3771  source_controller.Restore(
3772      entries.size() - 1,
3773      NavigationController::RESTORE_LAST_SESSION_EXITED_CLEANLY,
3774      &entries);
3775  ASSERT_EQ(0u, entries.size());
3776  source_controller.LoadIfNecessary();
3777  source_contents->CommitPendingNavigation();
3778
3779  // Load a page, then copy state from |source_contents|.
3780  NavigateAndCommit(kInitialUrl);
3781  contents()->ExpectSetHistoryLengthAndPrune(
3782      GetSiteInstanceFromEntry(controller_impl().GetEntryAtIndex(0)), 2,
3783      controller_impl().GetEntryAtIndex(0)->GetPageID());
3784  controller_impl().CopyStateFromAndPrune(&source_controller, false);
3785  ASSERT_EQ(3, controller_impl().GetEntryCount());
3786
3787  // Go back to the first entry one at a time and
3788  // verify that it works as expected.
3789  EXPECT_EQ(2, controller_impl().GetCurrentEntryIndex());
3790  EXPECT_EQ(kInitialUrl, controller_impl().GetActiveEntry()->GetURL());
3791
3792  controller_impl().GoBack();
3793  contents()->CommitPendingNavigation();
3794  EXPECT_EQ(1, controller_impl().GetCurrentEntryIndex());
3795  EXPECT_EQ(kRestoredUrls[1], controller_impl().GetActiveEntry()->GetURL());
3796
3797  controller_impl().GoBack();
3798  contents()->CommitPendingNavigation();
3799  EXPECT_EQ(0, controller_impl().GetCurrentEntryIndex());
3800  EXPECT_EQ(kRestoredUrls[0], controller_impl().GetActiveEntry()->GetURL());
3801}
3802
3803// Tests that navigations initiated from the page (with the history object)
3804// work as expected, creating pending entries.
3805TEST_F(NavigationControllerTest, HistoryNavigate) {
3806  NavigationControllerImpl& controller = controller_impl();
3807  const GURL url1("http://foo/1");
3808  const GURL url2("http://foo/2");
3809  const GURL url3("http://foo/3");
3810
3811  NavigateAndCommit(url1);
3812  NavigateAndCommit(url2);
3813  NavigateAndCommit(url3);
3814  controller.GoBack();
3815  contents()->CommitPendingNavigation();
3816
3817  // Simulate the page calling history.back(). It should create a pending entry.
3818  contents()->OnGoToEntryAtOffset(-1);
3819  EXPECT_EQ(0, controller.GetPendingEntryIndex());
3820  // The actual cross-navigation is suspended until the current RVH tells us
3821  // it unloaded, simulate that.
3822  contents()->ProceedWithCrossSiteNavigation();
3823  // Also make sure we told the page to navigate.
3824  const IPC::Message* message =
3825      process()->sink().GetFirstMessageMatching(FrameMsg_Navigate::ID);
3826  ASSERT_TRUE(message != NULL);
3827  Tuple1<FrameMsg_Navigate_Params> nav_params;
3828  FrameMsg_Navigate::Read(message, &nav_params);
3829  EXPECT_EQ(url1, nav_params.a.url);
3830  process()->sink().ClearMessages();
3831
3832  // Now test history.forward()
3833  contents()->OnGoToEntryAtOffset(2);
3834  EXPECT_EQ(2, controller.GetPendingEntryIndex());
3835  // The actual cross-navigation is suspended until the current RVH tells us
3836  // it unloaded, simulate that.
3837  contents()->ProceedWithCrossSiteNavigation();
3838  message = process()->sink().GetFirstMessageMatching(FrameMsg_Navigate::ID);
3839  ASSERT_TRUE(message != NULL);
3840  FrameMsg_Navigate::Read(message, &nav_params);
3841  EXPECT_EQ(url3, nav_params.a.url);
3842  process()->sink().ClearMessages();
3843
3844  controller.DiscardNonCommittedEntries();
3845
3846  // Make sure an extravagant history.go() doesn't break.
3847  contents()->OnGoToEntryAtOffset(120);  // Out of bounds.
3848  EXPECT_EQ(-1, controller.GetPendingEntryIndex());
3849  message = process()->sink().GetFirstMessageMatching(FrameMsg_Navigate::ID);
3850  EXPECT_TRUE(message == NULL);
3851}
3852
3853// Test call to PruneAllButLastCommitted for the only entry.
3854TEST_F(NavigationControllerTest, PruneAllButLastCommittedForSingle) {
3855  NavigationControllerImpl& controller = controller_impl();
3856  const GURL url1("http://foo1");
3857  NavigateAndCommit(url1);
3858
3859  contents()->ExpectSetHistoryLengthAndPrune(
3860      GetSiteInstanceFromEntry(controller.GetEntryAtIndex(0)), 0,
3861      controller.GetEntryAtIndex(0)->GetPageID());
3862
3863  controller.PruneAllButLastCommitted();
3864
3865  EXPECT_EQ(-1, controller.GetPendingEntryIndex());
3866  EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url1);
3867}
3868
3869// Test call to PruneAllButLastCommitted for first entry.
3870TEST_F(NavigationControllerTest, PruneAllButLastCommittedForFirst) {
3871  NavigationControllerImpl& controller = controller_impl();
3872  const GURL url1("http://foo/1");
3873  const GURL url2("http://foo/2");
3874  const GURL url3("http://foo/3");
3875
3876  NavigateAndCommit(url1);
3877  NavigateAndCommit(url2);
3878  NavigateAndCommit(url3);
3879  controller.GoBack();
3880  controller.GoBack();
3881  contents()->CommitPendingNavigation();
3882
3883  contents()->ExpectSetHistoryLengthAndPrune(
3884      GetSiteInstanceFromEntry(controller.GetEntryAtIndex(0)), 0,
3885      controller.GetEntryAtIndex(0)->GetPageID());
3886
3887  controller.PruneAllButLastCommitted();
3888
3889  EXPECT_EQ(-1, controller.GetPendingEntryIndex());
3890  EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url1);
3891}
3892
3893// Test call to PruneAllButLastCommitted for intermediate entry.
3894TEST_F(NavigationControllerTest, PruneAllButLastCommittedForIntermediate) {
3895  NavigationControllerImpl& controller = controller_impl();
3896  const GURL url1("http://foo/1");
3897  const GURL url2("http://foo/2");
3898  const GURL url3("http://foo/3");
3899
3900  NavigateAndCommit(url1);
3901  NavigateAndCommit(url2);
3902  NavigateAndCommit(url3);
3903  controller.GoBack();
3904  contents()->CommitPendingNavigation();
3905
3906  contents()->ExpectSetHistoryLengthAndPrune(
3907      GetSiteInstanceFromEntry(controller.GetEntryAtIndex(1)), 0,
3908      controller.GetEntryAtIndex(1)->GetPageID());
3909
3910  controller.PruneAllButLastCommitted();
3911
3912  EXPECT_EQ(-1, controller.GetPendingEntryIndex());
3913  EXPECT_EQ(controller.GetEntryAtIndex(0)->GetURL(), url2);
3914}
3915
3916// Test call to PruneAllButLastCommitted for a pending entry that is not yet in
3917// the list of entries.
3918TEST_F(NavigationControllerTest, PruneAllButLastCommittedForPendingNotInList) {
3919  NavigationControllerImpl& controller = controller_impl();
3920  const GURL url1("http://foo/1");
3921  const GURL url2("http://foo/2");
3922  const GURL url3("http://foo/3");
3923
3924  NavigateAndCommit(url1);
3925  NavigateAndCommit(url2);
3926
3927  // Create a pending entry that is not in the entry list.
3928  controller.LoadURL(
3929      url3, Referrer(), PAGE_TRANSITION_TYPED, std::string());
3930  EXPECT_TRUE(controller.GetPendingEntry());
3931  EXPECT_EQ(2, controller.GetEntryCount());
3932
3933  contents()->ExpectSetHistoryLengthAndPrune(
3934      NULL, 0, controller.GetPendingEntry()->GetPageID());
3935  controller.PruneAllButLastCommitted();
3936
3937  // We should only have the last committed and pending entries at this point,
3938  // and the pending entry should still not be in the entry list.
3939  EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
3940  EXPECT_EQ(url2, controller.GetEntryAtIndex(0)->GetURL());
3941  EXPECT_EQ(-1, controller.GetPendingEntryIndex());
3942  EXPECT_TRUE(controller.GetPendingEntry());
3943  EXPECT_EQ(1, controller.GetEntryCount());
3944
3945  // Try to commit the pending entry.
3946  main_test_rfh()->SendNavigate(2, url3);
3947  EXPECT_EQ(-1, controller.GetPendingEntryIndex());
3948  EXPECT_FALSE(controller.GetPendingEntry());
3949  EXPECT_EQ(2, controller.GetEntryCount());
3950  EXPECT_EQ(url3, controller.GetEntryAtIndex(1)->GetURL());
3951}
3952
3953// Test to ensure that when we do a history navigation back to the current
3954// committed page (e.g., going forward to a slow-loading page, then pressing
3955// the back button), we just stop the navigation to prevent the throbber from
3956// running continuously. Otherwise, the RenderViewHost forces the throbber to
3957// start, but WebKit essentially ignores the navigation and never sends a
3958// message to stop the throbber.
3959TEST_F(NavigationControllerTest, StopOnHistoryNavigationToCurrentPage) {
3960  NavigationControllerImpl& controller = controller_impl();
3961  const GURL url0("http://foo/0");
3962  const GURL url1("http://foo/1");
3963
3964  NavigateAndCommit(url0);
3965  NavigateAndCommit(url1);
3966
3967  // Go back to the original page, then forward to the slow page, then back
3968  controller.GoBack();
3969  contents()->CommitPendingNavigation();
3970
3971  controller.GoForward();
3972  EXPECT_EQ(1, controller.GetPendingEntryIndex());
3973
3974  controller.GoBack();
3975  EXPECT_EQ(-1, controller.GetPendingEntryIndex());
3976}
3977
3978TEST_F(NavigationControllerTest, IsInitialNavigation) {
3979  NavigationControllerImpl& controller = controller_impl();
3980  TestNotificationTracker notifications;
3981  RegisterForAllNavNotifications(&notifications, &controller);
3982
3983  // Initial state.
3984  EXPECT_TRUE(controller.IsInitialNavigation());
3985
3986  // After commit, it stays false.
3987  const GURL url1("http://foo1");
3988  main_test_rfh()->SendNavigate(0, url1);
3989  EXPECT_EQ(1U, navigation_entry_committed_counter_);
3990  navigation_entry_committed_counter_ = 0;
3991  EXPECT_FALSE(controller.IsInitialNavigation());
3992
3993  // After starting a new navigation, it stays false.
3994  const GURL url2("http://foo2");
3995  controller.LoadURL(
3996      url2, Referrer(), PAGE_TRANSITION_TYPED, std::string());
3997}
3998
3999// Check that the favicon is not reused across a client redirect.
4000// (crbug.com/28515)
4001TEST_F(NavigationControllerTest, ClearFaviconOnRedirect) {
4002  const GURL kPageWithFavicon("http://withfavicon.html");
4003  const GURL kPageWithoutFavicon("http://withoutfavicon.html");
4004  const GURL kIconURL("http://withfavicon.ico");
4005  const gfx::Image kDefaultFavicon = FaviconStatus().image;
4006
4007  NavigationControllerImpl& controller = controller_impl();
4008  TestNotificationTracker notifications;
4009  RegisterForAllNavNotifications(&notifications, &controller);
4010
4011  main_test_rfh()->SendNavigate(0, kPageWithFavicon);
4012  EXPECT_EQ(1U, navigation_entry_committed_counter_);
4013  navigation_entry_committed_counter_ = 0;
4014
4015  NavigationEntry* entry = controller.GetLastCommittedEntry();
4016  EXPECT_TRUE(entry);
4017  EXPECT_EQ(kPageWithFavicon, entry->GetURL());
4018
4019  // Simulate Chromium having set the favicon for |kPageWithFavicon|.
4020  content::FaviconStatus& favicon_status = entry->GetFavicon();
4021  favicon_status.image = CreateImage(SK_ColorWHITE);
4022  favicon_status.url = kIconURL;
4023  favicon_status.valid = true;
4024  EXPECT_FALSE(DoImagesMatch(kDefaultFavicon, entry->GetFavicon().image));
4025
4026  main_test_rfh()->SendNavigateWithTransition(
4027      0,  // same page ID.
4028      kPageWithoutFavicon,
4029      PAGE_TRANSITION_CLIENT_REDIRECT);
4030  EXPECT_EQ(1U, navigation_entry_committed_counter_);
4031  navigation_entry_committed_counter_ = 0;
4032
4033  entry = controller.GetLastCommittedEntry();
4034  EXPECT_TRUE(entry);
4035  EXPECT_EQ(kPageWithoutFavicon, entry->GetURL());
4036
4037  EXPECT_TRUE(DoImagesMatch(kDefaultFavicon, entry->GetFavicon().image));
4038}
4039
4040// Check that the favicon is not cleared for NavigationEntries which were
4041// previously navigated to.
4042TEST_F(NavigationControllerTest, BackNavigationDoesNotClearFavicon) {
4043  const GURL kUrl1("http://www.a.com/1");
4044  const GURL kUrl2("http://www.a.com/2");
4045  const GURL kIconURL("http://www.a.com/1/favicon.ico");
4046
4047  NavigationControllerImpl& controller = controller_impl();
4048  TestNotificationTracker notifications;
4049  RegisterForAllNavNotifications(&notifications, &controller);
4050
4051  main_test_rfh()->SendNavigate(0, kUrl1);
4052  EXPECT_EQ(1U, navigation_entry_committed_counter_);
4053  navigation_entry_committed_counter_ = 0;
4054
4055  // Simulate Chromium having set the favicon for |kUrl1|.
4056  gfx::Image favicon_image = CreateImage(SK_ColorWHITE);
4057  content::NavigationEntry* entry = controller.GetLastCommittedEntry();
4058  EXPECT_TRUE(entry);
4059  content::FaviconStatus& favicon_status = entry->GetFavicon();
4060  favicon_status.image = favicon_image;
4061  favicon_status.url = kIconURL;
4062  favicon_status.valid = true;
4063
4064  // Navigate to another page and go back to the original page.
4065  main_test_rfh()->SendNavigate(1, kUrl2);
4066  EXPECT_EQ(1U, navigation_entry_committed_counter_);
4067  navigation_entry_committed_counter_ = 0;
4068  main_test_rfh()->SendNavigateWithTransition(
4069      0,
4070      kUrl1,
4071      PAGE_TRANSITION_FORWARD_BACK);
4072  EXPECT_EQ(1U, navigation_entry_committed_counter_);
4073  navigation_entry_committed_counter_ = 0;
4074
4075  // Verify that the favicon for the page at |kUrl1| was not cleared.
4076  entry = controller.GetEntryAtIndex(0);
4077  EXPECT_TRUE(entry);
4078  EXPECT_EQ(kUrl1, entry->GetURL());
4079  EXPECT_TRUE(DoImagesMatch(favicon_image, entry->GetFavicon().image));
4080}
4081
4082// The test crashes on android: http://crbug.com/170449
4083#if defined(OS_ANDROID)
4084#define MAYBE_PurgeScreenshot DISABLED_PurgeScreenshot
4085#else
4086#define MAYBE_PurgeScreenshot PurgeScreenshot
4087#endif
4088// Tests that screenshot are purged correctly.
4089TEST_F(NavigationControllerTest, MAYBE_PurgeScreenshot) {
4090  NavigationControllerImpl& controller = controller_impl();
4091
4092  NavigationEntryImpl* entry;
4093
4094  // Navigate enough times to make sure that some screenshots are purged.
4095  for (int i = 0; i < 12; ++i) {
4096    const GURL url(base::StringPrintf("http://foo%d/", i));
4097    NavigateAndCommit(url);
4098    EXPECT_EQ(i, controller.GetCurrentEntryIndex());
4099  }
4100
4101  MockScreenshotManager* screenshot_manager =
4102      new MockScreenshotManager(&controller);
4103  controller.SetScreenshotManager(screenshot_manager);
4104  for (int i = 0; i < controller.GetEntryCount(); ++i) {
4105    entry = NavigationEntryImpl::FromNavigationEntry(
4106        controller.GetEntryAtIndex(i));
4107    screenshot_manager->TakeScreenshotFor(entry);
4108    EXPECT_TRUE(entry->screenshot().get());
4109  }
4110
4111  NavigateAndCommit(GURL("https://foo/"));
4112  EXPECT_EQ(13, controller.GetEntryCount());
4113  entry = NavigationEntryImpl::FromNavigationEntry(
4114      controller.GetEntryAtIndex(11));
4115  screenshot_manager->TakeScreenshotFor(entry);
4116
4117  for (int i = 0; i < 2; ++i) {
4118    entry = NavigationEntryImpl::FromNavigationEntry(
4119        controller.GetEntryAtIndex(i));
4120    EXPECT_FALSE(entry->screenshot().get()) << "Screenshot " << i
4121                                            << " not purged";
4122  }
4123
4124  for (int i = 2; i < controller.GetEntryCount() - 1; ++i) {
4125    entry = NavigationEntryImpl::FromNavigationEntry(
4126        controller.GetEntryAtIndex(i));
4127    EXPECT_TRUE(entry->screenshot().get()) << "Screenshot not found for " << i;
4128  }
4129
4130  // Navigate to index 5 and then try to assign screenshot to all entries.
4131  controller.GoToIndex(5);
4132  contents()->CommitPendingNavigation();
4133  EXPECT_EQ(5, controller.GetCurrentEntryIndex());
4134  for (int i = 0; i < controller.GetEntryCount() - 1; ++i) {
4135    entry = NavigationEntryImpl::FromNavigationEntry(
4136        controller.GetEntryAtIndex(i));
4137    screenshot_manager->TakeScreenshotFor(entry);
4138  }
4139
4140  for (int i = 10; i <= 12; ++i) {
4141    entry = NavigationEntryImpl::FromNavigationEntry(
4142        controller.GetEntryAtIndex(i));
4143    EXPECT_FALSE(entry->screenshot().get()) << "Screenshot " << i
4144                                            << " not purged";
4145    screenshot_manager->TakeScreenshotFor(entry);
4146  }
4147
4148  // Navigate to index 7 and assign screenshot to all entries.
4149  controller.GoToIndex(7);
4150  contents()->CommitPendingNavigation();
4151  EXPECT_EQ(7, controller.GetCurrentEntryIndex());
4152  for (int i = 0; i < controller.GetEntryCount() - 1; ++i) {
4153    entry = NavigationEntryImpl::FromNavigationEntry(
4154        controller.GetEntryAtIndex(i));
4155    screenshot_manager->TakeScreenshotFor(entry);
4156  }
4157
4158  for (int i = 0; i < 2; ++i) {
4159    entry = NavigationEntryImpl::FromNavigationEntry(
4160        controller.GetEntryAtIndex(i));
4161    EXPECT_FALSE(entry->screenshot().get()) << "Screenshot " << i
4162                                            << " not purged";
4163  }
4164
4165  // Clear all screenshots.
4166  EXPECT_EQ(13, controller.GetEntryCount());
4167  EXPECT_EQ(10, screenshot_manager->GetScreenshotCount());
4168  controller.ClearAllScreenshots();
4169  EXPECT_EQ(0, screenshot_manager->GetScreenshotCount());
4170  for (int i = 0; i < controller.GetEntryCount(); ++i) {
4171    entry = NavigationEntryImpl::FromNavigationEntry(
4172        controller.GetEntryAtIndex(i));
4173    EXPECT_FALSE(entry->screenshot().get()) << "Screenshot " << i
4174                                            << " not cleared";
4175  }
4176}
4177
4178// Test that the navigation controller clears its session history when a
4179// navigation commits with the clear history list flag set.
4180TEST_F(NavigationControllerTest, ClearHistoryList) {
4181  const GURL url1("http://foo1");
4182  const GURL url2("http://foo2");
4183  const GURL url3("http://foo3");
4184  const GURL url4("http://foo4");
4185
4186  NavigationControllerImpl& controller = controller_impl();
4187
4188  // Create a session history with three entries, second entry is active.
4189  NavigateAndCommit(url1);
4190  NavigateAndCommit(url2);
4191  NavigateAndCommit(url3);
4192  controller.GoBack();
4193  contents()->CommitPendingNavigation();
4194  EXPECT_EQ(3, controller.GetEntryCount());
4195  EXPECT_EQ(1, controller.GetCurrentEntryIndex());
4196
4197  // Create a new pending navigation, and indicate that the session history
4198  // should be cleared.
4199  NavigationController::LoadURLParams params(url4);
4200  params.should_clear_history_list = true;
4201  controller.LoadURLWithParams(params);
4202
4203  // Verify that the pending entry correctly indicates that the session history
4204  // should be cleared.
4205  NavigationEntryImpl* entry =
4206      NavigationEntryImpl::FromNavigationEntry(
4207          controller.GetPendingEntry());
4208  ASSERT_TRUE(entry);
4209  EXPECT_TRUE(entry->should_clear_history_list());
4210
4211  // Assume that the RV correctly cleared its history and commit the navigation.
4212  static_cast<TestRenderViewHost*>(contents()->GetPendingRenderViewHost())->
4213      set_simulate_history_list_was_cleared(true);
4214  contents()->CommitPendingNavigation();
4215
4216  // Verify that the NavigationController's session history was correctly
4217  // cleared.
4218  EXPECT_EQ(1, controller.GetEntryCount());
4219  EXPECT_EQ(0, controller.GetCurrentEntryIndex());
4220  EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
4221  EXPECT_EQ(-1, controller.GetPendingEntryIndex());
4222  EXPECT_FALSE(controller.CanGoBack());
4223  EXPECT_FALSE(controller.CanGoForward());
4224  EXPECT_EQ(url4, controller.GetVisibleEntry()->GetURL());
4225}
4226
4227}  // namespace content
4228