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