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