// Copyright 2008, Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "base/file_util.h" #include "base/path_service.h" #include "base/string_util.h" #include "chrome/browser/browser_type.h" #include "chrome/browser/navigation_controller.h" #include "chrome/browser/navigation_entry.h" #include "chrome/browser/profile_manager.h" #include "chrome/browser/history/history.h" #include "chrome/browser/session_service_test_helper.h" #include "chrome/browser/tab_contents.h" #include "chrome/browser/tab_contents_delegate.h" #include "chrome/browser/tab_contents_factory.h" #include "chrome/common/stl_util-inl.h" #include "chrome/test/testing_profile.h" #include "net/base/net_util.h" #include "testing/gtest/include/gtest/gtest.h" namespace { // TODO(darin): come up with a better way to define these integers // TODO(acw): we should have a real dynamic factory for content types. // That way we could have several implementation of // TabContents::CreateWithType(). Once this is done we'll be able to // have a unit test for NavigationController::Clone() const TabContentsType kTestContentsType1 = static_cast(TAB_CONTENTS_NUM_TYPES + 1); const TabContentsType kTestContentsType2 = static_cast(TAB_CONTENTS_NUM_TYPES + 2); class TestContents : public TabContents { public: BEGIN_MSG_MAP(TestContents) END_MSG_MAP() TestContents(TabContentsType type) : TabContents(type) { } // Just record the navigation so it can be checked by the test case bool Navigate(const NavigationEntry& entry, bool reload) { pending_entry_.reset(new NavigationEntry(entry)); return true; } void CompleteNavigation(int page_id) { DCHECK(pending_entry_.get()); pending_entry_->SetPageID(page_id); DidNavigateToEntry(pending_entry_.get()); controller()->SyncSessionWithEntryByPageID(type(), NULL, page_id); pending_entry_.release(); } NavigationEntry* pending_entry() const { return pending_entry_.get(); } void set_pending_entry(NavigationEntry* e) { pending_entry_.reset(e); } private: scoped_ptr pending_entry_; }; class TestContentsFactory : public TabContentsFactory { public: TestContentsFactory(TabContentsType type, const char* scheme) : type_(type), scheme_(scheme) { } virtual TabContents* CreateInstance() { return new TestContents(type_); } virtual bool CanHandleURL(const GURL& url) { return url.SchemeIs(scheme_); } private: TabContentsType type_; const char* scheme_; }; TestContentsFactory factory1(kTestContentsType1, "test1"); TestContentsFactory factory2(kTestContentsType2, "test2"); class NavigationControllerTest : public testing::Test, public TabContentsDelegate { public: NavigationControllerTest() : contents(NULL), profile(NULL) { } ~NavigationControllerTest() { delete profile; } // testing::Test methods: virtual void SetUp() { TabContents::RegisterFactory(kTestContentsType1, &factory1); TabContents::RegisterFactory(kTestContentsType2, &factory2); if (!profile) profile = new TestingProfile(); contents = new TestContents(kTestContentsType1); contents->set_delegate(this); contents->CreateView(::GetDesktopWindow(), gfx::Rect()); contents->SetupController(profile); } virtual void TearDown() { // Make sure contents is valid. NavigationControllerHistoryTest ends up // resetting this before TearDown is invoked. if (contents) ClearContents(); } void ClearContents() { contents->set_delegate(NULL); contents->CloseContents(); contents = NULL; TabContents::RegisterFactory(kTestContentsType1, NULL); TabContents::RegisterFactory(kTestContentsType2, NULL); } // TabContentsDelegate methods (only care about ReplaceContents): virtual void OpenURLFromTab(TabContents*, const GURL&, WindowOpenDisposition, PageTransition::Type) {} virtual void NavigationStateChanged(const TabContents*, unsigned flags) {} virtual void ReplaceContents(TabContents* source, TabContents* new_contents) { contents->set_delegate(NULL); contents = static_cast(new_contents); contents->set_delegate(this); } virtual void AddNewContents(TabContents*, TabContents*, WindowOpenDisposition, const gfx::Rect&, bool user_gesture) {} virtual void ActivateContents(TabContents*) {} virtual void LoadingStateChanged(TabContents*) {} virtual void NavigateToPage(TabContents*, const GURL&, PageTransition::Type) {} virtual void CloseContents(TabContents*) {} virtual void MoveContents(TabContents*, const gfx::Rect&) {} virtual bool IsPopup(TabContents*) { return false; } virtual void ToolbarSizeChanged(TabContents* source, bool is_animating) {} virtual void URLStarredChanged(TabContents* source, bool starred) {} virtual void UpdateTargetURL(TabContents* source, const GURL& url) {}; TestContents* contents; Profile* profile; }; class NavigationControllerHistoryTest : public NavigationControllerTest { public: NavigationControllerHistoryTest() : profile_manager_(NULL), url0("test1:foo1"), url1("test1:foo1"), url2("test1:foo1") { } virtual ~NavigationControllerHistoryTest() { // Prevent our base class from deleting the profile since profile's // lifetime is managed by profile_manager_. profile = NULL; STLDeleteElements(&windows_); } virtual void SetUp() { // Calculate the path for a scratch profile, and make sure it's empty. ASSERT_TRUE(PathService::Get(base::DIR_TEMP, &test_dir_)); file_util::AppendToPath(&test_dir_, L"NavigationControllerTest"); profile_path_ = test_dir_; file_util::AppendToPath(&profile_path_, L"New Profile"); file_util::Delete(test_dir_, true); CreateDirectory(test_dir_.c_str(), NULL); // Create a profile. profile_manager_ = new ProfileManager(); profile = ProfileManager::CreateProfile(profile_path_, L"New Profile", L"new-profile", L""); ASSERT_TRUE(profile); profile_manager_->AddProfile(profile); // Do the super thing. Notice that the above code sets profile, profile is // used in NavigationControllerTest::SetUp(), hence it now. NavigationControllerTest::SetUp(); // Force the session service to be created. SessionService* service = profile->GetSessionService(); service->SetWindowType(window_id, BrowserType::TABBED_BROWSER); service->SetWindowBounds(window_id, gfx::Rect(0, 1, 2, 3), false); service->SetTabIndexInWindow(window_id, contents->controller()->session_id(), 0); contents->controller()->SetWindowID(window_id); } virtual void TearDown() { NavigationControllerTest::TearDown(); helper_.set_service(NULL); // Make sure we wait for history to shut down before continuing. The task // we add will cause our message loop to quit once it is destroyed. HistoryService* history = profile->GetHistoryService(Profile::IMPLICIT_ACCESS); history->SetOnBackendDestroyTask(new MessageLoop::QuitTask); delete profile_manager_; MessageLoop::current()->Run(); ASSERT_TRUE(file_util::Delete(test_dir_, true)); ASSERT_FALSE(file_util::PathExists(test_dir_)); } // Deletes the current profile manager and creates a new one. Indirectly this // shuts down the history database and reopens it. void ReopenDatabase() { ClearContents(); helper_.set_service(NULL); delete profile_manager_; profile_manager_ = new ProfileManager(); profile_manager_->AddProfileByPath(profile_path_); profile = profile_manager_->GetProfileByPath(profile_path_); helper_.set_service(profile->GetSessionService()); } void GetLastSession() { Profile* profile = contents->profile(); profile->GetSessionService()->TabClosed( contents->controller()->window_id(), contents->controller()->session_id()); ReopenDatabase(); Time close_time; helper_.ReadWindows(&windows_); } CancelableRequestConsumer consumer; // URLs for testing. const GURL url0; const GURL url1; const GURL url2; std::vector windows_; SessionID window_id; SessionServiceTestHelper helper_; private: ProfileManager* profile_manager_; std::wstring test_dir_; std::wstring profile_path_; }; } // namespace TEST_F(NavigationControllerTest, Defaults) { EXPECT_TRUE(contents->is_active()); EXPECT_TRUE(contents->controller()); EXPECT_FALSE(contents->controller()->GetPendingEntry()); EXPECT_FALSE(contents->controller()->GetLastCommittedEntry()); EXPECT_EQ(contents->controller()->GetPendingEntryIndex(), -1); EXPECT_EQ(contents->controller()->GetLastCommittedEntryIndex(), -1); EXPECT_EQ(contents->controller()->GetEntryCount(), 0); EXPECT_FALSE(contents->controller()->CanGoBack()); EXPECT_FALSE(contents->controller()->CanGoForward()); EXPECT_FALSE(contents->controller()->CanStop()); } TEST_F(NavigationControllerTest, LoadURL) { const GURL url1("test1:foo1"); const GURL url2("test1:foo2"); contents->controller()->LoadURL(url1, PageTransition::TYPED); // the load should now be pending EXPECT_TRUE(contents->pending_entry()); EXPECT_EQ(contents->controller()->GetEntryCount(), 0); EXPECT_EQ(contents->controller()->GetLastCommittedEntryIndex(), -1); EXPECT_EQ(contents->controller()->GetPendingEntryIndex(), -1); EXPECT_FALSE(contents->controller()->GetLastCommittedEntry()); EXPECT_TRUE(contents->controller()->GetPendingEntry()); EXPECT_FALSE(contents->controller()->CanGoBack()); EXPECT_FALSE(contents->controller()->CanGoForward()); EXPECT_EQ(contents->GetMaxPageID(), -1); contents->CompleteNavigation(0); // the load should now be committed EXPECT_EQ(contents->controller()->GetEntryCount(), 1); EXPECT_EQ(contents->controller()->GetLastCommittedEntryIndex(), 0); EXPECT_EQ(contents->controller()->GetPendingEntryIndex(), -1); EXPECT_TRUE(contents->controller()->GetLastCommittedEntry()); EXPECT_FALSE(contents->controller()->GetPendingEntry()); EXPECT_FALSE(contents->controller()->CanGoBack()); EXPECT_FALSE(contents->controller()->CanGoForward()); EXPECT_EQ(contents->GetMaxPageID(), 0); // load another... contents->controller()->LoadURL(url2, PageTransition::TYPED); // the load should now be pending EXPECT_TRUE(contents->pending_entry()); EXPECT_EQ(contents->controller()->GetEntryCount(), 1); EXPECT_EQ(contents->controller()->GetLastCommittedEntryIndex(), 0); EXPECT_EQ(contents->controller()->GetPendingEntryIndex(), -1); EXPECT_TRUE(contents->controller()->GetLastCommittedEntry()); EXPECT_TRUE(contents->controller()->GetPendingEntry()); // TODO(darin): maybe this should really be true? EXPECT_FALSE(contents->controller()->CanGoBack()); EXPECT_FALSE(contents->controller()->CanGoForward()); EXPECT_EQ(contents->GetMaxPageID(), 0); contents->CompleteNavigation(1); // the load should now be committed EXPECT_EQ(contents->controller()->GetEntryCount(), 2); EXPECT_EQ(contents->controller()->GetLastCommittedEntryIndex(), 1); EXPECT_EQ(contents->controller()->GetPendingEntryIndex(), -1); EXPECT_TRUE(contents->controller()->GetLastCommittedEntry()); EXPECT_FALSE(contents->controller()->GetPendingEntry()); EXPECT_TRUE(contents->controller()->CanGoBack()); EXPECT_FALSE(contents->controller()->CanGoForward()); EXPECT_EQ(contents->GetMaxPageID(), 1); } // Tests what happens when the same page is loaded again. Should not create a // new session history entry. TEST_F(NavigationControllerTest, LoadURL_SamePage) { const GURL url1("test1:foo1"); contents->controller()->LoadURL(url1, PageTransition::TYPED); contents->CompleteNavigation(0); contents->controller()->LoadURL(url1, PageTransition::TYPED); contents->CompleteNavigation(0); // should not have produced a new session history entry EXPECT_EQ(contents->controller()->GetEntryCount(), 1); EXPECT_EQ(contents->controller()->GetLastCommittedEntryIndex(), 0); EXPECT_EQ(contents->controller()->GetPendingEntryIndex(), -1); EXPECT_TRUE(contents->controller()->GetLastCommittedEntry()); EXPECT_FALSE(contents->controller()->GetPendingEntry()); EXPECT_FALSE(contents->controller()->CanGoBack()); EXPECT_FALSE(contents->controller()->CanGoForward()); } TEST_F(NavigationControllerTest, LoadURL_Discarded) { const GURL url1("test1:foo1"); const GURL url2("test1:foo2"); contents->controller()->LoadURL(url1, PageTransition::TYPED); contents->CompleteNavigation(0); contents->controller()->LoadURL(url2, PageTransition::TYPED); contents->controller()->DiscardPendingEntry(); // should not have produced a new session history entry EXPECT_EQ(contents->controller()->GetEntryCount(), 1); EXPECT_EQ(contents->controller()->GetLastCommittedEntryIndex(), 0); EXPECT_EQ(contents->controller()->GetPendingEntryIndex(), -1); EXPECT_TRUE(contents->controller()->GetLastCommittedEntry()); EXPECT_FALSE(contents->controller()->GetPendingEntry()); EXPECT_FALSE(contents->controller()->CanGoBack()); EXPECT_FALSE(contents->controller()->CanGoForward()); } TEST_F(NavigationControllerTest, Reload) { const GURL url1("test1:foo1"); contents->controller()->LoadURL(url1, PageTransition::TYPED); contents->CompleteNavigation(0); contents->controller()->Reload(); // the reload is pending... EXPECT_EQ(contents->controller()->GetEntryCount(), 1); EXPECT_EQ(contents->controller()->GetLastCommittedEntryIndex(), 0); EXPECT_EQ(contents->controller()->GetPendingEntryIndex(), 0); EXPECT_TRUE(contents->controller()->GetLastCommittedEntry()); EXPECT_TRUE(contents->controller()->GetPendingEntry()); EXPECT_FALSE(contents->controller()->CanGoBack()); EXPECT_FALSE(contents->controller()->CanGoForward()); contents->CompleteNavigation(0); // now the reload is committed... EXPECT_EQ(contents->controller()->GetEntryCount(), 1); EXPECT_EQ(contents->controller()->GetLastCommittedEntryIndex(), 0); EXPECT_EQ(contents->controller()->GetPendingEntryIndex(), -1); EXPECT_TRUE(contents->controller()->GetLastCommittedEntry()); EXPECT_FALSE(contents->controller()->GetPendingEntry()); EXPECT_FALSE(contents->controller()->CanGoBack()); EXPECT_FALSE(contents->controller()->CanGoForward()); } // Tests what happens when a reload navigation produces a new page. TEST_F(NavigationControllerTest, Reload_GeneratesNewPage) { const GURL url1("test1:foo1"); const GURL url2("test1:foo2"); contents->controller()->LoadURL(url1, PageTransition::TYPED); contents->CompleteNavigation(0); contents->controller()->Reload(); contents->pending_entry()->SetURL(url2); contents->pending_entry()->SetTransitionType(PageTransition::LINK); contents->CompleteNavigation(1); // now the reload is committed... EXPECT_EQ(contents->controller()->GetEntryCount(), 2); EXPECT_EQ(contents->controller()->GetLastCommittedEntryIndex(), 1); EXPECT_EQ(contents->controller()->GetPendingEntryIndex(), -1); EXPECT_TRUE(contents->controller()->GetLastCommittedEntry()); EXPECT_FALSE(contents->controller()->GetPendingEntry()); EXPECT_TRUE(contents->controller()->CanGoBack()); EXPECT_FALSE(contents->controller()->CanGoForward()); } // Tests what happens when we navigate back successfully TEST_F(NavigationControllerTest, Back) { const GURL url1("test1:foo1"); const GURL url2("test1:foo2"); contents->controller()->LoadURL(url1, PageTransition::TYPED); contents->CompleteNavigation(0); contents->controller()->LoadURL(url2, PageTransition::TYPED); contents->CompleteNavigation(1); contents->controller()->GoBack(); // should now have a pending navigation to go back... EXPECT_EQ(contents->controller()->GetEntryCount(), 2); EXPECT_EQ(contents->controller()->GetLastCommittedEntryIndex(), 1); EXPECT_EQ(contents->controller()->GetPendingEntryIndex(), 0); EXPECT_TRUE(contents->controller()->GetLastCommittedEntry()); EXPECT_TRUE(contents->controller()->GetPendingEntry()); EXPECT_FALSE(contents->controller()->CanGoBack()); EXPECT_TRUE(contents->controller()->CanGoForward()); contents->CompleteNavigation(0); // the back navigation completed successfully EXPECT_EQ(contents->controller()->GetEntryCount(), 2); EXPECT_EQ(contents->controller()->GetLastCommittedEntryIndex(), 0); EXPECT_EQ(contents->controller()->GetPendingEntryIndex(), -1); EXPECT_TRUE(contents->controller()->GetLastCommittedEntry()); EXPECT_FALSE(contents->controller()->GetPendingEntry()); EXPECT_FALSE(contents->controller()->CanGoBack()); EXPECT_TRUE(contents->controller()->CanGoForward()); } // Tests what happens when a back navigation produces a new page. TEST_F(NavigationControllerTest, Back_GeneratesNewPage) { const GURL url1("test1:foo1"); const GURL url2("test1:foo2"); const GURL url3("test1:foo3"); contents->controller()->LoadURL(url1, PageTransition::TYPED); contents->CompleteNavigation(0); contents->controller()->LoadURL(url2, PageTransition::TYPED); contents->CompleteNavigation(1); contents->controller()->GoBack(); // should now have a pending navigation to go back... EXPECT_EQ(contents->controller()->GetEntryCount(), 2); EXPECT_EQ(contents->controller()->GetLastCommittedEntryIndex(), 1); EXPECT_EQ(contents->controller()->GetPendingEntryIndex(), 0); EXPECT_TRUE(contents->controller()->GetLastCommittedEntry()); EXPECT_TRUE(contents->controller()->GetPendingEntry()); EXPECT_FALSE(contents->controller()->CanGoBack()); EXPECT_TRUE(contents->controller()->CanGoForward()); contents->pending_entry()->SetURL(url3); contents->pending_entry()->SetTransitionType(PageTransition::LINK); contents->CompleteNavigation(2); // the back navigation resulted in a completely new navigation. // TODO(darin): perhaps this behavior will be confusing to users? EXPECT_EQ(contents->controller()->GetEntryCount(), 3); EXPECT_EQ(contents->controller()->GetLastCommittedEntryIndex(), 2); EXPECT_EQ(contents->controller()->GetPendingEntryIndex(), -1); EXPECT_TRUE(contents->controller()->GetLastCommittedEntry()); EXPECT_FALSE(contents->controller()->GetPendingEntry()); EXPECT_TRUE(contents->controller()->CanGoBack()); EXPECT_FALSE(contents->controller()->CanGoForward()); } // Tests what happens when we navigate forward successfully TEST_F(NavigationControllerTest, Forward) { const GURL url1("test1:foo1"); const GURL url2("test1:foo2"); contents->controller()->LoadURL(url1, PageTransition::TYPED); contents->CompleteNavigation(0); contents->controller()->LoadURL(url2, PageTransition::TYPED); contents->CompleteNavigation(1); contents->controller()->GoBack(); contents->CompleteNavigation(0); contents->controller()->GoForward(); // should now have a pending navigation to go forward... EXPECT_EQ(contents->controller()->GetEntryCount(), 2); EXPECT_EQ(contents->controller()->GetLastCommittedEntryIndex(), 0); EXPECT_EQ(contents->controller()->GetPendingEntryIndex(), 1); EXPECT_TRUE(contents->controller()->GetLastCommittedEntry()); EXPECT_TRUE(contents->controller()->GetPendingEntry()); EXPECT_TRUE(contents->controller()->CanGoBack()); EXPECT_FALSE(contents->controller()->CanGoForward()); contents->CompleteNavigation(1); // the forward navigation completed successfully EXPECT_EQ(contents->controller()->GetEntryCount(), 2); EXPECT_EQ(contents->controller()->GetLastCommittedEntryIndex(), 1); EXPECT_EQ(contents->controller()->GetPendingEntryIndex(), -1); EXPECT_TRUE(contents->controller()->GetLastCommittedEntry()); EXPECT_FALSE(contents->controller()->GetPendingEntry()); EXPECT_TRUE(contents->controller()->CanGoBack()); EXPECT_FALSE(contents->controller()->CanGoForward()); } // Tests what happens when a forward navigation produces a new page. TEST_F(NavigationControllerTest, Forward_GeneratesNewPage) { const GURL url1("test1:foo1"); const GURL url2("test1:foo2"); const GURL url3("test1:foo3"); contents->controller()->LoadURL(url1, PageTransition::TYPED); contents->CompleteNavigation(0); contents->controller()->LoadURL(url2, PageTransition::TYPED); contents->CompleteNavigation(1); contents->controller()->GoBack(); contents->CompleteNavigation(0); contents->controller()->GoForward(); // should now have a pending navigation to go forward... EXPECT_EQ(contents->controller()->GetEntryCount(), 2); EXPECT_EQ(contents->controller()->GetLastCommittedEntryIndex(), 0); EXPECT_EQ(contents->controller()->GetPendingEntryIndex(), 1); EXPECT_TRUE(contents->controller()->GetLastCommittedEntry()); EXPECT_TRUE(contents->controller()->GetPendingEntry()); EXPECT_TRUE(contents->controller()->CanGoBack()); EXPECT_FALSE(contents->controller()->CanGoForward()); contents->pending_entry()->SetURL(url3); contents->pending_entry()->SetTransitionType(PageTransition::LINK); contents->CompleteNavigation(2); EXPECT_EQ(contents->controller()->GetEntryCount(), 2); EXPECT_EQ(contents->controller()->GetLastCommittedEntryIndex(), 1); EXPECT_EQ(contents->controller()->GetPendingEntryIndex(), -1); EXPECT_TRUE(contents->controller()->GetLastCommittedEntry()); EXPECT_FALSE(contents->controller()->GetPendingEntry()); EXPECT_TRUE(contents->controller()->CanGoBack()); EXPECT_FALSE(contents->controller()->CanGoForward()); } TEST_F(NavigationControllerTest, LinkClick) { const GURL url1("test1:foo1"); const GURL url2("test1:foo2"); contents->controller()->LoadURL(url1, PageTransition::TYPED); contents->CompleteNavigation(0); contents->set_pending_entry(new NavigationEntry(kTestContentsType1, NULL, 0, url2, std::wstring(), PageTransition::LINK)); contents->CompleteNavigation(1); // should not have produced a new session history entry EXPECT_EQ(contents->controller()->GetEntryCount(), 2); EXPECT_EQ(contents->controller()->GetLastCommittedEntryIndex(), 1); EXPECT_EQ(contents->controller()->GetPendingEntryIndex(), -1); EXPECT_TRUE(contents->controller()->GetLastCommittedEntry()); EXPECT_FALSE(contents->controller()->GetPendingEntry()); EXPECT_TRUE(contents->controller()->CanGoBack()); EXPECT_FALSE(contents->controller()->CanGoForward()); } TEST_F(NavigationControllerTest, DISABLED_SwitchTypes) { const GURL url1("test1:foo"); const GURL url2("test2:foo"); contents->controller()->LoadURL(url1, PageTransition::TYPED); contents->CompleteNavigation(0); TestContents* initial_contents = contents; contents->controller()->LoadURL(url2, PageTransition::TYPED); // The tab contents should have been replaced ASSERT_TRUE(initial_contents != contents); contents->CompleteNavigation(0); // A second navigation entry should have been committed even though the // PageIDs are the same. PageIDs are scoped to the tab contents type. EXPECT_EQ(contents->controller()->GetEntryCount(), 2); EXPECT_EQ(contents->controller()->GetLastCommittedEntryIndex(), 1); EXPECT_EQ(contents->controller()->GetPendingEntryIndex(), -1); EXPECT_TRUE(contents->controller()->GetLastCommittedEntry()); EXPECT_FALSE(contents->controller()->GetPendingEntry()); EXPECT_TRUE(contents->controller()->CanGoBack()); EXPECT_FALSE(contents->controller()->CanGoForward()); // Navigate back... contents->controller()->GoBack(); ASSERT_TRUE(initial_contents == contents); // switched again! contents->CompleteNavigation(0); EXPECT_EQ(contents->controller()->GetEntryCount(), 2); EXPECT_EQ(contents->controller()->GetLastCommittedEntryIndex(), 0); EXPECT_EQ(contents->controller()->GetPendingEntryIndex(), -1); EXPECT_TRUE(contents->controller()->GetLastCommittedEntry()); EXPECT_FALSE(contents->controller()->GetPendingEntry()); EXPECT_FALSE(contents->controller()->CanGoBack()); EXPECT_TRUE(contents->controller()->CanGoForward()); } // Tests what happens when we begin to navigate to a new contents type, but // then that navigation gets discarded instead. TEST_F(NavigationControllerTest, DISABLED_SwitchTypes_Discard) { const GURL url1("test1:foo"); const GURL url2("test2:foo"); contents->controller()->LoadURL(url1, PageTransition::TYPED); contents->CompleteNavigation(0); TestContents* initial_contents = contents; contents->controller()->LoadURL(url2, PageTransition::TYPED); // The tab contents should have been replaced ASSERT_TRUE(initial_contents != contents); contents->controller()->DiscardPendingEntry(); // The tab contents should have been replaced back ASSERT_TRUE(initial_contents == contents); EXPECT_EQ(contents->controller()->GetEntryCount(), 1); EXPECT_EQ(contents->controller()->GetLastCommittedEntryIndex(), 0); EXPECT_EQ(contents->controller()->GetPendingEntryIndex(), -1); EXPECT_TRUE(contents->controller()->GetLastCommittedEntry()); EXPECT_FALSE(contents->controller()->GetPendingEntry()); EXPECT_FALSE(contents->controller()->CanGoBack()); EXPECT_FALSE(contents->controller()->CanGoForward()); } // Tests that we limit the number of navigation entries created correctly. TEST_F(NavigationControllerTest, EnforceMaxNavigationCount) { const size_t kMaxEntryCount = 5; contents->controller()->max_entry_count_ = kMaxEntryCount; int url_index; char buffer[128]; // Load up to the max count, all entries should be there. for (url_index = 0; url_index < kMaxEntryCount; url_index++) { SNPrintF(buffer, 128, "test1://www.a.com/%d", url_index); contents->controller()->LoadURL(GURL(buffer), PageTransition::TYPED); contents->CompleteNavigation(url_index); } EXPECT_EQ(contents->controller()->GetEntryCount(), kMaxEntryCount); // Navigate some more. SNPrintF(buffer, 128, "test1://www.a.com/%d", url_index); contents->controller()->LoadURL(GURL(buffer), PageTransition::TYPED); contents->CompleteNavigation(url_index); url_index++; // We expect http://www.a.com/0 to be gone. EXPECT_EQ(contents->controller()->GetEntryCount(), kMaxEntryCount); EXPECT_EQ(contents->controller()->GetEntryAtIndex(0)->GetURL(), GURL("test1://www.a.com/1")); // More navigations. for (int i = 0; i < 3; i++) { SNPrintF(buffer, 128, "test1://www.a.com/%d", url_index); contents->controller()->LoadURL(GURL(buffer), PageTransition::TYPED); contents->CompleteNavigation(url_index); url_index++; } EXPECT_EQ(contents->controller()->GetEntryCount(), kMaxEntryCount); EXPECT_EQ(contents->controller()->GetEntryAtIndex(0)->GetURL(), GURL("test1://www.a.com/4")); } // A basic test case. Navigates to a single url, and make sure the history // db matches. TEST_F(NavigationControllerHistoryTest, Basic) { contents->controller()->LoadURL(url0, PageTransition::TYPED); contents->CompleteNavigation(0); GetLastSession(); helper_.AssertSingleWindowWithSingleTab(windows_, 1); helper_.AssertTabEquals(0, 0, 1, *(windows_[0]->tabs[0])); TabNavigation nav1(0, url0, std::wstring(), std::string(), PageTransition::TYPED); helper_.AssertNavigationEquals(nav1, windows_[0]->tabs[0]->navigations[0]); } // Navigates to three urls, then goes back and make sure the history database // is in sync. TEST_F(NavigationControllerHistoryTest, NavigationThenBack) { contents->controller()->LoadURL(url0, PageTransition::TYPED); contents->CompleteNavigation(0); contents->controller()->LoadURL(url1, PageTransition::TYPED); contents->CompleteNavigation(1); contents->controller()->LoadURL(url2, PageTransition::TYPED); contents->CompleteNavigation(2); contents->controller()->GoBack(); contents->CompleteNavigation(1); GetLastSession(); helper_.AssertSingleWindowWithSingleTab(windows_, 3); helper_.AssertTabEquals(0, 1, 3, *(windows_[0]->tabs[0])); TabNavigation nav(0, url0, std::wstring(), std::string(), PageTransition::TYPED); helper_.AssertNavigationEquals(nav, windows_[0]->tabs[0]->navigations[0]); nav.index = 1; nav.url = url1; helper_.AssertNavigationEquals(nav, windows_[0]->tabs[0]->navigations[1]); nav.index = 2; nav.url = url2; helper_.AssertNavigationEquals(nav, windows_[0]->tabs[0]->navigations[2]); } // Navigates to three urls, then goes back twice, then loads a new url. TEST_F(NavigationControllerHistoryTest, NavigationPruning) { contents->controller()->LoadURL(url0, PageTransition::TYPED); contents->CompleteNavigation(0); contents->controller()->LoadURL(url1, PageTransition::TYPED); contents->CompleteNavigation(1); contents->controller()->LoadURL(url2, PageTransition::TYPED); contents->CompleteNavigation(2); contents->controller()->GoBack(); contents->CompleteNavigation(1); contents->controller()->GoBack(); contents->CompleteNavigation(0); contents->controller()->LoadURL(url2, PageTransition::TYPED); contents->CompleteNavigation(3); // Now have url0, and url2. GetLastSession(); helper_.AssertSingleWindowWithSingleTab(windows_, 2); helper_.AssertTabEquals(0, 1, 2, *(windows_[0]->tabs[0])); TabNavigation nav(0, url0, std::wstring(), std::string(), PageTransition::TYPED); helper_.AssertNavigationEquals(nav, windows_[0]->tabs[0]->navigations[0]); nav.index = 1; nav.url = url2; helper_.AssertNavigationEquals(nav, windows_[0]->tabs[0]->navigations[1]); }