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