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