diff options
-rw-r--r-- | chrome/browser/navigation_controller.cc | 28 | ||||
-rw-r--r-- | chrome/browser/navigation_controller.h | 19 | ||||
-rw-r--r-- | chrome/browser/navigation_controller_unittest.cc | 49 | ||||
-rw-r--r-- | chrome/browser/session_service.cc | 90 | ||||
-rw-r--r-- | chrome/browser/session_service.h | 15 | ||||
-rw-r--r-- | chrome/browser/session_service_unittest.cc | 60 | ||||
-rw-r--r-- | chrome/common/notification_types.h | 11 |
7 files changed, 232 insertions, 40 deletions
diff --git a/chrome/browser/navigation_controller.cc b/chrome/browser/navigation_controller.cc index a0dab1b..58781be 100644 --- a/chrome/browser/navigation_controller.cc +++ b/chrome/browser/navigation_controller.cc @@ -28,11 +28,16 @@ namespace { // Invoked when entries have been pruned, or removed. For example, if the // current entries are [google, digg, yahoo], with the current entry google, // and the user types in cnet, then digg and yahoo are pruned. -void NotifyPrunedEntries(NavigationController* nav_controller) { +void NotifyPrunedEntries(NavigationController* nav_controller, + bool from_front, + int count) { + NavigationController::PrunedDetails details; + details.from_front = from_front; + details.count = count; NotificationService::current()->Notify( NOTIFY_NAV_LIST_PRUNED, Source<NavigationController>(nav_controller), - NotificationService::NoDetails()); + Details<NavigationController::PrunedDetails>(&details)); } // Ensure the given NavigationEntry has a valid state, so that WebKit does not @@ -119,9 +124,8 @@ class TabContentsCollector : public Task { // NavigationController --------------------------------------------------- -// The maximum number of entries that a navigation controller can store. // static -const static size_t kMaxEntryCount = 50; +size_t NavigationController::max_entry_count_ = 50; // static bool NavigationController::check_for_repost_ = true; @@ -164,7 +168,6 @@ NavigationController::NavigationController(TabContents* contents, pending_entry_(NULL), last_committed_entry_index_(-1), pending_entry_index_(-1), - max_entry_count_(kMaxEntryCount), active_contents_(contents), max_restored_page_id_(-1), ssl_manager_(this, NULL), @@ -184,7 +187,6 @@ NavigationController::NavigationController( pending_entry_(NULL), last_committed_entry_index_(-1), pending_entry_index_(-1), - max_entry_count_(kMaxEntryCount), active_contents_(NULL), max_restored_page_id_(-1), ssl_manager_(this, NULL), @@ -857,7 +859,7 @@ void NavigationController::RemoveLastEntryForInterstitial() { NotifyNavigationEntryCommitted(&details); } - NotifyPrunedEntries(this); + NotifyPrunedEntries(this, false, 1); } } @@ -936,18 +938,20 @@ void NavigationController::InsertEntry(NavigationEntry* entry) { // Prune any entries which are in front of the current entry. if (current_size > 0) { - bool pruned = false; + int num_pruned = 0; while (last_committed_entry_index_ < (current_size - 1)) { - pruned = true; + num_pruned++; entries_.pop_back(); current_size--; } - if (pruned) // Only notify if we did prune something. - NotifyPrunedEntries(this); + if (num_pruned > 0) // Only notify if we did prune something. + NotifyPrunedEntries(this, false, num_pruned); } - if (entries_.size() >= max_entry_count_) + if (entries_.size() >= max_entry_count_) { RemoveEntryAtIndex(0); + NotifyPrunedEntries(this, true, 1); + } entries_.push_back(linked_ptr<NavigationEntry>(entry)); last_committed_entry_index_ = static_cast<int>(entries_.size()) - 1; diff --git a/chrome/browser/navigation_controller.h b/chrome/browser/navigation_controller.h index 3380732f..f0e6096 100644 --- a/chrome/browser/navigation_controller.h +++ b/chrome/browser/navigation_controller.h @@ -81,6 +81,16 @@ class NavigationController { } }; + // Details sent for NOTIFY_NAV_LIST_PRUNED. + struct PrunedDetails { + // If true, count items were removed from the front of the list, otherwise + // count items were removed from the back of the list. + bool from_front; + + // Number of items removed. + int count; + }; + // --------------------------------------------------------------------------- NavigationController(TabContents* initial_contents, Profile* profile); @@ -331,8 +341,13 @@ class NavigationController { // testing. static void DisablePromptOnRepost(); + // Maximum number of entries before we start removing entries from the front. + static void set_max_entry_count(size_t max_entry_count) { + max_entry_count_ = max_entry_count; + } + static size_t max_entry_count() { return max_entry_count_; } + private: - FRIEND_TEST(NavigationControllerTest, EnforceMaxNavigationCount); class RestoreHelper; friend class RestoreHelper; friend class TabContents; // For invoking OnReservedPageIDRange. @@ -535,7 +550,7 @@ class NavigationController { static bool check_for_repost_; // The maximum number of entries that a navigation controller can store. - size_t max_entry_count_; + static size_t max_entry_count_; DISALLOW_COPY_AND_ASSIGN(NavigationController); }; diff --git a/chrome/browser/navigation_controller_unittest.cc b/chrome/browser/navigation_controller_unittest.cc index 0f9b2ff..3f3e087 100644 --- a/chrome/browser/navigation_controller_unittest.cc +++ b/chrome/browser/navigation_controller_unittest.cc @@ -15,6 +15,7 @@ #include "chrome/browser/tab_contents.h" #include "chrome/browser/tab_contents_delegate.h" #include "chrome/browser/tab_contents_factory.h" +#include "chrome/common/notification_registrar.h" #include "chrome/common/notification_types.h" #include "chrome/common/stl_util-inl.h" #include "chrome/test/test_notification_tracker.h" @@ -1169,11 +1170,47 @@ TEST_F(NavigationControllerTest, SwitchTypesCleanup) { contents->controller()->GetTabContents(kTestContentsType2)); } +namespace { + +// NotificationObserver implementation used in verifying we've received the +// NOTIFY_NAV_LIST_PRUNED method. +class PrunedListener : public NotificationObserver { + public: + explicit PrunedListener(NavigationController* controller) + : notification_count_(0) { + registrar_.Add(this, NOTIFY_NAV_LIST_PRUNED, + Source<NavigationController>(controller)); + } + + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + if (type == NOTIFY_NAV_LIST_PRUNED) { + notification_count_++; + details_ = *(Details<NavigationController::PrunedDetails>(details).ptr()); + } + } + + // Number of times NOTIFY_NAV_LIST_PRUNED has been observed. + int notification_count_; + + // Details from the last NOTIFY_NAV_LIST_PRUNED. + NavigationController::PrunedDetails details_; + + private: + NotificationRegistrar registrar_; + + DISALLOW_COPY_AND_ASSIGN(PrunedListener); +}; + +} + // Tests that we limit the number of navigation entries created correctly. TEST_F(NavigationControllerTest, EnforceMaxNavigationCount) { + size_t original_count = NavigationController::max_entry_count(); const size_t kMaxEntryCount = 5; - contents->controller()->max_entry_count_ = kMaxEntryCount; + NavigationController::set_max_entry_count(kMaxEntryCount); int url_index; char buffer[128]; @@ -1187,6 +1224,9 @@ TEST_F(NavigationControllerTest, EnforceMaxNavigationCount) { EXPECT_EQ(contents->controller()->GetEntryCount(), kMaxEntryCount); + // Created a PrunedListener to observe prune notifications. + PrunedListener listener(contents->controller()); + // Navigate some more. SNPrintF(buffer, 128, "test1://www.a.com/%d", url_index); GURL url(buffer); @@ -1194,6 +1234,11 @@ TEST_F(NavigationControllerTest, EnforceMaxNavigationCount) { contents->CompleteNavigationAsRenderer(url_index, url); url_index++; + // We should have got a pruned navigation. + EXPECT_EQ(1, listener.notification_count_); + EXPECT_TRUE(listener.details_.from_front); + EXPECT_EQ(1, listener.details_.count); + // We expect http://www.a.com/0 to be gone. EXPECT_EQ(contents->controller()->GetEntryCount(), kMaxEntryCount); EXPECT_EQ(contents->controller()->GetEntryAtIndex(0)->url(), @@ -1210,6 +1255,8 @@ TEST_F(NavigationControllerTest, EnforceMaxNavigationCount) { EXPECT_EQ(contents->controller()->GetEntryCount(), kMaxEntryCount); EXPECT_EQ(contents->controller()->GetEntryAtIndex(0)->url(), GURL("test1://www.a.com/4")); + + NavigationController::set_max_entry_count(original_count); } // Tests that we can do a restore and navigate to the restored entries and diff --git a/chrome/browser/session_service.cc b/chrome/browser/session_service.cc index 2694357..6b44952 100644 --- a/chrome/browser/session_service.cc +++ b/chrome/browser/session_service.cc @@ -32,12 +32,15 @@ static const SessionCommand::id_type kCommandSetTabWindow = 0; static const SessionCommand::id_type kCommandSetTabIndexInWindow = 2; static const SessionCommand::id_type kCommandTabClosed = 3; static const SessionCommand::id_type kCommandWindowClosed = 4; -static const SessionCommand::id_type kCommandTabNavigationPathPruned = 5; +static const SessionCommand::id_type + kCommandTabNavigationPathPrunedFromBack = 5; static const SessionCommand::id_type kCommandUpdateTabNavigation = 6; static const SessionCommand::id_type kCommandSetSelectedNavigationIndex = 7; static const SessionCommand::id_type kCommandSetSelectedTabInIndex = 8; static const SessionCommand::id_type kCommandSetWindowType = 9; static const SessionCommand::id_type kCommandSetWindowBounds2 = 10; +static const SessionCommand::id_type + kCommandTabNavigationPathPrunedFromFront = 11; // Max number of navigation entries in each direction we'll persist. static const int kMaxNavigationCountToPersist = 6; @@ -73,7 +76,7 @@ struct IDAndIndexPayload { typedef IDAndIndexPayload TabIndexInWindowPayload; -typedef IDAndIndexPayload TabNavigationPathPrunedPayload; +typedef IDAndIndexPayload TabNavigationPathPrunedFromBackPayload; typedef IDAndIndexPayload SelectedNavigationIndexPayload; @@ -81,6 +84,8 @@ typedef IDAndIndexPayload SelectedTabInIndexPayload; typedef IDAndIndexPayload WindowTypePayload; +typedef IDAndIndexPayload TabNavigationPathPrunedFromFrontPayload; + } // namespace // SessionID ------------------------------------------------------------------ @@ -271,17 +276,43 @@ void SessionService::SetWindowType(const SessionID& window_id, ScheduleCommand(CreateSetWindowTypeCommand(window_id, type)); } -void SessionService::TabNavigationPathPruned(const SessionID& window_id, - const SessionID& tab_id, - int index) { +void SessionService::TabNavigationPathPrunedFromBack(const SessionID& window_id, + const SessionID& tab_id, + int count) { if (!ShouldTrackChangesToWindow(window_id)) return; - TabNavigationPathPrunedPayload payload = { 0 }; + TabNavigationPathPrunedFromBackPayload payload = { 0 }; payload.id = tab_id.id(); - payload.index = index; + payload.index = count; + SessionCommand* command = + new SessionCommand(kCommandTabNavigationPathPrunedFromBack, + sizeof(payload)); + memcpy(command->contents(), &payload, sizeof(payload)); + ScheduleCommand(command); +} + +void SessionService::TabNavigationPathPrunedFromFront( + const SessionID& window_id, + const SessionID& tab_id, + int count) { + if (!ShouldTrackChangesToWindow(window_id)) + return; + + // Update the range of indices. + if (tab_to_available_range_.find(tab_id.id()) != + tab_to_available_range_.end()) { + std::pair<int, int>& range = tab_to_available_range_[tab_id.id()]; + range.first = std::max(0, range.first - count); + range.second = std::max(0, range.second - count); + } + + TabNavigationPathPrunedFromFrontPayload payload = { 0 }; + payload.id = tab_id.id(); + payload.index = count; SessionCommand* command = - new SessionCommand(kCommandTabNavigationPathPruned, sizeof(payload)); + new SessionCommand(kCommandTabNavigationPathPrunedFromFront, + sizeof(payload)); memcpy(command->contents(), &payload, sizeof(payload)); ScheduleCommand(command); } @@ -414,10 +445,19 @@ void SessionService::Observe(NotificationType type, case NOTIFY_TAB_CLOSED: TabClosed(controller->window_id(), controller->session_id()); break; - case NOTIFY_NAV_LIST_PRUNED: - TabNavigationPathPruned(controller->window_id(), controller->session_id(), - controller->GetEntryCount()); + case NOTIFY_NAV_LIST_PRUNED: { + Details<NavigationController::PrunedDetails> pruned_details(details); + if (pruned_details->from_front) { + TabNavigationPathPrunedFromFront(controller->window_id(), + controller->session_id(), + pruned_details->count); + } else { + TabNavigationPathPrunedFromBack(controller->window_id(), + controller->session_id(), + controller->GetEntryCount()); + } break; + } case NOTIFY_NAV_ENTRY_CHANGED: { Details<NavigationController::EntryChangedDetails> changed(details); UpdateTabNavigation(controller->window_id(), controller->session_id(), @@ -807,8 +847,8 @@ bool SessionService::CreateTabsAndWindows( break; } - case kCommandTabNavigationPathPruned: { - TabNavigationPathPrunedPayload payload; + case kCommandTabNavigationPathPrunedFromBack: { + TabNavigationPathPrunedFromBackPayload payload; if (!command->GetPayload(&payload, sizeof(payload))) return true; SessionTab* tab = GetTab(payload.id, tabs); @@ -818,6 +858,30 @@ bool SessionService::CreateTabsAndWindows( break; } + case kCommandTabNavigationPathPrunedFromFront: { + TabNavigationPathPrunedFromFrontPayload payload; + if (!command->GetPayload(&payload, sizeof(payload)) || + payload.index <= 0) { + return true; + } + SessionTab* tab = GetTab(payload.id, tabs); + + // Update the selected navigation index. + tab->current_navigation_index = + std::max(-1, tab->current_navigation_index - payload.index); + + // And update the index of existing navigations. + for (std::vector<TabNavigation>::iterator i = tab->navigations.begin(); + i != tab->navigations.end();) { + i->index -= payload.index; + if (i->index < 0) + i = tab->navigations.erase(i); + else + ++i; + } + break; + } + case kCommandUpdateTabNavigation: { scoped_ptr<Pickle> pickle(command->PayloadAsPickle()); if (!pickle.get()) diff --git a/chrome/browser/session_service.h b/chrome/browser/session_service.h index aa2779f..4ce39cd 100644 --- a/chrome/browser/session_service.h +++ b/chrome/browser/session_service.h @@ -236,10 +236,17 @@ class SessionService : public CancelableRequestProvider, // (should_track_changes_for_browser_type returns true). void SetWindowType(const SessionID& window_id, BrowserType::Type type); - // Removes the navigation entries for tab_id whose indices are >= index. - void TabNavigationPathPruned(const SessionID& window_id, - const SessionID& tab_id, - int index); + // Invoked when the NavigationController has removed entries from the back of + // the list. |count| gives the number of entries in the navigation controller. + void TabNavigationPathPrunedFromBack(const SessionID& window_id, + const SessionID& tab_id, + int count); + + // Invoked when the NavigationController has removed entries from the front of + // the list. |count| gives the number of entries that were removed. + void TabNavigationPathPrunedFromFront(const SessionID& window_id, + const SessionID& tab_id, + int count); // Updates the navigation entry for the specified tab. void UpdateTabNavigation(const SessionID& window_id, diff --git a/chrome/browser/session_service_unittest.cc b/chrome/browser/session_service_unittest.cc index 59379a3..4ff7a21b 100644 --- a/chrome/browser/session_service_unittest.cc +++ b/chrome/browser/session_service_unittest.cc @@ -196,7 +196,7 @@ TEST_F(SessionServiceTest, Pruning) { TabNavigation& nav = (i % 2) == 0 ? nav1 : nav2; UpdateNavigation(window_id, tab_id, nav, i, true); } - service()->TabNavigationPathPruned(window_id, tab_id, 3); + service()->TabNavigationPathPrunedFromBack(window_id, tab_id, 3); ScopedVector<SessionWindow> windows; ReadWindows(&(windows.get())); @@ -403,3 +403,61 @@ TEST_F(SessionServiceTest, IgnorePopups) { helper_.AssertNavigationEquals(nav1, tab->navigations[0]); } +// Tests pruning from the front. +TEST_F(SessionServiceTest, PruneFromFront) { + const std::string base_url("http://google.com/"); + SessionID tab_id; + + helper_.PrepareTabInWindow(window_id, tab_id, 0, true); + + // Add 5 navigations, with the 4th selected. + for (int i = 0; i < 5; ++i) { + TabNavigation nav(0, GURL(base_url + IntToString(i)), + L"a", "b", PageTransition::QUALIFIER_MASK); + UpdateNavigation(window_id, tab_id, nav, i, (i == 3)); + } + + // Prune the first two navigations from the front. + helper_.service()->TabNavigationPathPrunedFromFront(window_id, tab_id, 2); + + // Read back in. + ScopedVector<SessionWindow> windows; + ReadWindows(&(windows.get())); + + ASSERT_EQ(1, windows->size()); + ASSERT_EQ(0, windows[0]->selected_tab_index); + ASSERT_EQ(window_id.id(), windows[0]->window_id.id()); + ASSERT_EQ(1, windows[0]->tabs.size()); + + // We should be left with three navigations, the 2nd selected. + SessionTab* tab = windows[0]->tabs[0]; + ASSERT_EQ(1, tab->current_navigation_index); + EXPECT_EQ(3U, tab->navigations.size()); + EXPECT_TRUE(GURL(base_url + IntToString(2)) == tab->navigations[0].url); + EXPECT_TRUE(GURL(base_url + IntToString(3)) == tab->navigations[1].url); + EXPECT_TRUE(GURL(base_url + IntToString(4)) == tab->navigations[2].url); +} + +// Prunes from front so that we have no entries. +TEST_F(SessionServiceTest, PruneToEmpty) { + const std::string base_url("http://google.com/"); + SessionID tab_id; + + helper_.PrepareTabInWindow(window_id, tab_id, 0, true); + + // Add 5 navigations, with the 4th selected. + for (int i = 0; i < 5; ++i) { + TabNavigation nav(0, GURL(base_url + IntToString(i)), + L"a", "b", PageTransition::QUALIFIER_MASK); + UpdateNavigation(window_id, tab_id, nav, i, (i == 3)); + } + + // Prune the first two navigations from the front. + helper_.service()->TabNavigationPathPrunedFromFront(window_id, tab_id, 5); + + // Read back in. + ScopedVector<SessionWindow> windows; + ReadWindows(&(windows.get())); + + ASSERT_EQ(0, windows->size()); +} diff --git a/chrome/common/notification_types.h b/chrome/common/notification_types.h index a57d978..e55630a 100644 --- a/chrome/common/notification_types.h +++ b/chrome/common/notification_types.h @@ -49,19 +49,16 @@ enum NotificationType { NOTIFY_NAV_ENTRY_COMMITTED, // Indicates that the NavigationController given in the Source has decreased - // its back/forward list count. This is usually the result of going back and - // then doing a new navigation, meaning all the "forward" items are deleted. + // its back/forward list count by removing entries from either the front or + // back of its list. This is usually the result of going back and then doing a + // new navigation, meaning all the "forward" items are deleted. // // This normally happens as a result of a new navigation. It will be followed // by a NOTIFY_NAV_ENTRY_COMMITTED message for the new page that caused the // pruning. It could also be a result of removing an item from the list to fix // up after interstitials. // - // The details are an integer indicating the number of items pruned. - // Watch out: the NavigationController may start throwing entries away once - // the list is a certain size, so you can't use the current size of the - // navigation list to tell how many items were pruned, you have to use the - // details provided here. + // The details are NavigationController::PrunedDetails. NOTIFY_NAV_LIST_PRUNED, // Indicates that a NavigationEntry has changed. The source will be the |