summaryrefslogtreecommitdiffstats
path: root/chrome/browser/bookmarks
diff options
context:
space:
mode:
authorsky@google.com <sky@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-10-23 16:47:41 +0000
committersky@google.com <sky@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-10-23 16:47:41 +0000
commit776e749879084abf997bed1838737fd0f8bd5a20 (patch)
treead594c86d14afa848c1fe34e6d3d44559363fd6e /chrome/browser/bookmarks
parent4b2170afea2d38cd98c7ea0e25a5c45151f7471e (diff)
downloadchromium_src-776e749879084abf997bed1838737fd0f8bd5a20.zip
chromium_src-776e749879084abf997bed1838737fd0f8bd5a20.tar.gz
chromium_src-776e749879084abf997bed1838737fd0f8bd5a20.tar.bz2
Adds models needed by the bookmark manager. Specifically a
BookmarkTableModel, which will be used to show one of the following: the children of a folder, recently bookmarked or the results of a search. And the tree model implementation that shows the folders. BUG=674 TEST=covered by unit tests Review URL: http://codereview.chromium.org/8063 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@3814 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/bookmarks')
-rw-r--r--chrome/browser/bookmarks/bookmark_folder_tree_model.cc178
-rw-r--r--chrome/browser/bookmarks/bookmark_folder_tree_model.h102
-rw-r--r--chrome/browser/bookmarks/bookmark_folder_tree_model_unittest.cc186
-rw-r--r--chrome/browser/bookmarks/bookmark_model.cc27
-rw-r--r--chrome/browser/bookmarks/bookmark_model.h13
-rw-r--r--chrome/browser/bookmarks/bookmark_table_model.cc325
-rw-r--r--chrome/browser/bookmarks/bookmark_table_model.h66
-rw-r--r--chrome/browser/bookmarks/bookmark_table_model_unittest.cc296
8 files changed, 1188 insertions, 5 deletions
diff --git a/chrome/browser/bookmarks/bookmark_folder_tree_model.cc b/chrome/browser/bookmarks/bookmark_folder_tree_model.cc
new file mode 100644
index 0000000..f365e76
--- /dev/null
+++ b/chrome/browser/bookmarks/bookmark_folder_tree_model.cc
@@ -0,0 +1,178 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/bookmarks/bookmark_folder_tree_model.h"
+
+#include "chrome/common/l10n_util.h"
+
+#include "generated_resources.h"
+
+BookmarkFolderTreeModel::BookmarkFolderTreeModel(BookmarkModel* model)
+ : views::TreeNodeModel<FolderNode>(new FolderNode(NULL)),
+ model_(model),
+ recently_bookmarked_node_(new FolderNode(NULL)),
+ search_node_(new FolderNode(NULL)){
+ recently_bookmarked_node_->SetTitle(
+ l10n_util::GetString(IDS_BOOKMARK_TREE_RECENTLY_BOOKMARKED_NODE_TITLE));
+ search_node_->SetTitle(
+ l10n_util::GetString(IDS_BOOKMARK_TREE_SEARCH_NODE_TITLE));
+ if (model_->IsLoaded())
+ AddRootChildren();
+ model_->AddObserver(this);
+}
+
+BookmarkFolderTreeModel::~BookmarkFolderTreeModel() {
+ if (model_)
+ model_->RemoveObserver(this);
+}
+
+BookmarkFolderTreeModel::NodeType BookmarkFolderTreeModel::GetNodeType(
+ views::TreeModelNode* node) {
+ if (node == recently_bookmarked_node_)
+ return RECENTLY_BOOKMARKED;
+ if (node == search_node_)
+ return SEARCH;
+ return BOOKMARK;
+}
+
+FolderNode* BookmarkFolderTreeModel::GetFolderNodeForBookmarkNode(
+ BookmarkNode* node) {
+ if (!node->is_folder())
+ return NULL;
+
+ return GetFolderNodeForBookmarkNode(AsNode(GetRoot()), node);
+}
+
+BookmarkNode* BookmarkFolderTreeModel::TreeNodeAsBookmarkNode(
+ views::TreeModelNode* node) {
+ if (GetNodeType(node) != BOOKMARK)
+ return NULL;
+ return AsNode(node)->value;
+}
+
+void BookmarkFolderTreeModel::Loaded(BookmarkModel* model) {
+ AddRootChildren();
+}
+
+void BookmarkFolderTreeModel::BookmarkModelBeingDeleted(BookmarkModel* model) {
+ DCHECK(model_);
+ model_->RemoveObserver(this);
+ model_ = NULL;
+}
+
+void BookmarkFolderTreeModel::BookmarkNodeMoved(BookmarkModel* model,
+ BookmarkNode* old_parent,
+ int old_index,
+ BookmarkNode* new_parent,
+ int new_index) {
+ BookmarkNode* moved_node = new_parent->GetChild(new_index);
+ if (!moved_node->is_folder())
+ return; // We're only showing folders, so we can ignore this.
+
+ FolderNode* moved_folder_node = GetFolderNodeForBookmarkNode(moved_node);
+ DCHECK(moved_folder_node);
+ int old_folder_index =
+ moved_folder_node->GetParent()->IndexOfChild(moved_folder_node);
+ Remove(moved_folder_node->GetParent(), old_folder_index);
+ int new_folder_index = CalculateIndexForChild(moved_node);
+ Add(GetFolderNodeForBookmarkNode(new_parent), new_folder_index,
+ moved_folder_node);
+}
+
+void BookmarkFolderTreeModel::BookmarkNodeAdded(BookmarkModel* model,
+ BookmarkNode* parent,
+ int index) {
+ BookmarkNode* new_node = parent->GetChild(index);
+ if (!new_node->is_folder())
+ return; // We're only showing folders, so we can ignore this.
+
+ int folder_index = CalculateIndexForChild(new_node);
+ Add(GetFolderNodeForBookmarkNode(parent), folder_index,
+ CreateFolderNode(new_node));
+}
+
+void BookmarkFolderTreeModel::BookmarkNodeRemoved(BookmarkModel* model,
+ BookmarkNode* parent,
+ int index,
+ BookmarkNode* node) {
+ if (!node->is_folder())
+ return; // We're only showing folders.
+
+ FolderNode* folder_node = GetFolderNodeForBookmarkNode(parent);
+ DCHECK(folder_node);
+ for (int i = 0; i < folder_node->GetChildCount(); ++i) {
+ if (folder_node->GetChild(i)->value == node) {
+ scoped_ptr<FolderNode> removed_node(Remove(folder_node, i));
+ return;
+ }
+ }
+
+ // If we get here it means a folder was removed that we didn't know about,
+ // which shouldn't happen.
+ NOTREACHED();
+}
+
+void BookmarkFolderTreeModel::BookmarkNodeChanged(BookmarkModel* model,
+ BookmarkNode* node) {
+ if (!node->is_folder())
+ return;
+
+ FolderNode* folder_node = GetFolderNodeForBookmarkNode(node);
+ if (!folder_node)
+ return;
+
+ folder_node->SetTitle(node->GetTitle());
+ if (GetObserver())
+ GetObserver()->TreeNodeChanged(this, folder_node);
+}
+
+void BookmarkFolderTreeModel::AddRootChildren() {
+ Add(AsNode(GetRoot()), 0, CreateFolderNode(model_->GetBookmarkBarNode()));
+ Add(AsNode(GetRoot()), 1, CreateFolderNode(model_->other_node()));
+ Add(AsNode(GetRoot()), 2, recently_bookmarked_node_);
+ Add(AsNode(GetRoot()), 3, search_node_);
+}
+
+FolderNode* BookmarkFolderTreeModel::GetFolderNodeForBookmarkNode(
+ FolderNode* folder_node,
+ BookmarkNode* node) {
+ DCHECK(node->is_folder());
+ if (folder_node->value == node)
+ return folder_node;
+ for (int i = 0; i < folder_node->GetChildCount(); ++i) {
+ FolderNode* result =
+ GetFolderNodeForBookmarkNode(folder_node->GetChild(i), node);
+ if (result)
+ return result;
+ }
+ return NULL;
+}
+
+FolderNode* BookmarkFolderTreeModel::CreateFolderNode(BookmarkNode* node) {
+ DCHECK(node->is_folder());
+ FolderNode* folder_node = new FolderNode(node);
+ folder_node->SetTitle(node->GetTitle());
+
+ // And clone the children folders.
+ for (int i = 0; i < node->GetChildCount(); ++i) {
+ BookmarkNode* child = node->GetChild(i);
+ if (child->is_folder())
+ folder_node->Add(folder_node->GetChildCount(), CreateFolderNode(child));
+ }
+ return folder_node;
+}
+
+int BookmarkFolderTreeModel::CalculateIndexForChild(BookmarkNode* node) {
+ BookmarkNode* parent = node->GetParent();
+ DCHECK(parent);
+ for (int i = 0, folder_count = 0; i < parent->GetChildCount(); ++i) {
+ BookmarkNode* child = parent->GetChild(i);
+ if (child == node)
+ return folder_count;
+ if (child->is_folder())
+ folder_count++;
+ }
+ NOTREACHED();
+ return 0;
+}
diff --git a/chrome/browser/bookmarks/bookmark_folder_tree_model.h b/chrome/browser/bookmarks/bookmark_folder_tree_model.h
new file mode 100644
index 0000000..c2f7093
--- /dev/null
+++ b/chrome/browser/bookmarks/bookmark_folder_tree_model.h
@@ -0,0 +1,102 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_BOOKMARKS_BOOKMARK_FOLDER_TREE_MODEL_H_
+#define CHROME_BROWSER_BOOKMARKS_BOOKMARK_FOLDER_TREE_MODEL_H_
+
+#include "chrome/browser/bookmarks/bookmark_model.h"
+#include "chrome/views/tree_node_model.h"
+
+// The type of nodes created by BookmarkFolderTreeModel.
+typedef views::TreeNodeWithValue<BookmarkNode*> FolderNode;
+
+// TreeModel implementation that shows the folders from the BookmarkModel.
+// The root node contains the following nodes:
+// bookmark bar, other folders, recently bookmarked and search.
+class BookmarkFolderTreeModel : public views::TreeNodeModel<FolderNode>,
+ public BookmarkModelObserver {
+ public:
+ // Type of the node.
+ enum NodeType {
+ // Represents an entry from the BookmarkModel.
+ BOOKMARK,
+ RECENTLY_BOOKMARKED,
+ SEARCH,
+ NONE // Used for no selection.
+ };
+
+ explicit BookmarkFolderTreeModel(BookmarkModel* model);
+ ~BookmarkFolderTreeModel();
+
+ // The tree is not editable.
+ virtual void SetTitle(views::TreeModelNode* node, const std::wstring& title) {
+ NOTREACHED();
+ }
+
+ // Returns the type of the specified node.
+ NodeType GetNodeType(views::TreeModelNode* node);
+
+ // Returns the FolderNode for the specified BookmarkNode.
+ FolderNode* GetFolderNodeForBookmarkNode(BookmarkNode* node);
+
+ // Converts the tree node into a BookmarkNode. Returns NULL if |node| is NULL
+ // or not of NodeType::BOOKMARK.
+ BookmarkNode* TreeNodeAsBookmarkNode(views::TreeModelNode* node);
+
+ // Returns the search node.
+ FolderNode* search_node() const { return search_node_; }
+
+ // BookmarkModelObserver implementation.
+ virtual void Loaded(BookmarkModel* model);
+ virtual void BookmarkModelBeingDeleted(BookmarkModel* model);
+ virtual void BookmarkNodeMoved(BookmarkModel* model,
+ BookmarkNode* old_parent,
+ int old_index,
+ BookmarkNode* new_parent,
+ int new_index);
+ virtual void BookmarkNodeAdded(BookmarkModel* model,
+ BookmarkNode* parent,
+ int index);
+ virtual void BookmarkNodeRemoved(BookmarkModel* model,
+ BookmarkNode* parent,
+ int index,
+ BookmarkNode* node);
+ virtual void BookmarkNodeChanged(BookmarkModel* model,
+ BookmarkNode* node);
+ // Folders don't have favicons, so we ignore this.
+ virtual void BookmarkNodeFavIconLoaded(BookmarkModel* model,
+ BookmarkNode* node) {}
+
+ private:
+ // Invoked from the constructor to create the children of the root node.
+ void AddRootChildren();
+
+ // Implementation of GetFolderNodeForBookmarkNode. If the |folder_node|
+ // represents |node|, |folder_node| is returned, otherwise this recurses
+ // through the children.
+ FolderNode* GetFolderNodeForBookmarkNode(FolderNode* folder_node,
+ BookmarkNode* node);
+
+ // Creates a new folder node for |node| and all its children.
+ FolderNode* CreateFolderNode(BookmarkNode* node);
+
+ // Returns the number of folders that precede |node| in |node|s parent.
+ // The returned value is the index of the folder node representing |node|
+ // in its parent.
+ // This is used when new bookmarks are created to determine where the
+ // corresponding folder node should be created.
+ int CalculateIndexForChild(BookmarkNode* node);
+
+ // The model we're getting data from. Owned by the Profile.
+ BookmarkModel* model_;
+
+ // The two special nodes. These are owned by the root tree node owned by
+ // TreeNodeModel.
+ FolderNode* recently_bookmarked_node_;
+ FolderNode* search_node_;
+
+ DISALLOW_COPY_AND_ASSIGN(BookmarkFolderTreeModel);
+};
+
+#endif // CHROME_BROWSER_BOOKMARKS_BOOKMARK_FOLDER_TREE_MODEL_H_
diff --git a/chrome/browser/bookmarks/bookmark_folder_tree_model_unittest.cc b/chrome/browser/bookmarks/bookmark_folder_tree_model_unittest.cc
new file mode 100644
index 0000000..c6f0a00
--- /dev/null
+++ b/chrome/browser/bookmarks/bookmark_folder_tree_model_unittest.cc
@@ -0,0 +1,186 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/string_util.h"
+#include "base/time.h"
+#include "chrome/browser/bookmarks/bookmark_folder_tree_model.h"
+#include "chrome/test/testing_profile.h"
+#include "chrome/views/tree_view.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#include "generated_resources.h"
+
+// Base class for bookmark model tests.
+// Initial state of the bookmark model is as follows:
+// bb
+// url1
+// f1
+// f11
+// o
+// url2
+// f2
+// url3
+class BookmarkFolderTreeModelTest : public testing::Test,
+ public views::TreeModelObserver {
+ public:
+ BookmarkFolderTreeModelTest()
+ : url1_("http://1"),
+ url2_("http://2"),
+ url3_("http://3"),
+ added_count_(0),
+ removed_count_(0),
+ changed_count_(0) {
+ }
+
+ virtual void SetUp() {
+ profile_.reset(new TestingProfile());
+ profile_->CreateBookmarkModel(true);
+ // Populate with some default data.
+ BookmarkNode* bb = bookmark_model()->GetBookmarkBarNode();
+ bookmark_model()->AddURL(bb, 0, L"url1", url1_);
+ BookmarkNode* f1 = bookmark_model()->AddGroup(bb, 1, L"f1");
+ bookmark_model()->AddGroup(f1, 0, L"f11");
+
+ BookmarkNode* other = bookmark_model()->other_node();
+ bookmark_model()->AddURL(other, 0, L"url2", url2_);
+ bookmark_model()->AddGroup(other, 1, L"f2");
+ bookmark_model()->AddURL(other, 2, L"url3", url3_);
+
+ model_.reset(new BookmarkFolderTreeModel(bookmark_model()));
+ model_->SetObserver(this);
+ }
+
+ virtual void TearDown() {
+ model_.reset(NULL);
+ profile_.reset(NULL);
+ }
+
+ BookmarkModel* bookmark_model() const {
+ return profile_->GetBookmarkModel();
+ }
+
+ virtual void TreeNodesAdded(views::TreeModel* model,
+ views::TreeModelNode* parent,
+ int start,
+ int count) {
+ added_count_++;
+ }
+
+ virtual void TreeNodesRemoved(views::TreeModel* model,
+ views::TreeModelNode* parent,
+ int start,
+ int count) {
+ removed_count_++;
+ }
+
+ virtual void TreeNodeChanged(views::TreeModel* model,
+ views::TreeModelNode* node) {
+ changed_count_++;
+ }
+
+ void VerifyAndClearObserverCounts(int changed_count, int added_count,
+ int removed_count) {
+ EXPECT_EQ(changed_count, changed_count_);
+ EXPECT_EQ(added_count, added_count_);
+ EXPECT_EQ(removed_count, removed_count_);
+ ResetCounts();
+ }
+
+ void ResetCounts() {
+ changed_count_ = removed_count_ = added_count_ = 0;
+ }
+
+ scoped_ptr<BookmarkFolderTreeModel> model_;
+
+ const GURL url1_;
+ const GURL url2_;
+ const GURL url3_;
+
+ private:
+ int changed_count_;
+ int added_count_;
+ int removed_count_;
+ scoped_ptr<TestingProfile> profile_;
+};
+
+// Verifies the root node has 4 nodes, and the contents of the bookmark bar
+// and other folders matches the initial state.
+TEST_F(BookmarkFolderTreeModelTest, InitialState) {
+ // Verify the first 4 nodes.
+ views::TreeModelNode* root = model_->GetRoot();
+ ASSERT_EQ(4, model_->GetChildCount(root));
+ EXPECT_EQ(BookmarkFolderTreeModel::BOOKMARK,
+ model_->GetNodeType(model_->GetChild(root, 0)));
+ EXPECT_EQ(BookmarkFolderTreeModel::BOOKMARK,
+ model_->GetNodeType(model_->GetChild(root, 1)));
+ EXPECT_EQ(BookmarkFolderTreeModel::RECENTLY_BOOKMARKED,
+ model_->GetNodeType(model_->GetChild(root, 2)));
+ EXPECT_EQ(BookmarkFolderTreeModel::SEARCH,
+ model_->GetNodeType(model_->GetChild(root, 3)));
+
+ // Verify the contents of the bookmark bar node.
+ views::TreeModelNode* bb_node = model_->GetChild(root, 0);
+ EXPECT_TRUE(model_->TreeNodeAsBookmarkNode(bb_node) ==
+ bookmark_model()->GetBookmarkBarNode());
+ ASSERT_EQ(1, model_->GetChildCount(bb_node));
+ EXPECT_TRUE(model_->TreeNodeAsBookmarkNode(model_->GetChild(bb_node, 0)) ==
+ bookmark_model()->GetBookmarkBarNode()->GetChild(1));
+
+ // Verify the contents of the other folders node.
+ views::TreeModelNode* other_node = model_->GetChild(root, 1);
+ EXPECT_TRUE(model_->TreeNodeAsBookmarkNode(other_node) ==
+ bookmark_model()->other_node());
+ ASSERT_EQ(1, model_->GetChildCount(other_node));
+ EXPECT_TRUE(model_->TreeNodeAsBookmarkNode(model_->GetChild(other_node, 0)) ==
+ bookmark_model()->other_node()->GetChild(1));
+}
+
+// Removes a URL node and makes sure we don't get any notification.
+TEST_F(BookmarkFolderTreeModelTest, RemoveURL) {
+ bookmark_model()->Remove(bookmark_model()->GetBookmarkBarNode(), 0);
+ VerifyAndClearObserverCounts(0, 0, 0);
+}
+
+// Changes the title of a URL and makes sure we don't get any notification.
+TEST_F(BookmarkFolderTreeModelTest, ChangeURL) {
+ bookmark_model()->SetTitle(
+ bookmark_model()->GetBookmarkBarNode()->GetChild(0), L"BLAH");
+ VerifyAndClearObserverCounts(0, 0, 0);
+}
+
+// Adds a URL and make sure we don't get notification.
+TEST_F(BookmarkFolderTreeModelTest, AddURL) {
+ bookmark_model()->AddURL(
+ bookmark_model()->other_node(), 0, L"url1", url1_);
+ VerifyAndClearObserverCounts(0, 0, 0);
+}
+
+// Removes a folder and makes sure we get the right notification.
+TEST_F(BookmarkFolderTreeModelTest, RemoveFolder) {
+ bookmark_model()->Remove(bookmark_model()->GetBookmarkBarNode(), 1);
+ VerifyAndClearObserverCounts(0, 0, 1);
+ // Make sure the node was removed.
+ EXPECT_EQ(0, model_->GetRoot()->GetChild(0)->GetChildCount());
+}
+
+// Adds a folder and makes sure we get the right notification.
+TEST_F(BookmarkFolderTreeModelTest, AddFolder) {
+ BookmarkNode* new_group =
+ bookmark_model()->AddGroup(
+ bookmark_model()->GetBookmarkBarNode(), 0, L"fa");
+ VerifyAndClearObserverCounts(0, 1, 0);
+ // Make sure the node was added at the right place.
+ // Make sure the node was removed.
+ ASSERT_EQ(2, model_->GetRoot()->GetChild(0)->GetChildCount());
+ EXPECT_TRUE(new_group == model_->TreeNodeAsBookmarkNode(
+ model_->GetRoot()->GetChild(0)->GetChild(0)));
+}
+
+// Changes the title of a folder and makes sure we don't get any notification.
+TEST_F(BookmarkFolderTreeModelTest, ChangeFolder) {
+ bookmark_model()->SetTitle(
+ bookmark_model()->GetBookmarkBarNode()->GetChild(1)->GetChild(0),
+ L"BLAH");
+ VerifyAndClearObserverCounts(1, 0, 0);
+}
diff --git a/chrome/browser/bookmarks/bookmark_model.cc b/chrome/browser/bookmarks/bookmark_model.cc
index fba0629..14430a3 100644
--- a/chrome/browser/bookmarks/bookmark_model.cc
+++ b/chrome/browser/bookmarks/bookmark_model.cc
@@ -201,6 +201,22 @@ void BookmarkModel::GetBookmarksMatchingText(
}
}
+bool BookmarkModel::DoesBookmarkMatchText(const std::wstring& text,
+ BookmarkNode* node) {
+ if (!node->is_url())
+ return false;
+
+ QueryParser parser;
+ ScopedVector<QueryNode> query_nodes;
+ parser.ParseQuery(text, &query_nodes.get());
+ if (query_nodes.empty())
+ return false;
+
+ Snippet::MatchPositions match_position;
+ return parser.DoesQueryMatch(node->GetTitle(), query_nodes.get(),
+ &match_position);
+}
+
void BookmarkModel::Remove(BookmarkNode* parent, int index) {
if (!loaded_ || !IsValidIndex(parent, index, false) || parent == &root_) {
NOTREACHED();
@@ -355,8 +371,11 @@ BookmarkNode* BookmarkModel::AddURLWithCreationTime(
new_node->date_added_ = creation_time;
new_node->type_ = history::StarredEntry::URL;
- AutoLock url_lock(url_lock_);
- nodes_ordered_by_url_set_.insert(new_node);
+ {
+ // Only hold the lock for the duration of the insert.
+ AutoLock url_lock(url_lock_);
+ nodes_ordered_by_url_set_.insert(new_node);
+ }
return AddNode(parent, index, new_node, was_bookmarked);
}
@@ -535,7 +554,7 @@ void BookmarkModel::RemoveAndDeleteNode(BookmarkNode* delete_me) {
store_->ScheduleSave();
FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
- BookmarkNodeRemoved(this, parent, index));
+ BookmarkNodeRemoved(this, parent, index, node.get()));
if (details.changed_urls.empty()) {
// No point in sending out notification if the starred state didn't change.
@@ -596,7 +615,7 @@ BookmarkNode* BookmarkModel::GetNodeByID(BookmarkNode* node, int id) {
bool BookmarkModel::IsValidIndex(BookmarkNode* parent,
int index,
bool allow_end) {
- return (parent &&
+ return (parent && parent->is_folder() &&
(index >= 0 && (index < parent->GetChildCount() ||
(allow_end && index == parent->GetChildCount()))));
}
diff --git a/chrome/browser/bookmarks/bookmark_model.h b/chrome/browser/bookmarks/bookmark_model.h
index d9f47ac..2f3f32d 100644
--- a/chrome/browser/bookmarks/bookmark_model.h
+++ b/chrome/browser/bookmarks/bookmark_model.h
@@ -147,9 +147,16 @@ class BookmarkModelObserver {
int index) = 0;
// Invoked when a node has been removed, the item may still be starred though.
+ // TODO(sky): merge these two into one.
virtual void BookmarkNodeRemoved(BookmarkModel* model,
BookmarkNode* parent,
- int index) = 0;
+ int index) {}
+ virtual void BookmarkNodeRemoved(BookmarkModel* model,
+ BookmarkNode* parent,
+ int old_index,
+ BookmarkNode* node) {
+ BookmarkNodeRemoved(model, parent, old_index);
+ }
// Invoked when the title or favicon of a node has changed.
virtual void BookmarkNodeChanged(BookmarkModel* model,
@@ -222,6 +229,10 @@ class BookmarkModel : public NotificationObserver, public BookmarkService {
size_t max_count,
std::vector<TitleMatch>* matches);
+ // Returns true if the specified bookmark's title matches the specified
+ // text.
+ bool DoesBookmarkMatchText(const std::wstring& text, BookmarkNode* node);
+
void AddObserver(BookmarkModelObserver* observer) {
observers_.AddObserver(observer);
}
diff --git a/chrome/browser/bookmarks/bookmark_table_model.cc b/chrome/browser/bookmarks/bookmark_table_model.cc
new file mode 100644
index 0000000..c55f6ff
--- /dev/null
+++ b/chrome/browser/bookmarks/bookmark_table_model.cc
@@ -0,0 +1,325 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/bookmarks/bookmark_table_model.h"
+
+#include <limits>
+
+#include "base/string_util.h"
+#include "base/time.h"
+#include "base/time_format.h"
+#include "chrome/app/theme/theme_resources.h"
+#include "chrome/common/resource_bundle.h"
+#include "googleurl/src/gurl.h"
+
+#include "generated_resources.h"
+
+namespace {
+
+// Number of bookmarks shown in recently bookmarked.
+const int kRecentlyBookmarkedCount = 50;
+
+// FolderBookmarkTableModel ----------------------------------------------------
+
+class FolderBookmarkTableModel : public BookmarkTableModel {
+ public:
+ FolderBookmarkTableModel(BookmarkModel* model, BookmarkNode* root_node)
+ : BookmarkTableModel(model),
+ root_node_(root_node) {
+ }
+
+ virtual int RowCount() {
+ return root_node_ ? root_node_->GetChildCount() : 0;
+ }
+
+ virtual BookmarkNode* GetNodeForRow(int row) {
+ DCHECK(root_node_);
+ return root_node_->GetChild(row);
+ }
+
+ virtual void BookmarkNodeMoved(BookmarkModel* model,
+ BookmarkNode* old_parent,
+ int old_index,
+ BookmarkNode* new_parent,
+ int new_index) {
+ if (observer() && (old_parent == root_node_ || new_parent == root_node_))
+ observer()->OnModelChanged();
+ }
+
+ virtual void BookmarkNodeAdded(BookmarkModel* model,
+ BookmarkNode* parent,
+ int index) {
+ if (root_node_ == parent && observer())
+ observer()->OnItemsAdded(index, 1);
+ }
+
+ virtual void BookmarkNodeRemoved(BookmarkModel* model,
+ BookmarkNode* parent,
+ int index,
+ BookmarkNode* node) {
+ if (root_node_->HasAncestor(node)) {
+ // We, our one of our ancestors was removed.
+ root_node_ = NULL;
+ if (observer())
+ observer()->OnModelChanged();
+ return;
+ }
+ if (root_node_ == parent && observer())
+ observer()->OnItemsRemoved(index, 1);
+ }
+
+ virtual void BookmarkNodeChanged(BookmarkModel* model,
+ BookmarkNode* node) {
+ NotifyChanged(node);
+ }
+
+ virtual void BookmarkNodeFavIconLoaded(BookmarkModel* model,
+ BookmarkNode* node) {
+ NotifyChanged(node);
+ }
+
+ private:
+ void NotifyChanged(BookmarkNode* node) {
+ if (node->GetParent() == root_node_ && observer())
+ observer()->OnItemsChanged(node->GetParent()->IndexOfChild(node), 1);
+ }
+
+ // The node we're showing the children of. This is set to NULL if the node
+ // (or one of its ancestors) is removed from the model.
+ BookmarkNode* root_node_;
+
+ DISALLOW_COPY_AND_ASSIGN(FolderBookmarkTableModel);
+};
+
+// VectorBackedBookmarkTableModel ----------------------------------------------
+
+class VectorBackedBookmarkTableModel : public BookmarkTableModel {
+ public:
+ explicit VectorBackedBookmarkTableModel(BookmarkModel* model)
+ : BookmarkTableModel(model) {
+ }
+
+ virtual BookmarkNode* GetNodeForRow(int row) {
+ return nodes_[row];
+ }
+
+ virtual int RowCount() {
+ return static_cast<int>(nodes_.size());
+ }
+
+ virtual void BookmarkNodeMoved(BookmarkModel* model,
+ BookmarkNode* old_parent,
+ int old_index,
+ BookmarkNode* new_parent,
+ int new_index) {
+ NotifyObserverOfChange(new_parent->GetChild(new_index));
+ }
+
+ virtual void BookmarkNodeFavIconLoaded(BookmarkModel* model,
+ BookmarkNode* node) {
+ NotifyObserverOfChange(node);
+ }
+
+ virtual void BookmarkNodeChanged(BookmarkModel* model,
+ BookmarkNode* node) {
+ NotifyObserverOfChange(node);
+ }
+
+ protected:
+ void NotifyObserverOfChange(BookmarkNode* node) {
+ if (!observer())
+ return;
+
+ int index = IndexOfNode(node);
+ if (index != -1)
+ observer()->OnItemsChanged(index, 1);
+ }
+
+ typedef std::vector<BookmarkNode*> Nodes;
+ Nodes nodes_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(VectorBackedBookmarkTableModel);
+};
+
+// RecentlyBookmarkedTableModel ------------------------------------------------
+
+class RecentlyBookmarkedTableModel : public VectorBackedBookmarkTableModel {
+ public:
+ explicit RecentlyBookmarkedTableModel(BookmarkModel* model)
+ : VectorBackedBookmarkTableModel(model) {
+ UpdateRecentlyBookmarked();
+ }
+
+ virtual void BookmarkNodeAdded(BookmarkModel* model,
+ BookmarkNode* parent,
+ int index) {
+ if (parent->GetChild(index)->is_url())
+ UpdateRecentlyBookmarked();
+ }
+
+ virtual void BookmarkNodeRemoved(BookmarkModel* model,
+ BookmarkNode* parent,
+ int old_index,
+ BookmarkNode* old_node) {
+ if (old_node->is_url())
+ UpdateRecentlyBookmarked();
+ }
+
+ private:
+ void UpdateRecentlyBookmarked() {
+ nodes_.clear();
+ model()->GetMostRecentlyAddedEntries(kRecentlyBookmarkedCount, &nodes_);
+ if (observer())
+ observer()->OnModelChanged();
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(RecentlyBookmarkedTableModel);
+};
+
+// BookmarkSearchTableModel ----------------------------------------------------
+
+class BookmarkSearchTableModel : public VectorBackedBookmarkTableModel {
+ public:
+ BookmarkSearchTableModel(BookmarkModel* model,
+ const std::wstring& search_text)
+ : VectorBackedBookmarkTableModel(model),
+ search_text_(search_text) {
+ std::vector<BookmarkModel::TitleMatch> matches;
+ model->GetBookmarksMatchingText(search_text,
+ std::numeric_limits<int>::max(), &matches);
+ for (size_t i = 0; i < matches.size(); ++i)
+ nodes_.push_back(matches[i].node);
+ }
+
+ virtual void BookmarkNodeAdded(BookmarkModel* model,
+ BookmarkNode* parent,
+ int index) {
+ BookmarkNode* node = parent->GetChild(index);
+ if (model->DoesBookmarkMatchText(search_text_, node)) {
+ nodes_.push_back(node);
+ if (observer())
+ observer()->OnItemsAdded(static_cast<int>(nodes_.size() - 1), 1);
+ }
+ }
+
+ virtual void BookmarkNodeRemoved(BookmarkModel* model,
+ BookmarkNode* parent,
+ int index,
+ BookmarkNode* node) {
+ int internal_index = IndexOfNode(node);
+ if (internal_index == -1)
+ return;
+
+ nodes_.erase(nodes_.begin() + static_cast<int>(internal_index));
+ if (observer())
+ observer()->OnItemsRemoved(internal_index, 1);
+ }
+
+ private:
+ const std::wstring search_text_;
+
+ DISALLOW_COPY_AND_ASSIGN(BookmarkSearchTableModel);
+};
+
+} // namespace
+
+// BookmarkTableModel ----------------------------------------------------------
+
+// static
+BookmarkTableModel* BookmarkTableModel::CreateRecentlyBookmarkedModel(
+ BookmarkModel* model) {
+ return new RecentlyBookmarkedTableModel(model);
+}
+
+// static
+BookmarkTableModel* BookmarkTableModel::CreateBookmarkTableModelForFolder(
+ BookmarkModel* model, BookmarkNode* node) {
+ return new FolderBookmarkTableModel(model, node);
+}
+
+// static
+BookmarkTableModel* BookmarkTableModel::CreateSearchTableModel(
+ BookmarkModel* model,
+ const std::wstring& text) {
+ return new BookmarkSearchTableModel(model, text);
+}
+
+BookmarkTableModel::BookmarkTableModel(BookmarkModel* model)
+ : model_(model),
+ observer_(NULL) {
+ model_->AddObserver(this);
+}
+
+BookmarkTableModel::~BookmarkTableModel() {
+ if (model_)
+ model_->RemoveObserver(this);
+}
+
+std::wstring BookmarkTableModel::GetText(int row, int column_id) {
+ BookmarkNode* node = GetNodeForRow(row);
+ switch (column_id) {
+ case IDS_BOOKMARK_TABLE_TITLE:
+ return node->GetTitle();
+
+ case IDS_BOOKMARK_TABLE_URL:
+ return node->is_url() ? UTF8ToWide(node->GetURL().spec()) :
+ std::wstring();
+
+ case IDS_BOOKMARK_TABLE_PATH: {
+ std::wstring path;
+ BuildPath(node->GetParent(), &path);
+ return path;
+ }
+ }
+ NOTREACHED();
+ return std::wstring();
+}
+
+SkBitmap BookmarkTableModel::GetIcon(int row) {
+ static SkBitmap* folder_icon = ResourceBundle::GetSharedInstance().
+ GetBitmapNamed(IDR_BOOKMARK_BAR_FOLDER);
+ static SkBitmap* default_icon = ResourceBundle::GetSharedInstance().
+ GetBitmapNamed(IDR_DEFAULT_FAVICON);
+
+ BookmarkNode* node = GetNodeForRow(row);
+ if (node->is_folder())
+ return *folder_icon;
+
+ if (node->GetFavIcon().empty())
+ return *default_icon;
+
+ return node->GetFavIcon();
+}
+
+void BookmarkTableModel::BookmarkModelBeingDeleted(BookmarkModel* model) {
+ model_->RemoveObserver(this);
+ model_ = NULL;
+}
+
+int BookmarkTableModel::IndexOfNode(BookmarkNode* node) {
+ for (int i = RowCount() - 1; i >= 0; --i) {
+ if (GetNodeForRow(i) == node)
+ return i;
+ }
+ return -1;
+}
+
+void BookmarkTableModel::BuildPath(BookmarkNode* node, std::wstring* path) {
+ if (!node) {
+ NOTREACHED();
+ return;
+ }
+ if (node == model()->GetBookmarkBarNode()) {
+ *path = l10n_util::GetString(IDS_BOOKMARK_TABLE_BOOKMARK_BAR_PATH);
+ return;
+ }
+ if (node == model()->other_node()) {
+ *path = l10n_util::GetString(IDS_BOOKMARK_TABLE_OTHER_BOOKMARKS_PATH);
+ return;
+ }
+ BuildPath(node->GetParent(), path);
+ path->append(l10n_util::GetString(IDS_BOOKMARK_TABLE_PATH_SEPARATOR));
+ path->append(node->GetTitle());
+}
diff --git a/chrome/browser/bookmarks/bookmark_table_model.h b/chrome/browser/bookmarks/bookmark_table_model.h
new file mode 100644
index 0000000..b7f295e
--- /dev/null
+++ b/chrome/browser/bookmarks/bookmark_table_model.h
@@ -0,0 +1,66 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_BOOKMARKS_BOOKMARK_TABLE_MODEL_H_
+#define CHROME_BROWSER_BOOKMARKS_BOOKMARK_TABLE_MODEL_H_
+
+#include "chrome/browser/bookmarks/bookmark_model.h"
+#include "chrome/views/table_view.h"
+
+// BookmarkTableModel provides a view of the BookmarkModel as a TableModel.
+// Three variations are provided:
+// . Recently created bookmarks.
+// . The children of a particular folder.
+// . All bookmarks matching the specified text.
+class BookmarkTableModel : public views::TableModel,
+ public BookmarkModelObserver {
+ public:
+ // Methods for creating the various BookmarkTableModels. Ownership passes
+ // to the caller.
+ static BookmarkTableModel* CreateRecentlyBookmarkedModel(
+ BookmarkModel* model);
+ static BookmarkTableModel* CreateBookmarkTableModelForFolder(
+ BookmarkModel* model, BookmarkNode* node);
+ static BookmarkTableModel* CreateSearchTableModel(
+ BookmarkModel* model,
+ const std::wstring& text);
+
+ explicit BookmarkTableModel(BookmarkModel* model);
+ virtual ~BookmarkTableModel();
+
+ // TableModel methods.
+ virtual std::wstring GetText(int row, int column_id);
+ virtual SkBitmap GetIcon(int row);
+ virtual void SetObserver(views::TableModelObserver* observer) {
+ observer_ = observer;
+ }
+
+ // BookmarkModelObserver methods.
+ virtual void Loaded(BookmarkModel* model) {}
+ virtual void BookmarkModelBeingDeleted(BookmarkModel* model);
+
+ // Returns the index of the specified node, or -1 if the node isn't in the
+ // model.
+ virtual int IndexOfNode(BookmarkNode* node);
+
+ // Returns the BookmarkNode at the specified index.
+ virtual BookmarkNode* GetNodeForRow(int row) = 0;
+
+ // Returns the underlying BookmarkModel.
+ BookmarkModel* model() const { return model_; }
+
+ protected:
+ views::TableModelObserver* observer() const { return observer_; }
+
+ private:
+ // Builds the path shown in the path column for the specified node.
+ void BuildPath(BookmarkNode* node, std::wstring* path);
+
+ BookmarkModel* model_;
+ views::TableModelObserver* observer_;
+
+ DISALLOW_COPY_AND_ASSIGN(BookmarkTableModel);
+};
+
+#endif // CHROME_BROWSER_BOOKMARKS_BOOKMARK_TABLE_MODEL_H_
diff --git a/chrome/browser/bookmarks/bookmark_table_model_unittest.cc b/chrome/browser/bookmarks/bookmark_table_model_unittest.cc
new file mode 100644
index 0000000..ed2b215
--- /dev/null
+++ b/chrome/browser/bookmarks/bookmark_table_model_unittest.cc
@@ -0,0 +1,296 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/string_util.h"
+#include "base/time.h"
+#include "chrome/browser/bookmarks/bookmark_table_model.h"
+#include "chrome/test/testing_profile.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#include "generated_resources.h"
+
+// Base class for bookmark model tests.
+// Initial state of the bookmark model is as follows:
+// bb
+// url1 (t0)
+// f1
+// o
+// url2 (t0 + 2)
+// f2
+// url3 (t0 + 1)
+class BookmarkTableModelTest : public testing::Test,
+ public views::TableModelObserver {
+ public:
+ BookmarkTableModelTest()
+ : url1_("http://1"),
+ url2_("http://2"),
+ url3_("http://3"),
+ changed_count_(0),
+ item_changed_count_(0),
+ added_count_(0),
+ removed_count_(0) {
+ }
+
+ virtual void SetUp() {
+ profile_.reset(new TestingProfile());
+ profile_->CreateBookmarkModel(true);
+ // Populate with some default data.
+ Time t0 = Time::Now();
+ BookmarkNode* bb = bookmark_model()->GetBookmarkBarNode();
+ bookmark_model()->AddURLWithCreationTime(bb, 0, L"a", url1_, t0);
+ bookmark_model()->AddGroup(bb, 1, L"f1");
+
+ BookmarkNode* other = bookmark_model()->other_node();
+ bookmark_model()->AddURLWithCreationTime(other, 0, L"b",
+ url2_, t0 + TimeDelta::FromDays(2));
+ bookmark_model()->AddGroup(other, 1, L"f2");
+ bookmark_model()->AddURLWithCreationTime(other, 2, L"c", url3_,
+ t0 + TimeDelta::FromDays(1));
+ }
+
+ virtual void TearDown() {
+ model_.reset(NULL);
+ profile_.reset(NULL);
+ }
+
+ BookmarkModel* bookmark_model() const {
+ return profile_->GetBookmarkModel();
+ }
+
+ virtual void OnModelChanged() {
+ changed_count_++;
+ }
+
+ virtual void OnItemsChanged(int start, int length) {
+ item_changed_count_++;
+ }
+
+ virtual void OnItemsAdded(int start, int length) {
+ added_count_++;
+ }
+
+ virtual void OnItemsRemoved(int start, int length) {
+ removed_count_++;
+ }
+
+ void VerifyAndClearOberserverCounts(int changed_count, int item_changed_count,
+ int added_count, int removed_count) {
+ EXPECT_EQ(changed_count, changed_count_);
+ EXPECT_EQ(item_changed_count, item_changed_count_);
+ EXPECT_EQ(added_count, added_count_);
+ EXPECT_EQ(removed_count, removed_count_);
+ ResetCounts();
+ }
+
+ void ResetCounts() {
+ changed_count_ = item_changed_count_ = removed_count_ = added_count_ = 0;
+ }
+
+ void SetModel(BookmarkTableModel* model) {
+ if (model_.get())
+ model_->SetObserver(NULL);
+ model_.reset(model);
+ if (model_.get())
+ model_->SetObserver(this);
+ }
+
+ scoped_ptr<BookmarkTableModel> model_;
+
+ const GURL url1_;
+ const GURL url2_;
+ const GURL url3_;
+
+ private:
+ int changed_count_;
+ int item_changed_count_;
+ int added_count_;
+ int removed_count_;
+ scoped_ptr<TestingProfile> profile_;
+};
+
+// Verifies the count when showing various nodes.
+TEST_F(BookmarkTableModelTest, FolderInitialState) {
+ SetModel(BookmarkTableModel::CreateBookmarkTableModelForFolder(
+ bookmark_model(), bookmark_model()->GetBookmarkBarNode()));
+ ASSERT_EQ(2, model_->RowCount());
+ EXPECT_TRUE(bookmark_model()->GetBookmarkBarNode()->GetChild(0) ==
+ model_->GetNodeForRow(0));
+ EXPECT_TRUE(bookmark_model()->GetBookmarkBarNode()->GetChild(1) ==
+ model_->GetNodeForRow(1));
+
+ SetModel(BookmarkTableModel::CreateBookmarkTableModelForFolder(
+ bookmark_model(), bookmark_model()->other_node()));
+ EXPECT_EQ(3, model_->RowCount());
+}
+
+// Verifies adding an item to folder model generates the correct event.
+TEST_F(BookmarkTableModelTest, AddToFolder) {
+ BookmarkNode* other = bookmark_model()->other_node();
+ SetModel(BookmarkTableModel::CreateBookmarkTableModelForFolder(
+ bookmark_model(), other));
+ BookmarkNode* new_node = bookmark_model()->AddURL(other, 0, L"new", url1_);
+ // Should have gotten notification of the add.
+ VerifyAndClearOberserverCounts(0, 0, 1, 0);
+ ASSERT_EQ(4, model_->RowCount());
+ EXPECT_TRUE(new_node == model_->GetNodeForRow(0));
+
+ // Add to the bookmark bar, this shouldn't generate an event.
+ bookmark_model()->AddURL(bookmark_model()->GetBookmarkBarNode(), 0, L"new",
+ url1_);
+ VerifyAndClearOberserverCounts(0, 0, 0, 0);
+}
+
+// Verifies removing an item from folder model generates the correct event.
+TEST_F(BookmarkTableModelTest, RemoveFromFolder) {
+ BookmarkNode* other = bookmark_model()->other_node();
+ SetModel(BookmarkTableModel::CreateBookmarkTableModelForFolder(
+ bookmark_model(), other));
+ bookmark_model()->Remove(other, 0);
+
+ // Should have gotten notification of the remove.
+ VerifyAndClearOberserverCounts(0, 0, 0, 1);
+ EXPECT_EQ(2, model_->RowCount());
+
+ // Remove from the bookmark bar, this shouldn't generate an event.
+ bookmark_model()->Remove(bookmark_model()->GetBookmarkBarNode(), 0);
+ VerifyAndClearOberserverCounts(0, 0, 0, 0);
+}
+
+// Verifies changing an item in the folder model generates the correct event.
+TEST_F(BookmarkTableModelTest, ChangeFolder) {
+ BookmarkNode* other = bookmark_model()->other_node();
+ SetModel(BookmarkTableModel::CreateBookmarkTableModelForFolder(
+ bookmark_model(), other));
+ bookmark_model()->SetTitle(other->GetChild(0), L"new");
+
+ // Should have gotten notification of the change.
+ VerifyAndClearOberserverCounts(0, 1, 0, 0);
+ EXPECT_EQ(3, model_->RowCount());
+
+ // Change a node in the bookmark bar, this shouldn't generate an event.
+ bookmark_model()->SetTitle(
+ bookmark_model()->GetBookmarkBarNode()->GetChild(0), L"new2");
+ VerifyAndClearOberserverCounts(0, 0, 0, 0);
+}
+
+// Verifies show recently added shows the recently added, in order.
+TEST_F(BookmarkTableModelTest, RecentlyBookmarkedOrder) {
+ SetModel(BookmarkTableModel::CreateRecentlyBookmarkedModel(bookmark_model()));
+ EXPECT_EQ(3, model_->RowCount());
+
+ BookmarkNode* bb = bookmark_model()->GetBookmarkBarNode();
+ BookmarkNode* other = bookmark_model()->other_node();
+ EXPECT_TRUE(other->GetChild(0) == model_->GetNodeForRow(0));
+ EXPECT_TRUE(other->GetChild(2) == model_->GetNodeForRow(1));
+ EXPECT_TRUE(bb->GetChild(0) == model_->GetNodeForRow(2));
+}
+
+// Verifies adding an item to recently added notifies observer.
+TEST_F(BookmarkTableModelTest, AddToRecentlyBookmarked) {
+ SetModel(BookmarkTableModel::CreateRecentlyBookmarkedModel(bookmark_model()));
+ bookmark_model()->AddURL(bookmark_model()->other_node(), 0, L"new", url1_);
+ // Should have gotten notification of the add.
+ VerifyAndClearOberserverCounts(1, 0, 0, 0);
+ EXPECT_EQ(4, model_->RowCount());
+
+ // Add a folder, this shouldn't change the model.
+ bookmark_model()->AddGroup(bookmark_model()->GetBookmarkBarNode(), 0, L"new");
+ VerifyAndClearOberserverCounts(0, 0, 0, 0);
+}
+
+// Verifies removing an item from recently added notifies observer.
+TEST_F(BookmarkTableModelTest, RemoveFromRecentlyBookmarked) {
+ SetModel(BookmarkTableModel::CreateRecentlyBookmarkedModel(bookmark_model()));
+ bookmark_model()->Remove(bookmark_model()->other_node(), 0);
+ // Should have gotten notification of the remove.
+ VerifyAndClearOberserverCounts(1, 0, 0, 0);
+ EXPECT_EQ(2, model_->RowCount());
+
+ // Remove a folder, this shouldn't change the model.
+ bookmark_model()->Remove(bookmark_model()->other_node(), 0);
+ VerifyAndClearOberserverCounts(0, 0, 0, 0);
+}
+
+// Verifies changing an item in recently added notifies observer.
+TEST_F(BookmarkTableModelTest, ChangeRecentlyBookmarked) {
+ SetModel(BookmarkTableModel::CreateRecentlyBookmarkedModel(bookmark_model()));
+ bookmark_model()->SetTitle(bookmark_model()->other_node()->GetChild(0),
+ L"new");
+ // Should have gotten notification of the change.
+ VerifyAndClearOberserverCounts(0, 1, 0, 0);
+
+ // Change a folder, this shouldn't change the model.
+ bookmark_model()->SetTitle(bookmark_model()->other_node()->GetChild(1),
+ L"new");
+ VerifyAndClearOberserverCounts(0, 0, 0, 0);
+}
+
+// Verifies search finds the correct bookmarks.
+TEST_F(BookmarkTableModelTest, Search) {
+ SetModel(BookmarkTableModel::CreateSearchTableModel(bookmark_model(), L"c"));
+ ASSERT_EQ(1, model_->RowCount());
+ EXPECT_TRUE(bookmark_model()->other_node()->GetChild(2) ==
+ model_->GetNodeForRow(0));
+ // Make sure IndexOfNode works.
+ EXPECT_EQ(0,
+ model_->IndexOfNode(bookmark_model()->other_node()->GetChild(2)));
+}
+
+// Verifies adding an item to search notifies observers.
+TEST_F(BookmarkTableModelTest, AddToSearch) {
+ SetModel(BookmarkTableModel::CreateSearchTableModel(bookmark_model(), L"c"));
+ BookmarkNode* new_node =
+ bookmark_model()->AddURL(bookmark_model()->other_node(), 0, L"c", url1_);
+ // Should have gotten notification of the add.
+ VerifyAndClearOberserverCounts(0, 0, 1, 0);
+ ASSERT_EQ(2, model_->RowCount());
+ // New node should have gone to end.
+ EXPECT_TRUE(model_->GetNodeForRow(1) == new_node);
+
+ // Add a folder, this shouldn't change the model.
+ bookmark_model()->AddGroup(bookmark_model()->GetBookmarkBarNode(), 0, L"new");
+ VerifyAndClearOberserverCounts(0, 0, 0, 0);
+ EXPECT_EQ(2, model_->RowCount());
+
+ // Add a url that doesn't match search, this shouldn't change the model.
+ bookmark_model()->AddGroup(bookmark_model()->GetBookmarkBarNode(), 0, L"new");
+ VerifyAndClearOberserverCounts(0, 0, 0, 0);
+ EXPECT_EQ(2, model_->RowCount());
+}
+
+// Verifies removing an item updates search.
+TEST_F(BookmarkTableModelTest, RemoveFromSearch) {
+ SetModel(BookmarkTableModel::CreateSearchTableModel(bookmark_model(), L"c"));
+ bookmark_model()->Remove(bookmark_model()->other_node(), 2);
+ // Should have gotten notification of the remove.
+ VerifyAndClearOberserverCounts(0, 0, 0, 1);
+ EXPECT_EQ(0, model_->RowCount());
+
+ // Remove a folder, this shouldn't change the model.
+ bookmark_model()->Remove(bookmark_model()->other_node(), 1);
+ VerifyAndClearOberserverCounts(0, 0, 0, 0);
+
+ // Remove another url that isn't in the model, this shouldn't change anything.
+ bookmark_model()->Remove(bookmark_model()->other_node(), 0);
+ VerifyAndClearOberserverCounts(0, 0, 0, 0);
+}
+
+// Verifies changing an item in search notifies observer.
+TEST_F(BookmarkTableModelTest, ChangeSearch) {
+ SetModel(BookmarkTableModel::CreateSearchTableModel(bookmark_model(), L"c"));
+ bookmark_model()->SetTitle(bookmark_model()->other_node()->GetChild(2),
+ L"new");
+ // Should have gotten notification of the change.
+ VerifyAndClearOberserverCounts(0, 1, 0, 0);
+
+ // Change a folder, this shouldn't change the model.
+ bookmark_model()->SetTitle(bookmark_model()->other_node()->GetChild(1),
+ L"new");
+ VerifyAndClearOberserverCounts(0, 0, 0, 0);
+
+ // Change a url that isn't in the model, this shouldn't send change.
+ bookmark_model()->SetTitle(bookmark_model()->other_node()->GetChild(0),
+ L"new");
+ VerifyAndClearOberserverCounts(0, 0, 0, 0);
+}