Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1638)

Unified Diff: chrome/browser/bookmarks/bookmark_tag_model.cc

Issue 26894002: Experimental bookmark model based on tags. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Created 7 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/bookmarks/bookmark_tag_model.cc
diff --git a/chrome/browser/bookmarks/bookmark_tag_model.cc b/chrome/browser/bookmarks/bookmark_tag_model.cc
new file mode 100644
index 0000000000000000000000000000000000000000..94f1384204ea91952735ae5c12c5a85291fb8be3
--- /dev/null
+++ b/chrome/browser/bookmarks/bookmark_tag_model.cc
@@ -0,0 +1,574 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/bookmarks/bookmark_tag_model.h"
+
+#include "base/json/json_string_value_serializer.h"
+#include "base/observer_list.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/bookmarks/bookmark_model_factory.h"
+#include "chrome/browser/bookmarks/bookmark_tag_model_observer.h"
+#include "ui/base/models/tree_node_iterator.h"
+
+namespace {
+// The key used to store the tag list in the metainfo of a bookmark.
+const char* TAG_KEY = "TAG_KEY";
+
+// Predicates to sort bookmarks.
+bool CompareBookmarkTitles(const BookmarkNode* a, const BookmarkNode* b) {
+ return a->GetTitle() < b->GetTitle();
+}
+bool CompareBookmarkUrl(const BookmarkNode* a, const BookmarkNode* b) {
+ return a->url() < b->url();
+}
+bool CompareBookmarkCreation(const BookmarkNode* a, const BookmarkNode* b) {
+ return a->date_added() < b->date_added();
+}
+
+// Comparator to sort tags by usage.
+struct TagComparator {
+ TagComparator(std::map<BookmarkTag, unsigned int>& tags) : tags_(tags) {
+ }
+ ~TagComparator() {}
+
+ bool operator()(BookmarkTag a, BookmarkTag b) {
+ return (tags_[a] < tags_[b]);
+ }
+
+ std::map<BookmarkTag, unsigned int>& tags_;
+};
+} // namespace
+
+BookmarkTagModel::BookmarkTagModel(BookmarkModel* bookmark_model)
+ : bookmark_model_(bookmark_model),
+ loaded_(false),
+ observers_(ObserverList<BookmarkTagModelObserver>::NOTIFY_EXISTING_ONLY),
+ inhibit_change_notifications_(false) {
+ bookmark_model_->AddObserver(this);
+ if (bookmark_model_->loaded())
+ Load();
+}
+
+BookmarkTagModel::~BookmarkTagModel() {
+ if (bookmark_model_)
Yaron 2013/10/11 10:50:41 Can this ever fail? Seems like the c-tor guarantee
noyau (Ping after 24h) 2013/10/11 12:09:21 The bookmark model notifies its observer when it i
+ bookmark_model_->RemoveObserver(this);
+}
+
+#pragma mark BookmarkModel forwarding.
+
+void BookmarkTagModel::AddObserver(BookmarkTagModelObserver* observer) {
+ observers_.AddObserver(observer);
+}
+
+void BookmarkTagModel::RemoveObserver(BookmarkTagModelObserver* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+void BookmarkTagModel::BeginExtensiveChanges() {
+ DCHECK(bookmark_model_);
Yaron 2013/10/11 10:50:41 These DCHECKS all seem unnecessary for reasons abo
noyau (Ping after 24h) 2013/10/11 12:09:21 These DCHECK are necessary for reasons above :)
+ bookmark_model_->BeginExtensiveChanges();
+}
+
+void BookmarkTagModel::EndExtensiveChanges() {
+ DCHECK(bookmark_model_);
+ bookmark_model_->EndExtensiveChanges();
+}
+
+bool BookmarkTagModel::IsDoingExtensiveChanges() const {
+ DCHECK(bookmark_model_);
+ return bookmark_model_->IsDoingExtensiveChanges();
+}
+
+void BookmarkTagModel::Remove(const BookmarkNode* bookmark) {
+ DCHECK(bookmark_model_);
+ DCHECK(loaded_);
+ const BookmarkNode *parent = bookmark->parent();
+ bookmark_model_->Remove(parent, parent->GetIndexOf(bookmark));
+}
+
+void BookmarkTagModel::RemoveAll() {
+ DCHECK(bookmark_model_);
+ DCHECK(loaded_);
+ bookmark_model_->RemoveAll();
+}
+
+const gfx::Image& BookmarkTagModel::GetFavicon(const BookmarkNode* bookmark) {
+ DCHECK(bookmark_model_);
+ DCHECK(loaded_);
+ return bookmark_model_->GetFavicon(bookmark);
+}
+
+void BookmarkTagModel::SetTitle(const BookmarkNode* bookmark,
+ const string16& title) {
+ DCHECK(bookmark_model_);
+ DCHECK(loaded_);
+ bookmark_model_->SetTitle(bookmark, title);
+}
+
+void BookmarkTagModel::SetURL(const BookmarkNode* bookmark, const GURL& url) {
+ DCHECK(bookmark_model_);
+ DCHECK(loaded_);
+ bookmark_model_->SetURL(bookmark, url);
+}
+
+void BookmarkTagModel::SetDateAdded(const BookmarkNode* bookmark,
+ base::Time date_added) {
+ DCHECK(bookmark_model_);
+ DCHECK(loaded_);
+ bookmark_model_->SetDateAdded(bookmark, date_added);
+}
+
+const BookmarkNode*
+ BookmarkTagModel::GetMostRecentlyAddedBookmarkForURL(const GURL& url) {
+ DCHECK(bookmark_model_);
+ DCHECK(loaded_);
+ return bookmark_model_->GetMostRecentlyAddedNodeForURL(url);
+}
+
+#pragma mark Tags.
+
+const BookmarkNode* BookmarkTagModel::AddURL(
+ const string16& title,
+ const GURL& url,
+ const std::set<BookmarkTag>& tags) {
+ DCHECK(bookmark_model_);
+ DCHECK(loaded_);
+
+ inhibit_change_notifications_ = true;
+ const BookmarkNode* parent = bookmark_model_->GetParentForNewNodes();
+ const BookmarkNode* bookmark = bookmark_model_->AddURL(
Yaron 2013/10/11 10:50:41 I'm pretty sure the underlying BookmarkModel will
noyau (Ping after 24h) 2013/10/11 12:09:21 Yes, it will, and should. This class relies on the
+ parent, 0, title, url);
+ AddTagsToBookmark(tags, bookmark);
+ inhibit_change_notifications_ = false;
+
+ FOR_EACH_OBSERVER(BookmarkTagModelObserver, observers_,
+ BookmarkNodeAdded(this, bookmark));
+
+ return bookmark;
+}
+
+std::set<BookmarkTag>
+ BookmarkTagModel::AllTagsForBookmark(const BookmarkNode* bookmark) {
+ DCHECK(loaded_);
+ return bookmark_to_tags_[bookmark];
+}
+
+void BookmarkTagModel::AddTagsToBookmark(
+ const std::set<BookmarkTag>& tags, const BookmarkNode* bookmark) {
+ std::set<BookmarkTag> all_tags(AllTagsForBookmark(bookmark));
+ for (std::set<BookmarkTag>::iterator it = tags.begin();
+ it != tags.end(); ++it) {
+ BookmarkTag trimmed_tag = CollapseWhitespace(*it, true);
+ if (trimmed_tag.empty())
+ continue;
+ all_tags.insert(trimmed_tag);
+ }
+ ReplaceTagsOnBookmark(all_tags, bookmark);
+}
+
+void BookmarkTagModel::AddTagsToBookmarks(
+ const std::set<BookmarkTag>& tags,
+ const std::set<const BookmarkNode*>& bookmarks) {
+ for (std::set<const BookmarkNode*>::iterator it = bookmarks.begin();
+ it != bookmarks.end(); ++it) {
+ AddTagsToBookmark(tags, *it);
+ }
+}
+
+void BookmarkTagModel::RemoveTagsFromBookmark(
+ const std::set<BookmarkTag>& tags,
+ const BookmarkNode* bookmark) {
+ std::set<BookmarkTag> all_tags(AllTagsForBookmark(bookmark));
+ for (std::set<BookmarkTag>::iterator it = tags.begin();
+ it != tags.end(); ++it) {
+ all_tags.erase(*it);
+ }
+ ReplaceTagsOnBookmark(all_tags, bookmark);
+}
+
+void BookmarkTagModel::RemoveTagsFromBookmarks(
+ const std::set<BookmarkTag>& tags,
+ const std::set<const BookmarkNode*>& bookmarks){
+ for (std::set<const BookmarkNode*>::iterator it = bookmarks.begin();
+ it != bookmarks.end(); ++it) {
+ RemoveTagsFromBookmark(tags, *it);
+ }
+}
+
+std::vector<const BookmarkNode*> BookmarkTagModel::BookmarksForTags(
+ const std::set<BookmarkTag>& tags,
+ BookmarkTagModel::BookmarkOrdering ordering) {
+ DCHECK(loaded_);
+ std::set<const BookmarkNode*> bookmarks;
+ for (std::set<BookmarkTag>::iterator it = tags.begin();
+ it != tags.end(); ++it) {
+ const std::set<const BookmarkNode*> subset(tag_to_bookmarks_[*it]);
+ bookmarks.insert(subset.begin(), subset.end());
noyau (Ping after 24h) 2013/10/11 12:09:21 GCC on Android fail to compile this insert() with
+ }
+
+ std::vector<const BookmarkNode*> sorted_bookmarks(bookmarks.begin(),
+ bookmarks.end());
+ switch (ordering) {
+ case UNSORTED_BOOKMARK_ORDERING:
+ break;
+ case TITLE_BOOKMARK_ORDERING:
+ std::sort(sorted_bookmarks.begin(), sorted_bookmarks.end(),
+ CompareBookmarkTitles);
+ break;
+ case URL_BOOKMARK_ORDERING:
+ std::sort(sorted_bookmarks.begin(), sorted_bookmarks.end(),
+ CompareBookmarkUrl);
+ break;
+ case CREATION_TIME_BOOKMARK_ORDERING:
+ std::sort(sorted_bookmarks.begin(), sorted_bookmarks.end(),
+ CompareBookmarkCreation);
+ break;
+ default:
+ NOTREACHED();
+ }
+ return sorted_bookmarks;
+}
+
+std::vector<const BookmarkNode*> BookmarkTagModel::BookmarksForTag(
+ const BookmarkTag& tag, BookmarkOrdering ordering) {
+ DCHECK(!tag.empty());
+ std::set<BookmarkTag> tagset;
+ tagset.insert(tag);
+ return BookmarksForTags(tagset, ordering);
+}
+
+std::vector<BookmarkTag> BookmarkTagModel::TagsRelatedToTag(
+ const BookmarkTag& tag, BookmarkTagModel::TagOrdering ordering) {
+ DCHECK(loaded_);
+ std::map<BookmarkTag, unsigned int> tags;
+
+ if (tag.empty()) {
+ // Returns all the tags.
+ for (std::map<const BookmarkTag, std::set<const BookmarkNode*> >::iterator
+ it = tag_to_bookmarks_.begin(); it != tag_to_bookmarks_.end(); ++it) {
+ tags[it->first] = it->second.size();
+ }
+ } else {
+ std::vector<const BookmarkNode*> bookmarks(
+ BookmarksForTag(tag, UNSORTED_BOOKMARK_ORDERING));
+
+ for (std::vector<const BookmarkNode*>::iterator it = bookmarks.begin();
+ it != bookmarks.end(); ++it) {
+ std::set<BookmarkTag> subset(bookmark_to_tags_[*it]);
+ for (std::set<BookmarkTag>::iterator tag_it = subset.begin();
+ tag_it != subset.end(); ++tag_it) {
+ tags[*tag_it] += 1;
+ }
+ }
+ tags.erase(tag); // A tag is not related to itself.
+ }
+
+ // There is no keys() method on std::map. Nobody thought it might be useful?
+ std::vector<BookmarkTag> sorted_tags;
+ for (std::map<BookmarkTag, unsigned int>::iterator it = tags.begin();
+ it != tags.end(); ++it) {
+ sorted_tags.push_back(it->first);
+ }
+
+ switch (ordering) {
+ case UNSORTED_TAG_ORDERING:
+ case ALPHABETICAL_TAG_ORDERING:
+ break; // std::map is already sorting its keys.
+ case MOST_USED_TAG_ORDERING:
+ std::sort(sorted_tags.begin(), sorted_tags.end(), TagComparator(tags));
+ break;
+ default:
+ NOTREACHED();
+ }
+ return sorted_tags;
+}
+
+#pragma mark Private methods.
rlarocque 2013/10/11 18:06:25 What does this do?
noyau (Ping after 24h) 2013/10/14 23:59:39 Mac tools read those and use the text as a separat
+
+std::set<BookmarkTag> BookmarkTagModel::ExtractTagsFromBookmark(
+ const BookmarkNode *bookmark) {
+ DCHECK(bookmark_model_);
+ // This is awful BTW. Metainfo is itself an encoded JSON, and here we decode
+ // another layer.
+
+ // Retrieve the encodedData from the bookmark. If there is no encoded data
+ // at all returns the name of all the ancestors as separate tags.
+ std::string encoded;
+ if (!bookmark->GetMetaInfo(TAG_KEY, &encoded)) {
+ std::set<BookmarkTag> tags;
+ const BookmarkNode* folder = bookmark->parent();
+ while (folder && folder->type() == BookmarkNode::FOLDER) {
+ BookmarkTag trimmed_tag = CollapseWhitespace(folder->GetTitle(), true);
+ if (!trimmed_tag.empty())
+ tags.insert(trimmed_tag);
+ folder = folder->parent();
+ }
+ return tags;
+ }
+
+ // Decode into a base::Value. If the data is not encoded properly as a list
+ // return an empty result.
+ JSONStringValueSerializer serializer(&encoded);
+ int error_code = 0;
+ std::string error_message;
+ base::Value* result = serializer.Deserialize(&error_code, &error_message);
noyau (Ping after 24h) 2013/10/11 12:09:21 Memory leak fixed (Thanks memory bot!)
+
+ if (error_code || !result->IsType(base::Value::TYPE_LIST))
+ return std::set<BookmarkTag>();
+
+ base::ListValue* list = NULL;
+ if (!result->GetAsList(&list) || list->empty())
+ return std::set<BookmarkTag>();
+
+ // Build the set.
+ std::set<BookmarkTag> return_value;
+
+ for (base::ListValue::iterator it = list->begin();
+ it != list->end(); ++it) {
+ base::Value* item = *it;
+ BookmarkTag tag;
+ if (!item->GetAsString(&tag))
+ continue;
+ return_value.insert(tag);
+ }
+ return return_value;
+}
+
+void BookmarkTagModel::ReplaceTagsOnBookmark(
+ const std::set<BookmarkTag>& tags, const BookmarkNode *bookmark) {
+ DCHECK(bookmark_model_);
+ DCHECK(loaded_);
+
+ // Build a ListValue.
+ std::vector<BookmarkTag> tag_vector(tags.begin(), tags.end());
+ base::ListValue list;
+ list.AppendStrings(tag_vector);
+
+ // Encodes it.
+ std::string encoded;
+ JSONStringValueSerializer serializer(&encoded);
+
+ // Pushes it in the bookmark's metainfo.
+ serializer.Serialize(list);
+ bookmark_model_->SetNodeMetaInfo(bookmark, TAG_KEY, encoded);
+}
+
+void BookmarkTagModel::Load() {
+ DCHECK(bookmark_model_);
+ DCHECK(!loaded_);
+ ui::TreeNodeIterator<const BookmarkNode> iterator(
+ bookmark_model_->root_node());
+ while (iterator.has_next())
+ LoadBookmark(iterator.Next());
+ loaded_ = true;
+ FOR_EACH_OBSERVER(BookmarkTagModelObserver, observers_,
+ Loaded(this));
+}
+
+void BookmarkTagModel::LoadBookmark(const BookmarkNode* bookmark) {
+ DCHECK(bookmark_model_);
+ if (bookmark->is_url()) {
+ std::set<BookmarkTag> tags(ExtractTagsFromBookmark(bookmark));
+
+ bookmark_to_tags_[bookmark] = tags;
+ for (std::set<BookmarkTag>::iterator it = tags.begin();
+ it != tags.end(); ++it) {
+ tag_to_bookmarks_[*it].insert(bookmark);
+ }
+ }
+}
+
+void BookmarkTagModel::RemoveBookmark(const BookmarkNode* bookmark) {
+ DCHECK(bookmark_model_);
+ if (bookmark->is_url()) {
+ std::set<BookmarkTag> tags(bookmark_to_tags_[bookmark]);
+ bookmark_to_tags_.erase(bookmark);
+
+ for (std::set<BookmarkTag>::iterator it = tags.begin();
+ it != tags.end(); ++it) {
+ tag_to_bookmarks_[*it].erase(bookmark);
+ // Remove the tags no longer used.
+ if (!tag_to_bookmarks_[*it].size())
+ tag_to_bookmarks_.erase(*it);
+ }
+ }
+}
+
+#pragma mark BookmarkModelObserver.
+
+// Invoked when the model has finished loading.
+void BookmarkTagModel::Loaded(BookmarkModel* model, bool ids_reassigned) {
+ Load();
+};
+
+// Invoked from the destructor of the BookmarkModel.
+void BookmarkTagModel::BookmarkModelBeingDeleted(BookmarkModel* model) {
+ DCHECK(bookmark_model_);
+ FOR_EACH_OBSERVER(BookmarkTagModelObserver, observers_,
+ BookmarkTagModelBeingDeleted(this));
+ bookmark_model_ = NULL;
+ observers_.Clear();
+}
+
+// Invoked when a node has moved.
+void BookmarkTagModel::BookmarkNodeMoved(BookmarkModel* model,
+ const BookmarkNode* old_parent,
+ int old_index,
+ const BookmarkNode* new_parent,
+ int new_index) {
+ DCHECK(loaded_);
+ const BookmarkNode* bookmark = new_parent->GetChild(new_index);
+ std::string encoded;
+ if (!bookmark->GetMetaInfo(TAG_KEY, &encoded)) {
+ // The bookmark moved and the system currently use its ancestors name as a
+ // poor approximation for tags.
+ FOR_EACH_OBSERVER(BookmarkTagModelObserver, observers_,
+ OnWillChangeBookmarkTags(this, bookmark));
+ RemoveBookmark(bookmark);
+ LoadBookmark(bookmark);
+ FOR_EACH_OBSERVER(BookmarkTagModelObserver, observers_,
+ BookmarkTagsChanged(this, bookmark));
+ }
+};
+
+// Invoked when a node has been added.
+void BookmarkTagModel::BookmarkNodeAdded(BookmarkModel* model,
+ const BookmarkNode* parent,
+ int index) {
+ DCHECK(loaded_);
+ const BookmarkNode* bookmark = parent->GetChild(index);
+ if (!bookmark->is_url())
+ return;
+ LoadBookmark(bookmark);
+
+ if (!inhibit_change_notifications_)
+ FOR_EACH_OBSERVER(BookmarkTagModelObserver, observers_,
+ BookmarkNodeAdded(this, bookmark));
+}
+
+// Invoked before a node is removed.
+// |parent| the parent of the node that will be removed.
+// |old_index| the index of the node about to be removed in |parent|.
+// |node| is the node to be removed.
+void BookmarkTagModel::OnWillRemoveBookmarks(BookmarkModel* model,
+ const BookmarkNode* parent,
+ int old_index,
+ const BookmarkNode* node) {
+ DCHECK(loaded_);
+ RemoveBookmark(node);
+ FOR_EACH_OBSERVER(BookmarkTagModelObserver, observers_,
+ OnWillRemoveBookmarks(this, node));
+}
+
+// Invoked when a node has been removed, the item may still be starred though.
+// |parent| the parent of the node that was removed.
+// |old_index| the index of the removed node in |parent| before it was
+// removed.
+// |node| is the node that was removed.
+void BookmarkTagModel::BookmarkNodeRemoved(BookmarkModel* model,
+ const BookmarkNode* parent,
+ int old_index,
+ const BookmarkNode* node) {
+ DCHECK(loaded_);
+ FOR_EACH_OBSERVER(BookmarkTagModelObserver, observers_,
+ BookmarkNodeRemoved(this, node));
+}
+
+// Invoked before the title or url of a node is changed.
+void BookmarkTagModel::OnWillChangeBookmarkNode(BookmarkModel* model,
+ const BookmarkNode* node) {
+ DCHECK(loaded_);
+ if (!inhibit_change_notifications_)
+ FOR_EACH_OBSERVER(BookmarkTagModelObserver, observers_,
+ OnWillChangeBookmarkNode(this, node));
+}
+
+// Invoked when the title or url of a node changes.
+void BookmarkTagModel::BookmarkNodeChanged(BookmarkModel* model,
+ const BookmarkNode* node) {
+ DCHECK(loaded_);
+ if (!inhibit_change_notifications_)
+ FOR_EACH_OBSERVER(BookmarkTagModelObserver, observers_,
+ BookmarkNodeChanged(this, node));
+}
+
+// Invoked before the metainfo of a node is changed.
+void BookmarkTagModel::OnWillChangeBookmarkMetaInfo(BookmarkModel* model,
+ const BookmarkNode* node) {
+ DCHECK(loaded_);
+ if (!inhibit_change_notifications_)
+ FOR_EACH_OBSERVER(BookmarkTagModelObserver, observers_,
+ OnWillChangeBookmarkTags(this, node));
+}
+
+// Invoked when the metainfo on a node changes.
+void BookmarkTagModel::BookmarkMetaInfoChanged(BookmarkModel* model,
+ const BookmarkNode* node) {
+ DCHECK(loaded_);
+ RemoveBookmark(node);
+ LoadBookmark(node);
+ if (!inhibit_change_notifications_)
+ FOR_EACH_OBSERVER(BookmarkTagModelObserver, observers_,
+ BookmarkTagsChanged(this, node));
+}
+
+// Invoked when a favicon has been loaded or changed.
+void BookmarkTagModel::BookmarkNodeFaviconChanged(BookmarkModel* model,
+ const BookmarkNode* node) {
+ DCHECK(loaded_);
+ FOR_EACH_OBSERVER(BookmarkTagModelObserver, observers_,
+ BookmarkNodeFaviconChanged(this, node));
+}
+
+// Invoked before the direct children of |node| have been reordered in some
+// way, such as sorted.
+void BookmarkTagModel::OnWillReorderBookmarkNode(BookmarkModel* model,
+ const BookmarkNode* node) {
+ // This model doesn't care.
+}
+
+// Invoked when the children (just direct children, not descendants) of
+// |node| have been reordered in some way, such as sorted.
+void BookmarkTagModel::BookmarkNodeChildrenReordered(BookmarkModel* model,
+ const BookmarkNode* node) {
+ // This model doesn't care.
+}
+
+// Invoked before an extensive set of model changes is about to begin.
+// This tells UI intensive observers to wait until the updates finish to
+// update themselves.
+// These methods should only be used for imports and sync.
+// Observers should still respond to BookmarkNodeRemoved immediately,
+// to avoid holding onto stale node pointers.
+void BookmarkTagModel::ExtensiveBookmarkChangesBeginning(BookmarkModel* model) {
+ DCHECK(loaded_);
+ FOR_EACH_OBSERVER(BookmarkTagModelObserver, observers_,
+ ExtensiveBookmarkChangesBeginning(this));
+}
+
+// Invoked after an extensive set of model changes has ended.
+// This tells observers to update themselves if they were waiting for the
+// update to finish.
+void BookmarkTagModel::ExtensiveBookmarkChangesEnded(BookmarkModel* model) {
+ DCHECK(loaded_);
+ FOR_EACH_OBSERVER(BookmarkTagModelObserver, observers_,
+ ExtensiveBookmarkChangesEnded(this));
+}
+
+// Invoked before all non-permanent bookmark nodes are removed.
+void BookmarkTagModel::OnWillRemoveAllBookmarks(BookmarkModel* model) {
+ DCHECK(loaded_);
+ FOR_EACH_OBSERVER(BookmarkTagModelObserver, observers_,
+ OnWillRemoveAllBookmarks(this));
+}
+
+// Invoked when all non-permanent bookmark nodes have been removed.
+void BookmarkTagModel::BookmarkAllNodesRemoved(BookmarkModel* model){
+ DCHECK(loaded_);
+ tag_to_bookmarks_.clear();
+ bookmark_to_tags_.clear();
+ FOR_EACH_OBSERVER(BookmarkTagModelObserver, observers_,
+ BookmarkAllNodesRemoved(this));
+}

Powered by Google App Engine
This is Rietveld 408576698