summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/navigation_controller.cc28
-rw-r--r--chrome/browser/navigation_controller.h19
-rw-r--r--chrome/browser/navigation_controller_unittest.cc49
-rw-r--r--chrome/browser/session_service.cc90
-rw-r--r--chrome/browser/session_service.h15
-rw-r--r--chrome/browser/session_service_unittest.cc60
-rw-r--r--chrome/common/notification_types.h11
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