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(&params, 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(&params, 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(&params, 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(&params, 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(&params, 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(&params, 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(&params, 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(&params, 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(&params, 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(&params, 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