diff options
author | sky@google.com <sky@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-08-21 15:20:33 +0000 |
---|---|---|
committer | sky@google.com <sky@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-08-21 15:20:33 +0000 |
commit | f25387b62a3cccde48622d0b7fca57cd6fb16ab7 (patch) | |
tree | 06ac2c1972d6608fb65979c3a279a6d214fecc6c /chrome/browser | |
parent | bcc682fc4f5050ac911635ab649fbd30002fc2b4 (diff) | |
download | chromium_src-f25387b62a3cccde48622d0b7fca57cd6fb16ab7.zip chromium_src-f25387b62a3cccde48622d0b7fca57cd6fb16ab7.tar.gz chromium_src-f25387b62a3cccde48622d0b7fca57cd6fb16ab7.tar.bz2 |
Moves bookmarks out of history into its own file (JSON).
Interesting points:
. Migration was a bit atypical. Here is the approach I took:
. If the URL db contains bookmarks it writes the bookmarks to a
temporary file.
. When the bookmark bar model is loaded it assumes bookmarks are
stored in a file. If the bookmarks file doesn't exist it then
attempts to load from history, after waiting for history to finish
processing tasks.
. I've broken having the omnibox query for starred only. This patch
was already too ginormous for me to contemplate this too. I'll return
to it after I land this.
. Similarly the history page isn't searching for starred titles
now. As we discussed with Glen, that is probably fine for now.
. I've converted NOTIFY_STARRED_FAVICON_CHANGED to
NOTIFY_FAVICON_CHANGED and it is notified ANY time a favicon
changes. I'm mildly concerned about the extra notifications, but
without having history know about starred it's the best I can do for
now.
. Autocomplete (specifically URLDatabase::AutocompleteForPrefix)
previously sorted by starred. It can no longer do this. I don't
think I can get this functionality back:( Luckily it only mattered
if you had a starred and non-starred URL with the same type count
that matched a query. Probably pretty rare.
What's left:
. Fix up HistoryContentsProvider to query for starred entries titles.
. Clean up the delete all case. I basically just made it compile; it
can be greatly simplified.
. Rename BookmarkBarModel to BookmarksModel.
BUG=1256202
TEST=this is a huge change to bookmarks. Thanfully it's pretty well
covered by tests, none-the-less make sure you exercise bookmarks
pretty heavily to make sure nothing is busted.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@1153 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
48 files changed, 1172 insertions, 2386 deletions
diff --git a/chrome/browser/autocomplete/history_contents_provider.cc b/chrome/browser/autocomplete/history_contents_provider.cc index c68b17c..564f832 100644 --- a/chrome/browser/autocomplete/history_contents_provider.cc +++ b/chrome/browser/autocomplete/history_contents_provider.cc @@ -30,6 +30,7 @@ #include "chrome/browser/autocomplete/history_contents_provider.h" #include "base/string_util.h" +#include "chrome/browser/bookmark_bar_model.h" #include "chrome/browser/history/query_parser.h" #include "chrome/browser/profile.h" #include "net/base/net_util.h" @@ -108,6 +109,7 @@ void HistoryContentsProvider::Start(const AutocompleteInput& input, return; } + // TODO(sky): this needs to query BookmarkBarModel for starred entries. if (!synchronous_only) { HistoryService* history = history_service_ ? history_service_ : profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); @@ -201,7 +203,8 @@ AutocompleteMatch HistoryContentsProvider::ResultToMatch( match.contents_class.push_back( ACMatchClassification(0, ACMatchClassification::URL)); match.description = result.title(); - match.starred = result.starred(); + match.starred = + (profile_ && profile_->GetBookmarkBarModel()->IsBookmarked(result.url())); ClassifyDescription(result, &match); return match; @@ -235,11 +238,13 @@ void HistoryContentsProvider::ClassifyDescription( int HistoryContentsProvider::CalculateRelevance( const history::URLResult& result) { bool in_title = !!result.title_match_positions().size(); + bool is_starred = + (profile_ && profile_->GetBookmarkBarModel()->IsBookmarked(result.url())); switch (input_type_) { case AutocompleteInput::UNKNOWN: case AutocompleteInput::REQUESTED_URL: - if (result.starred()) { + if (is_starred) { return in_title ? 1000 + star_title_count_++ : 550 + star_contents_count_++; } else { @@ -249,7 +254,7 @@ int HistoryContentsProvider::CalculateRelevance( case AutocompleteInput::QUERY: case AutocompleteInput::FORCED_QUERY: - if (result.starred()) { + if (is_starred) { return in_title ? 1200 + star_title_count_++ : 750 + star_contents_count_++; } else { diff --git a/chrome/browser/autocomplete/history_url_provider.cc b/chrome/browser/autocomplete/history_url_provider.cc index 1db8c00..d0ee441 100644 --- a/chrome/browser/autocomplete/history_url_provider.cc +++ b/chrome/browser/autocomplete/history_url_provider.cc @@ -48,6 +48,8 @@ #include "googleurl/src/url_util.h" #include "net/base/net_util.h" +// TODO(sky): this needs to check and update starred state. + HistoryURLProviderParams::HistoryURLProviderParams( const AutocompleteInput& input, bool trim_http, @@ -315,7 +317,6 @@ bool HistoryURLProvider::FixupExactSuggestion(history::URLDatabase* db, return false; } else { // We have data for this match, use it. - match.starred = info.starred(); match.deletable = true; match.description = info.title(); AutocompleteMatch::ClassifyMatchInString(params->input.text(), @@ -439,10 +440,6 @@ bool HistoryURLProvider::CompareHistoryMatch(const HistoryMatch& a, if (a.url_info.typed_count() != b.url_info.typed_count()) return a.url_info.typed_count() > b.url_info.typed_count(); - // Starred pages are better than unstarred pages. - if (a.url_info.starred() != b.url_info.starred()) - return a.url_info.starred(); - // For URLs that have each been typed once, a host (alone) is better than a // page inside. if (a.url_info.typed_count() == 1) { @@ -851,6 +848,5 @@ AutocompleteMatch HistoryURLProvider::HistoryMatchToACMatch( ACMatchClassification::NONE, &match.description_class); - match.starred = history_match.url_info.starred(); return match; } diff --git a/chrome/browser/autocomplete/history_url_provider_unittest.cc b/chrome/browser/autocomplete/history_url_provider_unittest.cc index 44486e7..9e70a38 100644 --- a/chrome/browser/autocomplete/history_url_provider_unittest.cc +++ b/chrome/browser/autocomplete/history_url_provider_unittest.cc @@ -31,7 +31,9 @@ #include "base/message_loop.h" #include "base/path_service.h" #include "chrome/browser/autocomplete/history_url_provider.h" +#include "chrome/browser/bookmark_bar_model.h" #include "chrome/browser/history/history.h" +#include "chrome/test/testing_profile.h" #include "testing/gtest/include/gtest/gtest.h" struct TestURLInfo { @@ -122,10 +124,10 @@ class HistoryURLProviderTest : public testing::Test, int num_results); ACMatches matches_; - scoped_refptr<HistoryService> history_service_; + scoped_ptr<TestingProfile> profile_; + HistoryService* history_service_; private: - std::wstring history_dir_; scoped_refptr<HistoryURLProvider> autocomplete_; }; @@ -135,32 +137,18 @@ void HistoryURLProviderTest::OnProviderUpdate(bool updated_matches) { } void HistoryURLProviderTest::SetUp() { - PathService::Get(base::DIR_TEMP, &history_dir_); - file_util::AppendToPath(&history_dir_, L"HistoryURLProviderTest"); - file_util::Delete(history_dir_, true); // Normally won't exist. - file_util::CreateDirectoryW(history_dir_); + profile_.reset(new TestingProfile()); + profile_->CreateBookmarkBarModel(); + profile_->CreateHistoryService(true); + history_service_ = profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); - history_service_ = new HistoryService; - history_service_->Init(history_dir_); - - autocomplete_ = new HistoryURLProvider(this, history_service_); + autocomplete_ = new HistoryURLProvider(this, profile_.get()); FillData(); } void HistoryURLProviderTest::TearDown() { - history_service_->SetOnBackendDestroyTask(new MessageLoop::QuitTask); - history_service_->Cleanup(); autocomplete_ = NULL; - history_service_ = NULL; - - // Wait for history thread to complete (the QuitTask will cause it to exit - // on destruction). Note: if this never terminates, somebody is probably - // leaking a reference to the history backend, so it never calls our - // destroy task. - MessageLoop::current()->Run(); - - file_util::Delete(history_dir_, true); } void HistoryURLProviderTest::FillData() { @@ -180,11 +168,8 @@ void HistoryURLProviderTest::FillData() { cur.visit_count, cur.typed_count, visit_time, false); if (cur.starred) { - history::StarredEntry star_entry; - star_entry.type = history::StarredEntry::URL; - star_entry.parent_group_id = HistoryService::kBookmarkBarID; - star_entry.url = current_url; - history_service_->CreateStarredEntry(star_entry, NULL, NULL); + profile_->GetBookmarkBarModel()->SetURLStarred( + current_url, std::wstring(), true); } } } @@ -288,7 +273,9 @@ TEST_F(HistoryURLProviderTest, PromoteShorterURLs) { arraysize(short_4)); } -TEST_F(HistoryURLProviderTest, Starred) { +// Bookmarks have been moved out of the history db, resulting in this no longer +// working. See TODO in URLDatabase::AutocompleteForPrefix. +TEST_F(HistoryURLProviderTest, DISABLED_Starred) { // Test that starred pages sort properly. const std::wstring star_1[] = { L"http://startest/", diff --git a/chrome/browser/autocomplete/search_provider.cc b/chrome/browser/autocomplete/search_provider.cc index fbc24b5..6f66694 100644 --- a/chrome/browser/autocomplete/search_provider.cc +++ b/chrome/browser/autocomplete/search_provider.cc @@ -31,6 +31,7 @@ #include "base/message_loop.h" #include "base/string_util.h" +#include "chrome/browser/bookmark_bar_model.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/google_util.h" #include "chrome/browser/profile.h" @@ -238,7 +239,9 @@ void SearchProvider::OnQueryURLComplete(HistoryService::Handle handle, bool success, const history::URLRow* url_row, history::VisitVector* unused) { - bool is_starred = success ? url_row->starred() : false; + bool is_starred = + (success && profile_ && + profile_->GetBookmarkBarModel()->IsBookmarked(url_row->url())); star_requests_pending_ = false; // We can't just use star_request_consumer_.HasPendingRequests() here; // see comment in ConvertResultsToAutocompleteMatches(). diff --git a/chrome/browser/bookmark_bar_model.cc b/chrome/browser/bookmark_bar_model.cc index c6c9312..e9a0511 100644 --- a/chrome/browser/bookmark_bar_model.cc +++ b/chrome/browser/bookmark_bar_model.cc @@ -30,11 +30,36 @@ #include "chrome/browser/bookmark_bar_model.h" #include "base/gfx/png_decoder.h" +#include "chrome/browser/history/query_parser.h" #include "chrome/browser/profile.h" +#include "chrome/browser/bookmark_storage.h" +#include "chrome/common/scoped_vector.h" + #include "generated_resources.h" +namespace { + +// Functions used for sorting. +bool MoreRecentlyModified(BookmarkBarNode* n1, BookmarkBarNode* n2) { + return n1->date_group_modified() > n2->date_group_modified(); +} + +bool MoreRecentlyAdded(BookmarkBarNode* n1, BookmarkBarNode* n2) { + return n1->date_added() > n2->date_added(); +} + +} // namespace + // BookmarkBarNode ------------------------------------------------------------ +namespace { + +// ID for BookmarkBarNodes. +// Various places assume an invalid id if == 0, for that reason we start with 1. +int next_id_ = 1; + +} + const SkBitmap& BookmarkBarNode::GetFavIcon() { if (!loaded_favicon_) { loaded_favicon_ = true; @@ -43,95 +68,74 @@ const SkBitmap& BookmarkBarNode::GetFavIcon() { return favicon_; } -BookmarkBarNode::BookmarkBarNode(BookmarkBarModel* model) +BookmarkBarNode::BookmarkBarNode(BookmarkBarModel* model, const GURL& url) : model_(model), - group_id_(0), - star_id_(0), + id_(next_id_++), loaded_favicon_(false), favicon_load_handle_(0), - type_(history::StarredEntry::BOOKMARK_BAR), + url_(url), + type_(!url.is_empty() ? history::StarredEntry::URL : + history::StarredEntry::BOOKMARK_BAR), date_added_(Time::Now()) { - DCHECK(model_); } void BookmarkBarNode::Reset(const history::StarredEntry& entry) { - // We should either have no id, or the id of the new entry should match - // this. - DCHECK(!star_id_ || star_id_ == entry.id); - star_id_ = entry.id; - group_id_ = entry.group_id; - url_ = entry.url; + DCHECK(entry.type != history::StarredEntry::URL || + entry.url == url_); + favicon_ = SkBitmap(); - loaded_favicon_ = false; - favicon_load_handle_ = 0; type_ = entry.type; date_added_ = entry.date_added; date_group_modified_ = entry.date_group_modified; SetTitle(entry.title); } -void BookmarkBarNode::SetURL(const GURL& url) { - DCHECK(favicon_load_handle_ == 0); - loaded_favicon_ = false; - favicon_load_handle_ = 0; - url_ = url; - favicon_ .reset(); -} - -history::StarredEntry BookmarkBarNode::GetEntry() { - history::StarredEntry entry; - entry.id = GetStarID(); - entry.group_id = group_id_; - entry.url = GetURL(); - entry.title = GetTitle(); - entry.type = type_; - entry.date_added = date_added_; - entry.date_group_modified = date_group_modified_; - // Only set the parent and visual order if we have a valid parent (the root - // node is not in the db and has a group_id of 0). - if (GetParent() && GetParent()->group_id_) { - entry.visual_order = GetParent()->IndexOfChild(this); - entry.parent_group_id = GetParent()->GetGroupID(); - } - return entry; -} - // BookmarkBarModel ----------------------------------------------------------- BookmarkBarModel::BookmarkBarModel(Profile* profile) : profile_(profile), loaded_(false), #pragma warning(suppress: 4355) // Okay to pass "this" here. - root_(this), - // See declaration for description. - next_group_id_(HistoryService::kBookmarkBarID + 1), + root_(this, GURL()), bookmark_bar_node_(NULL), other_node_(NULL) { - // Notifications we want. - if (profile_) - NotificationService::current()->AddObserver( - this, NOTIFY_STARRED_FAVICON_CHANGED, Source<Profile>(profile_)); - - if (!profile || !profile_->GetHistoryService(Profile::EXPLICIT_ACCESS)) { - // Profile/HistoryService is NULL during testing. - CreateBookmarkBarNode(); - CreateOtherBookmarksNode(); - AddRootChildren(NULL); + // Create the bookmark bar and other bookmarks folders. These always exist. + CreateBookmarkBarNode(); + CreateOtherBookmarksNode(); + + // And add them to the root. + // + // WARNING: order is important here, various places assume bookmark bar then + // other node. + root_.Add(0, bookmark_bar_node_); + root_.Add(1, other_node_); + + if (!profile_) { + // Profile is null during testing. loaded_ = true; return; } - // Request the entries on the bookmark bar. - profile_->GetHistoryService(Profile::EXPLICIT_ACCESS)-> - GetAllStarredEntries(&load_consumer_, - NewCallback(this, - &BookmarkBarModel::OnGotStarredEntries)); + // Listen for changes to starred icons so that we can update the favicon of + // the node appropriately. + NotificationService::current()->AddObserver( + this, NOTIFY_FAVICON_CHANGED, Source<Profile>(profile_)); + + // Load the bookmarks. BookmarkStorage notifies us when done. + store_ = new BookmarkStorage(profile_, this); + store_->LoadBookmarks(false); } BookmarkBarModel::~BookmarkBarModel() { if (profile_) { NotificationService::current()->RemoveObserver( - this, NOTIFY_STARRED_FAVICON_CHANGED, Source<Profile>(profile_)); + this, NOTIFY_FAVICON_CHANGED, Source<Profile>(profile_)); + } + + if (store_) { + // The store maintains a reference back to us. We need to tell it we're gone + // so that it doesn't try and invoke a method back on us again. + store_->BookmarkModelDeleted(); } } @@ -160,38 +164,50 @@ std::vector<BookmarkBarNode*> BookmarkBarModel::GetMostRecentlyModifiedGroups( return nodes; } -void BookmarkBarModel::Remove(BookmarkBarNode* parent, int index) { -#ifndef NDEBUG - CheckIndex(parent, index, false); -#endif - if (parent != &root_) { - RemoveAndDeleteNode(parent->GetChild(index)); - } else { - NOTREACHED(); // Can't remove from the root. +void BookmarkBarModel::GetMostRecentlyAddedEntries( + size_t count, + std::vector<BookmarkBarNode*>* nodes) { + for (NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.begin(); + i != nodes_ordered_by_url_set_.end(); ++i) { + std::vector<BookmarkBarNode*>::iterator insert_position = + std::upper_bound(nodes->begin(), nodes->end(), *i, &MoreRecentlyAdded); + if (nodes->size() < count || insert_position != nodes->end()) { + nodes->insert(insert_position, *i); + while (nodes->size() > count) + nodes->pop_back(); + } } } -void BookmarkBarModel::RemoveFromBookmarkBar(BookmarkBarNode* node) { - if (!node->HasAncestor(bookmark_bar_node_)) +void BookmarkBarModel::GetBookmarksMatchingText( + const std::wstring& text, + std::vector<BookmarkBarNode*>* nodes) { + QueryParser parser; + ScopedVector<QueryNode> query_nodes; + parser.ParseQuery(text, &query_nodes.get()); + if (query_nodes.empty()) return; - if (node != &root_ && node != bookmark_bar_node_ && node != other_node_) { - Move(node, other_node_, other_node_->GetChildCount()); - } else { - NOTREACHED(); // Can't move the root, bookmark bar or other nodes. + for (NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.begin(); + i != nodes_ordered_by_url_set_.end(); ++i) { + if (parser.DoesQueryMatch((*i)->GetTitle(), query_nodes.get())) + nodes->push_back(*i); } } +void BookmarkBarModel::Remove(BookmarkBarNode* parent, int index) { + if (!loaded_ || !IsValidIndex(parent, index, false) || parent == &root_) { + NOTREACHED(); + return; + } + RemoveAndDeleteNode(parent->GetChild(index)); +} + void BookmarkBarModel::Move(BookmarkBarNode* node, BookmarkBarNode* new_parent, int index) { - DCHECK(node && new_parent); - DCHECK(node->GetParent()); -#ifndef NDEBUG - CheckIndex(new_parent, index, true); -#endif - - if (new_parent == &root_ || node == &root_ || node == bookmark_bar_node_ || + if (!loaded_ || !node || !IsValidIndex(new_parent, index, true) || + new_parent == &root_ || node == &root_ || node == bookmark_bar_node_ || node == other_node_) { NOTREACHED(); return; @@ -218,10 +234,9 @@ void BookmarkBarModel::Move(BookmarkBarNode* node, index--; new_parent->Add(index, node); - HistoryService* history = !profile_ ? NULL : - profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); - if (history) - history->UpdateStarredEntry(node->GetEntry()); + if (store_.get()) + store_->ScheduleSave(); + FOR_EACH_OBSERVER(BookmarkBarModelObserver, observers_, BookmarkNodeMoved(this, old_parent, old_index, new_parent, index)); @@ -229,47 +244,44 @@ void BookmarkBarModel::Move(BookmarkBarNode* node, void BookmarkBarModel::SetTitle(BookmarkBarNode* node, const std::wstring& title) { - DCHECK(node); + if (!node) { + NOTREACHED(); + return; + } if (node->GetTitle() == title) return; + node->SetTitle(title); - HistoryService* history = !profile_ ? NULL : - profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); - if (history) - history->UpdateStarredEntry(node->GetEntry()); + + if (store_.get()) + store_->ScheduleSave(); + FOR_EACH_OBSERVER(BookmarkBarModelObserver, observers_, BookmarkNodeChanged(this, node)); } BookmarkBarNode* BookmarkBarModel::GetNodeByURL(const GURL& url) { - BookmarkBarNode tmp_node(this); - tmp_node.url_ = url; + BookmarkBarNode tmp_node(this, url); NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.find(&tmp_node); return (i != nodes_ordered_by_url_set_.end()) ? *i : NULL; } -BookmarkBarNode* BookmarkBarModel::GetNodeByGroupID( - history::UIStarID group_id) { +BookmarkBarNode* BookmarkBarModel::GetNodeByID(int id) { // TODO(sky): TreeNode needs a method that visits all nodes using a predicate. - return GetNodeByGroupID(&root_, group_id); + return GetNodeByID(&root_, id); } BookmarkBarNode* BookmarkBarModel::AddGroup( BookmarkBarNode* parent, int index, const std::wstring& title) { - DCHECK(IsLoaded()); -#ifndef NDEBUG - CheckIndex(parent, index, true); -#endif - if (parent == &root_) { + if (!loaded_ || parent == &root_ || !IsValidIndex(parent, index, true)) { // Can't add to the root. NOTREACHED(); return NULL; } - BookmarkBarNode* new_node = new BookmarkBarNode(this); - new_node->group_id_ = next_group_id_++; + BookmarkBarNode* new_node = new BookmarkBarNode(this, GURL()); new_node->SetTitle(title); new_node->type_ = history::StarredEntry::USER_GROUP; @@ -289,15 +301,11 @@ BookmarkBarNode* BookmarkBarModel::AddURLWithCreationTime( const std::wstring& title, const GURL& url, const Time& creation_time) { - DCHECK(IsLoaded() && url.is_valid() && parent); - if (parent == &root_) { - // Can't add to the root. + if (!loaded_ || !url.is_valid() || parent == &root_ || + !IsValidIndex(parent, index, true)) { NOTREACHED(); return NULL; } -#ifndef NDEBUG - CheckIndex(parent, index, true); -#endif BookmarkBarNode* existing_node = GetNodeByURL(url); if (existing_node) { @@ -308,9 +316,8 @@ BookmarkBarNode* BookmarkBarModel::AddURLWithCreationTime( SetDateGroupModified(parent, creation_time); - BookmarkBarNode* new_node = new BookmarkBarNode(this); + BookmarkBarNode* new_node = new BookmarkBarNode(this, url); new_node->SetTitle(title); - new_node->SetURL(url); new_node->date_added_ = creation_time; new_node->type_ = history::StarredEntry::URL; @@ -342,139 +349,84 @@ void BookmarkBarModel::FavIconLoaded(BookmarkBarNode* node) { BookmarkNodeFavIconLoaded(this, node)); } -void BookmarkBarModel::RemoveNode(BookmarkBarNode* node) { - DCHECK(node && node != bookmark_bar_node_ && node != other_node_); +void BookmarkBarModel::RemoveNode(BookmarkBarNode* node, + std::set<GURL>* removed_urls) { + if (!loaded_ || !node || node == &root_ || node == bookmark_bar_node_ || + node == other_node_) { + NOTREACHED(); + return; + } + if (node->GetType() == history::StarredEntry::URL) { NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.find(node); DCHECK(i != nodes_ordered_by_url_set_.end()); nodes_ordered_by_url_set_.erase(i); - } - - NodeToHandleMap::iterator i = node_to_handle_map_.find(node); - if (i != node_to_handle_map_.end()) { - HistoryService* history = !profile_ ? NULL : - profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); - if (history) - request_consumer_.SetClientData(history, i->second, NULL); - node_to_handle_map_.erase(i); + removed_urls->insert(node->GetURL()); } CancelPendingFavIconLoadRequests(node); // Recurse through children. for (int i = node->GetChildCount() - 1; i >= 0; --i) - RemoveNode(node->GetChild(i)); + RemoveNode(node->GetChild(i), removed_urls); } -void BookmarkBarModel::OnGotStarredEntries( - HistoryService::Handle, - std::vector<history::StarredEntry>* entries) { +void BookmarkBarModel::OnBookmarkStorageLoadedBookmarks( + bool file_exists, + bool loaded_from_history) { if (loaded_) { NOTREACHED(); return; } - DCHECK(entries); - // Create tree nodes for each of the elements. - PopulateNodes(entries); - - // Yes, we've finished loading. - loaded_ = true; + if (file_exists || loaded_from_history || !profile_ || + !profile_->GetHistoryService(Profile::EXPLICIT_ACCESS)) { + if (loaded_from_history) { + // We were just populated from the historical file. Schedule a save so + // that the main file is up to date. + store_->ScheduleSave(); + } - FOR_EACH_OBSERVER(BookmarkBarModelObserver, observers_, Loaded(this)); + // The file exists, we're loaded. + DoneLoading(); + return; + } - NotificationService::current()->Notify( - NOTIFY_BOOKMARK_MODEL_LOADED, - Source<Profile>(profile_), - NotificationService::NoDetails()); + // The file doesn't exist. If the bookmarks were in the db the history db + // will copy them to a file on init. Schedule an empty request to history so + // that we know history will have saved the bookmarks (if it had them). When + // the callback is processed + // when done we'll try and load the bookmarks again. + profile_->GetHistoryService(Profile::EXPLICIT_ACCESS)-> + ScheduleEmptyCallback(&load_consumer_, + NewCallback(this, &BookmarkBarModel::OnHistoryDone)); } -void BookmarkBarModel::PopulateNodes( - std::vector<history::StarredEntry>* entries) { - std::map<history::UIStarID,history::StarID> group_id_to_id_map; - IDToNodeMap id_to_node_map; - - // Iterate through the entries building a mapping between group_id and id as - // well as creating the bookmark bar node and other node. - for (std::vector<history::StarredEntry>::const_iterator i = entries->begin(); - i != entries->end(); ++i) { - if (i->type == history::StarredEntry::URL) - continue; - - if (i->type == history::StarredEntry::OTHER) - other_node_ = CreateRootNodeFromStarredEntry(*i); - else if (i->type == history::StarredEntry::BOOKMARK_BAR) - bookmark_bar_node_ = CreateRootNodeFromStarredEntry(*i); - - group_id_to_id_map[i->group_id] = i->id; +void BookmarkBarModel::OnHistoryDone(HistoryService::Handle handle) { + if (loaded_) { + NOTREACHED(); + return; } - // Add the bookmark bar and other nodes to the root node. - AddRootChildren(&id_to_node_map); - - // If the db was corrupt and we didn't get the bookmark bar/other nodes, - // AddRootChildren will create them. Update the map to make sure it includes - // these nodes. - group_id_to_id_map[other_node_->group_id_] = other_node_->star_id_; - group_id_to_id_map[bookmark_bar_node_->group_id_] = - bookmark_bar_node_->star_id_; - - // Iterate through the entries again creating the nodes. - for (std::vector<history::StarredEntry>::iterator i = entries->begin(); - i != entries->end(); ++i) { - if (!i->parent_group_id) { - DCHECK(i->type == history::StarredEntry::BOOKMARK_BAR || - i->type == history::StarredEntry::OTHER); - // Ignore entries not parented to the bookmark bar. - continue; - } + // If the bookmarks were stored in the db the db will have migrated them to + // a file now. Try loading from the file. + store_->LoadBookmarks(true); +} - BookmarkBarNode* node = id_to_node_map[i->id]; - if (!node) { - // Creating a node results in creating the parent. As such, it is - // possible for the node representing a group to have been created before - // encountering the details. +void BookmarkBarModel::DoneLoading() { + // Update nodes_ordered_by_url_set_ from the nodes. + PopulateNodesByURL(&root_); - // The created nodes are owned by the root node. - node = new BookmarkBarNode(this); - id_to_node_map[i->id] = node; - } - node->Reset(*i); - - DCHECK(group_id_to_id_map.find(i->parent_group_id) != - group_id_to_id_map.end()); - history::StarID parent_id = group_id_to_id_map[i->parent_group_id]; - BookmarkBarNode* parent = id_to_node_map[parent_id]; - if (!parent) { - // Haven't encountered the parent yet, create it now. - parent = new BookmarkBarNode(this); - id_to_node_map[parent_id] = parent; - } - - if (i->type == history::StarredEntry::URL) - nodes_ordered_by_url_set_.insert(node); - else - next_group_id_ = std::max(next_group_id_, i->group_id + 1); + loaded_ = true; - // Add the node to its parent. entries is ordered by parent then - // visual order so that we know we maintain visual order by always adding - // to the end. - parent->Add(parent->GetChildCount(), node); - } -} + // Notify our direct observers. + FOR_EACH_OBSERVER(BookmarkBarModelObserver, observers_, Loaded(this)); -void BookmarkBarModel::OnCreatedEntry(HistoryService::Handle handle, - history::StarID id) { - BookmarkBarNode* node = request_consumer_.GetClientData( - profile_->GetHistoryService(Profile::EXPLICIT_ACCESS), handle); - // Node is NULL if the node was removed by the user before the request - // was processed. - if (node) { - DCHECK(!node->star_id_); - node->star_id_ = id; - DCHECK(node_to_handle_map_.find(node) != node_to_handle_map_.end()); - node_to_handle_map_.erase(node_to_handle_map_.find(node)); - } + // And generic notification. + NotificationService::current()->Notify( + NOTIFY_BOOKMARK_MODEL_LOADED, + Source<Profile>(profile_), + NotificationService::NoDetails()); } void BookmarkBarModel::RemoveAndDeleteNode(BookmarkBarNode* delete_me) { @@ -484,84 +436,79 @@ void BookmarkBarModel::RemoveAndDeleteNode(BookmarkBarNode* delete_me) { DCHECK(parent); int index = parent->IndexOfChild(node.get()); parent->Remove(index); - RemoveNode(node.get()); + history::URLsStarredDetails details(false); + RemoveNode(node.get(), &details.changed_urls); + + if (store_.get()) + store_->ScheduleSave(); - HistoryService* history = profile_ ? - profile_->GetHistoryService(Profile::EXPLICIT_ACCESS) : NULL; - if (history) { - if (node->GetType() == history::StarredEntry::URL) { - history->DeleteStarredURL(node->GetURL()); - } else { - history->DeleteStarredGroup(node->GetGroupID()); - } - } FOR_EACH_OBSERVER(BookmarkBarModelObserver, observers_, BookmarkNodeRemoved(this, parent, index)); + + NotificationService::current()->Notify(NOTIFY_URLS_STARRED, + Source<Profile>(profile_), + Details<history::URLsStarredDetails>(&details)); } BookmarkBarNode* BookmarkBarModel::AddNode(BookmarkBarNode* parent, - int index, - BookmarkBarNode* node) { + int index, + BookmarkBarNode* node) { parent->Add(index, node); - // NOTE: As history calls us back when we invoke CreateStarredEntry, we have - // to be sure to invoke it after we've updated the nodes appropriately. - HistoryService* history = !profile_ ? NULL : - profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); - if (history) { - HistoryService::Handle handle = - history->CreateStarredEntry(node->GetEntry(), &request_consumer_, - NewCallback(this, &BookmarkBarModel::OnCreatedEntry)); - request_consumer_.SetClientData(history, handle, node); - node_to_handle_map_[node] = handle; - } + if (store_.get()) + store_->ScheduleSave(); + FOR_EACH_OBSERVER(BookmarkBarModelObserver, observers_, BookmarkNodeAdded(this, parent, index)); + + if (node->GetType() == history::StarredEntry::URL) { + history::URLsStarredDetails details(true); + details.changed_urls.insert(node->GetURL()); + NotificationService::current()->Notify(NOTIFY_URLS_STARRED, + Source<Profile>(profile_), + Details<history::URLsStarredDetails>(&details)); + } return node; } -BookmarkBarNode* BookmarkBarModel::GetNodeByGroupID( - BookmarkBarNode* node, - history::UIStarID group_id) { - if (node->GetType() == history::StarredEntry::USER_GROUP && - node->GetGroupID() == group_id) { +BookmarkBarNode* BookmarkBarModel::GetNodeByID(BookmarkBarNode* node, + int id) { + if (node->id() == id) return node; - } + for (int i = 0; i < node->GetChildCount(); ++i) { - BookmarkBarNode* result = GetNodeByGroupID(node->GetChild(i), group_id); + BookmarkBarNode* result = GetNodeByID(node->GetChild(i), id); if (result) return result; } return NULL; } +bool BookmarkBarModel::IsValidIndex(BookmarkBarNode* parent, + int index, + bool allow_end) { + return (parent && + (index >= 0 && (index < parent->GetChildCount() || + (allow_end && index == parent->GetChildCount())))); + } + void BookmarkBarModel::SetDateGroupModified(BookmarkBarNode* parent, const Time time) { DCHECK(parent); parent->date_group_modified_ = time; - HistoryService* history = profile_ ? - profile_->GetHistoryService(Profile::EXPLICIT_ACCESS) : NULL; - if (history) - history->UpdateStarredEntry(parent->GetEntry()); + + if (store_.get()) + store_->ScheduleSave(); } void BookmarkBarModel::CreateBookmarkBarNode() { history::StarredEntry entry; - entry.id = HistoryService::kBookmarkBarID; - entry.group_id = HistoryService::kBookmarkBarID; entry.type = history::StarredEntry::BOOKMARK_BAR; bookmark_bar_node_ = CreateRootNodeFromStarredEntry(entry); } void BookmarkBarModel::CreateOtherBookmarksNode() { history::StarredEntry entry; - entry.id = HistoryService::kBookmarkBarID; - for (NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.begin(); - i != nodes_ordered_by_url_set_.end(); ++i) { - entry.id = std::max(entry.id, (*i)->GetStarID()); - } - entry.id++; - entry.group_id = next_group_id_++; entry.type = history::StarredEntry::OTHER; other_node_ = CreateRootNodeFromStarredEntry(entry); } @@ -570,43 +517,15 @@ BookmarkBarNode* BookmarkBarModel::CreateRootNodeFromStarredEntry( const history::StarredEntry& entry) { DCHECK(entry.type == history::StarredEntry::BOOKMARK_BAR || entry.type == history::StarredEntry::OTHER); - BookmarkBarNode* node = new BookmarkBarNode(this); + BookmarkBarNode* node = new BookmarkBarNode(this, GURL()); node->Reset(entry); if (entry.type == history::StarredEntry::BOOKMARK_BAR) node->SetTitle(l10n_util::GetString(IDS_BOOMARK_BAR_FOLDER_NAME)); else node->SetTitle(l10n_util::GetString(IDS_BOOMARK_BAR_OTHER_FOLDER_NAME)); - next_group_id_ = std::max(next_group_id_, entry.group_id + 1); return node; } -void BookmarkBarModel::AddRootChildren(IDToNodeMap* id_to_node_map) { - // When invoked we should have created the bookmark bar and other nodes. If - // not, it indicates the db couldn't be loaded correctly or is corrupt. - // Force creation so that bookmark bar model is still usable. - if (!bookmark_bar_node_) { - LOG(WARNING) << "No bookmark bar entry in the database. This indicates " - "bookmarks database couldn't be loaded or is corrupt."; - CreateBookmarkBarNode(); - } - if (!other_node_) { - LOG(WARNING) << "No other folders bookmark bar entry in the database. This " - "indicates bookmarks database couldn't be loaded or is " - "corrupt."; - CreateOtherBookmarksNode(); - } - - if (id_to_node_map) { - (*id_to_node_map)[other_node_->GetStarID()] = other_node_; - (*id_to_node_map)[bookmark_bar_node_->GetStarID()] = bookmark_bar_node_; - } - - // WARNING: order is important here, various places assume bookmark bar then - // other node. - root_.Add(0, bookmark_bar_node_); - root_.Add(1, other_node_); -} - void BookmarkBarModel::OnFavIconDataAvailable( HistoryService::Handle handle, bool know_favicon, @@ -640,6 +559,7 @@ void BookmarkBarModel::LoadFavIcon(BookmarkBarNode* node) { node->GetURL(), &load_consumer_, NewCallback(this, &BookmarkBarModel::OnFavIconDataAvailable)); load_consumer_.SetClientData(history_service, handle, node); + node->favicon_load_handle_ = handle; } void BookmarkBarModel::CancelPendingFavIconLoadRequests(BookmarkBarNode* node) { @@ -652,17 +572,12 @@ void BookmarkBarModel::CancelPendingFavIconLoadRequests(BookmarkBarNode* node) { } } -// static -bool BookmarkBarModel::MoreRecentlyModified(BookmarkBarNode* n1, - BookmarkBarNode* n2) { - return n1->date_group_modified_ > n2->date_group_modified_; -} - void BookmarkBarModel::GetMostRecentlyModifiedGroupNodes( BookmarkBarNode* parent, size_t count, std::vector<BookmarkBarNode*>* nodes) { - if (parent->group_id_ && parent->date_group_modified_ > Time()) { + if (parent != &root_ && parent->is_folder() && + parent->date_group_modified() > Time()) { if (count == 0) { nodes->push_back(parent); } else { @@ -679,9 +594,8 @@ void BookmarkBarModel::GetMostRecentlyModifiedGroupNodes( // (which have a time of 0). for (int i = 0; i < parent->GetChildCount(); ++i) { BookmarkBarNode* child = parent->GetChild(i); - if (child->GetType() != history::StarredEntry::URL) { + if (child->is_folder()) GetMostRecentlyModifiedGroupNodes(child, count, nodes); - } } } @@ -689,7 +603,7 @@ void BookmarkBarModel::Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details) { switch (type) { - case NOTIFY_STARRED_FAVICON_CHANGED: { + case NOTIFY_FAVICON_CHANGED: { // Prevent the observers from getting confused for multiple favicon loads. Details<history::FavIconChangeDetails> favicon_details(details); for (std::set<GURL>::const_iterator i = favicon_details->urls.begin(); @@ -711,3 +625,10 @@ void BookmarkBarModel::Observe(NotificationType type, break; } } + +void BookmarkBarModel::PopulateNodesByURL(BookmarkBarNode* node) { + if (node->is_url()) + nodes_ordered_by_url_set_.insert(node); + for (int i = 0; i < node->GetChildCount(); ++i) + PopulateNodesByURL(node->GetChild(i)); +} diff --git a/chrome/browser/bookmark_bar_model.h b/chrome/browser/bookmark_bar_model.h index f7ecf71..a481004 100644 --- a/chrome/browser/bookmark_bar_model.h +++ b/chrome/browser/bookmark_bar_model.h @@ -31,6 +31,7 @@ #define CHROME_BROWSER_BOOKMARK_BAR_MODEL_H_ #include "base/observer_list.h" +#include "chrome/browser/bookmark_storage.h" #include "chrome/browser/cancelable_request.h" #include "chrome/browser/history/history.h" #include "chrome/browser/history/history_types.h" @@ -43,6 +44,10 @@ class BookmarkBarModel; class BookmarkCodec; class Profile; +namespace history { +class StarredURLDatabase; +} + // BookmarkBarNode ------------------------------------------------------------ // BookmarkBarNode contains information about a starred entry: title, URL, @@ -52,9 +57,11 @@ class Profile; class BookmarkBarNode : public ChromeViews::TreeNode<BookmarkBarNode> { friend class BookmarkBarModel; friend class BookmarkCodec; + friend class history::StarredURLDatabase; + FRIEND_TEST(BookmarkBarModelTest, MostRecentlyAddedEntries); public: - explicit BookmarkBarNode(BookmarkBarModel* model); + BookmarkBarNode(BookmarkBarModel* model, const GURL& url); virtual ~BookmarkBarNode() {} @@ -65,21 +72,15 @@ class BookmarkBarNode : public ChromeViews::TreeNode<BookmarkBarNode> { // Returns the URL. const GURL& GetURL() const { return url_; } - // Returns the start ID corresponding to this node. - // TODO(sky): bug 1256202, make this an ever increasing integer assigned on - // reading, but not archived. Best to set it automatically in the constructor. - history::StarID GetStarID() const { return star_id_; } + // Returns a unique id for this node. + // + // NOTE: this id is only unique for the session and NOT unique across + // sessions. Don't persist it! + int id() const { return id_; } // Returns the type of this node. history::StarredEntry::Type GetType() const { return type_; } - // Returns a StarredEntry for the node. - history::StarredEntry GetEntry(); - - // Returns the ID of group. - // TODO(sky): bug 1256202, nuke this. - history::UIStarID GetGroupID() { return group_id_; } - // Called when the favicon becomes invalid. void InvalidateFavicon() { loaded_favicon_ = false; @@ -93,18 +94,22 @@ class BookmarkBarNode : public ChromeViews::TreeNode<BookmarkBarNode> { // for folders (including the bookmark and other folder). Time date_group_modified() const { return date_group_modified_; } + // Convenience for testing if this nodes represents a group. A group is + // a node whose type is not URL. + bool is_folder() const { return type_ != history::StarredEntry::URL; } + + // Is this a URL? + bool is_url() const { return type_ == history::StarredEntry::URL; } + private: // Resets the properties of the node from the supplied entry. void Reset(const history::StarredEntry& entry); - // Resets the URL. Take care to cancel loading before invoking this. - void SetURL(const GURL& url); - - // The model. + // The model. This is NULL when created by StarredURLDatabase for migration. BookmarkBarModel* model_; // Unique identifier for this node. - history::StarID star_id_; + const int id_; // Whether the favicon has been loaded. bool loaded_favicon_; @@ -116,16 +121,14 @@ class BookmarkBarNode : public ChromeViews::TreeNode<BookmarkBarNode> { // from the HistoryService. HistoryService::Handle favicon_load_handle_; - // URL. - GURL url_; + // The URL. BookmarkBarModel maintains maps off this URL, it is important that + // it not change once the node has been created. + const GURL url_; // Type of node. // TODO(sky): bug 1256202, convert this into a type defined here. history::StarredEntry::Type type_; - // Group ID. - history::UIStarID group_id_; - // Date we were created. Time date_added_; @@ -189,6 +192,7 @@ class BookmarkBarModelObserver { class BookmarkBarModel : public NotificationObserver { friend class BookmarkBarNode; friend class BookmarkBarModelTest; + friend class BookmarkStorage; public: explicit BookmarkBarModel(Profile* profile); @@ -212,6 +216,14 @@ class BookmarkBarModel : public NotificationObserver { // modified groups. This never returns an empty vector. std::vector<BookmarkBarNode*> GetMostRecentlyModifiedGroups(size_t max_count); + // Returns the most recently added bookmarks. + void GetMostRecentlyAddedEntries(size_t count, + std::vector<BookmarkBarNode*>* nodes); + + // Returns the bookmarks whose title contains text. + void GetBookmarksMatchingText(const std::wstring& text, + std::vector<BookmarkBarNode*>* nodes); + void AddObserver(BookmarkBarModelObserver* observer) { observers_.AddObserver(observer); } @@ -224,12 +236,6 @@ class BookmarkBarModel : public NotificationObserver { // unstars all nodes. Observers are notified immediately. void Remove(BookmarkBarNode* parent, int index); - // If the specified node is on the bookmark bar it is removed from the - // bookmark bar to be a child of the other node. If the node is not on the - // bookmark bar this does nothing. This is a convenience for invoking - // Move to the other node. - void RemoveFromBookmarkBar(BookmarkBarNode* node); - // Moves the specified entry to a new location. void Move(BookmarkBarNode* node, BookmarkBarNode* new_parent, int index); @@ -243,9 +249,14 @@ class BookmarkBarModel : public NotificationObserver { // the specified URL. BookmarkBarNode* GetNodeByURL(const GURL& url); - // Returns the node with the specified group id, or NULL if there is no node - // with the specified id. - BookmarkBarNode* GetNodeByGroupID(history::UIStarID group_id); + // Returns true if there is a bookmark for the specified URL. + bool IsBookmarked(const GURL& url) { + return GetNodeByURL(url) != NULL; + } + + // Returns the node with the specified id, or NULL if there is no node with + // the specified id. + BookmarkBarNode* GetNodeByID(int id); // Adds a new group node at the specified position. BookmarkBarNode* AddGroup(BookmarkBarNode* parent, @@ -287,30 +298,38 @@ class BookmarkBarModel : public NotificationObserver { } }; - // Maps from star id of node to actual node. Used during creation of nodes - // from history::StarredEntries. - typedef std::map<history::StarID,BookmarkBarNode*> IDToNodeMap; - // Overriden to notify the observer the favicon has been loaded. void FavIconLoaded(BookmarkBarNode* node); - // NOTE: this does NOT override EntryAdded/EntryChanged as we assume all - // mutation to entries on the bookmark bar occurs through our methods. - - // Removes the node for internal maps. This does NOT delete the node. - void RemoveNode(BookmarkBarNode* node); - - // Callback from the database with the starred entries. Adds the appropriate - // entries to the root node. - void OnGotStarredEntries(HistoryService::Handle, - std::vector<history::StarredEntry>* entries); + // Removes the node from internal maps and recurces through all children. If + // the node is a url, its url is added to removed_urls. + // + // This does NOT delete the node. + void RemoveNode(BookmarkBarNode* node, std::set<GURL>* removed_urls); + + // Callback from BookmarkStorage that it has finished loading. This method + // may be hit twice. In particular, on construction BookmarkBarModel asks + // BookmarkStorage to load the bookmarks. BookmarkStorage invokes this method + // with loaded_from_history false and file_exists indicating whether the + // bookmarks file exists. If the file doesn't exist, we query history. When + // history calls us back (OnHistoryDone) we then ask BookmarkStorage to load + // from the migration file. BookmarkStorage again invokes this method, but + // with |loaded_from_history| true. + void OnBookmarkStorageLoadedBookmarks(bool file_exists, + bool loaded_from_history); + + // Used for migrating bookmarks from history to standalone file. + // + // Callback from history that it is done with an empty request. This is used + // if there is no bookmarks file. Once done, we attempt to load from the + // temporary file creating during migration. + void OnHistoryDone(HistoryService::Handle handle); - // Invoked from OnGotStarredEntries to create all the BookmarkNodes for - // the specified entries. - void PopulateNodes(std::vector<history::StarredEntry>* entries); + // Invoked when loading is finished. Sets loaded_ and notifies observers. + void DoneLoading(); - // Callback from AddFolder/AddURL. - void OnCreatedEntry(HistoryService::Handle handle, history::StarID id); + // Populates nodes_ordered_by_url_set_ from root. + void PopulateNodesByURL(BookmarkBarNode* node); // Removes the node from its parent, sends notification, and deletes it. // type specifies how the node should be removed. @@ -321,36 +340,22 @@ class BookmarkBarModel : public NotificationObserver { int index, BookmarkBarNode* node); - // Implementation of GetNodeByGrouID. - BookmarkBarNode* GetNodeByGroupID(BookmarkBarNode* node, - history::UIStarID group_id); - - // Adds the bookmark bar and other nodes to the root node. If id_to_node_map - // is non-null, the bookmark bar node and other bookmark nodes are added to - // it. - void AddRootChildren(IDToNodeMap* id_to_node_map); - -#ifndef NDEBUG - void CheckIndex(BookmarkBarNode* parent, int index, bool allow_end) { - DCHECK(parent); - DCHECK(index >= 0 && - (index < parent->GetChildCount() || - allow_end && index == parent->GetChildCount())); - } -#endif + // Implementation of GetNodeByID. + BookmarkBarNode* GetNodeByID(BookmarkBarNode* node, int id); + + // Returns true if the parent and index are valid. + bool IsValidIndex(BookmarkBarNode* parent, int index, bool allow_end); // Sets the date modified time of the specified node. void SetDateGroupModified(BookmarkBarNode* parent, const Time time); - // Creates the bookmark bar/other nodes. This is used during testing (when we - // don't load from the DB), as well as if the db doesn't give us back a - // bookmark bar or other node (which should only happen if there is an error - // in loading the DB). + // Creates the bookmark bar/other nodes. These call into + // CreateRootNodeFromStarredEntry. void CreateBookmarkBarNode(); void CreateOtherBookmarksNode(); // Creates a root node (either the bookmark bar node or other node) from the - // specified starred entry. Only used once when loading. + // specified starred entry. BookmarkBarNode* CreateRootNodeFromStarredEntry( const history::StarredEntry& entry); @@ -370,9 +375,6 @@ class BookmarkBarModel : public NotificationObserver { // If we're waiting on a favicon for node, the load request is canceled. void CancelPendingFavIconLoadRequests(BookmarkBarNode* node); - // Returns true if n1's date modified time is newer than n2s. - static bool MoreRecentlyModified(BookmarkBarNode* n1, BookmarkBarNode* n2); - // Returns up to count of the most recently modified groups. This may not // add anything. void GetMostRecentlyModifiedGroupNodes(BookmarkBarNode* parent, @@ -404,26 +406,12 @@ class BookmarkBarModel : public NotificationObserver { typedef std::set<BookmarkBarNode*,NodeURLComparator> NodesOrderedByURLSet; NodesOrderedByURLSet nodes_ordered_by_url_set_; - // Maps from node to request handle. This is used when creating new nodes. - typedef std::map<BookmarkBarNode*,HistoryService::Handle> NodeToHandleMap; - NodeToHandleMap node_to_handle_map_; - - // Used when creating new entries/groups. Maps to the newly created - // node. - CancelableRequestConsumerT<BookmarkBarNode*,NULL> request_consumer_; - - // ID of the next group we create. - // - // After the first load, this is set to the max group_id from the database. - // - // This value is incremented every time a new group is created. It is - // important that the value assigned to a group node remain unique during - // a run of Chrome (BookmarkEditorView for one relies on this). - history::StarID next_group_id_; - - // Used for loading favicons. + // Used for loading favicons and the empty history request. CancelableRequestConsumerT<BookmarkBarNode*, NULL> load_consumer_; + // Reads/writes bookmarks to disk. + scoped_refptr<BookmarkStorage> store_; + DISALLOW_EVIL_CONSTRUCTORS(BookmarkBarModel); }; diff --git a/chrome/browser/bookmark_bar_model_unittest.cc b/chrome/browser/bookmark_bar_model_unittest.cc index 5a95963..2f52e19 100644 --- a/chrome/browser/bookmark_bar_model_unittest.cc +++ b/chrome/browser/bookmark_bar_model_unittest.cc @@ -28,10 +28,10 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "base/string_util.h" -#ifdef USE_BOOKMARK_CODEC #include "chrome/browser/bookmark_codec.h" -#endif // USE_BOOKMARK_CODEC #include "chrome/browser/bookmark_bar_model.h" +#include "chrome/common/chrome_constants.h" +#include "chrome/common/chrome_paths.h" #include "chrome/test/testing_profile.h" #include "chrome/views/tree_node_model.h" #include "testing/gtest/include/gtest/gtest.h" @@ -130,10 +130,6 @@ class BookmarkBarModelTest : public testing::Test, ASSERT_EQ(changed_count, this->changed_count); } - history::UIStarID GetMaxGroupID() { - return GetMaxGroupID(model.root_node()); - } - void AssertNodesEqual(BookmarkBarNode* expected, BookmarkBarNode* actual) { ASSERT_TRUE(expected); ASSERT_TRUE(actual); @@ -170,26 +166,20 @@ class BookmarkBarModelTest : public testing::Test, int changed_count; ObserverDetails observer_details; - - private: - history::UIStarID GetMaxGroupID(BookmarkBarNode* node) { - history::UIStarID max_id = node->GetGroupID(); - for (int i = 0; i < node->GetChildCount(); ++i) { - max_id = std::max(max_id, GetMaxGroupID(node->GetChild(i))); - } - return max_id; - } - - DISALLOW_EVIL_CONSTRUCTORS(BookmarkBarModelTest); }; TEST_F(BookmarkBarModelTest, InitialState) { - BookmarkBarNode* root = model.GetBookmarkBarNode(); - ASSERT_TRUE(root != NULL); - ASSERT_EQ(0, root->GetChildCount()); - ASSERT_EQ(history::StarredEntry::BOOKMARK_BAR, root->GetType()); - ASSERT_EQ(HistoryService::kBookmarkBarID, root->GetStarID()); - ASSERT_EQ(HistoryService::kBookmarkBarID, root->GetGroupID()); + BookmarkBarNode* bb_node = model.GetBookmarkBarNode(); + ASSERT_TRUE(bb_node != NULL); + EXPECT_EQ(0, bb_node->GetChildCount()); + EXPECT_EQ(history::StarredEntry::BOOKMARK_BAR, bb_node->GetType()); + + BookmarkBarNode* other_node = model.other_node(); + ASSERT_TRUE(other_node != NULL); + EXPECT_EQ(0, other_node->GetChildCount()); + EXPECT_EQ(history::StarredEntry::OTHER, other_node->GetType()); + + EXPECT_TRUE(bb_node->id() != other_node->id()); } TEST_F(BookmarkBarModelTest, AddURL) { @@ -206,19 +196,14 @@ TEST_F(BookmarkBarModelTest, AddURL) { ASSERT_TRUE(url == new_node->GetURL()); ASSERT_EQ(history::StarredEntry::URL, new_node->GetType()); ASSERT_TRUE(new_node == model.GetNodeByURL(url)); - ASSERT_EQ(new_node->GetGroupID(), new_node->GetEntry().group_id); - ASSERT_EQ(new_node->GetTitle(), new_node->GetEntry().title); - ASSERT_EQ(new_node->GetParent()->GetGroupID(), - new_node->GetEntry().parent_group_id); - ASSERT_EQ(0, new_node->GetEntry().visual_order); - ASSERT_TRUE(new_node->GetURL() == new_node->GetEntry().url); - ASSERT_EQ(history::StarredEntry::URL, new_node->GetEntry().type); + + EXPECT_TRUE(new_node->id() != root->id() && + new_node->id() != model.other_node()->id()); } TEST_F(BookmarkBarModelTest, AddGroup) { BookmarkBarNode* root = model.GetBookmarkBarNode(); const std::wstring title(L"foo"); - const history::UIStarID max_group_id = GetMaxGroupID(); BookmarkBarNode* new_node = model.AddGroup(root, 0, title); AssertObserverCount(1, 0, 0, 0); @@ -227,20 +212,15 @@ TEST_F(BookmarkBarModelTest, AddGroup) { ASSERT_EQ(1, root->GetChildCount()); ASSERT_EQ(title, new_node->GetTitle()); ASSERT_EQ(history::StarredEntry::USER_GROUP, new_node->GetType()); - ASSERT_EQ(max_group_id + 1, new_node->GetGroupID()); - ASSERT_EQ(new_node->GetGroupID(), new_node->GetEntry().group_id); - ASSERT_EQ(new_node->GetTitle(), new_node->GetEntry().title); - ASSERT_EQ(new_node->GetParent()->GetGroupID(), - new_node->GetEntry().parent_group_id); - ASSERT_EQ(0, new_node->GetEntry().visual_order); - ASSERT_EQ(history::StarredEntry::USER_GROUP, new_node->GetEntry().type); + + EXPECT_TRUE(new_node->id() != root->id() && + new_node->id() != model.other_node()->id()); // Add another group, just to make sure group_ids are incremented correctly. ClearCounts(); BookmarkBarNode* new_node2 = model.AddGroup(root, 0, title); AssertObserverCount(1, 0, 0, 0); observer_details.AssertEquals(root, NULL, 0, -1); - ASSERT_EQ(max_group_id + 2, new_node2->GetGroupID()); } TEST_F(BookmarkBarModelTest, RemoveURL) { @@ -282,15 +262,6 @@ TEST_F(BookmarkBarModelTest, RemoveGroup) { ASSERT_TRUE(model.GetNodeByURL(url) == NULL); } -TEST_F(BookmarkBarModelTest, VisualOrderIncrements) { - BookmarkBarNode* root = model.GetBookmarkBarNode(); - BookmarkBarNode* group1 = model.AddGroup(root, 0, L"foo"); - EXPECT_EQ(0, group1->GetEntry().visual_order); - BookmarkBarNode* group2 = model.AddGroup(root, 0, L"foo"); - EXPECT_EQ(1, group1->GetEntry().visual_order); - EXPECT_EQ(0, group2->GetEntry().visual_order); -} - TEST_F(BookmarkBarModelTest, SetTitle) { BookmarkBarNode* root = model.GetBookmarkBarNode(); std::wstring title(L"foo"); @@ -333,22 +304,6 @@ TEST_F(BookmarkBarModelTest, Move) { EXPECT_EQ(0, root->GetChildCount()); } -TEST_F(BookmarkBarModelTest, RemoveFromBookmarkBar) { - BookmarkBarNode* root = model.GetBookmarkBarNode(); - const std::wstring title(L"foo"); - const GURL url("http://foo.com"); - - BookmarkBarNode* new_node = model.AddURL(root, 0, title, url); - - ClearCounts(); - - model.RemoveFromBookmarkBar(new_node); - AssertObserverCount(0, 1, 0, 0); - observer_details.AssertEquals(root, model.other_node(), 0, 0); - - ASSERT_EQ(0, root->GetChildCount()); -} - // Tests that adding a URL to a folder updates the last modified time. TEST_F(BookmarkBarModelTest, ParentForNewNodes) { ASSERT_EQ(model.GetBookmarkBarNode(), model.GetParentForNewNodes()); @@ -383,6 +338,42 @@ TEST_F(BookmarkBarModelTest, MostRecentlyModifiedGroups) { ASSERT_TRUE(most_recent_groups[0] != group); } +// Make sure MostRecentlyAddedEntries stays in sync. +TEST_F(BookmarkBarModelTest, MostRecentlyAddedEntries) { + // Add a couple of nodes such that the following holds for the time of the + // nodes: n1 > n2 > n3 > n4. + Time base_time = Time::Now(); + BookmarkBarNode* n1 = model.AddURL( + model.GetBookmarkBarNode(), 0, L"blah", GURL("http://foo.com/0")); + BookmarkBarNode* n2 = model.AddURL( + model.GetBookmarkBarNode(), 1, L"blah", GURL("http://foo.com/1")); + BookmarkBarNode* n3 = model.AddURL( + model.GetBookmarkBarNode(), 2, L"blah", GURL("http://foo.com/2")); + BookmarkBarNode* n4 = model.AddURL( + model.GetBookmarkBarNode(), 3, L"blah", GURL("http://foo.com/3")); + n1->date_added_ = base_time + TimeDelta::FromDays(4); + n2->date_added_ = base_time + TimeDelta::FromDays(3); + n3->date_added_ = base_time + TimeDelta::FromDays(2); + n4->date_added_ = base_time + TimeDelta::FromDays(1); + + // Make sure order is honored. + std::vector<BookmarkBarNode*> recently_added; + model.GetMostRecentlyAddedEntries(2, &recently_added); + ASSERT_EQ(2, recently_added.size()); + ASSERT_TRUE(n1 == recently_added[0]); + ASSERT_TRUE(n2 == recently_added[1]); + + // swap 1 and 2, then check again. + recently_added.clear(); + std::swap(n1->date_added_, n2->date_added_); + model.GetMostRecentlyAddedEntries(4, &recently_added); + ASSERT_EQ(4, recently_added.size()); + ASSERT_TRUE(n2 == recently_added[0]); + ASSERT_TRUE(n1 == recently_added[1]); + ASSERT_TRUE(n3 == recently_added[2]); + ASSERT_TRUE(n4 == recently_added[3]); +} + namespace { // See comment in PopulateNodeFromString. @@ -471,11 +462,9 @@ static void PopulateBookmarkBarNode(TestNode* parent, class BookmarkBarModelTestWithProfile : public testing::Test, public BookmarkBarModelObserver { public: - BookmarkBarModelTestWithProfile() {} - virtual void SetUp() { profile_.reset(new TestingProfile()); - profile_->CreateHistoryService(); + profile_->CreateHistoryService(true); } virtual void TearDown() { @@ -489,19 +478,21 @@ class BookmarkBarModelTestWithProfile : public testing::Test, // Verifies the contents of the bookmark bar node match the contents of the // TestNode. void VerifyModelMatchesNode(TestNode* expected, BookmarkBarNode* actual) { - EXPECT_EQ(expected->GetChildCount(), actual->GetChildCount()); + ASSERT_EQ(expected->GetChildCount(), actual->GetChildCount()); for (int i = 0; i < expected->GetChildCount(); ++i) { TestNode* expected_child = expected->GetChild(i); BookmarkBarNode* actual_child = actual->GetChild(i); - EXPECT_EQ(expected_child->GetTitle(), actual_child->GetTitle()); + ASSERT_EQ(expected_child->GetTitle(), actual_child->GetTitle()); if (expected_child->value == history::StarredEntry::USER_GROUP) { - EXPECT_TRUE(actual_child->GetType() == + ASSERT_TRUE(actual_child->GetType() == history::StarredEntry::USER_GROUP); // Recurse throught children. VerifyModelMatchesNode(expected_child, actual_child); + if (HasFatalFailure()) + return; } else { // No need to check the URL, just the title is enough. - EXPECT_TRUE(actual_child->GetType() == + ASSERT_TRUE(actual_child->GetType() == history::StarredEntry::URL); } } @@ -510,12 +501,13 @@ class BookmarkBarModelTestWithProfile : public testing::Test, // Creates the bookmark bar model. If the bookmark bar model has already been // created a new one is created and the old one destroyed. void CreateBookmarkBarModel() { - // NOTE: this order is important. We need to make sure the backend has - // processed all pending requests from the current BookmarkBarModel before - // destroying it. By creating a new BookmarkBarModel first and blocking - // until done, we ensure all requests from the old model have completed. + bb_model_.reset(NULL); + BookmarkBarModel* new_model = new BookmarkBarModel(profile_.get()); - BlockTillLoaded(new_model); + if (!new_model->IsLoaded()) + BlockTillLoaded(new_model); + else + new_model->AddObserver(this); bb_model_.reset(new_model); } @@ -526,7 +518,7 @@ class BookmarkBarModelTestWithProfile : public testing::Test, // Need to shutdown the old one before creating a new one. profile_.reset(NULL); profile_.reset(new TestingProfile()); - profile_->CreateHistoryService(); + profile_->CreateHistoryService(true); } scoped_ptr<BookmarkBarModel> bb_model_; @@ -558,8 +550,6 @@ class BookmarkBarModelTestWithProfile : public testing::Test, BookmarkBarNode* node) {} virtual void BookmarkNodeFavIconLoaded(BookmarkBarModel* model, BookmarkBarNode* node) {} - - DISALLOW_EVIL_CONSTRUCTORS(BookmarkBarModelTestWithProfile); }; // Creates a set of nodes in the bookmark bar model, then recreates the @@ -601,44 +591,113 @@ TEST_F(BookmarkBarModelTestWithProfile, CreateAndRestore) { } } -#ifdef USE_BOOKMARK_CODEC -// Creates a set of nodes in the bookmark bar model, then recreates the -// bookmark bar model which triggers loading from the db and checks the loaded -// structure to make sure it is what we first created. -TEST_F(BookmarkBarModelTest, TestJSONCodec) { - struct TestData { - // Structure of the children of the bookmark bar model node. - const std::wstring bbn_contents; - // Structure of the children of the other node. - const std::wstring other_contents; - } data[] = { - // See PopulateNodeFromString for a description of these strings. - { L"", L"" }, - { L"a", L"b" }, - { L"a [ b ]", L"" }, - { L"", L"[ b ] a [ c [ d e [ f ] ] ]" }, - { L"a [ b ]", L"" }, - { L"a b c [ d e [ f ] ]", L"g h i [ j k [ l ] ]"}, - }; - for (int i = 0; i < arraysize(data); ++i) { - BookmarkBarModel expected_model(NULL); - - TestNode bbn; - PopulateNodeFromString(data[i].bbn_contents, &bbn); - PopulateBookmarkBarNode(&bbn, &expected_model, - expected_model.GetBookmarkBarNode()); - - TestNode other; - PopulateNodeFromString(data[i].other_contents, &other); - PopulateBookmarkBarNode(&other, &expected_model, - expected_model.other_node()); - - BookmarkBarModel actual_model(NULL); - BookmarkCodec codec; - scoped_ptr<Value> encoded_value(codec.Encode(&expected_model)); - codec.Decode(&actual_model, *(encoded_value.get())); +// Test class that creates a BookmarkBarModel with a real history backend. +class BookmarkBarModelTestWithProfile2 : + public BookmarkBarModelTestWithProfile { + public: + virtual void SetUp() { + profile_.reset(new TestingProfile()); + } - AssertModelsEqual(&expected_model, &actual_model); + protected: + // Verifies the state of the model matches that of the state in the saved + // history file. + void VerifyExpectedState() { + // Here's the structure we expect: + // bbn + // www.google.com - Google + // F1 + // http://www.google.com/intl/en/ads/ - Google Advertising + // F11 + // http://www.google.com/services/ - Google Business Solutions + // other + // OF1 + // http://www.google.com/intl/en/about.html - About Google + BookmarkBarNode* bbn = bb_model_->GetBookmarkBarNode(); + ASSERT_EQ(2, bbn->GetChildCount()); + + BookmarkBarNode* child = bbn->GetChild(0); + ASSERT_EQ(history::StarredEntry::URL, child->GetType()); + ASSERT_EQ(L"Google", child->GetTitle()); + ASSERT_TRUE(child->GetURL() == GURL("http://www.google.com")); + + child = bbn->GetChild(1); + ASSERT_TRUE(child->is_folder()); + ASSERT_EQ(L"F1", child->GetTitle()); + ASSERT_EQ(2, child->GetChildCount()); + + BookmarkBarNode* parent = child; + child = parent->GetChild(0); + ASSERT_EQ(history::StarredEntry::URL, child->GetType()); + ASSERT_EQ(L"Google Advertising", child->GetTitle()); + ASSERT_TRUE(child->GetURL() == GURL("http://www.google.com/intl/en/ads/")); + + child = parent->GetChild(1); + ASSERT_TRUE(child->is_folder()); + ASSERT_EQ(L"F11", child->GetTitle()); + ASSERT_EQ(1, child->GetChildCount()); + + parent = child; + child = parent->GetChild(0); + ASSERT_EQ(history::StarredEntry::URL, child->GetType()); + ASSERT_EQ(L"Google Business Solutions", child->GetTitle()); + ASSERT_TRUE(child->GetURL() == GURL("http://www.google.com/services/")); + + parent = bb_model_->other_node(); + ASSERT_EQ(2, parent->GetChildCount()); + + child = parent->GetChild(0); + ASSERT_TRUE(child->is_folder()); + ASSERT_EQ(L"OF1", child->GetTitle()); + ASSERT_EQ(0, child->GetChildCount()); + + child = parent->GetChild(1); + ASSERT_EQ(history::StarredEntry::URL, child->GetType()); + ASSERT_EQ(L"About Google", child->GetTitle()); + ASSERT_TRUE(child->GetURL() == + GURL("http://www.google.com/intl/en/about.html")); + + ASSERT_TRUE(bb_model_->GetNodeByURL(GURL("http://www.google.com")) != NULL); } +}; + +// Tests migrating bookmarks from db into file. This copies an old history db +// file containing bookmarks and make sure they are loaded correctly and +// persisted correctly. +TEST_F(BookmarkBarModelTestWithProfile2, MigrateFromDBToFileTest) { + // Copy db file over that contains starred table. + std::wstring old_history_path; + PathService::Get(chrome::DIR_TEST_DATA, &old_history_path); + file_util::AppendToPath(&old_history_path, L"bookmarks"); + file_util::AppendToPath(&old_history_path, L"History_with_starred"); + std::wstring new_history_path = profile_->GetPath(); + file_util::Delete(new_history_path, true); + file_util::CreateDirectory(new_history_path); + file_util::AppendToPath(&new_history_path, chrome::kHistoryFilename); + file_util::CopyFile(old_history_path, new_history_path); + + // Create the history service making sure it doesn't blow away the file we + // just copied. + profile_->CreateHistoryService(false); + + CreateBookmarkBarModel(); + + // Make sure we loaded OK. + VerifyExpectedState(); + if (HasFatalFailure()) + return; + + // Create again. This time we shouldn't load from history at all. + CreateBookmarkBarModel(); + + // Make sure we loaded OK. + VerifyExpectedState(); + if (HasFatalFailure()) + return; + + // Recreate the history service (with a clean db). Do this just to make sure + // we're loading correctly from the bookmarks file. + profile_->CreateHistoryService(true); + CreateBookmarkBarModel(); + VerifyExpectedState(); } -#endif // USE_BOOKMARK_CODEC diff --git a/chrome/browser/bookmark_codec.cc b/chrome/browser/bookmark_codec.cc index 37f0ed0..d82a9e2 100644 --- a/chrome/browser/bookmark_codec.cc +++ b/chrome/browser/bookmark_codec.cc @@ -34,9 +34,11 @@ #include "chrome/browser/bookmark_bar_model.h" #include "googleurl/src/gurl.h" +#include "generated_resources.h" + // Key names. static const wchar_t* kRootsKey = L"roots"; -static const wchar_t* kRootFolderNameKey = L"root"; +static const wchar_t* kRootFolderNameKey = L"bookmark_bar"; static const wchar_t* kOtherBookmarFolderNameKey = L"other"; static const wchar_t* kVersionKey = L"version"; static const wchar_t* kTypeKey = L"type"; @@ -54,9 +56,14 @@ static const wchar_t* kTypeFolder = L"folder"; static const int kCurrentVersion = 1; Value* BookmarkCodec::Encode(BookmarkBarModel* model) { + return Encode(model->GetBookmarkBarNode(), model->other_node()); +} + +Value* BookmarkCodec::Encode(BookmarkBarNode* bookmark_bar_node, + BookmarkBarNode* other_folder_node) { DictionaryValue* roots = new DictionaryValue(); - roots->Set(kRootFolderNameKey, EncodeNode(model->GetBookmarkBarNode())); - roots->Set(kOtherBookmarFolderNameKey, EncodeNode(model->other_node())); + roots->Set(kRootFolderNameKey, EncodeNode(bookmark_bar_node)); + roots->Set(kOtherBookmarFolderNameKey, EncodeNode(other_folder_node)); DictionaryValue* main = new DictionaryValue(); main->SetInteger(kVersionKey, kCurrentVersion); @@ -91,12 +98,18 @@ bool BookmarkCodec::Decode(BookmarkBarModel* model, const Value& value) { return false; // Invalid type for root folder and/or other folder. DecodeNode(model, *static_cast<DictionaryValue*>(root_folder_value), - model->GetBookmarkBarNode()); + NULL, model->GetBookmarkBarNode()); DecodeNode(model, *static_cast<DictionaryValue*>(other_folder_value), - model->other_node()); - // Need to reset these as Decode sets the type to FOLDER. + NULL, model->other_node()); + // Need to reset the type as decoding resets the type to FOLDER. Similarly + // we need to reset the title as the title is persisted and restored from + // the file. model->GetBookmarkBarNode()->type_ = history::StarredEntry::BOOKMARK_BAR; model->other_node()->type_ = history::StarredEntry::OTHER; + model->GetBookmarkBarNode()->SetTitle( + l10n_util::GetString(IDS_BOOMARK_BAR_FOLDER_NAME)); + model->other_node()->SetTitle( + l10n_util::GetString(IDS_BOOMARK_BAR_OTHER_FOLDER_NAME)); return true; } @@ -134,27 +147,26 @@ bool BookmarkCodec::DecodeChildren(BookmarkBarModel* model, if (child_value->GetType() != Value::TYPE_DICTIONARY) return false; - BookmarkBarNode* child = new BookmarkBarNode(model); - parent->Add(static_cast<int>(i), child); - if (!DecodeNode(model, *static_cast<DictionaryValue*>(child_value), child)) + if (!DecodeNode(model, *static_cast<DictionaryValue*>(child_value), parent, + NULL)) { return false; + } } return true; } bool BookmarkCodec::DecodeNode(BookmarkBarModel* model, const DictionaryValue& value, + BookmarkBarNode* parent, BookmarkBarNode* node) { + bool created_node = (node == NULL); std::wstring title; if (!value.GetString(kNameKey, &title)) return false; - node->SetTitle(title); std::wstring date_added_string; if (!value.GetString(kDateAddedKey, &date_added_string)) return false; - node->date_added_ = - Time::FromInternalValue(StringToInt64(date_added_string)); std::wstring type_string; if (!value.GetString(kTypeKey, &type_string)) @@ -167,16 +179,15 @@ bool BookmarkCodec::DecodeNode(BookmarkBarModel* model, std::wstring url_string; if (!value.GetString(kURLKey, &url_string)) return false; - node->SetURL(GURL(url_string)); + if (!node) + node = new BookmarkBarNode(model, GURL(url_string)); + if (parent) + parent->Add(parent->GetChildCount(), node); node->type_ = history::StarredEntry::URL; } else { - node->type_ = history::StarredEntry::USER_GROUP; - std::wstring last_modified_date; if (!value.GetString(kDateModifiedKey, &last_modified_date)) return false; - node->date_group_modified_ = - Time::FromInternalValue(StringToInt64(last_modified_date)); Value* child_values; if (!value.Get(kChildrenKey, &child_values)) @@ -185,13 +196,21 @@ bool BookmarkCodec::DecodeNode(BookmarkBarModel* model, if (child_values->GetType() != Value::TYPE_LIST) return false; - if (!DecodeChildren(model, *static_cast<ListValue*>(child_values), node)) { - // There was an error in building the children. Delete all the children. - while (node->GetChildCount()) - delete node->Remove(node->GetChildCount() - 1); + if (!node) + node = new BookmarkBarNode(model, GURL()); + node->type_ = history::StarredEntry::USER_GROUP; + node->date_group_modified_ = + Time::FromInternalValue(StringToInt64(last_modified_date)); + + if (parent) + parent->Add(parent->GetChildCount(), node); + + if (!DecodeChildren(model, *static_cast<ListValue*>(child_values), node)) return false; - } } + node->SetTitle(title); + node->date_added_ = + Time::FromInternalValue(StringToInt64(date_added_string)); return true; } diff --git a/chrome/browser/bookmark_codec.h b/chrome/browser/bookmark_codec.h index ffe51c2..912b8ae 100644 --- a/chrome/browser/bookmark_codec.h +++ b/chrome/browser/bookmark_codec.h @@ -49,9 +49,18 @@ class BookmarkCodec { BookmarkCodec() {} // Encodes the model to a JSON value. It's up to the caller to delete the - // returned object. + // returned object. This is invoked to encode the contents of the bookmark bar + // model and is currently a convenience to invoking Encode that takes the + // bookmark bar node and other folder node. Value* Encode(BookmarkBarModel* model); + // Encodes the bookmark bar and other folders returning the JSON value. It's + // up to the caller to delete the returned object. + // This method is public for use by StarredURLDatabase in migrating the + // bookmarks out of the database. + Value* Encode(BookmarkBarNode* bookmark_bar_node, + BookmarkBarNode* other_folder_node); + // Decodes the previously encoded value to the specified model. Returns true // on success, false otherwise. If there is an error (such as unexpected // version) all children are removed from the bookmark bar and other folder @@ -69,9 +78,11 @@ class BookmarkCodec { BookmarkBarNode* parent); // Decodes the supplied node from the supplied value. Child nodes are - // created appropriately by way of DecodeChildren. + // created appropriately by way of DecodeChildren. If node is NULL a new + // node is created and added to parent, otherwise node is used. bool DecodeNode(BookmarkBarModel* model, const DictionaryValue& value, + BookmarkBarNode* parent, BookmarkBarNode* node); DISALLOW_COPY_AND_ASSIGN(BookmarkCodec); diff --git a/chrome/browser/bookmark_drag_data.cc b/chrome/browser/bookmark_drag_data.cc index e4f4b22..8c14a2f 100644 --- a/chrome/browser/bookmark_drag_data.cc +++ b/chrome/browser/bookmark_drag_data.cc @@ -48,12 +48,10 @@ BookmarkDragData::BookmarkDragData(BookmarkBarNode* node) : is_url(node->GetType() == history::StarredEntry::URL), url(node->GetURL()), title(node->GetTitle()), - is_valid(true) { - if (!is_url) { - group_id_ = node->GetGroupID(); - DCHECK(group_id_); + is_valid(true), + id_(node->id()) { + if (!is_url) AddChildren(node); - } } void BookmarkDragData::Write(OSExchangeData* data) const { @@ -89,8 +87,8 @@ bool BookmarkDragData::Read(const OSExchangeData& data) { } BookmarkBarNode* BookmarkDragData::GetNode(BookmarkBarModel* model) const { - DCHECK(!is_url && group_id_ && is_valid); - return model->GetNodeByGroupID(group_id_); + DCHECK(!is_url && id_ && is_valid); + return model->GetNodeByID(id_); } void BookmarkDragData::WriteToPickle(Pickle* pickle) const { @@ -99,7 +97,7 @@ void BookmarkDragData::WriteToPickle(Pickle* pickle) const { pickle->WriteString(url.spec()); pickle->WriteWString(title); if (!is_url) { - pickle->WriteInt64(group_id_); + pickle->WriteInt(id_); pickle->WriteInt(static_cast<int>(children.size())); for (std::vector<BookmarkDragData>::const_iterator i = children.begin(); i != children.end(); ++i) { @@ -119,9 +117,9 @@ bool BookmarkDragData::ReadFromPickle(Pickle* pickle, void** iterator) { } url = GURL(url_spec); if (!is_url) { - group_id_ = 0; + id_ = 0; children.clear(); - if (!pickle->ReadInt64(iterator, &group_id_)) + if (!pickle->ReadInt(iterator, &id_)) return false; int children_count; if (!pickle->ReadInt(iterator, &children_count)) diff --git a/chrome/browser/bookmark_drag_data.h b/chrome/browser/bookmark_drag_data.h index 054b922..feb54af 100644 --- a/chrome/browser/bookmark_drag_data.h +++ b/chrome/browser/bookmark_drag_data.h @@ -27,8 +27,8 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#ifndef CHROME_BROWSER_BOOKMARK_DRAG_DATA -#define CHROME_BROWSER_BOOKMARK_DRAG_DATA +#ifndef CHROME_BROWSER_BOOKMARK_DRAG_DATA_ +#define CHROME_BROWSER_BOOKMARK_DRAG_DATA_ #include <vector> @@ -99,8 +99,8 @@ struct BookmarkDragData { // Adds to children an entry for each child of node. void AddChildren(BookmarkBarNode* node); - // If we're a group, this is our id. - history::UIStarID group_id_; + // ID (node->id()) of the node this BookmarkDragData was created from. + int id_; }; -#endif // CHROME_BROWSER_BOOKMARK_DRAG_DATA +#endif // CHROME_BROWSER_BOOKMARK_DRAG_DATA_ diff --git a/chrome/browser/bookmark_storage.cc b/chrome/browser/bookmark_storage.cc index e1fed29..8c402b8 100644 --- a/chrome/browser/bookmark_storage.cc +++ b/chrome/browser/bookmark_storage.cc @@ -39,6 +39,7 @@ #include "chrome/browser/bookmark_bar_model.h" #include "chrome/browser/bookmark_codec.h" #include "chrome/browser/profile.h" +#include "chrome/common/chrome_constants.h" #include "chrome/common/json_value_serializer.h" namespace { @@ -50,90 +51,36 @@ const wchar_t* const kBackupExtension = L"bak"; // kBookmarksFileName. const wchar_t* const kTmpExtension = L"tmp"; -// Name of file containing bookmarks. -const wchar_t* const kBookmarksFileName = L"bookmarks.json"; - // How often we save. -static const int kSaveDelayMS = 2500; - -class BookmarkStorageBackend : - public base::RefCountedThreadSafe<BookmarkStorageBackend> { - public: - explicit BookmarkStorageBackend(const std::wstring& path); - - // Writes the specified value to disk. This takes ownership of |value| and - // deletes it when done. - void Write(Value* value); - - // Reads the bookmarks from kBookmarksFileName. Notifies |service| with - // the results on the specified MessageLoop. - void Read(scoped_refptr<BookmarkStorage> service, - MessageLoop* message_loop); - - private: - // Path we read/write to. - const std::wstring path_; - - DISALLOW_EVIL_CONSTRUCTORS(BookmarkStorageBackend); -}; - -BookmarkStorageBackend::BookmarkStorageBackend(const std::wstring& path) - : path_(path) { - // Make a backup of the current file. - std::wstring backup_path = path; - file_util::ReplaceExtension(&backup_path, kBackupExtension); - file_util::CopyFile(path, backup_path); -} - -void BookmarkStorageBackend::Write(Value* value) { - DCHECK(value); - - // We own Value. - scoped_ptr<Value> value_ref(value); - - std::string content; - JSONWriter::Write(value, true, &content); - - // Write to a temp file, then rename. - std::wstring tmp_file = path_; - file_util::ReplaceExtension(&tmp_file, kTmpExtension); - - int bytes_written = file_util::WriteFile(tmp_file, content.c_str(), - static_cast<int>(content.length())); - if (bytes_written != -1) { - MoveFileEx(tmp_file.c_str(), path_.c_str(), - MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING); - } -} - -void BookmarkStorageBackend::Read(scoped_refptr<BookmarkStorage> service, - MessageLoop* message_loop) { - JSONFileValueSerializer serializer(path_); - Value* root = NULL; - serializer.Deserialize(&root); - - // BookmarkStorage takes ownership of root. - message_loop->PostTask(FROM_HERE, NewRunnableMethod( - service.get(), &BookmarkStorage::LoadedBookmarks, root)); -} +const int kSaveDelayMS = 2500; } // namespace +// BookmarkStorage ------------------------------------------------------------- + BookmarkStorage::BookmarkStorage(Profile* profile, BookmarkBarModel* model) : model_(model), #pragma warning(suppress: 4355) // Okay to pass "this" here. - save_factory_(this) { + save_factory_(this), + backend_thread_(g_browser_process->file_thread()) { std::wstring path = profile->GetPath(); - file_util::AppendToPath(&path, kBookmarksFileName); - backend_ = new BookmarkStorageBackend(path); + file_util::AppendToPath(&path, chrome::kBookmarksFileName); + std::wstring tmp_history_path = profile->GetPath(); + file_util::AppendToPath(&tmp_history_path, chrome::kHistoryBookmarksFileName); + backend_ = new BookmarkStorageBackend(path, tmp_history_path); } -void BookmarkStorage::LoadBookmarks() { - backend_thread()->message_loop()->PostTask( - FROM_HERE, - NewRunnableMethod(backend_.get(), &BookmarkStorageBackend::Read, - scoped_refptr<BookmarkStorage>(this), - MessageLoop::current())); +void BookmarkStorage::LoadBookmarks(bool load_from_history) { + if (!backend_thread()) { + backend_->Read(scoped_refptr<BookmarkStorage>(this), NULL, + load_from_history); + } else { + backend_thread()->message_loop()->PostTask( + FROM_HERE, + NewRunnableMethod(backend_.get(), &BookmarkStorageBackend::Read, + scoped_refptr<BookmarkStorage>(this), + MessageLoop::current(), load_from_history)); + } } void BookmarkStorage::ScheduleSave() { @@ -154,7 +101,9 @@ void BookmarkStorage::BookmarkModelDeleted() { model_ = NULL; } -void BookmarkStorage::LoadedBookmarks(Value* root_value) { +void BookmarkStorage::LoadedBookmarks(Value* root_value, + bool bookmark_file_exists, + bool loaded_from_history) { scoped_ptr<Value> value_ref(root_value); if (model_) { @@ -162,8 +111,8 @@ void BookmarkStorage::LoadedBookmarks(Value* root_value) { BookmarkCodec codec; codec.Decode(model_, *root_value); } - // TODO(sky): bug 1256202 need to invoke a method back on the model telling - // it all has loaded. + model_->OnBookmarkStorageLoadedBookmarks(bookmark_file_exists, + loaded_from_history); } } @@ -178,8 +127,69 @@ void BookmarkStorage::SaveNow() { BookmarkCodec codec; Value* value = codec.Encode(model_); // The backend deletes value in write. - backend_thread()->message_loop()->PostTask( - FROM_HERE, - NewRunnableMethod(backend_.get(), &BookmarkStorageBackend::Write, - value)); + if (!backend_thread()) { + backend_->Write(value); + } else { + backend_thread()->message_loop()->PostTask( + FROM_HERE, + NewRunnableMethod(backend_.get(), &BookmarkStorageBackend::Write, + value)); + } +} + +// BookmarkStorageBackend ------------------------------------------------------ + +BookmarkStorageBackend::BookmarkStorageBackend( + const std::wstring& path, + const std::wstring& tmp_history_path) + : path_(path), + tmp_history_path_(tmp_history_path) { + // Make a backup of the current file. + std::wstring backup_path = path; + file_util::ReplaceExtension(&backup_path, kBackupExtension); + file_util::CopyFile(path, backup_path); +} + +void BookmarkStorageBackend::Write(Value* value) { + DCHECK(value); + + // We own Value. + scoped_ptr<Value> value_ref(value); + + std::string content; + JSONWriter::Write(value, true, &content); + + // Write to a temp file, then rename. + std::wstring tmp_file = path_; + file_util::ReplaceExtension(&tmp_file, kTmpExtension); + + int bytes_written = file_util::WriteFile(tmp_file, content.c_str(), + static_cast<int>(content.length())); + if (bytes_written != -1) { + MoveFileEx(tmp_file.c_str(), path_.c_str(), + MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING); + // Nuke the history file so that we don't attempt to load from it again. + file_util::Delete(tmp_history_path_, false); + } +} + +void BookmarkStorageBackend::Read(scoped_refptr<BookmarkStorage> service, + MessageLoop* message_loop, + bool load_from_history) { + const std::wstring& path = load_from_history ? tmp_history_path_ : path_; + bool bookmark_file_exists = file_util::PathExists(path); + Value* root = NULL; + if (bookmark_file_exists) { + JSONFileValueSerializer serializer(path); + serializer.Deserialize(&root); + } + + // BookmarkStorage takes ownership of root. + if (message_loop) { + message_loop->PostTask(FROM_HERE, NewRunnableMethod( + service.get(), &BookmarkStorage::LoadedBookmarks, root, + bookmark_file_exists, load_from_history)); + } else { + service->LoadedBookmarks(root, bookmark_file_exists, load_from_history); + } } diff --git a/chrome/browser/bookmark_storage.h b/chrome/browser/bookmark_storage.h index 8ba00d0..5792c00 100644 --- a/chrome/browser/bookmark_storage.h +++ b/chrome/browser/bookmark_storage.h @@ -27,17 +27,6 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// BookmarkService handles reading/write the bookmark bar model. The -// BookmarkBarModel uses the BookmarkService to load bookmarks from -// disk, as well as notifying the BookmarkService every time the model -// changes. -// -// Internally BookmarkService uses BookmarkCoded to do the actual read/write. - -// NOTE: This class is currently unsed. The plan is to move bookmarks -// out of the history db using this class and BookmarksCodec instead -// (bug 1256202). - #ifndef CHROME_BROWSER_BOOKMARK_STORAGE_H_ #define CHROME_BROWSER_BOOKMARK_STORAGE_H_ @@ -49,9 +38,12 @@ class BookmarkBarModel; class Profile; class Value; -namespace { -class BookmarkStorageBackend; -} +// BookmarkStorage handles reading/write the bookmark bar model. The +// BookmarkBarModel uses the BookmarkStorage to load bookmarks from +// disk, as well as notifying the BookmarkStorage every time the model +// changes. +// +// Internally BookmarkStorage uses BookmarkCodec to do the actual read/write. class BookmarkStorage : public base::RefCountedThreadSafe<BookmarkStorage> { friend class BookmarkStorageBackend; @@ -60,8 +52,10 @@ class BookmarkStorage : public base::RefCountedThreadSafe<BookmarkStorage> { // Creates a BookmarkStorage for the specified model BookmarkStorage(Profile* profile, BookmarkBarModel* model); - // Loads the bookmarks into the model, notifying the model when done. - void LoadBookmarks(); + // Loads the bookmarks into the model, notifying the model when done. If + // load_from_history is true, the bookmarks are loaded from the file written + // by history (StarredURLDatabase). + void LoadBookmarks(bool load_from_history); // Schedules saving the bookmark bar model to disk. void ScheduleSave(); @@ -72,13 +66,15 @@ class BookmarkStorage : public base::RefCountedThreadSafe<BookmarkStorage> { private: // Callback from backend with the results of the bookmark file. - void LoadedBookmarks(Value* root_value); + void LoadedBookmarks(Value* root_value, + bool bookmark_file_exists, + bool loaded_from_history); // Schedules a save on the backend thread. void SaveNow(); // Returns the thread the backend is run on. - Thread* backend_thread() { return g_browser_process->file_thread(); } + Thread* backend_thread() const { return backend_thread_; } // The model. The model is NULL once BookmarkModelDeleted has been invoked. BookmarkBarModel* model_; @@ -89,7 +85,38 @@ class BookmarkStorage : public base::RefCountedThreadSafe<BookmarkStorage> { // The backend handles actual reading/writing to disk. scoped_refptr<BookmarkStorageBackend> backend_; + // Thread read/writing is run on. This comes from the profile, and is null + // during testing. + Thread* backend_thread_; + DISALLOW_COPY_AND_ASSIGN(BookmarkStorage); }; +// Used to save the bookmarks on the file thread. +class BookmarkStorageBackend : + public base::RefCountedThreadSafe<BookmarkStorageBackend> { + public: + explicit BookmarkStorageBackend(const std::wstring& path, + const std::wstring& tmp_histor_path); + + // Writes the specified value to disk. This takes ownership of |value| and + // deletes it when done. + void Write(Value* value); + + // Reads the bookmarks from kBookmarksFileName. Notifies |service| with + // the results on the specified MessageLoop. + void Read(scoped_refptr<BookmarkStorage> service, + MessageLoop* message_loop, + bool load_from_history); + + private: + // Path we read/write to. + const std::wstring path_; + + // Path bookmarks are read from if asked to load from history file. + const std::wstring tmp_history_path_; + + DISALLOW_EVIL_CONSTRUCTORS(BookmarkStorageBackend); +}; + #endif // CHROME_BROWSER_BOOKMARK_STORAGE_H_ diff --git a/chrome/browser/browser.vcproj b/chrome/browser/browser.vcproj index 5fee2a1..0747b6f 100644 --- a/chrome/browser/browser.vcproj +++ b/chrome/browser/browser.vcproj @@ -818,6 +818,14 @@ > </File> <File + RelativePath=".\bookmark_codec.cc" + > + </File> + <File + RelativePath=".\bookmark_codec.h" + > + </File> + <File RelativePath=".\bookmark_drag_data.cc" > </File> @@ -825,6 +833,14 @@ RelativePath=".\bookmark_drag_data.h" > </File> + <File + RelativePath=".\bookmark_storage.cc" + > + </File> + <File + RelativePath=".\bookmark_storage.h" + > + </File> </Filter> <Filter Name="Tab Contents" diff --git a/chrome/browser/dom_ui/new_tab_ui.cc b/chrome/browser/dom_ui/new_tab_ui.cc index ef36f49..58ce89d 100644 --- a/chrome/browser/dom_ui/new_tab_ui.cc +++ b/chrome/browser/dom_ui/new_tab_ui.cc @@ -518,37 +518,63 @@ void TemplateURLHandler::OnTemplateURLModelChanged() { } RecentlyBookmarkedHandler::RecentlyBookmarkedHandler(DOMUIHost* dom_ui_host) - : dom_ui_host_(dom_ui_host) { + : dom_ui_host_(dom_ui_host), + model_(NULL) { dom_ui_host->RegisterMessageCallback("getRecentlyBookmarked", NewCallback(this, &RecentlyBookmarkedHandler::HandleGetRecentlyBookmarked)); } +RecentlyBookmarkedHandler::~RecentlyBookmarkedHandler() { + if (model_) + model_->RemoveObserver(this); +} + void RecentlyBookmarkedHandler::HandleGetRecentlyBookmarked(const Value*) { - HistoryService* hs = - dom_ui_host_->profile()->GetHistoryService(Profile::EXPLICIT_ACCESS); - if (hs) { - HistoryService::Handle handle = hs->GetMostRecentStarredEntries( - kRecentBookmarks, - &cancelable_consumer_, - NewCallback(this, - &RecentlyBookmarkedHandler::OnMostRecentStarredEntries)); + if (!model_) { + model_ = dom_ui_host_->profile()->GetBookmarkBarModel(); + model_->AddObserver(this); } + // If the model is loaded, synchronously send the bookmarks down. Otherwise + // when the model loads we'll send the bookmarks down. + if (model_->IsLoaded()) + SendBookmarksToPage(); } -void RecentlyBookmarkedHandler::OnMostRecentStarredEntries( - HistoryService::Handle request_handle, - std::vector<history::StarredEntry>* entries) { +void RecentlyBookmarkedHandler::SendBookmarksToPage() { + std::vector<BookmarkBarNode*> recently_bookmarked; + model_->GetMostRecentlyAddedEntries(kRecentBookmarks, &recently_bookmarked); ListValue list_value; - for (size_t i = 0; i < entries->size(); ++i) { - const history::StarredEntry& entry = (*entries)[i]; + for (size_t i = 0; i < recently_bookmarked.size(); ++i) { + BookmarkBarNode* node = recently_bookmarked[i]; DictionaryValue* entry_value = new DictionaryValue; - SetURLAndTitle(entry_value, entry.title, entry.url); + SetURLAndTitle(entry_value, node->GetTitle(), node->GetURL()); list_value.Append(entry_value); } dom_ui_host_->CallJavascriptFunction(L"recentlyBookmarked", list_value); } +void RecentlyBookmarkedHandler::Loaded(BookmarkBarModel* model) { + SendBookmarksToPage(); +} + +void RecentlyBookmarkedHandler::BookmarkNodeAdded(BookmarkBarModel* model, + BookmarkBarNode* parent, + int index) { + SendBookmarksToPage(); +} + +void RecentlyBookmarkedHandler::BookmarkNodeRemoved(BookmarkBarModel* model, + BookmarkBarNode* parent, + int index) { + SendBookmarksToPage(); +} + +void RecentlyBookmarkedHandler::BookmarkNodeChanged(BookmarkBarModel* model, + BookmarkBarNode* node) { + SendBookmarksToPage(); +} + RecentlyClosedTabsHandler::RecentlyClosedTabsHandler(DOMUIHost* dom_ui_host) : dom_ui_host_(dom_ui_host), tab_restore_service_(NULL), diff --git a/chrome/browser/dom_ui/new_tab_ui.h b/chrome/browser/dom_ui/new_tab_ui.h index 71a4f50..944fa88 100644 --- a/chrome/browser/dom_ui/new_tab_ui.h +++ b/chrome/browser/dom_ui/new_tab_ui.h @@ -30,6 +30,7 @@ #ifndef CHROME_BROWSER_DOM_UI_NEW_TAB_UI_H__ #define CHROME_BROWSER_DOM_UI_NEW_TAB_UI_H__ +#include "chrome/browser/bookmark_bar_model.h" #include "chrome/browser/dom_ui/dom_ui_host.h" #include "chrome/browser/dom_ui/chrome_url_data_manager.h" #include "chrome/browser/history/history.h" @@ -202,9 +203,11 @@ class TemplateURLHandler : public DOMMessageHandler, DISALLOW_EVIL_CONSTRUCTORS(TemplateURLHandler); }; -class RecentlyBookmarkedHandler : public DOMMessageHandler { +class RecentlyBookmarkedHandler : public DOMMessageHandler, + public BookmarkBarModelObserver { public: explicit RecentlyBookmarkedHandler(DOMUIHost* dom_ui_host); + ~RecentlyBookmarkedHandler(); // Callback which navigates to the bookmarks page. void HandleShowBookmarkPage(const Value*); @@ -213,13 +216,32 @@ class RecentlyBookmarkedHandler : public DOMMessageHandler { // It takes no arguments. void HandleGetRecentlyBookmarked(const Value*); - void OnMostRecentStarredEntries( - HistoryService::Handle request_handle, - std::vector<history::StarredEntry>* entries); - private: + void SendBookmarksToPage(); + + // BookmarkBarModelObserver methods. These invoke SendBookmarksToPage. + virtual void Loaded(BookmarkBarModel* model); + virtual void BookmarkNodeAdded(BookmarkBarModel* model, + BookmarkBarNode* parent, + int index); + virtual void BookmarkNodeRemoved(BookmarkBarModel* model, + BookmarkBarNode* parent, + int index); + virtual void BookmarkNodeChanged(BookmarkBarModel* model, + BookmarkBarNode* node); + + // These two won't effect what is shown, so they do nothing. + virtual void BookmarkNodeMoved(BookmarkBarModel* model, + BookmarkBarNode* old_parent, + int old_index, + BookmarkBarNode* new_parent, + int new_index) {} + virtual void BookmarkNodeFavIconLoaded(BookmarkBarModel* model, + BookmarkBarNode* node) {} + DOMUIHost* dom_ui_host_; - CancelableRequestConsumerT<int, 0> cancelable_consumer_; + // The model we're getting bookmarks from. The model is owned by the Profile. + BookmarkBarModel* model_; DISALLOW_EVIL_CONSTRUCTORS(RecentlyBookmarkedHandler); }; diff --git a/chrome/browser/history/archived_database.cc b/chrome/browser/history/archived_database.cc index 19f6436..40ba38b 100644 --- a/chrome/browser/history/archived_database.cc +++ b/chrome/browser/history/archived_database.cc @@ -34,7 +34,7 @@ namespace history { namespace { -static const int kDatabaseVersion = 1; +static const int kCurrentVersionNumber = 2; } // namespace @@ -73,15 +73,8 @@ bool ArchivedDatabase::Init(const std::wstring& file_name) { BeginTransaction(); // Version check. - if (!meta_table_.Init(std::string(), kDatabaseVersion, db_)) + if (!meta_table_.Init(std::string(), kCurrentVersionNumber, db_)) return false; - if (meta_table_.GetCompatibleVersionNumber() > kDatabaseVersion) { - // We ignore this error and just run without the database. Normally, if - // the user is running two versions, the main history DB will give a - // warning about a version from the future. - LOG(WARNING) << "Archived database is a future version."; - return false; - } // Create the tables. if (!CreateURLTable(false) || !InitVisitTable() || @@ -89,6 +82,9 @@ bool ArchivedDatabase::Init(const std::wstring& file_name) { return false; CreateMainURLIndex(); + if (EnsureCurrentVersion() != INIT_OK) + return false; + // Succeeded: keep the DB open by detaching the auto-closer. scoper.Detach(); db_closer_.Attach(&db_, &statement_cache_); @@ -123,4 +119,36 @@ SqliteStatementCache& ArchivedDatabase::GetStatementCache() { return *statement_cache_; } +// Migration ------------------------------------------------------------------- + +InitStatus ArchivedDatabase::EnsureCurrentVersion() { + // We can't read databases newer than we were designed for. + if (meta_table_.GetCompatibleVersionNumber() > kCurrentVersionNumber) + return INIT_TOO_NEW; + + // NOTICE: If you are changing structures for things shared with the archived + // history file like URLs, visits, or downloads, that will need migration as + // well. Instead of putting such migration code in this class, it should be + // in the corresponding file (url_database.cc, etc.) and called from here and + // from the archived_database.cc. + + // When the version is too old, we just try to continue anyway, there should + // not be a released product that makes a database too old for us to handle. + int cur_version = meta_table_.GetVersionNumber(); + + // Put migration code here + + if (cur_version == 1) { + if (!DropStarredIDFromURLs()) + return INIT_FAILURE; + cur_version = 2; + meta_table_.SetVersionNumber(cur_version); + meta_table_.SetCompatibleVersionNumber(cur_version); + } + + LOG_IF(WARNING, cur_version < kCurrentVersionNumber) << + "Archived database version " << cur_version << " is too old to handle."; + + return INIT_OK; +} } // namespace history diff --git a/chrome/browser/history/archived_database.h b/chrome/browser/history/archived_database.h index 985dd35..d6617ff 100644 --- a/chrome/browser/history/archived_database.h +++ b/chrome/browser/history/archived_database.h @@ -67,6 +67,15 @@ class ArchivedDatabase : public URLDatabase, virtual sqlite3* GetDB(); virtual SqliteStatementCache& GetStatementCache(); + // Makes sure the version is up-to-date, updating if necessary. If the + // database is too old to migrate, the user will be notified. In this case, or + // for other errors, false will be returned. True means it is up-to-date and + // ready for use. + // + // This assumes it is called from the init function inside a transaction. It + // may commit the transaction and start a new one if migration requires it. + InitStatus EnsureCurrentVersion(); + // The database. // // The close scoper will free the database and delete the statement cache in diff --git a/chrome/browser/history/expire_history_backend.cc b/chrome/browser/history/expire_history_backend.cc index ba42e07..e8065ff 100644 --- a/chrome/browser/history/expire_history_backend.cc +++ b/chrome/browser/history/expire_history_backend.cc @@ -124,8 +124,8 @@ void ExpireHistoryBackend::DeleteURL(const GURL& url) { text_db_->DeleteURLFromUncommitted(url); // Collect all the visits and delete them. Note that we don't give up if - // there are no visits, since the URL could still have an entry (for example, - // if it's starred) that we should delete. + // there are no visits, since the URL could still have an entry that we should + // delete. // TODO(brettw): bug 1171148: We should also delete from the archived DB. VisitVector visits; main_db_->GetVisitsForURL(url_row.id(), &visits); @@ -205,14 +205,6 @@ void ExpireHistoryBackend::DeleteFaviconsIfPossible( void ExpireHistoryBackend::BroadcastDeleteNotifications( DeleteDependencies* dependencies) { - // Broadcast the "unstarred" notification. - if (!dependencies->unstarred_urls.empty()) { - URLsStarredDetails* unstarred_details = new URLsStarredDetails(false); - unstarred_details->changed_urls.swap(dependencies->unstarred_urls); - unstarred_details->star_entries.swap(dependencies->unstarred_entries); - delegate_->BroadcastNotifications(NOTIFY_URLS_STARRED, unstarred_details); - } - if (!dependencies->deleted_urls.empty()) { // Broadcast the URL deleted notification. Note that we also broadcast when // we were requested to delete everything even if that was a NOP, since @@ -284,15 +276,6 @@ void ExpireHistoryBackend::DeleteOneURL( thumb_db_->DeleteThumbnail(url_row.id()); main_db_->DeleteSegmentForURL(url_row.id()); - // The starred table may need to have its corresponding item deleted. - if (url_row.star_id()) { - // Note that this will update the URLRow's starred field, but our variable - // won't be updated correspondingly. - main_db_->DeleteStarredEntry(url_row.star_id(), - &dependencies->unstarred_urls, - &dependencies->unstarred_entries); - } - // Collect shared information. if (url_row.favicon_id()) dependencies->affected_favicons.insert(url_row.favicon_id()); @@ -362,8 +345,8 @@ void ExpireHistoryBackend::ExpireURLsForVisits( else url_row.set_last_visit(Time()); - // Don't delete starred URLs or ones with visits still in the DB. - if (url_row.starred() || !url_row.last_visit().is_null()) { + // Don't delete URLs with visits still in the DB. + if (!url_row.last_visit().is_null()) { // We're not deleting the URL, update its counts when we're deleting those // visits. // NOTE: The calls to std::max() below are a backstop, but they should diff --git a/chrome/browser/history/expire_history_backend.h b/chrome/browser/history/expire_history_backend.h index 4862f9a..0ed5676 100644 --- a/chrome/browser/history/expire_history_backend.h +++ b/chrome/browser/history/expire_history_backend.h @@ -81,8 +81,7 @@ class ExpireHistoryBackend { // will continue until the object is deleted. void StartArchivingOldStuff(TimeDelta expiration_threshold); - // Deletes everything associated with a URL, regardless of whether it is - // starred or not. + // Deletes everything associated with a URL. void DeleteURL(const GURL& url); // Removes all visits in the given time range, updating the URLs accordingly. @@ -128,10 +127,6 @@ class ExpireHistoryBackend { // that we will need to check when the delete operations are complete. std::set<FavIconID> affected_favicons; - // URLs that were unstarred as a result of the delete. - std::set<GURL> unstarred_urls; - std::vector<StarredEntry> unstarred_entries; - // Tracks the set of databases that have changed so we can optimize when // when we're done. TextDatabaseManager::ChangeSet text_db_changes; diff --git a/chrome/browser/history/expire_history_backend_unittest.cc b/chrome/browser/history/expire_history_backend_unittest.cc index 0cd12da..28d90ab 100644 --- a/chrome/browser/history/expire_history_backend_unittest.cc +++ b/chrome/browser/history/expire_history_backend_unittest.cc @@ -111,7 +111,7 @@ class ExpireHistoryTest : public testing::Test, std::wstring history_name(dir_); file_util::AppendToPath(&history_name, L"History"); main_db_.reset(new HistoryDatabase); - if (main_db_->Init(history_name) != INIT_OK) + if (main_db_->Init(history_name, std::wstring()) != INIT_OK) main_db_.reset(); std::wstring archived_name(dir_); @@ -460,32 +460,6 @@ TEST_F(ExpireHistoryTest, DeleteURLWithoutFavicon) { EXPECT_TRUE(HasFavIcon(last_row.favicon_id())); } -// DeletdeURL should delete URLs that are starred. -TEST_F(ExpireHistoryTest, DeleteStarredURL) { - URLID url_ids[3]; - Time visit_times[4]; - AddExampleData(url_ids, visit_times); - - URLRow url_row; - ASSERT_TRUE(main_db_->GetURLRow(url_ids[2], &url_row)); - - // Star the last URL. - StarredEntry starred; - starred.type = StarredEntry::URL; - starred.url = url_row.url(); - starred.url_id = url_row.id(); - starred.parent_group_id = HistoryService::kBookmarkBarID; - ASSERT_TRUE(main_db_->CreateStarredEntry(&starred)); - ASSERT_TRUE(main_db_->GetURLRow(url_ids[2], &url_row)); - - // Now delete it, this should delete it even though it's starred. - expirer_.DeleteURL(url_row.url()); - - // All the normal data + the favicon should be gone. - EnsureURLInfoGone(url_row); - EXPECT_FALSE(HasFavIcon(url_row.favicon_id())); -} - // Expires all URLs more recent than a given time, with no starred items. // Our time threshold is such that one URL should be updated (we delete one of // the two visits) and one is deleted. @@ -540,62 +514,6 @@ TEST_F(ExpireHistoryTest, FlushRecentURLsUnstarred) { EXPECT_FALSE(HasFavIcon(url_row2.favicon_id())); } -// Expire a starred URL, it shouldn't get deleted and its visit counts should -// be updated properly. -TEST_F(ExpireHistoryTest, FlushRecentURLsStarred) { - URLID url_ids[3]; - Time visit_times[4]; - AddExampleData(url_ids, visit_times); - - URLRow url_row1, url_row2; - ASSERT_TRUE(main_db_->GetURLRow(url_ids[1], &url_row1)); - ASSERT_TRUE(main_db_->GetURLRow(url_ids[2], &url_row2)); - - // Star the last two URLs. - StarredEntry starred1; - starred1.type = StarredEntry::URL; - starred1.url = url_row1.url(); - starred1.url_id = url_row1.id(); - starred1.parent_group_id = HistoryService::kBookmarkBarID; - ASSERT_TRUE(main_db_->CreateStarredEntry(&starred1)); - ASSERT_TRUE(main_db_->GetURLRow(url_ids[1], &url_row1)); - - StarredEntry starred2; - starred2.type = StarredEntry::URL; - starred2.url = url_row2.url(); - starred2.url_id = url_row2.id(); - starred2.parent_group_id = HistoryService::kBookmarkBarID; - ASSERT_TRUE(main_db_->CreateStarredEntry(&starred2)); - ASSERT_TRUE(main_db_->GetURLRow(url_ids[2], &url_row2)); - - // This should delete the last two visits. - expirer_.ExpireHistoryBetween(visit_times[2], Time()); - - // The URL rows should still exist. - URLRow new_url_row1, new_url_row2; - ASSERT_TRUE(main_db_->GetURLRow(url_ids[1], &new_url_row1)); - ASSERT_TRUE(main_db_->GetURLRow(url_ids[2], &new_url_row2)); - - // The visit times should be updated. - EXPECT_TRUE(new_url_row1.last_visit() == visit_times[1]); - EXPECT_TRUE(new_url_row2.last_visit().is_null()); // No last visit time. - - // Visit counts should be updated. - EXPECT_EQ(0, new_url_row1.typed_count()); - EXPECT_EQ(1, new_url_row1.visit_count()); - EXPECT_EQ(0, new_url_row2.typed_count()); - EXPECT_EQ(0, new_url_row2.visit_count()); - - // Thumbnails and favicons should still exist. Note that we keep thumbnails - // that may have been updated since the time threshold. Since the URL still - // exists in history, this should not be a privacy problem, we only update - // the visit counts in this case for consistency anyway. - EXPECT_TRUE(HasFavIcon(new_url_row1.favicon_id())); - EXPECT_TRUE(HasThumbnail(new_url_row1.id())); - EXPECT_TRUE(HasFavIcon(new_url_row2.favicon_id())); - EXPECT_TRUE(HasThumbnail(new_url_row2.id())); -} - TEST_F(ExpireHistoryTest, ArchiveHistoryBeforeUnstarred) { URLID url_ids[3]; Time visit_times[4]; @@ -634,53 +552,6 @@ TEST_F(ExpireHistoryTest, ArchiveHistoryBeforeUnstarred) { EXPECT_EQ(1, archived_visits.size()); } -TEST_F(ExpireHistoryTest, ArchiveHistoryBeforeStarred) { - URLID url_ids[3]; - Time visit_times[4]; - AddExampleData(url_ids, visit_times); - - URLRow url_row0, url_row1; - ASSERT_TRUE(main_db_->GetURLRow(url_ids[0], &url_row0)); - ASSERT_TRUE(main_db_->GetURLRow(url_ids[1], &url_row1)); - - // Star the URLs. We use fake star IDs here, but that doesn't matter. - url_row0.set_star_id(1); - main_db_->UpdateURLRow(url_row0.id(), url_row0); - url_row1.set_star_id(2); - main_db_->UpdateURLRow(url_row1.id(), url_row1); - - // Now archive the first three visits (first two URLs). The first two visits - // should be, the thirddeleted, but the URL records should not. - expirer_.ArchiveHistoryBefore(visit_times[2]); - - // The first URL should have its visit deleted, but it should still be present - // in the main DB and not in the archived one since it is starred. - URLRow temp_row; - ASSERT_TRUE(main_db_->GetURLRow(url_ids[0], &temp_row)); - // Note that the ID is different in the archived DB, so look up by URL. - EXPECT_FALSE(archived_db_->GetRowForURL(temp_row.url(), NULL)); - VisitVector visits; - main_db_->GetVisitsForURL(temp_row.id(), &visits); - EXPECT_EQ(0, visits.size()); - - // The second URL should have its first visit deleted and its second visit - // archived. It should be present in both the main DB (because it's starred) - // and the archived DB (for the archived visit). - ASSERT_TRUE(main_db_->GetURLRow(url_ids[1], &temp_row)); - main_db_->GetVisitsForURL(temp_row.id(), &visits); - EXPECT_EQ(0, visits.size()); - - // Note that the ID is different in the archived DB, so look up by URL. - ASSERT_TRUE(archived_db_->GetRowForURL(temp_row.url(), &temp_row)); - archived_db_->GetVisitsForURL(temp_row.id(), &visits); - ASSERT_EQ(1, visits.size()); - EXPECT_TRUE(visit_times[2] == visits[0].visit_time); - - // The third URL should be unchanged. - EXPECT_TRUE(main_db_->GetURLRow(url_ids[2], &temp_row)); - EXPECT_FALSE(archived_db_->GetRowForURL(temp_row.url(), NULL)); -} - // Tests the return values from ArchiveSomeOldHistory. The rest of the // functionality of this function is tested by the ArchiveHistoryBefore* // tests which use this function internally. diff --git a/chrome/browser/history/history.cc b/chrome/browser/history/history.cc index d4e6879..bebc348 100644 --- a/chrome/browser/history/history.cc +++ b/chrome/browser/history/history.cc @@ -241,6 +241,13 @@ HistoryService::Handle HistoryService::ScheduleDBTask( request); } +HistoryService::Handle HistoryService::ScheduleEmptyCallback( + CancelableRequestConsumerBase* consumer, + EmptyHistoryCallback* callback) { + return Schedule(PRIORITY_UI, &HistoryBackend::ProcessEmptyRequest, + consumer, new history::EmptyHistoryRequest(callback)); +} + HistoryService::Handle HistoryService::QuerySegmentUsageSince( CancelableRequestConsumerBase* consumer, const Time from_time, @@ -435,55 +442,6 @@ void HistoryService::SetImportedFavicons( &HistoryBackend::SetImportedFavicons, favicon_usage); } -HistoryService::Handle HistoryService::GetAllStarredEntries( - CancelableRequestConsumerBase* consumer, - GetStarredEntriesCallback* callback) { - return Schedule(PRIORITY_UI, &HistoryBackend::GetAllStarredEntries, - consumer, - new history::GetStarredEntriesRequest(callback)); -} - -void HistoryService::UpdateStarredEntry(const history::StarredEntry& entry) { - ScheduleAndForget(PRIORITY_UI, &HistoryBackend::UpdateStarredEntry, entry); -} - -HistoryService::Handle HistoryService::CreateStarredEntry( - const history::StarredEntry& entry, - CancelableRequestConsumerBase* consumer, - CreateStarredEntryCallback* callback) { - DCHECK(entry.type != history::StarredEntry::BOOKMARK_BAR && - entry.type != history::StarredEntry::OTHER); - if (!consumer) { - ScheduleTask(PRIORITY_UI, - NewRunnableMethod(history_backend_.get(), - &HistoryBackend::CreateStarredEntry, - scoped_refptr<history::CreateStarredEntryRequest>(), - entry)); - return 0; - } - return Schedule(PRIORITY_UI, &HistoryBackend::CreateStarredEntry, consumer, - new history::CreateStarredEntryRequest(callback), entry); -} - -void HistoryService::DeleteStarredGroup(history::UIStarID group_id) { - ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::DeleteStarredGroup, - group_id); -} - -void HistoryService::DeleteStarredURL(const GURL& url) { - ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::DeleteStarredURL, url); -} - -HistoryService::Handle HistoryService::GetMostRecentStarredEntries( - int max_count, - CancelableRequestConsumerBase* consumer, - GetMostRecentStarredEntriesCallback* callback) { - return Schedule(PRIORITY_UI, &HistoryBackend::GetMostRecentStarredEntries, - consumer, - new history::GetMostRecentStarredEntriesRequest(callback), - max_count); -} - void HistoryService::IterateURLs(URLEnumerator* enumerator) { ScheduleAndForget(PRIORITY_NORMAL, &HistoryBackend::IterateURLs, enumerator); } diff --git a/chrome/browser/history/history.h b/chrome/browser/history/history.h index 9dca73a..3db1e23 100644 --- a/chrome/browser/history/history.h +++ b/chrome/browser/history/history.h @@ -393,29 +393,6 @@ class HistoryService : public CancelableRequestProvider, void SetImportedFavicons( const std::vector<history::ImportedFavIconUsage>& favicon_usage); - // Starring ------------------------------------------------------------------ - - // Starring mutation methods are private, go through the BookmarkBarModel - // instead. - // - // The typedefs are public to allow template magic to work. - - typedef Callback2<Handle, std::vector<history::StarredEntry>* >::Type - GetStarredEntriesCallback; - - typedef Callback2<Handle, history::StarID>::Type CreateStarredEntryCallback; - - typedef Callback2<Handle, std::vector<history::StarredEntry>* >::Type - GetMostRecentStarredEntriesCallback; - - // Fetches up to max_count starred entries of type URL. - // The results are ordered by date added in descending order (most recent - // first). - Handle GetMostRecentStarredEntries( - int max_count, - CancelableRequestConsumerBase* consumer, - GetMostRecentStarredEntriesCallback* callback); - // Database management operations -------------------------------------------- // Delete all the information related to a single url. @@ -544,6 +521,13 @@ class HistoryService : public CancelableRequestProvider, Handle ScheduleDBTask(HistoryDBTask* task, CancelableRequestConsumerBase* consumer); + typedef Callback1<Handle>::Type EmptyHistoryCallback; + + // Schedules a task that does nothing on the backend. This can be used to get + // notification when then history backend is done processing requests. + Handle ScheduleEmptyCallback(CancelableRequestConsumerBase* consumer, + EmptyHistoryCallback* callback); + // Testing ------------------------------------------------------------------- // Designed for unit tests, this passes the given task on to the history @@ -597,51 +581,6 @@ class HistoryService : public CancelableRequestProvider, friend class HistoryOperation; friend class HistoryURLProviderTest; - // Starring ------------------------------------------------------------------ - - // These are private as they should only be invoked from the bookmark bar - // model. - - // Fetches all the starred entries (both groups and entries). - Handle GetAllStarredEntries( - CancelableRequestConsumerBase* consumer, - GetStarredEntriesCallback* callback); - - // Updates the title, parent and visual order of the specified entry. The key - // used to identify the entry is NOT entry.id, rather it is the url (if the - // type is URL), or the group_id (if the type is other than URL). - // - // This can NOT be used to change the type of an entry. - // - // After updating the entry, NOTIFY_STAR_ENTRY_CHANGED is sent. - void UpdateStarredEntry(const history::StarredEntry& entry); - - // Creates a starred entry at the specified position. This can be used - // for creating groups and nodes. - // - // If the entry is a URL and the URL is already starred, this behaves the - // same as invoking UpdateStarredEntry. If the entry is a URL and the URL is - // not starred, the URL is starred appropriately. - // - // This honors the title, parent_group_id, visual_order and url (for URL - // nodes) of the specified entry. All other attributes are ignored. - // - // NOTE: consumer and callback may be null, in which case the request - // isn't cancelable and 0 is returned. - Handle CreateStarredEntry(const history::StarredEntry& entry, - CancelableRequestConsumerBase* consumer, - CreateStarredEntryCallback* callback); - - // Deletes the specified starred group. All children groups are deleted and - // starred descendants unstarred. If successful, this sends out the - // notification NOTIFY_URLS_STARRED. To delete a starred URL, do - // DeletedStarredEntry(id). - void DeleteStarredGroup(history::UIStarID group_id); - - // Deletes the specified starred URL. If successful, this sends out the - // notification NOTIFY_URLS_STARRED. - void DeleteStarredURL(const GURL& url); - // Implementation of NotificationObserver. virtual void Observe(NotificationType type, const NotificationSource& source, diff --git a/chrome/browser/history/history_backend.cc b/chrome/browser/history/history_backend.cc index d9ef224..88e40ed 100644 --- a/chrome/browser/history/history_backend.cc +++ b/chrome/browser/history/history_backend.cc @@ -268,10 +268,13 @@ void HistoryBackend::Init() { file_util::AppendToPath(&history_name, chrome::kHistoryFilename); std::wstring thumbnail_name = GetThumbnailFileName(); std::wstring archived_name = GetArchivedFileName(); + std::wstring tmp_bookmarks_file = history_dir_; + file_util::AppendToPath(&tmp_bookmarks_file, + chrome::kHistoryBookmarksFileName); // History database. db_.reset(new HistoryDatabase()); - switch (db_->Init(history_name)) { + switch (db_->Init(history_name, tmp_bookmarks_file)) { case INIT_OK: break; case INIT_FAILURE: @@ -995,16 +998,6 @@ void HistoryBackend::QueryHistory(scoped_refptr<QueryHistoryRequest> request, if (db_.get()) { if (text_query.empty()) { - if (options.begin_time.is_null() && options.end_time.is_null() && - options.only_starred && options.max_count == 0) { - DLOG(WARNING) << "Querying for all bookmarks. You should probably use " - "the dedicated starring functions which will also report unvisited " - "bookmarks and will be faster."; - // If this case is needed and the starring functions aren't right, we - // can optimize the case where we're just querying for starred URLs - // and remove the warning. - } - // Basic history query for the main database. QueryHistoryBasic(db_.get(), db_.get(), options, &request->value); @@ -1060,14 +1053,8 @@ void HistoryBackend::QueryHistoryBasic(URLDatabase* url_db, // catch any interesting stuff. This will update it if it exists in the // main DB, and do nothing otherwise. db_->GetRowForURL(url_result.url(), &url_result); - } else { - // URLs not in the main DB can't be starred, reset this just in case. - url_result.set_star_id(0); } - if (!url_result.starred() && options.only_starred) - continue; // Want non-starred items filtered out. - url_result.set_visit_time(visit.visit_time); // We don't set any of the query-specific parts of the URLResult, since @@ -1076,62 +1063,6 @@ void HistoryBackend::QueryHistoryBasic(URLDatabase* url_db, } } -void HistoryBackend::QueryStarredEntriesByText( - URLQuerier* querier, - const std::wstring& text_query, - const QueryOptions& options, - QueryResults* results) { - // Collect our prepends so we can bulk add them at the end. It is more - // efficient to bluk prepend the URLs than to do one at a time. - QueryResults our_results; - - // We can use only the main DB (not archived) for this operation since we know - // that all currently starred URLs will be in the main DB. - std::set<URLID> ids; - db_->GetURLsForTitlesMatching(text_query, &ids); - - VisitRow visit; - PageVisit page_visit; - URLResult url_result; - for (std::set<URLID>::iterator i = ids.begin(); i != ids.end(); ++i) { - // Turn the ID (associated with the main DB) into a visit row. - if (!db_->GetURLRow(*i, &url_result)) - continue; // Not found, some crazy error. - - // Make sure we haven't already reported this URL and don't add it if so. - if (querier->HasURL(url_result.url())) - continue; - - // Consistency check, all URLs should be valid and starred. - if (!url_result.url().is_valid() || !url_result.starred()) - continue; - - db_->GetStarredEntry(url_result.star_id(), &url_result.starred_entry_); - - // Use the last visit time as the visit time for this result. - // TODO(brettw) when we are not querying for the most recent visit only, - // we should get all the visits in the time range for the given URL and add - // separate results for each of them. Until then, just treat as unique: - // - // Just add this one visit for the last time they visited it, except for - // starred only queries which have no visit times. - if (options.only_starred) { - url_result.set_visit_time(Time()); - } else { - url_result.set_visit_time(url_result.last_visit()); - } - our_results.AppendURLBySwapping(&url_result); - } - - // Now prepend all of the bookmark matches we found. We do this by appending - // the old values to the new ones and swapping the results. - if (our_results.size() > 0) { - our_results.AppendResultsBySwapping(results, - options.most_recent_visit_only); - our_results.Swap(results); - } -} - void HistoryBackend::QueryHistoryFTS(const std::wstring& text_query, const QueryOptions& options, QueryResults* result) { @@ -1162,8 +1093,6 @@ void HistoryBackend::QueryHistoryFTS(const std::wstring& text_query, if (!url_result.url().is_valid()) continue; // Don't report invalid URLs in case of corruption. - if (options.only_starred && !url_result.star_id()) - continue; // Don't add this unstarred item. // Copy over the FTS stuff that the URLDatabase doesn't know about. // We do this with swap() to avoid copying, since we know we don't @@ -1179,21 +1108,10 @@ void HistoryBackend::QueryHistoryFTS(const std::wstring& text_query, // has the time, we can avoid an extra query of the visits table. url_result.set_visit_time(fts_matches[i].time); - if (options.only_starred) { - // When querying for starred pages fetch the starred entry. - DCHECK(url_result.star_id()); - db_->GetStarredEntry(url_result.star_id(), &url_result.starred_entry_); - } else { - url_result.ResetStarredEntry(); - } - // Add it to the vector, this will clear our |url_row| object as a // result of the swap. result->AppendURLBySwapping(&url_result); } - - if (options.include_all_starred) - QueryStarredEntriesByText(&querier, text_query, options, result); } // Frontend to GetMostRecentRedirectsFrom from the history thread. @@ -1399,7 +1317,7 @@ void HistoryBackend::SetImportedFavicons( Time now = Time::Now(); // Track all starred URLs that had their favicons set or updated. - std::set<GURL> starred_favicons_changed; + std::set<GURL> favicons_changed; for (size_t i = 0; i < favicon_usage.size(); i++) { FavIconID favicon_id = thumbnail_db_->GetFavIconIDForFavIconURL( @@ -1422,16 +1340,15 @@ void HistoryBackend::SetImportedFavicons( url_row.set_favicon_id(favicon_id); db_->UpdateURLRow(url_row.id(), url_row); - if (url_row.starred()) - starred_favicons_changed.insert(*url); + favicons_changed.insert(*url); } } - if (!starred_favicons_changed.empty()) { + if (!favicons_changed.empty()) { // Send the notification about the changed favicons for starred URLs. FavIconChangeDetails* changed_details = new FavIconChangeDetails; - changed_details->urls.swap(starred_favicons_changed); - BroadcastNotifications(NOTIFY_STARRED_FAVICON_CHANGED, changed_details); + changed_details->urls.swap(favicons_changed); + BroadcastNotifications(NOTIFY_FAVICON_CHANGED, changed_details); } } @@ -1544,7 +1461,7 @@ void HistoryBackend::SetFavIconMapping(const GURL& page_url, redirects = &dummy_list; } - std::set<GURL> starred_favicons_changed; + std::set<GURL> favicons_changed; // Save page <-> favicon association. for (HistoryService::RedirectList::const_iterator i(redirects->begin()); @@ -1569,147 +1486,15 @@ void HistoryBackend::SetFavIconMapping(const GURL& page_url, thumbnail_db_->DeleteFavIcon(old_id); } - if (row.starred()) - starred_favicons_changed.insert(row.url()); - } - - if (!starred_favicons_changed.empty()) { - // Send the notification about the changed favicons for starred URLs. - FavIconChangeDetails* changed_details = new FavIconChangeDetails; - changed_details->urls.swap(starred_favicons_changed); - BroadcastNotifications(NOTIFY_STARRED_FAVICON_CHANGED, changed_details); - } - - ScheduleCommit(); -} - -void HistoryBackend::GetAllStarredEntries( - scoped_refptr<GetStarredEntriesRequest> request) { - if (request->canceled()) - return; - // Only query for the entries if the starred table is valid. If the starred - // table isn't valid, we may get back garbage which could cause the UI grief. - // - // TODO(sky): bug 1207654: this is temporary, the UI should really query for - // valid state than avoid GetAllStarredEntries if not valid. - if (db_.get() && db_->is_starred_valid()) - db_->GetStarredEntries(0, &(request->value)); - request->ForwardResult( - GetStarredEntriesRequest::TupleType(request->handle(), - &(request->value))); -} - -void HistoryBackend::UpdateStarredEntry(const StarredEntry& new_entry) { - if (!db_.get()) - return; - - StarredEntry resulting_entry = new_entry; - if (!db_->UpdateStarredEntry(&resulting_entry) || !delegate_.get()) - return; - - ScheduleCommit(); - - // Send out notification that the star entry changed. - StarredEntryDetails* entry_details = new StarredEntryDetails(); - entry_details->entry = resulting_entry; - BroadcastNotifications(NOTIFY_STAR_ENTRY_CHANGED, entry_details); -} - -void HistoryBackend::CreateStarredEntry( - scoped_refptr<CreateStarredEntryRequest> request, - const StarredEntry& entry) { - // This method explicitly allows request to be NULL. - if (request.get() && request->canceled()) - return; - - StarID id = 0; - StarredEntry resulting_entry(entry); - if (db_.get()) { - if (entry.type == StarredEntry::USER_GROUP) { - id = db_->CreateStarredEntry(&resulting_entry); - if (id) { - // Broadcast group created notifications. - StarredEntryDetails* entry_details = new StarredEntryDetails; - entry_details->entry = resulting_entry; - BroadcastNotifications(NOTIFY_STAR_GROUP_CREATED, entry_details); - } - } else if (entry.type == StarredEntry::URL) { - // Currently, we only allow one starred entry for this URL. Therefore, we - // check for an existing starred entry for this URL and update it if it - // exists. - if (!db_->GetStarIDForEntry(resulting_entry)) { - // Adding a new starred URL. - id = db_->CreateStarredEntry(&resulting_entry); - - // Broadcast starred notification. - URLsStarredDetails* details = new URLsStarredDetails(true); - details->changed_urls.insert(resulting_entry.url); - details->star_entries.push_back(resulting_entry); - BroadcastNotifications(NOTIFY_URLS_STARRED, details); - } else { - // Updating an existing one. - db_->UpdateStarredEntry(&resulting_entry); - - // Broadcast starred update notification. - StarredEntryDetails* entry_details = new StarredEntryDetails; - entry_details->entry = resulting_entry; - BroadcastNotifications(NOTIFY_STAR_ENTRY_CHANGED, entry_details); - } - } else { - NOTREACHED(); - } - } - - ScheduleCommit(); - - if (request.get()) { - request->ForwardResult( - CreateStarredEntryRequest::TupleType(request->handle(), id)); + favicons_changed.insert(row.url()); } -} -void HistoryBackend::DeleteStarredGroup(UIStarID group_id) { - if (!db_.get()) - return; - - DeleteStarredEntry(db_->GetStarIDForGroupID(group_id)); -} - -void HistoryBackend::DeleteStarredURL(const GURL& url) { - if (!db_.get()) - return; - - history::StarredEntry entry; - entry.url = url; - DeleteStarredEntry(db_->GetStarIDForEntry(entry)); -} - -void HistoryBackend::DeleteStarredEntry(history::StarID star_id) { - if (!star_id) { - NOTREACHED() << "Deleting a nonexistent entry"; - return; - } - - // Delete the entry. - URLsStarredDetails* details = new URLsStarredDetails(false); - db_->DeleteStarredEntry(star_id, &details->changed_urls, - &details->star_entries); + // Send the notification about the changed favicons. + FavIconChangeDetails* changed_details = new FavIconChangeDetails; + changed_details->urls.swap(favicons_changed); + BroadcastNotifications(NOTIFY_FAVICON_CHANGED, changed_details); ScheduleCommit(); - - BroadcastNotifications(NOTIFY_URLS_STARRED, details); -} - -void HistoryBackend::GetMostRecentStarredEntries( - scoped_refptr<GetMostRecentStarredEntriesRequest> request, - int max_count) { - if (request->canceled()) - return; - if (db_.get()) - db_->GetMostRecentStarredEntries(max_count, &(request->value)); - request->ForwardResult( - GetMostRecentStarredEntriesRequest::TupleType(request->handle(), - &(request->value))); } void HistoryBackend::Commit() { @@ -1859,6 +1644,11 @@ void HistoryBackend::ProcessDBTask( } } +void HistoryBackend::ProcessEmptyRequest( + scoped_refptr<EmptyHistoryRequest> request) { + request->ForwardResult(EmptyHistoryRequest::TupleType()); +} + void HistoryBackend::BroadcastNotifications( NotificationType type, HistoryDetails* details_deleted) { @@ -1887,7 +1677,6 @@ void HistoryBackend::DeleteAllHistory() { // Get starred entries and their corresponding URL rows. std::vector<StarredEntry> starred_entries; - db_->GetStarredEntries(0, &starred_entries); std::vector<URLRow> kept_urls; for (size_t i = 0; i < starred_entries.size(); i++) { @@ -2032,19 +1821,6 @@ bool HistoryBackend::ClearAllMainHistory( if (!db_->CommitTemporaryURLTable()) return false; - // The starred table references the old URL IDs. We need to fix it up to refer - // to the new ones. - for (std::vector<StarredEntry>::iterator i = starred_entries->begin(); - i != starred_entries->end(); - ++i) { - if (i->type != StarredEntry::URL) - continue; - - DCHECK(old_to_new.find(i->url_id) != old_to_new.end()); - i->url_id = old_to_new[i->url_id]; - db_->UpdateURLIDForStar(i->id, i->url_id); - } - // Delete the old tables and recreate them empty. db_->RecreateAllButStarAndURLTables(); diff --git a/chrome/browser/history/history_backend.h b/chrome/browser/history/history_backend.h index c1136de..f73e653 100644 --- a/chrome/browser/history/history_backend.h +++ b/chrome/browser/history/history_backend.h @@ -205,26 +205,6 @@ class HistoryBackend : public base::RefCountedThreadSafe<HistoryBackend>, void SetImportedFavicons( const std::vector<ImportedFavIconUsage>& favicon_usage); - // Starring ------------------------------------------------------------------ - - void GetAllStarredEntries( - scoped_refptr<GetStarredEntriesRequest> request); - - void UpdateStarredEntry(const StarredEntry& new_entry); - - void CreateStarredEntry(scoped_refptr<CreateStarredEntryRequest> request, - const StarredEntry& entry); - - void DeleteStarredGroup(UIStarID group_id); - - void DeleteStarredURL(const GURL& url); - - void DeleteStarredEntry(history::StarID star_id); - - void GetMostRecentStarredEntries( - scoped_refptr<GetMostRecentStarredEntriesRequest> request, - int max_count); - // Downloads ----------------------------------------------------------------- void QueryDownloads(scoped_refptr<DownloadQueryRequest> request); @@ -263,6 +243,8 @@ class HistoryBackend : public base::RefCountedThreadSafe<HistoryBackend>, void ProcessDBTask(scoped_refptr<HistoryDBTaskRequest> request); + void ProcessEmptyRequest(scoped_refptr<EmptyHistoryRequest> request); + // Deleting ------------------------------------------------------------------ void DeleteURL(const GURL& url); @@ -338,15 +320,6 @@ class HistoryBackend : public base::RefCountedThreadSafe<HistoryBackend>, const QueryOptions& options, QueryResults* result); - // Queries the starred database for all URL entries whose title contains the - // specified text. This is called as necessary from QueryHistoryFTS. The - // matches will be added to the beginning of the result vector in no - // particular order. - void QueryStarredEntriesByText(URLQuerier* querier, - const std::wstring& text_query, - const QueryOptions& options, - QueryResults* results); - // Committing ---------------------------------------------------------------- // We always keep a transaction open on the history database so that multiple diff --git a/chrome/browser/history/history_backend_unittest.cc b/chrome/browser/history/history_backend_unittest.cc index e1e6ffe..68bfc19 100644 --- a/chrome/browser/history/history_backend_unittest.cc +++ b/chrome/browser/history/history_backend_unittest.cc @@ -205,14 +205,6 @@ TEST_F(HistoryBackendTest, DeleteAll) { JPEGCodec::Decode(kWeewarThumbnail, sizeof(kWeewarThumbnail))); backend_->thumbnail_db_->SetPageThumbnail(row2_id, *weewar_bitmap, score); - // Mark one of the URLs bookmarked. - StarredEntry entry; - entry.type = StarredEntry::URL; - entry.url = row1.url(); - entry.parent_group_id = HistoryService::kBookmarkBarID; - StarID star_id = backend_->db_->CreateStarredEntry(&entry); - ASSERT_TRUE(star_id); - // Set full text index for each one. backend_->text_database_->AddPageData(row1.url(), row1_id, visit1_id, row1.last_visit(), @@ -224,12 +216,8 @@ TEST_F(HistoryBackendTest, DeleteAll) { // Now finally clear all history. backend_->DeleteAllHistory(); - // The first URL should be preserved, along with its visits, but the time - // should be cleared. - EXPECT_TRUE(backend_->db_->GetRowForURL(row1.url(), &outrow1)); - EXPECT_EQ(2, outrow1.visit_count()); - EXPECT_EQ(1, outrow1.typed_count()); - EXPECT_TRUE(Time() == outrow1.last_visit()); + // The first URL should be deleted. + EXPECT_FALSE(backend_->db_->GetRowForURL(row1.url(), &outrow1)); // The second row should be deleted. URLRow outrow2; @@ -246,27 +234,15 @@ TEST_F(HistoryBackendTest, DeleteAll) { &out_data)); EXPECT_FALSE(backend_->thumbnail_db_->GetPageThumbnail(row2_id, &out_data)); - // We should have a favicon for the first URL only. We look them up by favicon - // URL since the IDs may hav changed. + // Make sure the favicons were deleted. + // TODO(sky): would be nice if this didn't happen. FavIconID out_favicon1 = backend_->thumbnail_db_-> GetFavIconIDForFavIconURL(favicon_url1); - EXPECT_TRUE(out_favicon1); + EXPECT_FALSE(out_favicon1); FavIconID out_favicon2 = backend_->thumbnail_db_-> GetFavIconIDForFavIconURL(favicon_url2); EXPECT_FALSE(out_favicon2) << "Favicon not deleted"; - // The remaining URL should still reference the same favicon, even if its - // ID has changed. - EXPECT_EQ(out_favicon1, outrow1.favicon_id()); - - // The first URL should still be bookmarked and have an entry. The star ID - // must not have changed. - EXPECT_TRUE(outrow1.starred()); - StarredEntry outentry; - EXPECT_TRUE(backend_->db_->GetStarredEntry(star_id, &outentry)); - EXPECT_EQ(outrow1.id(), outentry.url_id); - EXPECT_TRUE(outrow1.url() == outentry.url); - // The full text database should have no data. std::vector<TextDatabase::Match> text_matches; Time first_time_searched; diff --git a/chrome/browser/history/history_database.cc b/chrome/browser/history/history_database.cc index e606aeb..d79d14a 100644 --- a/chrome/browser/history/history_database.cc +++ b/chrome/browser/history/history_database.cc @@ -43,7 +43,7 @@ namespace history { namespace { // Current version number. -const int kCurrentVersionNumber = 15; +const int kCurrentVersionNumber = 16; } // namespace @@ -55,7 +55,8 @@ HistoryDatabase::HistoryDatabase() HistoryDatabase::~HistoryDatabase() { } -InitStatus HistoryDatabase::Init(const std::wstring& history_name) { +InitStatus HistoryDatabase::Init(const std::wstring& history_name, + const std::wstring& bookmarks_path) { // Open the history database, using the narrow version of open indicates to // sqlite that we want the database to be in UTF-8 if it doesn't already // exist. @@ -94,18 +95,16 @@ InitStatus HistoryDatabase::Init(const std::wstring& history_name) { return INIT_FAILURE; if (!CreateURLTable(false) || !InitVisitTable() || !InitKeywordSearchTermsTable() || !InitDownloadTable() || - !InitSegmentTables() || !InitStarTable()) + !InitSegmentTables()) return INIT_FAILURE; CreateMainURLIndex(); CreateSupplimentaryURLIndices(); // Version check. - InitStatus version_status = EnsureCurrentVersion(); + InitStatus version_status = EnsureCurrentVersion(bookmarks_path); if (version_status != INIT_OK) return version_status; - EnsureStarredIntegrity(); - // Succeeded: keep the DB open by detaching the auto-closer. scoper.Detach(); db_closer_.Attach(&db_, &statement_cache_); @@ -222,7 +221,8 @@ SqliteStatementCache& HistoryDatabase::GetStatementCache() { // Migration ------------------------------------------------------------------- -InitStatus HistoryDatabase::EnsureCurrentVersion() { +InitStatus HistoryDatabase::EnsureCurrentVersion( + const std::wstring& tmp_bookmarks_path) { // We can't read databases newer than we were designed for. if (meta_table_.GetCompatibleVersionNumber() > kCurrentVersionNumber) return INIT_TOO_NEW; @@ -239,6 +239,16 @@ InitStatus HistoryDatabase::EnsureCurrentVersion() { // Put migration code here + if (cur_version == 15) { + if (!MigrateBookmarksToFile(tmp_bookmarks_path)) + return INIT_FAILURE; + if (!DropStarredIDFromURLs()) + return INIT_FAILURE; + cur_version = 16; + meta_table_.SetVersionNumber(cur_version); + meta_table_.SetCompatibleVersionNumber(cur_version); + } + LOG_IF(WARNING, cur_version < kCurrentVersionNumber) << "History database version " << cur_version << " is too old to handle."; diff --git a/chrome/browser/history/history_database.h b/chrome/browser/history/history_database.h index 92c356d..f386a2a 100644 --- a/chrome/browser/history/history_database.h +++ b/chrome/browser/history/history_database.h @@ -56,6 +56,9 @@ class TextDatabaseManager; // as the storage interface. Logic for manipulating this storage layer should // be in HistoryBackend.cc. class HistoryDatabase : public DownloadDatabase, + // TODO(sky): See if we can nuke StarredURLDatabase and just create on the + // stack for migration. Then HistoryDatabase would directly descend from + // URLDatabase. public StarredURLDatabase, public VisitDatabase, public VisitSegmentDatabase { @@ -84,7 +87,8 @@ class HistoryDatabase : public DownloadDatabase, // Must call this function to complete initialization. Will return true on // success. On false, no other function should be called. You may want to call // BeginExclusiveMode after this when you are ready. - InitStatus Init(const std::wstring& history_name); + InitStatus Init(const std::wstring& history_name, + const std::wstring& tmp_bookmarks_path); // Call to set the mode on the database to exclusive. The default locking mode // is "normal" but we want to run in exclusive mode for slightly better @@ -141,6 +145,9 @@ class HistoryDatabase : public DownloadDatabase, // visit id wasn't found. SegmentID GetSegmentID(VisitID visit_id); + // Drops the starred table and star_id from urls. + bool MigrateFromVersion15ToVersion16(); + private: // Implemented for URLDatabase. virtual sqlite3* GetDB(); @@ -161,7 +168,7 @@ class HistoryDatabase : public DownloadDatabase, // // This assumes it is called from the init function inside a transaction. It // may commit the transaction and start a new one if migration requires it. - InitStatus EnsureCurrentVersion(); + InitStatus EnsureCurrentVersion(const std::wstring& tmp_bookmarks_path); // --------------------------------------------------------------------------- diff --git a/chrome/browser/history/history_marshaling.h b/chrome/browser/history/history_marshaling.h index d1c4501..d0722e4 100644 --- a/chrome/browser/history/history_marshaling.h +++ b/chrome/browser/history/history_marshaling.h @@ -102,19 +102,6 @@ typedef CancelableRequest<HistoryService::ThumbnailDataCallback> typedef CancelableRequest<HistoryService::FavIconDataCallback> GetFavIconRequest; -// Starring ------------------------------------------------------------------- - -typedef CancelableRequest1<HistoryService::GetStarredEntriesCallback, - std::vector<history::StarredEntry> > - GetStarredEntriesRequest; - -typedef CancelableRequest1<HistoryService::GetMostRecentStarredEntriesCallback, - std::vector<history::StarredEntry> > - GetMostRecentStarredEntriesRequest; - -typedef CancelableRequest<HistoryService::CreateStarredEntryCallback> - CreateStarredEntryRequest; - // Downloads ------------------------------------------------------------------ typedef CancelableRequest1<HistoryService::DownloadQueryCallback, @@ -155,6 +142,9 @@ typedef CancelableRequest1<HistoryService::HistoryDBTaskCallback, scoped_refptr<HistoryDBTask> > HistoryDBTaskRequest; +typedef CancelableRequest<HistoryService::EmptyHistoryCallback> + EmptyHistoryRequest; + } // namespace history #endif // CHROME_BROWSER_HISTORY_HISTORY_MARSHALING_H__ diff --git a/chrome/browser/history/history_notifications.h b/chrome/browser/history/history_notifications.h index d5146ea..66379fd 100644 --- a/chrome/browser/history/history_notifications.h +++ b/chrome/browser/history/history_notifications.h @@ -72,7 +72,6 @@ struct URLsDeletedDetails : public HistoryDetails { // Details for NOTIFY_URLS_STARRED. struct URLsStarredDetails : public HistoryDetails { - URLsStarredDetails(bool being_starred) : starred(being_starred) {} // The new starred state of the list of URLs. True when they are being @@ -81,18 +80,9 @@ struct URLsStarredDetails : public HistoryDetails { // The list of URLs that are changing. std::set<GURL> changed_urls; - - // The star entries that were added or removed as the result of starring - // the entry. This may be empty. - std::vector<StarredEntry> star_entries; -}; - -// Details for NOTIFY_STAR_ENTRY_CHANGED and others. -struct StarredEntryDetails : public HistoryDetails { - StarredEntry entry; }; -// Details for NOTIFY_STARRED_FAVICON_CHANGED. +// Details for NOTIFY_FAVICON_CHANGED. struct FavIconChangeDetails : public HistoryDetails { std::set<GURL> urls; }; diff --git a/chrome/browser/history/history_querying_unittest.cc b/chrome/browser/history/history_querying_unittest.cc index 9d5f74d..7f4c7d5 100644 --- a/chrome/browser/history/history_querying_unittest.cc +++ b/chrome/browser/history/history_querying_unittest.cc @@ -132,19 +132,6 @@ class HistoryQueryTest : public testing::Test { history_->SetPageTitle(url, test_entries[i].title); history_->SetPageContents(url, test_entries[i].body); } - - // Set one of the pages starred. - history::StarredEntry entry; - entry.id = 5; - entry.title = L"Some starred page"; - entry.date_added = Time::Now(); - entry.parent_group_id = StarredEntry::BOOKMARK_BAR; - entry.group_id = 0; - entry.visual_order = 0; - entry.type = history::StarredEntry::URL; - entry.url = GURL(test_entries[0].url); - entry.date_group_modified = Time::Now(); - history_->CreateStarredEntry(entry, NULL, NULL); } virtual void TearDown() { @@ -189,9 +176,6 @@ TEST_F(HistoryQueryTest, Basic) { EXPECT_TRUE(NthResultIs(results, 3, 1)); EXPECT_TRUE(NthResultIs(results, 4, 0)); - // Check that the starred one is marked starred. - EXPECT_TRUE(results[4].starred()); - // Next query a time range. The beginning should be inclusive, the ending // should be exclusive. options.begin_time = test_entries[3].time; @@ -247,11 +231,10 @@ TEST_F(HistoryQueryTest, FTS) { // this query will return the starred item twice since we requested all // starred entries and no de-duping. QueryHistory(std::wstring(L"some"), options, &results); - EXPECT_EQ(4, results.size()); - EXPECT_TRUE(NthResultIs(results, 0, 4)); // Starred item. - EXPECT_TRUE(NthResultIs(results, 1, 2)); - EXPECT_TRUE(NthResultIs(results, 2, 3)); - EXPECT_TRUE(NthResultIs(results, 3, 1)); + EXPECT_EQ(3, results.size()); + EXPECT_TRUE(NthResultIs(results, 0, 2)); + EXPECT_TRUE(NthResultIs(results, 1, 3)); + EXPECT_TRUE(NthResultIs(results, 2, 1)); // Do a query that should only match one of them. QueryHistory(std::wstring(L"PAGETWO"), options, &results); @@ -262,7 +245,6 @@ TEST_F(HistoryQueryTest, FTS) { // should be exclusive. options.begin_time = test_entries[1].time; options.end_time = test_entries[3].time; - options.include_all_starred = false; QueryHistory(std::wstring(L"some"), options, &results); EXPECT_EQ(1, results.size()); EXPECT_TRUE(NthResultIs(results, 0, 1)); @@ -295,10 +277,9 @@ TEST_F(HistoryQueryTest, FTSCount) { // get the N most recent entries. options.max_count = 2; QueryHistory(std::wstring(L"some"), options, &results); - EXPECT_EQ(3, results.size()); - EXPECT_TRUE(NthResultIs(results, 0, 4)); - EXPECT_TRUE(NthResultIs(results, 1, 2)); - EXPECT_TRUE(NthResultIs(results, 2, 3)); + EXPECT_EQ(2, results.size()); + EXPECT_TRUE(NthResultIs(results, 0, 2)); + EXPECT_TRUE(NthResultIs(results, 1, 3)); // Now query a subset of the pages and limit by N items. "FOO" should match // the 2nd & 3rd pages, but we should only get the 3rd one because of the one diff --git a/chrome/browser/history/history_types.cc b/chrome/browser/history/history_types.cc index 920b0bc..758a166 100644 --- a/chrome/browser/history/history_types.cc +++ b/chrome/browser/history/history_types.cc @@ -43,7 +43,6 @@ void URLRow::Swap(URLRow* other) { std::swap(typed_count_, other->typed_count_); std::swap(last_visit_, other->last_visit_); std::swap(hidden_, other->hidden_); - std::swap(star_id_, other->star_id_); std::swap(favicon_id_, other->favicon_id_); } @@ -54,7 +53,6 @@ void URLRow::Initialize() { last_visit_ = Time(); hidden_ = false; favicon_id_ = 0; - star_id_ = 0; } // VisitRow -------------------------------------------------------------------- @@ -113,7 +111,6 @@ void URLResult::Swap(URLResult* other) { std::swap(visit_time_, other->visit_time_); snippet_.Swap(&other->snippet_); title_match_positions_.swap(other->title_match_positions_); - starred_entry_.Swap(&other->starred_entry_); } // QueryResults ---------------------------------------------------------------- diff --git a/chrome/browser/history/history_types.h b/chrome/browser/history/history_types.h index 738eda02..cbbd1f7 100644 --- a/chrome/browser/history/history_types.h +++ b/chrome/browser/history/history_types.h @@ -136,14 +136,6 @@ class URLRow { hidden_ = hidden; } - StarID star_id() const { return star_id_; } - void set_star_id(StarID star_id) { - star_id_ = star_id; - } - - // Simple boolean query to see if the page is starred. - bool starred() const { return (star_id_ != 0); } - // ID of the favicon. A value of 0 means the favicon isn't known yet. FavIconID favicon_id() const { return favicon_id_; } void set_favicon_id(FavIconID favicon_id) { @@ -190,13 +182,6 @@ class URLRow { // is usually for subframes. bool hidden_; - // ID of the starred entry. - // - // NOTE: This is ignored by Add/UpdateURL. To modify the starred state you - // must invoke SetURLStarred. - // TODO: revisit this to see if this limitation can be removed. - StarID star_id_; - // The ID of the favicon for this url. FavIconID favicon_id_; @@ -372,11 +357,6 @@ class URLResult : public URLRow { virtual void Swap(URLResult* other); - // Returns the starred entry for this url. This is only set if the query - // was configured to search for starred only entries (only_starred is true). - const StarredEntry& starred_entry() const { return starred_entry_; } - void ResetStarredEntry() { starred_entry_ = StarredEntry(); } - private: friend class HistoryBackend; @@ -387,9 +367,6 @@ class URLResult : public URLRow { Snippet snippet_; Snippet::MatchPositions title_match_positions_; - // See comment above getter. - StarredEntry starred_entry_; - // We support the implicit copy constructor and operator=. }; @@ -488,8 +465,6 @@ class QueryResults { struct QueryOptions { QueryOptions() : most_recent_visit_only(false), - only_starred(false), - include_all_starred(true), max_count(0) { } @@ -521,26 +496,6 @@ struct QueryOptions { // Defaults to false (all visits). bool most_recent_visit_only; - // Indicates if only starred items should be matched. - // - // Defaults to false. - bool only_starred; - - // When true, and we're doing a full text query, starred entries that have - // never been visited or have been visited outside of the given time range - // will also be included in the results. These items will appear at the - // beginning of the result set. Non-full-text queries won't check this flag - // and will never return unvisited bookmarks. - // - // When false, full text queries will not return unvisited bookmarks, they - // will only be included when they were visited in the given time range. - // - // You probably want to use this in conjunction with most_recent_visit_only - // since it will cause duplicates otherwise. - // - // Defaults to true. - bool include_all_starred; - // The maximum number of results to return. The results will be sorted with // the most recent first, so older results may not be returned if there is not // enough room. When 0, this will return everything (the default). diff --git a/chrome/browser/history/history_unittest.cc b/chrome/browser/history/history_unittest.cc index 37b9179..cc0bfb4 100644 --- a/chrome/browser/history/history_unittest.cc +++ b/chrome/browser/history/history_unittest.cc @@ -120,12 +120,6 @@ class BackendDelegate : public HistoryBackend::Delegate { HistoryTest* history_test_; }; -bool IsURLStarred(URLDatabase* db, const GURL& url) { - URLRow row; - EXPECT_TRUE(db->GetRowForURL(url, &row)) << "URL not found"; - return row.starred(); -} - } // namespace // This must be outside the anonymous namespace for the friend statement in @@ -272,21 +266,6 @@ class HistoryTest : public testing::Test { MessageLoop::current()->Quit(); } - void SetURLStarred(const GURL& url, bool starred) { - history::StarredEntry entry; - entry.type = history::StarredEntry::URL; - entry.url = url; - history::StarID star_id = db_->GetStarIDForEntry(entry); - if (star_id && !starred) { - // Unstar. - backend_->DeleteStarredEntry(star_id); - } else if (!star_id && starred) { - // Star. - entry.parent_group_id = HistoryService::kBookmarkBarID; - backend_->CreateStarredEntry(NULL, entry); - } - } - // PageUsageData vector to test segments. ScopedVector<PageUsageData> page_usage_data_; @@ -870,56 +849,4 @@ TEST_F(HistoryTest, HistoryDBTaskCanceled) { ASSERT_FALSE(task->done_invoked); } -TEST_F(HistoryTest, Starring) { - CreateBackendAndDatabase(); - - // Add one page and star it. - GURL simple_page("http://google.com"); - scoped_refptr<HistoryAddPageArgs> args(MakeAddArgs(simple_page)); - backend_->AddPage(args); - EXPECT_FALSE(IsURLStarred(db_, simple_page)); - EXPECT_FALSE(IsURLStarred(in_mem_backend_->db(), simple_page)); - SetURLStarred(simple_page, true); - - // The URL should be starred in both the main and memory DBs. - EXPECT_TRUE(IsURLStarred(db_, simple_page)); - EXPECT_TRUE(IsURLStarred(in_mem_backend_->db(), simple_page)); - - // Unstar it. - SetURLStarred(simple_page, false); - EXPECT_FALSE(IsURLStarred(db_, simple_page)); - EXPECT_FALSE(IsURLStarred(in_mem_backend_->db(), simple_page)); -} - -TEST_F(HistoryTest, SetStarredOnPageWithTypeCount0) { - CreateBackendAndDatabase(); - - // Add a page to the backend. - const GURL url(L"http://google.com/"); - scoped_refptr<HistoryAddPageArgs> args(new HistoryAddPageArgs( - url, Time::Now(), NULL, 1, GURL(), HistoryService::RedirectList(), - PageTransition::LINK)); - backend_->AddPage(args); - - // Now fetch the URLInfo from the in memory db, it should not be there since - // it was not typed. - URLRow url_info; - EXPECT_EQ(0, in_mem_backend_->db()->GetRowForURL(url, &url_info)); - - // Mark the URL starred. - SetURLStarred(url, true); - - // The type count is 0, so the page shouldn't be starred in the in memory - // db. - EXPECT_EQ(0, in_mem_backend_->db()->GetRowForURL(url, &url_info)); - EXPECT_TRUE(IsURLStarred(db_, url)); - - // Now unstar it. - SetURLStarred(url, false); - - // Make sure both the back end and in memory DB think it is unstarred. - EXPECT_EQ(0, in_mem_backend_->db()->GetRowForURL(url, &url_info)); - EXPECT_FALSE(IsURLStarred(db_, url)); -} - } // namespace history diff --git a/chrome/browser/history/in_memory_history_backend.cc b/chrome/browser/history/in_memory_history_backend.cc index b3619a9..d745423 100644 --- a/chrome/browser/history/in_memory_history_backend.cc +++ b/chrome/browser/history/in_memory_history_backend.cc @@ -54,7 +54,6 @@ InMemoryHistoryBackend::~InMemoryHistoryBackend() { service->RemoveObserver(this, NOTIFY_HISTORY_URL_VISITED, source); service->RemoveObserver(this, NOTIFY_HISTORY_TYPED_URLS_MODIFIED, source); service->RemoveObserver(this, NOTIFY_HISTORY_URLS_DELETED, source); - service->RemoveObserver(this, NOTIFY_URLS_STARRED, source); } } @@ -87,7 +86,6 @@ void InMemoryHistoryBackend::AttachToHistoryService(Profile* profile) { service->AddObserver(this, NOTIFY_HISTORY_URL_VISITED, source); service->AddObserver(this, NOTIFY_HISTORY_TYPED_URLS_MODIFIED, source); service->AddObserver(this, NOTIFY_HISTORY_URLS_DELETED, source); - service->AddObserver(this, NOTIFY_URLS_STARRED, source); } void InMemoryHistoryBackend::Observe(NotificationType type, @@ -110,9 +108,6 @@ void InMemoryHistoryBackend::Observe(NotificationType type, case NOTIFY_HISTORY_URLS_DELETED: OnURLsDeleted(*Details<history::URLsDeletedDetails>(details).ptr()); break; - case NOTIFY_URLS_STARRED: - OnURLsStarred(*Details<history::URLsStarredDetails>(details).ptr()); - break; default: // For simplicity, the unit tests send us all notifications, even when // we haven't registered for them, so don't assert here. @@ -164,21 +159,4 @@ void InMemoryHistoryBackend::OnURLsDeleted(const URLsDeletedDetails& details) { } } -void InMemoryHistoryBackend::OnURLsStarred( - const history::URLsStarredDetails& details) { - DCHECK(db_.get()); - - for (std::set<GURL>::const_iterator i = details.changed_urls.begin(); - i != details.changed_urls.end(); ++i) { - URLRow row; - if (db_->GetRowForURL(*i, &row)) { - // NOTE: We currently don't care about the star id from the in memory - // db, so that we use a fake value. If this does become a problem, - // then the notification will have to propagate the star id. - row.set_star_id(details.starred ? kBogusStarredID : 0); - db_->UpdateURLRow(row.id(), row); - } - } -} - } // namespace history diff --git a/chrome/browser/history/in_memory_history_backend.h b/chrome/browser/history/in_memory_history_backend.h index 9134bf0..c86ea56 100644 --- a/chrome/browser/history/in_memory_history_backend.h +++ b/chrome/browser/history/in_memory_history_backend.h @@ -85,9 +85,6 @@ class InMemoryHistoryBackend : public NotificationObserver { // Handler for NOTIFY_HISTORY_URLS_DELETED. void OnURLsDeleted(const URLsDeletedDetails& details); - // Handler for NOTIFY_URLS_STARRED. - void OnURLsStarred(const URLsStarredDetails& details); - scoped_ptr<InMemoryDatabase> db_; // The profile that this object is attached. May be NULL before diff --git a/chrome/browser/history/starred_url_database.cc b/chrome/browser/history/starred_url_database.cc index 7294222..950cee3 100644 --- a/chrome/browser/history/starred_url_database.cc +++ b/chrome/browser/history/starred_url_database.cc @@ -29,7 +29,11 @@ #include "chrome/browser/history/starred_url_database.h" +#include "base/file_util.h" #include "base/logging.h" +#include "base/json_writer.h" +#include "chrome/browser/bookmark_bar_model.h" +#include "chrome/browser/bookmark_codec.h" #include "chrome/browser/history/history.h" #include "chrome/browser/history/query_parser.h" #include "chrome/browser/meta_table_helper.h" @@ -102,181 +106,35 @@ void FillInStarredEntry(SQLStatement* s, StarredEntry* entry) { } // namespace -StarredURLDatabase::StarredURLDatabase() - : is_starred_valid_(true), check_starred_integrity_on_mutation_(true) { +StarredURLDatabase::StarredURLDatabase() { } StarredURLDatabase::~StarredURLDatabase() { } -bool StarredURLDatabase::InitStarTable() { - if (!DoesSqliteTableExist(GetDB(), "starred")) { - if (sqlite3_exec(GetDB(), "CREATE TABLE starred (" - "id INTEGER PRIMARY KEY," - "type INTEGER NOT NULL DEFAULT 0," - "url_id INTEGER NOT NULL DEFAULT 0," - "group_id INTEGER NOT NULL DEFAULT 0," - "title VARCHAR," - "date_added INTEGER NOT NULL," - "visual_order INTEGER DEFAULT 0," - "parent_id INTEGER DEFAULT 0," - "date_modified INTEGER DEFAULT 0 NOT NULL)", - NULL, NULL, NULL) != SQLITE_OK) { - NOTREACHED(); - return false; - } - if (sqlite3_exec(GetDB(), "CREATE INDEX starred_index " - "ON starred(id,url_id)", NULL, NULL, NULL)) { - NOTREACHED(); - return false; - } - // Add an entry that represents the bookmark bar. The title is ignored in - // the UI. - StarID bookmark_id = CreateStarredEntryRow( - 0, HistoryService::kBookmarkBarID, 0, L"bookmark-bar", Time::Now(), 0, - history::StarredEntry::BOOKMARK_BAR); - if (bookmark_id != HistoryService::kBookmarkBarID) { - NOTREACHED(); - return false; - } - - // Add an entry that represents other. The title is ignored in the UI. - StarID other_id = CreateStarredEntryRow( - 0, HistoryService::kBookmarkBarID + 1, 0, L"other", Time::Now(), 0, - history::StarredEntry::OTHER); - if (!other_id) { - NOTREACHED(); - return false; - } - } - - sqlite3_exec(GetDB(), "CREATE INDEX starred_group_index" - "ON starred(group_id)", NULL, NULL, NULL); - return true; -} - -bool StarredURLDatabase::EnsureStarredIntegrity() { - if (!is_starred_valid_) - return false; - - // Assume invalid, we'll set to true if succesful. - is_starred_valid_ = false; - - std::set<StarredNode*> roots; - std::set<StarID> groups_with_duplicate_ids; - std::set<StarredNode*> unparented_urls; - std::set<StarID> empty_url_ids; - - if (!BuildStarNodes(&roots, &groups_with_duplicate_ids, &unparented_urls, - &empty_url_ids)) { - return false; - } - - // The table may temporarily get into an invalid state while updating the - // integrity. - bool org_check_starred_integrity_on_mutation = - check_starred_integrity_on_mutation_; - check_starred_integrity_on_mutation_ = false; - is_starred_valid_ = - EnsureStarredIntegrityImpl(&roots, groups_with_duplicate_ids, - &unparented_urls, empty_url_ids); - check_starred_integrity_on_mutation_ = - org_check_starred_integrity_on_mutation; - - STLDeleteElements(&roots); - STLDeleteElements(&unparented_urls); - return is_starred_valid_; -} - -StarID StarredURLDatabase::GetStarIDForEntry(const StarredEntry& entry) { - if (entry.type == StarredEntry::URL) { - URLRow url_row; - URLID url_id = GetRowForURL(entry.url, &url_row); - if (!url_id) - return 0; - return url_row.star_id(); - } - return GetStarIDForGroupID(entry.group_id); -} - -StarID StarredURLDatabase::GetStarIDForGroupID(UIStarID group_id) { - DCHECK(group_id); - - SQLITE_UNIQUE_STATEMENT(statement, GetStatementCache(), - "SELECT id FROM starred WHERE group_id = ?"); - if (!statement.is_valid()) - return false; - - statement->bind_int64(0, group_id); - if (statement->step() == SQLITE_ROW) - return statement->column_int64(0); - return 0; -} - -void StarredURLDatabase::DeleteStarredEntry( - StarID star_id, - std::set<GURL>* unstarred_urls, - std::vector<StarredEntry>* deleted_entries) { - DeleteStarredEntryImpl(star_id, unstarred_urls, deleted_entries); - if (check_starred_integrity_on_mutation_) - CheckStarredIntegrity(); -} +bool StarredURLDatabase::MigrateBookmarksToFile(const std::wstring& path) { + if (!DoesSqliteTableExist(GetDB(), "starred")) + return true; -bool StarredURLDatabase::UpdateStarredEntry(StarredEntry* entry) { - // Determine the ID used by the database. - const StarID id = GetStarIDForEntry(*entry); - if (!id) { - NOTREACHED() << "request to update unknown star entry"; + if (EnsureStarredIntegrity() && !MigrateBookmarksToFileImpl(path)) { + NOTREACHED() << " Bookmarks migration failed"; return false; } - StarredEntry original_entry; - if (!GetStarredEntry(id, &original_entry)) { - NOTREACHED() << "Unknown star entry"; + if (sqlite3_exec(GetDB(), "DROP TABLE starred", NULL, NULL, + NULL) != SQLITE_OK) { + NOTREACHED() << "Unable to drop starred table"; return false; } - - if (entry->parent_group_id != original_entry.parent_group_id) { - // Parent has changed. - if (original_entry.parent_group_id) { - AdjustStarredVisualOrder(original_entry.parent_group_id, - original_entry.visual_order, -1); - } - if (entry->parent_group_id) { - AdjustStarredVisualOrder(entry->parent_group_id, entry->visual_order, 1); - } - } else if (entry->visual_order != original_entry.visual_order && - entry->parent_group_id) { - // Same parent, but visual order changed. - // Shift everything down from the old location. - AdjustStarredVisualOrder(original_entry.parent_group_id, - original_entry.visual_order, -1); - // Make room for new location by shifting everything up from the new - // location. - AdjustStarredVisualOrder(original_entry.parent_group_id, - entry->visual_order, 1); - } - - // And update title, parent and visual order. - UpdateStarredEntryRow(id, entry->title, entry->parent_group_id, - entry->visual_order, entry->date_group_modified); - entry->url_id = original_entry.url_id; - entry->date_added = original_entry.date_added; - entry->id = original_entry.id; - if (check_starred_integrity_on_mutation_) - CheckStarredIntegrity(); return true; } -bool StarredURLDatabase::GetStarredEntries( - UIStarID parent_group_id, +bool StarredURLDatabase::GetAllStarredEntries( std::vector<StarredEntry>* entries) { DCHECK(entries); std::string sql = "SELECT "; sql.append(kHistoryStarFields); sql.append("FROM starred LEFT JOIN urls ON starred.url_id = urls.id "); - if (parent_group_id) - sql += "WHERE starred.parent_id = ? "; sql += "ORDER BY parent_id, visual_order"; SQLStatement s; @@ -284,8 +142,6 @@ bool StarredURLDatabase::GetStarredEntries( NOTREACHED() << "Statement prepare failed"; return false; } - if (parent_group_id) - s.bind_int64(0, parent_group_id); history::StarredEntry entry; while (s.step() == SQLITE_ROW) { @@ -299,6 +155,25 @@ bool StarredURLDatabase::GetStarredEntries( return true; } +bool StarredURLDatabase::EnsureStarredIntegrity() { + std::set<StarredNode*> roots; + std::set<StarID> groups_with_duplicate_ids; + std::set<StarredNode*> unparented_urls; + std::set<StarID> empty_url_ids; + + if (!BuildStarNodes(&roots, &groups_with_duplicate_ids, &unparented_urls, + &empty_url_ids)) { + return false; + } + + bool valid = EnsureStarredIntegrityImpl(&roots, groups_with_duplicate_ids, + &unparented_urls, empty_url_ids); + + STLDeleteElements(&roots); + STLDeleteElements(&unparented_urls); + return valid; +} + bool StarredURLDatabase::UpdateStarredEntryRow(StarID star_id, const std::wstring& title, UIStarID parent_group_id, @@ -380,8 +255,6 @@ StarID StarredURLDatabase::CreateStarredEntryRow(URLID url_id, } bool StarredURLDatabase::DeleteStarredEntryRow(StarID star_id) { - if (check_starred_integrity_on_mutation_) - DCHECK(star_id && star_id != HistoryService::kBookmarkBarID); SQLITE_UNIQUE_STATEMENT(statement, GetStatementCache(), "DELETE FROM starred WHERE id=?"); if (!statement.is_valid()) @@ -433,11 +306,6 @@ StarID StarredURLDatabase::CreateStarredEntry(StarredEntry* entry) { url_row.set_hidden(false); entry->url_id = this->AddURL(url_row); } else { - // Update the existing row for this URL. - if (url_row.starred()) { - DCHECK(false) << "We are starring an already-starred URL"; - return 0; - } entry->url_id = url_row.id(); // The caller doesn't have to set this. } @@ -447,7 +315,6 @@ StarID StarredURLDatabase::CreateStarredEntry(StarredEntry* entry) { entry->visual_order, entry->type); // Update the URL row to refer to this new starred entry. - url_row.set_star_id(entry->id); UpdateURLRow(entry->url_id, url_row); break; } @@ -456,119 +323,9 @@ StarID StarredURLDatabase::CreateStarredEntry(StarredEntry* entry) { NOTREACHED(); break; } - if (check_starred_integrity_on_mutation_) - CheckStarredIntegrity(); return entry->id; } -void StarredURLDatabase::GetMostRecentStarredEntries( - int max_count, - std::vector<StarredEntry>* entries) { - DCHECK(max_count && entries); - SQLITE_UNIQUE_STATEMENT(s, GetStatementCache(), - "SELECT" STAR_FIELDS "FROM starred " - "LEFT JOIN urls ON starred.url_id = urls.id " - "WHERE starred.type=0 ORDER BY starred.date_added DESC, " - "starred.id DESC " // This is for testing, when the dates are - // typically the same. - "LIMIT ?"); - if (!s.is_valid()) - return; - s->bind_int(0, max_count); - - while (s->step() == SQLITE_ROW) { - history::StarredEntry entry; - FillInStarredEntry(s.statement(), &entry); - entries->push_back(entry); - } -} - -void StarredURLDatabase::GetURLsForTitlesMatching(const std::wstring& query, - std::set<URLID>* ids) { - QueryParser parser; - ScopedVector<QueryNode> nodes; - parser.ParseQuery(query, &nodes.get()); - if (nodes.empty()) - return; - - SQLITE_UNIQUE_STATEMENT(s, GetStatementCache(), - "SELECT url_id, title FROM starred WHERE type=?"); - if (!s.is_valid()) - return; - - s->bind_int(0, static_cast<int>(StarredEntry::URL)); - while (s->step() == SQLITE_ROW) { - if (parser.DoesQueryMatch(s->column_string16(1), nodes.get())) - ids->insert(s->column_int64(0)); - } -} - -bool StarredURLDatabase::UpdateURLIDForStar(StarID star_id, URLID new_url_id) { - SQLITE_UNIQUE_STATEMENT(s, GetStatementCache(), - "UPDATE starred SET url_id=? WHERE id=?"); - if (!s.is_valid()) - return false; - s->bind_int64(0, new_url_id); - s->bind_int64(1, star_id); - return s->step() == SQLITE_DONE; -} - -void StarredURLDatabase::DeleteStarredEntryImpl( - StarID star_id, - std::set<GURL>* unstarred_urls, - std::vector<StarredEntry>* deleted_entries) { - - StarredEntry deleted_entry; - if (!GetStarredEntry(star_id, &deleted_entry)) - return; // Couldn't find information on the entry. - - if (deleted_entry.type == StarredEntry::BOOKMARK_BAR || - deleted_entry.type == StarredEntry::OTHER) { - return; - } - - if (deleted_entry.parent_group_id != 0) { - // This entry has a parent. All siblings after this entry need to be shifted - // down by 1. - AdjustStarredVisualOrder(deleted_entry.parent_group_id, - deleted_entry.visual_order, -1); - } - - deleted_entries->push_back(deleted_entry); - - switch (deleted_entry.type) { - case StarredEntry::URL: { - unstarred_urls->insert(deleted_entry.url); - - // Need to unmark the starred flag in the URL database. - URLRow url_row; - if (GetURLRow(deleted_entry.url_id, &url_row)) { - url_row.set_star_id(0); - UpdateURLRow(deleted_entry.url_id, url_row); - } - break; - } - - case StarredEntry::USER_GROUP: { - // Need to delete all the children of the group. Use the db version which - // avoids shifting the visual order. - std::vector<StarredEntry> descendants; - GetStarredEntries(deleted_entry.group_id, &descendants); - for (std::vector<StarredEntry>::iterator i = - descendants.begin(); i != descendants.end(); ++i) { - // Recurse down into the child. - DeleteStarredEntryImpl(i->id, unstarred_urls, deleted_entries); - } - break; - } - - default: - NOTREACHED(); - break; - } - DeleteStarredEntryRow(star_id); -} - UIStarID StarredURLDatabase::GetMaxGroupID() { SQLStatement max_group_id_statement; if (max_group_id_statement.prepare(GetDB(), @@ -589,7 +346,7 @@ bool StarredURLDatabase::BuildStarNodes( std::set<StarredNode*>* unparented_urls, std::set<StarID>* empty_url_ids) { std::vector<StarredEntry> star_entries; - if (!GetStarredEntries(0, &star_entries)) { + if (!GetAllStarredEntries(&star_entries)) { NOTREACHED() << "Unable to get bookmarks from database"; return false; } @@ -699,11 +456,9 @@ bool StarredURLDatabase::EnsureStarredIntegrityImpl( if (!bookmark_node) { LOG(WARNING) << "No bookmark bar folder in database"; // If there is no bookmark bar entry in the db things are really - // screwed. The UI assumes the bookmark bar entry has a particular - // ID which is hard to enforce when creating a new entry. As this - // shouldn't happen, we take the drastic route of recreating the - // entire table. - return RecreateStarredEntries(); + // screwed. Return false, which won't trigger migration and we'll just + // drop the tables. + return false; } // Make sure the other node exists. @@ -803,86 +558,96 @@ bool StarredURLDatabase::Move(StarredNode* source, StarredNode* new_parent) { return true; } -bool StarredURLDatabase::RecreateStarredEntries() { - if (sqlite3_exec(GetDB(), "DROP TABLE starred", NULL, NULL, - NULL) != SQLITE_OK) { - NOTREACHED() << "Unable to drop starred table"; +bool StarredURLDatabase::MigrateBookmarksToFileImpl(const std::wstring& path) { + std::vector<history::StarredEntry> entries; + if (!GetAllStarredEntries(&entries)) return false; - } - if (!InitStarTable()) { - NOTREACHED() << "Unable to create starred table"; - return false; - } - - if (sqlite3_exec(GetDB(), "UPDATE urls SET starred_id=0", NULL, NULL, - NULL) != SQLITE_OK) { - NOTREACHED() << "Unable to mark all URLs as unstarred"; - return false; + // Create the bookmark bar and other folder nodes. + history::StarredEntry entry; + entry.type = history::StarredEntry::BOOKMARK_BAR; + BookmarkBarNode bookmark_bar_node(NULL, GURL()); + bookmark_bar_node.Reset(entry); + entry.type = history::StarredEntry::OTHER; + BookmarkBarNode other_node(NULL, GURL()); + other_node.Reset(entry); + + std::map<history::UIStarID, history::StarID> group_id_to_id_map; + typedef std::map<history::StarID, BookmarkBarNode*> IDToNodeMap; + IDToNodeMap id_to_node_map; + + history::UIStarID other_folder_group_id = 0; + history::StarID other_folder_id = 0; + + // Iterate through the entries building a mapping between group_id and id. + for (std::vector<history::StarredEntry>::const_iterator i = entries.begin(); + i != entries.end(); ++i) { + if (i->type != history::StarredEntry::URL) { + group_id_to_id_map[i->group_id] = i->id; + if (i->type == history::StarredEntry::OTHER) { + other_folder_id = i->id; + other_folder_group_id = i->group_id; + } + } } - return true; -} -void StarredURLDatabase::CheckVisualOrder(StarredNode* node) { - for (int i = 0; i < node->GetChildCount(); ++i) { - DCHECK(i == node->GetChild(i)->value.visual_order); - CheckVisualOrder(node->GetChild(i)); - } -} + // Register the bookmark bar and other folder nodes in the maps. + id_to_node_map[HistoryService::kBookmarkBarID] = &bookmark_bar_node; + group_id_to_id_map[HistoryService::kBookmarkBarID] = + HistoryService::kBookmarkBarID; + if (other_folder_group_id) { + id_to_node_map[other_folder_id] = &other_node; + group_id_to_id_map[other_folder_group_id] = other_folder_id; + } + + // Iterate through the entries again creating the nodes. + for (std::vector<history::StarredEntry>::iterator i = entries.begin(); + i != entries.end(); ++i) { + if (!i->parent_group_id) { + DCHECK(i->type == history::StarredEntry::BOOKMARK_BAR || + i->type == history::StarredEntry::OTHER); + // Only entries with no parent should be the bookmark bar and other + // bookmarks folders. + continue; + } -void StarredURLDatabase::CheckStarredIntegrity() { -#ifndef NDEBUG - std::set<StarredNode*> roots; - std::set<StarID> groups_with_duplicate_ids; - std::set<StarredNode*> unparented_urls; - std::set<StarID> empty_url_ids; + BookmarkBarNode* node = id_to_node_map[i->id]; + if (!node) { + // Creating a node results in creating the parent. As such, it is + // possible for the node representing a group to have been created before + // encountering the details. - if (!BuildStarNodes(&roots, &groups_with_duplicate_ids, &unparented_urls, - &empty_url_ids)) { - return; - } + // The created nodes are owned by the root node. + node = new BookmarkBarNode(NULL, i->url); + id_to_node_map[i->id] = node; + } + node->Reset(*i); + + DCHECK(group_id_to_id_map.find(i->parent_group_id) != + group_id_to_id_map.end()); + history::StarID parent_id = group_id_to_id_map[i->parent_group_id]; + BookmarkBarNode* parent = id_to_node_map[parent_id]; + if (!parent) { + // Haven't encountered the parent yet, create it now. + parent = new BookmarkBarNode(NULL, GURL()); + id_to_node_map[parent_id] = parent; + } - // There must be root and bookmark bar nodes. - if (!GetNodeByType(roots, StarredEntry::BOOKMARK_BAR)) - NOTREACHED() << "No bookmark bar folder in starred"; - else - CheckVisualOrder(GetNodeByType(roots, StarredEntry::BOOKMARK_BAR)); - - if (!GetNodeByType(roots, StarredEntry::OTHER)) - NOTREACHED() << "No other bookmarks folder in starred"; - else - CheckVisualOrder(GetNodeByType(roots, StarredEntry::OTHER)); - - // The only roots should be the bookmark bar and other nodes. Also count how - // many bookmark bar and other folders exists. - int bookmark_bar_count = 0; - int other_folder_count = 0; - for (std::set<StarredNode*>::const_iterator i = roots.begin(); - i != roots.end(); ++i) { - if ((*i)->value.type == StarredEntry::USER_GROUP) - NOTREACHED() << "Bookmark folder not in bookmark bar or other folders"; - else if ((*i)->value.type == StarredEntry::BOOKMARK_BAR) - bookmark_bar_count++; - else if ((*i)->value.type == StarredEntry::OTHER) - other_folder_count++; + // Add the node to its parent. |entries| is ordered by parent then + // visual order so that we know we maintain visual order by always adding + // to the end. + parent->Add(parent->GetChildCount(), node); } - DCHECK(bookmark_bar_count == 1) << "Should be only one bookmark bar entry"; - DCHECK(other_folder_count == 1) << "Should be only one other folder entry"; - // There shouldn't be any folders with the same group id. - if (!groups_with_duplicate_ids.empty()) - NOTREACHED() << "Bookmark folder with duplicate ids exist"; + // Save to file. + BookmarkCodec encoder; + scoped_ptr<Value> encoded_bookmarks( + encoder.Encode(&bookmark_bar_node, &other_node)); + std::string content; + JSONWriter::Write(encoded_bookmarks.get(), true, &content); - // And all URLs should be parented. - if (!unparented_urls.empty()) - NOTREACHED() << "Bookmarks not on the bookmark/'other folder' exist"; - - if (!empty_url_ids.empty()) - NOTREACHED() << "Bookmarks with no corresponding URL exist"; - - STLDeleteElements(&roots); - STLDeleteElements(&unparented_urls); -#endif NDEBUG + return (file_util::WriteFile(path, content.c_str(), + static_cast<int>(content.length())) != -1); } } // namespace history diff --git a/chrome/browser/history/starred_url_database.h b/chrome/browser/history/starred_url_database.h index d58571d..e7a774f 100644 --- a/chrome/browser/history/starred_url_database.h +++ b/chrome/browser/history/starred_url_database.h @@ -44,12 +44,9 @@ class SqliteStatementCache; namespace history { -// Encapsulates a URL database plus starred information. -// -// WARNING: many of the following methods allow you to update, delete or -// insert starred entries specifying a visual order. These methods do NOT -// adjust the visual order of surrounding entries. You must explicitly do -// it yourself using AdjustStarredVisualOrder as appropriate. +// Bookmarks were originally part of the url database, they have since been +// moved to a separate file. This file exists purely for historical reasons and +// contains just enough to allow migration. class StarredURLDatabase : public URLDatabase { public: // Must call InitStarTable() AND any additional init functions provided by @@ -57,87 +54,22 @@ class StarredURLDatabase : public URLDatabase { StarredURLDatabase(); virtual ~StarredURLDatabase(); - // Returns the id of the starred entry. This does NOT return entry.id, - // rather the database is queried for the id based on entry.url or - // entry.group_id. - StarID GetStarIDForEntry(const StarredEntry& entry); - - // Returns the internal star ID for the externally-generated group ID. - StarID GetStarIDForGroupID(UIStarID group_id); - - // Gets the details for the specified star entry in entry. - bool GetStarredEntry(StarID star_id, StarredEntry* entry); - - // Creates a starred entry with the requested information. The structure will - // be updated with the ID of the newly created entry. The URL table will be - // updated to point to the entry. The URL row will be created if it doesn't - // exist. - // - // We currently only support one entry per URL. This URL should not already be - // starred when calling this function or it will fail and will return 0. - StarID CreateStarredEntry(StarredEntry* entry); - - // Returns starred entries. - // This returns three different result types: - // only_on_bookmark_bar == true: only those entries on the bookmark bar are - // returned. - // only_on_bookmark_bar == false and parent_id == 0: all starred - // entries/groups. - // otherwise only the direct children (groups and entries) of parent_id are - // returned. - bool GetStarredEntries(UIStarID parent_group_id, - std::vector<StarredEntry>* entries); - - // Deletes a starred entry. This adjusts the visual order of all siblings - // after the entry. The deleted entry(s) will be *appended* to - // |*deleted_entries| (so delete can be called more than once with the same - // list) and deleted URLs will additionally be added to the set. - // - // This function invokes DeleteStarredEntryImpl to do the actual work, which - // recurses. - void DeleteStarredEntry(StarID star_id, - std::set<GURL>* unstarred_urls, - std::vector<StarredEntry>* deleted_entries); - - // Implementation to update the database for UpdateStarredEntry. Returns true - // on success. If successful, the parent_id, url_id, id and date_added fields - // of the entry are reset from that of the database. - bool UpdateStarredEntry(StarredEntry* entry); - - // Gets up to max_count starred entries of type URL adding them to entries. - // The results are ordered by date added in descending order (most recent - // first). - void GetMostRecentStarredEntries(int max_count, - std::vector<StarredEntry>* entries); - - // Gets the URLIDs for all starred entries of type URL whose title matches - // the specified query. - void GetURLsForTitlesMatching(const std::wstring& query, - std::set<URLID>* ids); - - // Returns true if the starred table is in a sane state. If false, the starred - // table is likely corrupt and shouldn't be used. - bool is_starred_valid() const { return is_starred_valid_; } - - // Updates the URL ID for the given starred entry. This is used by the expirer - // when URL IDs change. It can't use UpdateStarredEntry since that doesn't - // set the URLID, and it does a bunch of other logic that we don't need. - bool UpdateURLIDForStar(StarID star_id, URLID new_url_id); - protected: // The unit tests poke our innards. friend class HistoryTest; + friend class StarredURLDatabaseTest; FRIEND_TEST(HistoryTest, CreateStarGroup); + // Writes bookmarks to the specified file. + bool MigrateBookmarksToFile(const std::wstring& path); + // Returns the database and statement cache for the functions in this // interface. The decendent of this class implements these functions to // return its objects. virtual sqlite3* GetDB() = 0; virtual SqliteStatementCache& GetStatementCache() = 0; - // Creates the starred tables if necessary. - bool InitStarTable(); - + private: // Makes sure the starred table is in a sane state. This does the following: // . Makes sure there is a bookmark bar and other nodes. If no bookmark bar // node is found, the table is dropped and recreated. @@ -155,17 +87,8 @@ class StarredURLDatabase : public URLDatabase { // This should be invoked after migration. bool EnsureStarredIntegrity(); - // Creates a starred entry with the specified parameters in the database. - // Returns the newly created id, or 0 on failure. - // - // WARNING: Does not update the visual order. - StarID CreateStarredEntryRow(URLID url_id, - UIStarID group_id, - UIStarID parent_group_id, - const std::wstring& title, - const Time& date_added, - int visual_order, - StarredEntry::Type type); + // Gets all the starred entries. + bool GetAllStarredEntries(std::vector<StarredEntry>* entries); // Sets the title, parent_id, parent_group_id, visual_order and date_modifed // of the specified star entry. @@ -185,26 +108,39 @@ class StarredURLDatabase : public URLDatabase { int start_visual_order, int delta); + // Creates a starred entry with the specified parameters in the database. + // Returns the newly created id, or 0 on failure. + // + // WARNING: Does not update the visual order. + StarID CreateStarredEntryRow(URLID url_id, + UIStarID group_id, + UIStarID parent_group_id, + const std::wstring& title, + const Time& date_added, + int visual_order, + StarredEntry::Type type); + // Deletes the entry from the starred database base on the starred id (NOT // the url id). // // WARNING: Does not update the visual order. bool DeleteStarredEntryRow(StarID star_id); - // Should the integrity of the starred table be checked on every mutation? - // Default is true. Set to false when running tests that need to allow the - // table to be in an invalid state while testing. - bool check_starred_integrity_on_mutation_; + // Gets the details for the specified star entry in entry. + bool GetStarredEntry(StarID star_id, StarredEntry* entry); + + // Creates a starred entry with the requested information. The structure will + // be updated with the ID of the newly created entry. The URL table will be + // updated to point to the entry. The URL row will be created if it doesn't + // exist. + // + // We currently only support one entry per URL. This URL should not already be + // starred when calling this function or it will fail and will return 0. + StarID CreateStarredEntry(StarredEntry* entry); - private: // Used when checking integrity of starred table. typedef ChromeViews::TreeNodeWithValue<history::StarredEntry> StarredNode; - // Implementation of DeleteStarredEntryImpl. - void DeleteStarredEntryImpl(StarID star_id, - std::set<GURL>* unstarred_urls, - std::vector<StarredEntry>* deleted_entries); - // Returns the max group id, or 0 if there is an error. UIStarID GetMaxGroupID(); @@ -262,24 +198,9 @@ class StarredURLDatabase : public URLDatabase { // needs to be moved. bool Move(StarredNode* source, StarredNode* new_parent); - // Drops and recreates the starred table as well as marking all URLs as - // unstarred. This is used as a last resort if the bookmarks table is - // totally corrupt. - bool RecreateStarredEntries(); - - // Iterates through the children of node make sure the visual order matches - // the order in node's children. DCHECKs if the order differs. This recurses. - void CheckVisualOrder(StarredNode* node); - - // Makes sure the starred table is in a sane state, if not this DCHECKs. - // This is invoked internally after any mutation to the starred table. - // - // This is a no-op in release builds. - void CheckStarredIntegrity(); - - // True if the starred table is valid. This is initialized in - // EnsureStarredIntegrityImpl. - bool is_starred_valid_; + // Does the work of migrating bookmarks to a temporary file that + // BookmarkStorage will read from. + bool MigrateBookmarksToFileImpl(const std::wstring& path); DISALLOW_EVIL_CONSTRUCTORS(StarredURLDatabase); }; diff --git a/chrome/browser/history/starred_url_database_unittest.cc b/chrome/browser/history/starred_url_database_unittest.cc index aca91d9..c59c122 100644 --- a/chrome/browser/history/starred_url_database_unittest.cc +++ b/chrome/browser/history/starred_url_database_unittest.cc @@ -34,6 +34,7 @@ #include "base/string_util.h" #include "chrome/browser/history/history.h" #include "chrome/browser/history/starred_url_database.h" +#include "chrome/common/chrome_paths.h" #include "chrome/common/sqlite_utils.h" #include "testing/gtest/include/gtest/gtest.h" @@ -51,18 +52,6 @@ class StarredURLDatabaseTest : public testing::Test, EXPECT_TRUE(AddURL(row)); } - StarredEntry GetStarredEntryByURL(const GURL& url) { - std::vector<StarredEntry> starred; - EXPECT_TRUE(GetStarredEntries(0, &starred)); - for (std::vector<StarredEntry>::iterator i = starred.begin(); - i != starred.end(); ++i) { - if (i->url == url) - return *i; - } - EXPECT_TRUE(false); - return StarredEntry(); - } - void CompareEntryByID(const StarredEntry& entry) { DCHECK(entry.id != 0); StarredEntry db_value; @@ -82,43 +71,20 @@ class StarredURLDatabaseTest : public testing::Test, int GetStarredEntryCount() { DCHECK(db_); std::vector<StarredEntry> entries; - GetStarredEntries(0, &entries); + GetAllStarredEntries(&entries); return static_cast<int>(entries.size()); } - bool GetURLStarred(const GURL& url) { - URLRow row; - if (GetRowForURL(url, &row)) - return row.starred(); - return false; + StarID CreateStarredEntry(StarredEntry* entry) { + return StarredURLDatabase::CreateStarredEntry(entry); + } + + bool GetStarredEntry(StarID star_id, StarredEntry* entry) { + return StarredURLDatabase::GetStarredEntry(star_id, entry); } - void VerifyInitialState() { - std::vector<StarredEntry> star_entries; - EXPECT_TRUE(GetStarredEntries(0, &star_entries)); - EXPECT_EQ(2, star_entries.size()); - - int bb_index = -1; - size_t other_index = -1; - if (star_entries[0].type == history::StarredEntry::BOOKMARK_BAR) { - bb_index = 0; - other_index = 1; - } else { - bb_index = 1; - other_index = 0; - } - EXPECT_EQ(HistoryService::kBookmarkBarID, star_entries[bb_index].id); - EXPECT_EQ(HistoryService::kBookmarkBarID, star_entries[bb_index].group_id); - EXPECT_EQ(0, star_entries[bb_index].parent_group_id); - EXPECT_EQ(StarredEntry::BOOKMARK_BAR, star_entries[bb_index].type); - - EXPECT_TRUE(star_entries[other_index].id != HistoryService::kBookmarkBarID - && star_entries[other_index].id != 0); - EXPECT_TRUE(star_entries[other_index].group_id != - HistoryService::kBookmarkBarID && - star_entries[other_index].group_id != 0); - EXPECT_EQ(0, star_entries[other_index].parent_group_id); - EXPECT_EQ(StarredEntry::OTHER, star_entries[other_index].type); + bool EnsureStarredIntegrity() { + return StarredURLDatabase::EnsureStarredIntegrity(); } private: @@ -129,13 +95,19 @@ class StarredURLDatabaseTest : public testing::Test, db_file_.append(L"VisitTest.db"); file_util::Delete(db_file_, false); + // Copy db file over that contains starred table. + std::wstring old_history_path; + PathService::Get(chrome::DIR_TEST_DATA, &old_history_path); + file_util::AppendToPath(&old_history_path, L"bookmarks"); + file_util::AppendToPath(&old_history_path, L"History_with_empty_starred"); + file_util::CopyFile(old_history_path, db_file_); + EXPECT_EQ(SQLITE_OK, sqlite3_open(WideToUTF8(db_file_).c_str(), &db_)); statement_cache_ = new SqliteStatementCache(db_); // Initialize the tables for this test. CreateURLTable(false); CreateMainURLIndex(); - InitStarTable(); EnsureStarredIntegrity(); } void TearDown() { @@ -159,307 +131,7 @@ class StarredURLDatabaseTest : public testing::Test, //----------------------------------------------------------------------------- -TEST_F(StarredURLDatabaseTest, CreateStarredEntryUnstarred) { - StarredEntry entry; - entry.title = L"blah"; - entry.date_added = Time::Now(); - entry.url = GURL("http://www.google.com"); - entry.parent_group_id = HistoryService::kBookmarkBarID; - - StarID id = CreateStarredEntry(&entry); - - EXPECT_NE(0, id); - EXPECT_EQ(id, entry.id); - CompareEntryByID(entry); -} - -TEST_F(StarredURLDatabaseTest, UpdateStarredEntry) { - const int initial_count = GetStarredEntryCount(); - StarredEntry entry; - entry.title = L"blah"; - entry.date_added = Time::Now(); - entry.url = GURL("http://www.google.com"); - entry.parent_group_id = HistoryService::kBookmarkBarID; - - StarID id = CreateStarredEntry(&entry); - EXPECT_NE(0, id); - EXPECT_EQ(id, entry.id); - CompareEntryByID(entry); - - // Now invoke create again, as the URL hasn't changed this should just - // update the title. - EXPECT_TRUE(UpdateStarredEntry(&entry)); - CompareEntryByID(entry); - - // There should only be two entries (one for the url we created, one for the - // bookmark bar). - EXPECT_EQ(initial_count + 1, GetStarredEntryCount()); -} - -TEST_F(StarredURLDatabaseTest, DeleteStarredGroup) { - const int initial_count = GetStarredEntryCount(); - StarredEntry entry; - entry.type = StarredEntry::USER_GROUP; - entry.title = L"blah"; - entry.date_added = Time::Now(); - entry.group_id = 10; - entry.parent_group_id = HistoryService::kBookmarkBarID; - - StarID id = CreateStarredEntry(&entry); - EXPECT_NE(0, id); - EXPECT_NE(HistoryService::kBookmarkBarID, id); - CompareEntryByID(entry); - - EXPECT_EQ(initial_count + 1, GetStarredEntryCount()); - - // Now delete it. - std::set<GURL> unstarred_urls; - std::vector<StarredEntry> deleted_entries; - DeleteStarredEntry(entry.id, &unstarred_urls, &deleted_entries); - ASSERT_EQ(0, unstarred_urls.size()); - ASSERT_EQ(1, deleted_entries.size()); - EXPECT_EQ(deleted_entries[0].id, id); - - // Should only have the bookmark bar. - EXPECT_EQ(initial_count, GetStarredEntryCount()); -} - -TEST_F(StarredURLDatabaseTest, DeleteStarredGroupWithChildren) { - const int initial_count = GetStarredEntryCount(); - - StarredEntry entry; - entry.type = StarredEntry::USER_GROUP; - entry.title = L"blah"; - entry.date_added = Time::Now(); - entry.group_id = 10; - entry.parent_group_id = HistoryService::kBookmarkBarID; - - StarID id = CreateStarredEntry(&entry); - EXPECT_NE(0, id); - EXPECT_NE(HistoryService::kBookmarkBarID, id); - CompareEntryByID(entry); - - EXPECT_EQ(initial_count + 1, GetStarredEntryCount()); - - // Create a child of the group. - StarredEntry url_entry1; - url_entry1.parent_group_id = entry.group_id; - url_entry1.title = L"blah"; - url_entry1.date_added = Time::Now(); - url_entry1.url = GURL("http://www.google.com"); - StarID url_id1 = CreateStarredEntry(&url_entry1); - EXPECT_NE(0, url_id1); - CompareEntryByID(url_entry1); - - // Create a sibling of the group. - StarredEntry url_entry2; - url_entry2.parent_group_id = HistoryService::kBookmarkBarID; - url_entry2.title = L"blah"; - url_entry2.date_added = Time::Now(); - url_entry2.url = GURL("http://www.google2.com"); - url_entry2.visual_order = 1; - StarID url_id2 = CreateStarredEntry(&url_entry2); - EXPECT_NE(0, url_id2); - CompareEntryByID(url_entry2); - - EXPECT_EQ(initial_count + 3, GetStarredEntryCount()); - - // Now delete the group, which should delete url_entry1 and shift down the - // visual order of url_entry2. - std::set<GURL> unstarred_urls; - std::vector<StarredEntry> deleted_entries; - DeleteStarredEntry(entry.id, &unstarred_urls, &deleted_entries); - EXPECT_EQ(2, deleted_entries.size()); - EXPECT_EQ(1, unstarred_urls.size()); - EXPECT_TRUE((deleted_entries[0].id == id) || (deleted_entries[1].id == id)); - EXPECT_TRUE((deleted_entries[0].id == url_id1) || - (deleted_entries[1].id == url_id1)); - - url_entry2.visual_order--; - CompareEntryByID(url_entry2); - - // Should have the bookmark bar, and url_entry2. - EXPECT_EQ(initial_count + 1, GetStarredEntryCount()); -} - -TEST_F(StarredURLDatabaseTest, InitialState) { - VerifyInitialState(); -} - -TEST_F(StarredURLDatabaseTest, CreateStarGroup) { - const int initial_count = GetStarredEntryCount(); - std::wstring title(L"title"); - Time time(Time::Now()); - int visual_order = 0; - - UIStarID ui_group_id = 10; - UIStarID ui_parent_group_id = HistoryService::kBookmarkBarID; - - StarredEntry entry; - entry.type = StarredEntry::USER_GROUP; - entry.group_id = ui_group_id; - entry.parent_group_id = ui_parent_group_id; - entry.title = title; - entry.date_added = time; - entry.visual_order = visual_order; - StarID group_id = CreateStarredEntry(&entry); - - EXPECT_NE(0, group_id); - EXPECT_NE(HistoryService::kBookmarkBarID, group_id); - - EXPECT_EQ(group_id, GetStarIDForGroupID(ui_group_id)); - - StarredEntry group_entry; - EXPECT_TRUE(GetStarredEntry(group_id, &group_entry)); - EXPECT_EQ(ui_group_id, group_entry.group_id); - EXPECT_EQ(ui_parent_group_id, group_entry.parent_group_id); - EXPECT_TRUE(group_entry.title == title); - // Don't use Time.== here as the conversion to time_t when writing to the db - // is lossy. - EXPECT_TRUE(group_entry.date_added.ToTimeT() == time.ToTimeT()); - EXPECT_EQ(visual_order, group_entry.visual_order); - - // Update the date modified. - Time date_modified = Time::Now(); - entry.date_group_modified = date_modified; - UpdateStarredEntry(&group_entry); - EXPECT_TRUE(GetStarredEntry(group_id, &group_entry)); - EXPECT_TRUE(entry.date_group_modified.ToTimeT() == date_modified.ToTimeT()); -} - -TEST_F(StarredURLDatabaseTest, UpdateStarredEntryChangeVisualOrder) { - const int initial_count = GetStarredEntryCount(); - GURL url1(L"http://google.com/1"); - GURL url2(L"http://google.com/2"); - - // Star url1, making it a child of the bookmark bar. - StarredEntry entry; - entry.url = url1; - entry.title = L"FOO"; - entry.parent_group_id = HistoryService::kBookmarkBarID; - CreateStarredEntry(&entry); - - // Make sure it was created correctly. - entry = GetStarredEntryByURL(url1); - EXPECT_EQ(GetRowForURL(url1, NULL), entry.url_id); - CompareEntryByID(entry); - - // Star url2, making it a child of the bookmark bar, placing it after url2. - StarredEntry entry2; - entry2.url = url2; - entry2.visual_order = 1; - entry2.title = std::wstring(); - entry2.parent_group_id = HistoryService::kBookmarkBarID; - CreateStarredEntry(&entry2); - entry2 = GetStarredEntryByURL(url2); - EXPECT_EQ(GetRowForURL(url2, NULL), entry2.url_id); - CompareEntryByID(entry2); - - // Move url2 to be before url1. - entry2.visual_order = 0; - UpdateStarredEntry(&entry2); - CompareEntryByID(entry2); - - // URL1's visual order should have shifted by one to accommodate URL2. - entry.visual_order = 1; - CompareEntryByID(entry); - - // Now move URL1 to position 0, which will move url2 to position 1. - entry.visual_order = 0; - UpdateStarredEntry(&entry); - CompareEntryByID(entry); - - entry2.visual_order = 1; - CompareEntryByID(entry2); - - // Create a new group between url1 and url2. - StarredEntry g_entry; - g_entry.group_id = 10; - g_entry.parent_group_id = HistoryService::kBookmarkBarID; - g_entry.title = L"blah"; - g_entry.visual_order = 1; - g_entry.date_added = Time::Now(); - g_entry.type = StarredEntry::USER_GROUP; - g_entry.id = CreateStarredEntry(&g_entry); - EXPECT_NE(0, g_entry.id); - CompareEntryByID(g_entry); - - // URL2 should have shifted to position 2. - entry2.visual_order = 2; - CompareEntryByID(entry2); - - // Move url2 inside of group 1. - entry2.visual_order = 0; - entry2.parent_group_id = g_entry.group_id; - UpdateStarredEntry(&entry2); - CompareEntryByID(entry2); - - // Move url1 to be a child of group 1 at position 0. - entry.parent_group_id = g_entry.group_id; - entry.visual_order = 0; - UpdateStarredEntry(&entry); - CompareEntryByID(entry); - - // url2 should have moved to position 1. - entry2.visual_order = 1; - CompareEntryByID(entry2); - - // And the group should have shifted to position 0. - g_entry.visual_order = 0; - CompareEntryByID(g_entry); - - EXPECT_EQ(initial_count + 3, GetStarredEntryCount()); - - // Delete the group, which should unstar url1 and url2. - std::set<GURL> unstarred_urls; - std::vector<StarredEntry> deleted_entries; - DeleteStarredEntry(g_entry.id, &unstarred_urls, &deleted_entries); - EXPECT_EQ(initial_count, GetStarredEntryCount()); - URLRow url_row; - GetRowForURL(url1, &url_row); - EXPECT_EQ(0, url_row.star_id()); - GetRowForURL(url2, &url_row); - EXPECT_EQ(0, url_row.star_id()); -} - -TEST_F(StarredURLDatabaseTest, GetMostRecentStarredEntries) { - // Initially there shouldn't be any entries (only the bookmark bar, which - // isn't returned by GetMostRecentStarredEntries). - std::vector<StarredEntry> starred_entries; - GetMostRecentStarredEntries(10, &starred_entries); - EXPECT_EQ(0, starred_entries.size()); - - // Star url1. - GURL url1(L"http://google.com/1"); - StarredEntry entry1; - entry1.type = StarredEntry::URL; - entry1.url = url1; - entry1.parent_group_id = HistoryService::kBookmarkBarID; - CreateStarredEntry(&entry1); - - // Get the recent ones. - GetMostRecentStarredEntries(10, &starred_entries); - ASSERT_EQ(1, starred_entries.size()); - EXPECT_TRUE(starred_entries[0].url == url1); - - // Add url2 and star it. - GURL url2(L"http://google.com/2"); - StarredEntry entry2; - entry2.type = StarredEntry::URL; - entry2.url = url2; - entry2.parent_group_id = HistoryService::kBookmarkBarID; - CreateStarredEntry(&entry2); - - starred_entries.clear(); - GetMostRecentStarredEntries(10, &starred_entries); - ASSERT_EQ(2, starred_entries.size()); - EXPECT_TRUE(starred_entries[0].url == url2); - EXPECT_TRUE(starred_entries[1].url == url1); -} - TEST_F(StarredURLDatabaseTest, FixOrphanedGroup) { - check_starred_integrity_on_mutation_ = false; - const size_t initial_count = GetStarredEntryCount(); // Create a group that isn't parented to the other/bookmark folders. @@ -482,8 +154,6 @@ TEST_F(StarredURLDatabaseTest, FixOrphanedGroup) { } TEST_F(StarredURLDatabaseTest, FixOrphanedBookmarks) { - check_starred_integrity_on_mutation_ = false; - const size_t initial_count = GetStarredEntryCount(); // Create two bookmarks that aren't in a random folder no on the bookmark bar. @@ -516,8 +186,6 @@ TEST_F(StarredURLDatabaseTest, FixOrphanedBookmarks) { } TEST_F(StarredURLDatabaseTest, FixGroupCycleDepth0) { - check_starred_integrity_on_mutation_ = false; - const size_t initial_count = GetStarredEntryCount(); // Create a group that is parented to itself. @@ -540,8 +208,6 @@ TEST_F(StarredURLDatabaseTest, FixGroupCycleDepth0) { } TEST_F(StarredURLDatabaseTest, FixGroupCycleDepth1) { - check_starred_integrity_on_mutation_ = false; - const size_t initial_count = GetStarredEntryCount(); StarredEntry entry1; @@ -574,8 +240,6 @@ TEST_F(StarredURLDatabaseTest, FixGroupCycleDepth1) { } TEST_F(StarredURLDatabaseTest, FixVisualOrder) { - check_starred_integrity_on_mutation_ = false; - const size_t initial_count = GetStarredEntryCount(); // Star two urls. @@ -607,29 +271,7 @@ TEST_F(StarredURLDatabaseTest, FixVisualOrder) { CompareEntryByID(entry2); } -TEST_F(StarredURLDatabaseTest, RestoreOtherAndBookmarkBar) { - std::vector<StarredEntry> entries; - GetStarredEntries(0, &entries); - - check_starred_integrity_on_mutation_ = false; - - for (std::vector<StarredEntry>::iterator i = entries.begin(); - i != entries.end(); ++i) { - if (i->type != StarredEntry::USER_GROUP) { - // Use this directly, otherwise we assert trying to delete other/bookmark - // bar. - DeleteStarredEntryRow(i->id); - - ASSERT_TRUE(EnsureStarredIntegrity()); - - VerifyInitialState(); - } - } -} - TEST_F(StarredURLDatabaseTest, FixDuplicateGroupIDs) { - check_starred_integrity_on_mutation_ = false; - const size_t initial_count = GetStarredEntryCount(); // Create two groups with the same group id. diff --git a/chrome/browser/history/text_database.h b/chrome/browser/history/text_database.h index f04f8c0..4bcccd0 100644 --- a/chrome/browser/history/text_database.h +++ b/chrome/browser/history/text_database.h @@ -142,8 +142,6 @@ class TextDatabase { // Querying ------------------------------------------------------------------ // Executes the given query. See QueryOptions for more info on input. - // Note that the options.only_starred is ignored since this database does not - // have access to star information. // // The results are appended to any existing ones in |*results|, and the first // time considered for the output is in |first_time_searched| diff --git a/chrome/browser/history/text_database_manager.h b/chrome/browser/history/text_database_manager.h index 2506835..7685135b 100644 --- a/chrome/browser/history/text_database_manager.h +++ b/chrome/browser/history/text_database_manager.h @@ -165,8 +165,6 @@ class TextDatabaseManager { void OptimizeChangedDatabases(const ChangeSet& change_set); // Executes the given query. See QueryOptions for more info on input. - // Note that the options.only_starred is ignored since this database does not - // have access to star information. // // The results are filled into |results|, and the first time considered for // the output is in |first_time_searched| (see QueryResults for more). diff --git a/chrome/browser/history/url_database.cc b/chrome/browser/history/url_database.cc index 0ce8072..899f04c 100644 --- a/chrome/browser/history/url_database.cc +++ b/chrome/browser/history/url_database.cc @@ -74,7 +74,6 @@ void URLDatabase::FillURLRow(SQLStatement& s, history::URLRow* i) { i->last_visit_ = Time::FromInternalValue(s.column_int64(5)); i->hidden_ = s.column_int(6) != 0; i->favicon_id_ = s.column_int64(7); - i->star_id_ = s.column_int64(8); } bool URLDatabase::GetURLRow(URLID url_id, URLRow* info) { @@ -115,7 +114,7 @@ bool URLDatabase::UpdateURLRow(URLID url_id, const history::URLRow& info) { SQLITE_UNIQUE_STATEMENT(statement, GetStatementCache(), "UPDATE urls SET title=?,visit_count=?,typed_count=?,last_visit_time=?," - "hidden=?,starred_id=?,favicon_id=?" + "hidden=?,favicon_id=?" "WHERE id=?"); if (!statement.is_valid()) return false; @@ -125,9 +124,8 @@ bool URLDatabase::UpdateURLRow(URLID url_id, statement->bind_int(2, info.typed_count()); statement->bind_int64(3, info.last_visit().ToInternalValue()); statement->bind_int(4, info.hidden() ? 1 : 0); - statement->bind_int64(5, info.star_id()); - statement->bind_int64(6, info.favicon_id()); - statement->bind_int64(7, url_id); + statement->bind_int64(5, info.favicon_id()); + statement->bind_int64(6, url_id); return statement->step() == SQLITE_DONE; } @@ -139,8 +137,8 @@ URLID URLDatabase::AddURLInternal(const history::URLRow& info, // invalid in the insert syntax. #define ADDURL_COMMON_SUFFIX \ "(url,title,visit_count,typed_count,"\ - "last_visit_time,hidden,starred_id,favicon_id)"\ - "VALUES(?,?,?,?,?,?,?,?)" + "last_visit_time,hidden,favicon_id)"\ + "VALUES(?,?,?,?,?,?,?)" const char* statement_name; const char* statement_sql; if (is_temporary) { @@ -163,8 +161,7 @@ URLID URLDatabase::AddURLInternal(const history::URLRow& info, statement->bind_int(3, info.typed_count()); statement->bind_int64(4, info.last_visit().ToInternalValue()); statement->bind_int(5, info.hidden() ? 1 : 0); - statement->bind_int64(6, info.star_id()); - statement->bind_int64(7, info.favicon_id()); + statement->bind_int64(6, info.favicon_id()); if (statement->step() != SQLITE_DONE) return 0; @@ -250,21 +247,15 @@ bool URLDatabase::IsFavIconUsed(FavIconID favicon_id) { } void URLDatabase::AutocompleteForPrefix(const std::wstring& prefix, - size_t max_results, - std::vector<history::URLRow>* results) { + size_t max_results, + std::vector<history::URLRow>* results) { + // TODO(sky): this query should order by starred as the second order by + // clause. results->clear(); - - // NOTE: Sorting by "starred_id == 0" is subtle. First we do the comparison, - // and, according to the SQLite docs, get a numeric result (all binary - // operators except "||" result in a numeric value), which is either 1 or 0 - // (implied by documentation showing the results of various comparison - // operations to always be 1 or 0). Now we sort ascending, meaning all 0s go - // first -- so, all URLs where "starred_id == 0" is 0, i.e. all starred URLs. SQLITE_UNIQUE_STATEMENT(statement, GetStatementCache(), "SELECT" HISTORY_URL_ROW_FIELDS "FROM urls " "WHERE url >= ? AND url < ? AND hidden = 0 " - "ORDER BY typed_count DESC, starred_id == 0, visit_count DESC, " - "last_visit_time DESC " + "ORDER BY typed_count DESC, visit_count DESC, last_visit_time DESC " "LIMIT ?"); if (!statement.is_valid()) return; @@ -443,6 +434,36 @@ bool URLDatabase::MigrateFromVersion11ToVersion12() { return true; } +bool URLDatabase::DropStarredIDFromURLs() { + if (!DoesSqliteColumnExist(GetDB(), "urls", "starred_id", NULL)) + return true; // urls is already updated, no need to continue. + + // Create a temporary table to contain the new URLs table. + if (!CreateTemporaryURLTable()) { + NOTREACHED(); + return false; + } + + // Copy the contents. + const char* insert_statement = + "INSERT INTO temp_urls (id, url, title, visit_count, typed_count, " + "last_visit_time, hidden, favicon_id) " + "SELECT id, url, title, visit_count, typed_count, last_visit_time, " + "hidden, favicon_id FROM urls"; + if (sqlite3_exec(GetDB(), insert_statement, NULL, NULL, NULL) != SQLITE_OK) { + NOTREACHED(); + return false; + } + + // Rename/commit the tmp table. + CommitTemporaryURLTable(); + + // This isn't created by CommitTemporaryURLTable. + CreateSupplimentaryURLIndices(); + + return true; +} + bool URLDatabase::CreateURLTable(bool is_temporary) { const char* name = is_temporary ? "temp_urls" : "urls"; if (DoesSqliteTableExist(GetDB(), name)) @@ -459,8 +480,7 @@ bool URLDatabase::CreateURLTable(bool is_temporary) { "typed_count INTEGER DEFAULT 0 NOT NULL," "last_visit_time INTEGER NOT NULL," "hidden INTEGER DEFAULT 0 NOT NULL," - "favicon_id INTEGER DEFAULT 0 NOT NULL," - "starred_id INTEGER DEFAULT 0 NOT NULL)"); + "favicon_id INTEGER DEFAULT 0 NOT NULL)"); return sqlite3_exec(GetDB(), sql.c_str(), NULL, NULL, NULL) == SQLITE_OK; } @@ -476,10 +496,6 @@ void URLDatabase::CreateSupplimentaryURLIndices() { // Add a favicon index. This is useful when we delete urls. sqlite3_exec(GetDB(), "CREATE INDEX urls_favicon_id_INDEX ON urls (favicon_id)", NULL, NULL, NULL); - - // Index on starred_id. - sqlite3_exec(GetDB(), "CREATE INDEX urls_starred_id_INDEX ON urls " - "(starred_id)", NULL, NULL, NULL); } } // namespace history diff --git a/chrome/browser/history/url_database.h b/chrome/browser/history/url_database.h index 300c05b..f27c4b9 100644 --- a/chrome/browser/history/url_database.h +++ b/chrome/browser/history/url_database.h @@ -247,6 +247,8 @@ class URLDatabase { int max_count, std::vector<KeywordSearchTermVisit>* matches); + // Migration ----------------------------------------------------------------- + // Do to a bug we were setting the favicon of about:blank. This forces // about:blank to have no icon or title. Returns true on success, false if // the favicon couldn't be updated. @@ -263,6 +265,11 @@ class URLDatabase { // fields following kURLRowFields. static const int kNumURLRowFields; + // Drops the starred_id column from urls, returning true on success. This does + // nothing (and returns true) if the urls doesn't contain the starred_id + // column. + bool DropStarredIDFromURLs(); + // Initialization functions. The indexing functions are separate from the // table creation functions so the in-memory database and the temporary tables // used when clearing history can populate the table and then create the @@ -319,7 +326,7 @@ class URLDatabase { // string dynamically anyway, use the constant, it will save space. #define HISTORY_URL_ROW_FIELDS \ " urls.id, urls.url, urls.title, urls.visit_count, urls.typed_count, " \ - "urls.last_visit_time, urls.hidden, urls.favicon_id, urls.starred_id " + "urls.last_visit_time, urls.hidden, urls.favicon_id " } // history diff --git a/chrome/browser/history/url_database_unittest.cc b/chrome/browser/history/url_database_unittest.cc index 7f2bdae..4983a5d 100644 --- a/chrome/browser/history/url_database_unittest.cc +++ b/chrome/browser/history/url_database_unittest.cc @@ -48,8 +48,7 @@ bool IsURLRowEqual(const URLRow& a, a.visit_count() == b.visit_count() && a.typed_count() == b.typed_count() && a.last_visit() - b.last_visit() <= TimeDelta::FromSeconds(1) && - a.hidden() == b.hidden() && - a.starred() == b.starred(); + a.hidden() == b.hidden(); } class URLDatabaseTest : public testing::Test, diff --git a/chrome/browser/history_model.cc b/chrome/browser/history_model.cc index 80088d6..911d841 100644 --- a/chrome/browser/history_model.cc +++ b/chrome/browser/history_model.cc @@ -85,7 +85,12 @@ history::URLID HistoryModel::GetURLID(int index) { } bool HistoryModel::IsStarred(int index) { - return results_[index].starred(); + if (star_state_[index] == UNKNOWN) { + bool is_starred = + profile_->GetBookmarkBarModel()->IsBookmarked(GetURL(index)); + star_state_[index] = is_starred ? STARRED : NOT_STARRED; + } + return (star_state_[index] == STARRED); } const Snippet& HistoryModel::GetSnippet(int index) { @@ -138,7 +143,6 @@ void HistoryModel::InitVisitRequest(int depth) { start_exploded.day_of_month = 1; options.begin_time = Time::FromLocalExploded(start_exploded); - options.only_starred = false; options.max_count = max_total_results; } else { Time::Exploded exploded; @@ -166,8 +170,6 @@ void HistoryModel::InitVisitRequest(int depth) { } options.begin_time = Time::FromLocalExploded(exploded); - options.only_starred = false; - // Subtract off the number of pages we already got. options.max_count = max_total_results - static_cast<int>(results_.size()); } @@ -261,6 +263,8 @@ void HistoryModel::VisitedPagesQueryComplete( is_search_results_ = !search_text_.empty(); + if (changed) + star_state_.reset(new StarState[results_.size()]); if (changed && observer_) observer_->ModelChanged(true); @@ -287,10 +291,8 @@ bool HistoryModel::UpdateStarredStateOfURL(const GURL& url, bool is_starred) { size_t num_matches; const size_t* match_indices = results_.MatchesForURL(url, &num_matches); for (size_t i = 0; i < num_matches; i++) { - // Update the view. (We don't care about the ID, just 0 or nonzero.) - history::URLResult& result = results_[match_indices[i]]; - if (result.starred() != is_starred) { - result.set_star_id(is_starred ? 1 : 0); + if (IsStarred(static_cast<int>(match_indices[i])) != is_starred) { + star_state_[match_indices[i]] = is_starred ? STARRED : NOT_STARRED; changed = true; } } diff --git a/chrome/browser/history_model.h b/chrome/browser/history_model.h index 89e6e79..d5595ee 100644 --- a/chrome/browser/history_model.h +++ b/chrome/browser/history_model.h @@ -92,6 +92,18 @@ class HistoryModel : public BaseHistoryModel, // Contents of the current query. history::QueryResults results_; + // We lazily ask the BookmarkBarModel for whether a URL is starred. This + // enum gives the state of a particular entry. + enum StarState { + UNKNOWN = 0, // Indicates we haven't determined the state yet. + STARRED, + NOT_STARRED + }; + + // star_state_ has an entry for each element of results_ indicating whether + // the URL is starred. + scoped_array<StarState> star_state_; + // How many months back the current query has gone. int search_depth_; diff --git a/chrome/browser/views/bookmark_editor_view.cc b/chrome/browser/views/bookmark_editor_view.cc index 8ae30ed..a4e9e8d 100644 --- a/chrome/browser/views/bookmark_editor_view.cc +++ b/chrome/browser/views/bookmark_editor_view.cc @@ -420,9 +420,9 @@ void BookmarkEditorView::ExpandAndSelect() { tree_view_.ExpandAll(); BookmarkBarNode* to_select = bb_model_->GetNodeByURL(url_); - history::UIStarID group_id_to_select = - to_select ? to_select->GetParent()->GetGroupID() : - bb_model_->GetParentForNewNodes()->GetGroupID(); + int group_id_to_select = + to_select ? to_select->GetParent()->id() : + bb_model_->GetParentForNewNodes()->id(); DCHECK(group_id_to_select); // GetMostRecentParent should never return NULL. BookmarkNode* b_node = @@ -448,9 +448,9 @@ void BookmarkEditorView::CreateNodes(BookmarkBarNode* bb_node, BookmarkEditorView::BookmarkNode* b_node) { for (int i = 0; i < bb_node->GetChildCount(); ++i) { BookmarkBarNode* child_bb_node = bb_node->GetChild(i); - if (child_bb_node->GetType() != history::StarredEntry::URL) { + if (child_bb_node->is_folder()) { BookmarkNode* new_b_node = new BookmarkNode(child_bb_node->GetTitle(), - child_bb_node->GetGroupID()); + child_bb_node->id()); b_node->Add(b_node->GetChildCount(), new_b_node); CreateNodes(child_bb_node, new_b_node); } @@ -459,7 +459,7 @@ void BookmarkEditorView::CreateNodes(BookmarkBarNode* bb_node, BookmarkEditorView::BookmarkNode* BookmarkEditorView::FindNodeWithID( BookmarkEditorView::BookmarkNode* node, - history::UIStarID id) { + int id) { if (node->value == id) return node; for (int i = 0; i < node->GetChildCount(); ++i) { @@ -556,8 +556,7 @@ void BookmarkEditorView::ApplyNameChangesAndCreateNewGroups( // is the same). for (int j = 0; j < bb_node->GetChildCount(); ++j) { BookmarkBarNode* node = bb_node->GetChild(j); - if (node->GetType() != history::StarredEntry::URL && - node->GetGroupID() == child_b_node->value) { + if (node->is_folder() && node->id() == child_b_node->value) { child_bb_node = node; break; } diff --git a/chrome/browser/views/bookmark_editor_view.h b/chrome/browser/views/bookmark_editor_view.h index 8662b07..19bb4a1 100644 --- a/chrome/browser/views/bookmark_editor_view.h +++ b/chrome/browser/views/bookmark_editor_view.h @@ -135,7 +135,7 @@ class BookmarkEditorView : public ChromeViews::View, private: // Type of node in the tree. - typedef ChromeViews::TreeNodeWithValue<history::UIStarID> BookmarkNode; + typedef ChromeViews::TreeNodeWithValue<int> BookmarkNode; // Model for the TreeView. Trivial subclass that doesn't allow titles with // empty strings. @@ -197,8 +197,7 @@ class BookmarkEditorView : public ChromeViews::View, BookmarkNode* b_node); // Returns the node with the specified id, or NULL if one can't be found. - BookmarkNode* FindNodeWithID(BookmarkEditorView::BookmarkNode* node, - history::UIStarID id); + BookmarkNode* FindNodeWithID(BookmarkEditorView::BookmarkNode* node, int id); // Invokes ApplyEdits with the selected node. void ApplyEdits(); |