diff options
Diffstat (limited to 'chrome')
-rw-r--r-- | chrome/browser/bookmarks/bookmark_utils.cc | 45 | ||||
-rw-r--r-- | chrome/browser/bookmarks/bookmark_utils.h | 27 | ||||
-rw-r--r-- | chrome/browser/bookmarks/bookmark_utils_unittest.cc | 143 | ||||
-rw-r--r-- | chrome/browser/extensions/api/bookmarks/bookmarks_api.cc | 34 | ||||
-rw-r--r-- | chrome/common/extensions/api/bookmarks.json | 32 |
5 files changed, 244 insertions, 37 deletions
diff --git a/chrome/browser/bookmarks/bookmark_utils.cc b/chrome/browser/bookmarks/bookmark_utils.cc index 0c4cd01..f2397ac 100644 --- a/chrome/browser/bookmarks/bookmark_utils.cc +++ b/chrome/browser/bookmarks/bookmark_utils.cc @@ -95,6 +95,9 @@ bool PruneInvisibleFolders(const BookmarkNode* node) { namespace bookmark_utils { +QueryFields::QueryFields() {} +QueryFields::~QueryFields() {} + void CloneBookmarkNode(BookmarkModel* model, const std::vector<BookmarkNodeData::Element>& elements, const BookmarkNode* parent, @@ -216,25 +219,41 @@ bool MoreRecentlyAdded(const BookmarkNode* n1, const BookmarkNode* n2) { return n1->date_added() > n2->date_added(); } -void GetBookmarksContainingText(BookmarkModel* model, - const base::string16& text, - size_t max_count, - const std::string& languages, - std::vector<const BookmarkNode*>* nodes) { - std::vector<base::string16> words; +void GetBookmarksMatchingProperties(BookmarkModel* model, + const QueryFields& query, + size_t max_count, + const std::string& languages, + std::vector<const BookmarkNode*>* nodes) { + std::vector<base::string16> query_words; QueryParser parser; - parser.ParseQueryWords(base::i18n::ToLower(text), &words); - if (words.empty()) - return; + if (query.word_phrase_query) { + parser.ParseQueryWords(base::i18n::ToLower(*query.word_phrase_query), + &query_words); + } ui::TreeNodeIterator<const BookmarkNode> iterator(model->root_node()); while (iterator.has_next()) { const BookmarkNode* node = iterator.Next(); - if (DoesBookmarkContainWords(node, words, languages)) { - nodes->push_back(node); - if (nodes->size() == max_count) - return; + if (!query_words.empty() && + !DoesBookmarkContainWords(node, query_words, languages)) { + continue; } + if (query.url) { + // Check against bare url spec and IDN-decoded url. + if (!node->is_url() || + !(UTF8ToUTF16(node->url().spec()) == *query.url || + net::FormatUrl( + node->url(), languages, net::kFormatUrlOmitNothing, + net::UnescapeRule::NORMAL, NULL, NULL, NULL) == *query.url)) { + continue; + } + } + if (query.title && node->GetTitle() != *query.title) + continue; + + nodes->push_back(node); + if (nodes->size() == max_count) + return; } } diff --git a/chrome/browser/bookmarks/bookmark_utils.h b/chrome/browser/bookmarks/bookmark_utils.h index 49272f9..fa5086d 100644 --- a/chrome/browser/bookmarks/bookmark_utils.h +++ b/chrome/browser/bookmarks/bookmark_utils.h @@ -22,6 +22,16 @@ class PrefRegistrySyncable; // that show bookmarks: bookmark manager, bookmark bar view ... namespace bookmark_utils { +// Fields to use when finding matching bookmarks. +struct QueryFields { + QueryFields(); + ~QueryFields(); + + scoped_ptr<base::string16> word_phrase_query; + scoped_ptr<base::string16> url; + scoped_ptr<base::string16> title; +}; + // Clones bookmark node, adding newly created nodes to |parent| starting at // |index_to_add_at|. If |reset_node_times| is true cloned bookmarks and // folders will receive new creation times and folder modification times @@ -63,14 +73,15 @@ void GetMostRecentlyAddedEntries(BookmarkModel* model, // Returns true if |n1| was added more recently than |n2|. bool MoreRecentlyAdded(const BookmarkNode* n1, const BookmarkNode* n2); -// Returns up to |max_count| bookmarks from |model| whose url or title contains -// the text |text|. |languages| is user's accept-language setting to decode -// IDN. -void GetBookmarksContainingText(BookmarkModel* model, - const base::string16& text, - size_t max_count, - const std::string& languages, - std::vector<const BookmarkNode*>* nodes); +// Returns up to |max_count| bookmarks from |model| whose url or title contain +// the text |query.word_phrase_query| and exactly match |query.url| and +// |query.title|, for all of the preceding fields that are not NULL. +// |languages| is user's accept-language setting to decode IDN. +void GetBookmarksMatchingProperties(BookmarkModel* model, + const QueryFields& query, + size_t max_count, + const std::string& languages, + std::vector<const BookmarkNode*>* nodes); // Register user preferences for Bookmarks Bar. void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry); diff --git a/chrome/browser/bookmarks/bookmark_utils_unittest.cc b/chrome/browser/bookmarks/bookmark_utils_unittest.cc index bfd284e..c2aac0c 100644 --- a/chrome/browser/bookmarks/bookmark_utils_unittest.cc +++ b/chrome/browser/bookmarks/bookmark_utils_unittest.cc @@ -30,7 +30,7 @@ class BookmarkUtilsTest : public ::testing::Test { base::MessageLoopForUI loop; }; -TEST_F(BookmarkUtilsTest, GetBookmarksContainingText) { +TEST_F(BookmarkUtilsTest, GetBookmarksMatchingPropertiesWordPhraseQuery) { BookmarkModel model(NULL); const BookmarkNode* node1 = model.AddURL(model.other_node(), 0, @@ -46,26 +46,155 @@ TEST_F(BookmarkUtilsTest, GetBookmarksContainingText) { ASCIIToUTF16("foo")); std::vector<const BookmarkNode*> nodes; - GetBookmarksContainingText( - &model, ASCIIToUTF16("foo"), 100, string(), &nodes); + QueryFields query; + query.word_phrase_query.reset(new string16); + *query.word_phrase_query = ASCIIToUTF16("foo"); + GetBookmarksMatchingProperties(&model, query, 100, string(), &nodes); ASSERT_EQ(2U, nodes.size()); EXPECT_TRUE(nodes[0] == folder1); EXPECT_TRUE(nodes[1] == node1); nodes.clear(); - GetBookmarksContainingText( - &model, ASCIIToUTF16("cnn"), 100, string(), &nodes); + *query.word_phrase_query = ASCIIToUTF16("cnn"); + GetBookmarksMatchingProperties(&model, query, 100, string(), &nodes); ASSERT_EQ(1U, nodes.size()); EXPECT_TRUE(nodes[0] == node2); nodes.clear(); - GetBookmarksContainingText( - &model, ASCIIToUTF16("foo bar"), 100, string(), &nodes); + *query.word_phrase_query = ASCIIToUTF16("foo bar"); + GetBookmarksMatchingProperties(&model, query, 100, string(), &nodes); ASSERT_EQ(1U, nodes.size()); EXPECT_TRUE(nodes[0] == node1); nodes.clear(); } +// Check exact matching against a URL query. +TEST_F(BookmarkUtilsTest, GetBookmarksMatchingPropertiesUrl) { + BookmarkModel model(NULL); + const BookmarkNode* node1 = model.AddURL(model.other_node(), + 0, + ASCIIToUTF16("Google"), + GURL("https://www.google.com/")); + model.AddURL(model.other_node(), + 0, + ASCIIToUTF16("Google Calendar"), + GURL("https://www.google.com/calendar")); + + model.AddFolder(model.other_node(), + 0, + ASCIIToUTF16("Folder")); + + std::vector<const BookmarkNode*> nodes; + QueryFields query; + query.url.reset(new string16); + *query.url = ASCIIToUTF16("https://www.google.com/"); + GetBookmarksMatchingProperties(&model, query, 100, string(), &nodes); + ASSERT_EQ(1U, nodes.size()); + EXPECT_TRUE(nodes[0] == node1); + nodes.clear(); + + *query.url = ASCIIToUTF16("calendar"); + GetBookmarksMatchingProperties(&model, query, 100, string(), &nodes); + ASSERT_EQ(0U, nodes.size()); + nodes.clear(); + + // Empty URL should not match folders. + *query.url = ASCIIToUTF16(""); + GetBookmarksMatchingProperties(&model, query, 100, string(), &nodes); + ASSERT_EQ(0U, nodes.size()); + nodes.clear(); +} + +// Check exact matching against a title query. +TEST_F(BookmarkUtilsTest, GetBookmarksMatchingPropertiesTitle) { + BookmarkModel model(NULL); + const BookmarkNode* node1 = model.AddURL(model.other_node(), + 0, + ASCIIToUTF16("Google"), + GURL("https://www.google.com/")); + model.AddURL(model.other_node(), + 0, + ASCIIToUTF16("Google Calendar"), + GURL("https://www.google.com/calendar")); + + const BookmarkNode* folder1 = model.AddFolder(model.other_node(), + 0, + ASCIIToUTF16("Folder")); + + std::vector<const BookmarkNode*> nodes; + QueryFields query; + query.title.reset(new string16); + *query.title = ASCIIToUTF16("Google"); + GetBookmarksMatchingProperties(&model, query, 100, string(), &nodes); + ASSERT_EQ(1U, nodes.size()); + EXPECT_TRUE(nodes[0] == node1); + nodes.clear(); + + *query.title = ASCIIToUTF16("Calendar"); + GetBookmarksMatchingProperties(&model, query, 100, string(), &nodes); + ASSERT_EQ(0U, nodes.size()); + nodes.clear(); + + // Title should match folders. + *query.title = ASCIIToUTF16("Folder"); + GetBookmarksMatchingProperties(&model, query, 100, string(), &nodes); + ASSERT_EQ(1U, nodes.size()); + EXPECT_TRUE(nodes[0] == folder1); + nodes.clear(); +} + +// Check matching against a query with multiple predicates. +TEST_F(BookmarkUtilsTest, GetBookmarksMatchingPropertiesConjunction) { + BookmarkModel model(NULL); + const BookmarkNode* node1 = model.AddURL(model.other_node(), + 0, + ASCIIToUTF16("Google"), + GURL("https://www.google.com/")); + model.AddURL(model.other_node(), + 0, + ASCIIToUTF16("Google Calendar"), + GURL("https://www.google.com/calendar")); + + model.AddFolder(model.other_node(), + 0, + ASCIIToUTF16("Folder")); + + std::vector<const BookmarkNode*> nodes; + QueryFields query; + + // Test all fields matching. + query.word_phrase_query.reset(new string16(ASCIIToUTF16("www"))); + query.url.reset(new string16(ASCIIToUTF16("https://www.google.com/"))); + query.title.reset(new string16(ASCIIToUTF16("Google"))); + GetBookmarksMatchingProperties(&model, query, 100, string(), &nodes); + ASSERT_EQ(1U, nodes.size()); + EXPECT_TRUE(nodes[0] == node1); + nodes.clear(); + + scoped_ptr<string16>* fields[] = { + &query.word_phrase_query, &query.url, &query.title }; + + // Test two fields matching. + for (size_t i = 0; i < arraysize(fields); i++) { + scoped_ptr<string16> original_value(fields[i]->release()); + GetBookmarksMatchingProperties(&model, query, 100, string(), &nodes); + ASSERT_EQ(1U, nodes.size()); + EXPECT_TRUE(nodes[0] == node1); + nodes.clear(); + fields[i]->reset(original_value.release()); + } + + // Test two fields matching with one non-matching field. + for (size_t i = 0; i < arraysize(fields); i++) { + scoped_ptr<string16> original_value(fields[i]->release()); + fields[i]->reset(new string16(ASCIIToUTF16("fjdkslafjkldsa"))); + GetBookmarksMatchingProperties(&model, query, 100, string(), &nodes); + ASSERT_EQ(0U, nodes.size()); + nodes.clear(); + fields[i]->reset(original_value.release()); + } +} + TEST_F(BookmarkUtilsTest, CopyPaste) { BookmarkModel model(NULL); const BookmarkNode* node = model.AddURL(model.other_node(), diff --git a/chrome/browser/extensions/api/bookmarks/bookmarks_api.cc b/chrome/browser/extensions/api/bookmarks/bookmarks_api.cc index ccfed7e..9f6dbd6 100644 --- a/chrome/browser/extensions/api/bookmarks/bookmarks_api.cc +++ b/chrome/browser/extensions/api/bookmarks/bookmarks_api.cc @@ -438,12 +438,34 @@ bool BookmarksSearchFunction::RunImpl() { PrefService* prefs = user_prefs::UserPrefs::Get(GetProfile()); std::string lang = prefs->GetString(prefs::kAcceptLanguages); std::vector<const BookmarkNode*> nodes; - bookmark_utils::GetBookmarksContainingText( - BookmarkModelFactory::GetForProfile(GetProfile()), - UTF8ToUTF16(params->query), - std::numeric_limits<int>::max(), - lang, - &nodes); + if (params->query.as_string) { + bookmark_utils::QueryFields query; + query.word_phrase_query.reset( + new string16(UTF8ToUTF16(*params->query.as_string))); + bookmark_utils::GetBookmarksMatchingProperties( + BookmarkModelFactory::GetForProfile(GetProfile()), + query, + std::numeric_limits<int>::max(), + lang, + &nodes); + } else { + DCHECK(params->query.as_object); + const bookmarks::Search::Params::Query::Object& object = + *params->query.as_object; + bookmark_utils::QueryFields query; + if (object.query) + query.word_phrase_query.reset(new string16(UTF8ToUTF16(*object.query))); + if (object.url) + query.url.reset(new string16(UTF8ToUTF16(*object.url))); + if (object.title) + query.title.reset(new string16(UTF8ToUTF16(*object.title))); + bookmark_utils::GetBookmarksMatchingProperties( + BookmarkModelFactory::GetForProfile(GetProfile()), + query, + std::numeric_limits<int>::max(), + lang, + &nodes); + } std::vector<linked_ptr<BookmarkTreeNode> > tree_nodes; for (std::vector<const BookmarkNode*>::iterator node_iter = nodes.begin(); diff --git a/chrome/common/extensions/api/bookmarks.json b/chrome/common/extensions/api/bookmarks.json index e168362..fc0d1fe 100644 --- a/chrome/common/extensions/api/bookmarks.json +++ b/chrome/common/extensions/api/bookmarks.json @@ -195,11 +195,37 @@ { "name": "search", "type": "function", - "description": "Searches for BookmarkTreeNodes matching the given query.", + "description": "Searches for BookmarkTreeNodes matching the given query. Queries specified with an object produce BookmarkTreeNodes matching all specified properties.", "parameters": [ { - "type": "string", - "name": "query" + "name": "query", + "choices": [ + { + "type": "string", + "description": "A string of words and quoted phrases that are matched against bookmark URLs and titles." + }, + { + "type": "object", + "description": "An object specifying properties and values to match when searching. Produces bookmarks matching all properties.", + "properties": { + "query": { + "type": "string", + "optional": true, + "description": "A string of words and quoted phrases that are matched against bookmark URLs and titles." + }, + "url": { + "type": "string", + "optional": true, + "description": "The URL of the bookmark; matches verbatim. Note that folders have no URL." + }, + "title": { + "type": "string", + "optional": true, + "description": "The title of the bookmark; matches verbatim." + } + } + } + ] }, { "type": "function", |