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