diff options
author | wittman@chromium.org <wittman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-12-17 08:04:03 +0000 |
---|---|---|
committer | wittman@chromium.org <wittman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-12-17 08:04:03 +0000 |
commit | 308ec7ed3874d364653f0e8f19ed082784d2eb9b (patch) | |
tree | bf6ea874d74e2a2c2b88c232c046f4c6ac836b83 | |
parent | 420644e3034a6d64392d44935490818a5c50e6fe (diff) | |
download | chromium_src-308ec7ed3874d364653f0e8f19ed082784d2eb9b.zip chromium_src-308ec7ed3874d364653f0e8f19ed082784d2eb9b.tar.gz chromium_src-308ec7ed3874d364653f0e8f19ed082784d2eb9b.tar.bz2 |
chrome.bookmark.search exact URL/title query support
Allow queries for exact URL and title strings within
chrome.bookmark.search by accepting an object for the query parameter,
with the desired query predicates specified on the object.
API proposal document: https://docs.google.com/a/chromium.org/document/d/1XZHmtCBQNqaGUZLJPbSL7TXkP3AMhqyxwJk6ez5pMMs/edit
BUG=311282
Review URL: https://codereview.chromium.org/113073002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@241203 0039d316-1c4b-4281-b951-d872f2087c98
-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", |