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