Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(369)

Side by Side Diff: content/browser/web_contents/navigation_controller_impl_unittest.cc

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

Powered by Google App Engine
This is Rietveld 408576698