summaryrefslogtreecommitdiffstats
path: root/chrome/browser/bookmark_bar_model.cc
diff options
context:
space:
mode:
authorinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-26 23:55:29 +0000
committerinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-26 23:55:29 +0000
commit09911bf300f1a419907a9412154760efd0b7abc3 (patch)
treef131325fb4e2ad12c6d3504ab75b16dd92facfed /chrome/browser/bookmark_bar_model.cc
parent586acc5fe142f498261f52c66862fa417c3d52d2 (diff)
downloadchromium_src-09911bf300f1a419907a9412154760efd0b7abc3.zip
chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.gz
chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.bz2
Add chrome to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/bookmark_bar_model.cc')
-rw-r--r--chrome/browser/bookmark_bar_model.cc716
1 files changed, 716 insertions, 0 deletions
diff --git a/chrome/browser/bookmark_bar_model.cc b/chrome/browser/bookmark_bar_model.cc
new file mode 100644
index 0000000..661f7c5
--- /dev/null
+++ b/chrome/browser/bookmark_bar_model.cc
@@ -0,0 +1,716 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "chrome/browser/bookmark_bar_model.h"
+
+#include "base/gfx/png_decoder.h"
+#include "chrome/browser/profile.h"
+#include "generated_resources.h"
+
+// BookmarkBarNode ------------------------------------------------------------
+
+const SkBitmap& BookmarkBarNode::GetFavIcon() {
+ if (!loaded_favicon_) {
+ loaded_favicon_ = true;
+ model_->LoadFavIcon(this);
+ }
+ return favicon_;
+}
+
+BookmarkBarNode::BookmarkBarNode(BookmarkBarModel* model)
+ : model_(model),
+ group_id_(0),
+ star_id_(0),
+ loaded_favicon_(false),
+ favicon_load_handle_(0),
+ type_(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;
+ 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),
+ bookmark_bar_node_(NULL),
+ other_node_(NULL) {
+ if (!profile) {
+ // Profile is NULL during testing.
+ CreateBookmarkBarNode();
+ CreateOtherBookmarksNode();
+ AddRootChildren(NULL);
+ loaded_ = true;
+ return;
+ }
+
+ // Notifications we want.
+ NotificationService::current()->AddObserver(
+ this, NOTIFY_STARRED_FAVICON_CHANGED, Source<Profile>(profile_));
+
+ // Request the entries from the database.
+ HistoryService* history_service =
+ profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
+ if (!history_service)
+ return;
+
+ // Request the entries on the bookmark bar.
+ history_service->GetAllStarredEntries(&load_consumer_,
+ NewCallback(this, &BookmarkBarModel::OnGotStarredEntries));
+}
+
+BookmarkBarModel::~BookmarkBarModel() {
+ if (profile_) {
+ NotificationService::current()->RemoveObserver(
+ this, NOTIFY_STARRED_FAVICON_CHANGED, Source<Profile>(profile_));
+ }
+}
+
+BookmarkBarNode* BookmarkBarModel::GetParentForNewNodes() {
+ std::vector<BookmarkBarNode*> nodes;
+
+ GetMostRecentlyModifiedGroupNodes(&root_, 1, &nodes);
+ return nodes.empty() ? bookmark_bar_node_ : nodes[0];
+}
+
+std::vector<BookmarkBarNode*> BookmarkBarModel::GetMostRecentlyModifiedGroups(
+ size_t max_count) {
+ std::vector<BookmarkBarNode*> nodes;
+ GetMostRecentlyModifiedGroupNodes(&root_, max_count, &nodes);
+
+ if (nodes.size() < max_count) {
+ // Add the bookmark bar and other nodes if there is space.
+ if (find(nodes.begin(), nodes.end(), bookmark_bar_node_) == nodes.end())
+ nodes.push_back(bookmark_bar_node_);
+
+ if (nodes.size() < max_count &&
+ find(nodes.begin(), nodes.end(), other_node_) == nodes.end()) {
+ nodes.push_back(other_node_);
+ }
+ }
+ 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::RemoveFromBookmarkBar(BookmarkBarNode* node) {
+ if (!node->HasAncestor(bookmark_bar_node_))
+ 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.
+ }
+}
+
+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_ ||
+ node == other_node_) {
+ NOTREACHED();
+ return;
+ }
+
+ if (new_parent->HasAncestor(node)) {
+ // Can't make an ancestor of the node be a child of the node.
+ NOTREACHED();
+ return;
+ }
+
+ SetDateGroupModified(new_parent, Time::Now());
+
+ BookmarkBarNode* old_parent = node->GetParent();
+ int old_index = old_parent->IndexOfChild(node);
+
+ if (old_parent == new_parent &&
+ (index == old_index || index == old_index + 1)) {
+ // Node is already in this position, nothing to do.
+ return;
+ }
+
+ if (old_parent == new_parent && index > old_index)
+ index--;
+ new_parent->Add(index, node);
+
+ HistoryService* history = !profile_ ? NULL :
+ profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
+ if (history)
+ history->UpdateStarredEntry(node->GetEntry());
+ FOR_EACH_OBSERVER(BookmarkBarModelObserver, observers_,
+ BookmarkNodeMoved(this, old_parent, old_index,
+ new_parent, index));
+}
+
+void BookmarkBarModel::SetTitle(BookmarkBarNode* node,
+ const std::wstring& title) {
+ DCHECK(node);
+ if (node->GetTitle() == title)
+ return;
+ node->SetTitle(title);
+ HistoryService* history = !profile_ ? NULL :
+ profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
+ if (history)
+ history->UpdateStarredEntry(node->GetEntry());
+ FOR_EACH_OBSERVER(BookmarkBarModelObserver, observers_,
+ BookmarkNodeChanged(this, node));
+}
+
+BookmarkBarNode* BookmarkBarModel::GetNodeByURL(const GURL& url) {
+ BookmarkBarNode tmp_node(this);
+ tmp_node.url_ = 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) {
+ // TODO(sky): TreeNode needs a method that visits all nodes using a predicate.
+ return GetNodeByGroupID(&root_, group_id);
+}
+
+BookmarkBarNode* BookmarkBarModel::AddGroup(
+ BookmarkBarNode* parent,
+ int index,
+ const std::wstring& title) {
+ DCHECK(IsLoaded());
+#ifndef NDEBUG
+ CheckIndex(parent, index, true);
+#endif
+ if (parent == &root_) {
+ // Can't add to the root.
+ NOTREACHED();
+ return NULL;
+ }
+
+ BookmarkBarNode* new_node = new BookmarkBarNode(this);
+ new_node->group_id_ = next_group_id_++;
+ new_node->SetTitle(title);
+ new_node->type_ = history::StarredEntry::USER_GROUP;
+
+ return AddNode(parent, index, new_node);
+}
+
+BookmarkBarNode* BookmarkBarModel::AddURL(BookmarkBarNode* parent,
+ int index,
+ const std::wstring& title,
+ const GURL& url) {
+ return AddURLWithCreationTime(parent, index, title, url, Time::Now());
+}
+
+BookmarkBarNode* BookmarkBarModel::AddURLWithCreationTime(
+ BookmarkBarNode* parent,
+ int index,
+ 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.
+ NOTREACHED();
+ return NULL;
+ }
+#ifndef NDEBUG
+ CheckIndex(parent, index, true);
+#endif
+
+ BookmarkBarNode* existing_node = GetNodeByURL(url);
+ if (existing_node) {
+ Move(existing_node, parent, index);
+ SetTitle(existing_node, title);
+ return existing_node;
+ }
+
+ SetDateGroupModified(parent, creation_time);
+
+ BookmarkBarNode* new_node = new BookmarkBarNode(this);
+ new_node->SetTitle(title);
+ new_node->SetURL(url);
+ new_node->date_added_ = creation_time;
+ new_node->type_ = history::StarredEntry::URL;
+
+ nodes_ordered_by_url_set_.insert(new_node);
+
+ return AddNode(parent, index, new_node);
+}
+
+void BookmarkBarModel::SetURLStarred(const GURL& url,
+ const std::wstring& title,
+ bool is_starred) {
+ BookmarkBarNode* node = GetNodeByURL(url);
+ if (is_starred && !node) {
+ // Add the url.
+ BookmarkBarNode* parent = GetParentForNewNodes();
+ AddURL(parent, parent->GetChildCount(), title, url);
+ } else if (!is_starred && node) {
+ Remove(node->GetParent(), node->GetParent()->IndexOfChild(node));
+ }
+}
+
+void BookmarkBarModel::ResetDateGroupModified(BookmarkBarNode* node) {
+ SetDateGroupModified(node, Time());
+}
+
+void BookmarkBarModel::FavIconLoaded(BookmarkBarNode* node) {
+ // Send out notification to the observer.
+ FOR_EACH_OBSERVER(BookmarkBarModelObserver, observers_,
+ BookmarkNodeFavIconLoaded(this, node));
+}
+
+void BookmarkBarModel::RemoveNode(BookmarkBarNode* node) {
+ DCHECK(node && node != bookmark_bar_node_ && node != other_node_);
+ 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);
+ }
+
+ CancelPendingFavIconLoadRequests(node);
+
+ // Recurse through children.
+ for (int i = node->GetChildCount() - 1; i >= 0; --i)
+ RemoveNode(node->GetChild(i));
+}
+
+void BookmarkBarModel::OnGotStarredEntries(
+ HistoryService::Handle,
+ std::vector<history::StarredEntry>* entries) {
+ if (loaded_) {
+ NOTREACHED();
+ return;
+ }
+
+ DCHECK(entries);
+ // Create tree nodes for each of the elements.
+ PopulateNodes(entries);
+
+ // Yes, we've finished loading.
+ loaded_ = true;
+
+ FOR_EACH_OBSERVER(BookmarkBarModelObserver, observers_, Loaded(this));
+
+ NotificationService::current()->Notify(
+ NOTIFY_BOOKMARK_MODEL_LOADED,
+ Source<Profile>(profile_),
+ NotificationService::NoDetails());
+}
+
+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;
+ }
+
+ // 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;
+ }
+
+ 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.
+
+ // 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);
+
+ // 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);
+ }
+}
+
+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));
+ }
+}
+
+void BookmarkBarModel::RemoveAndDeleteNode(BookmarkBarNode* delete_me) {
+ scoped_ptr<BookmarkBarNode> node(delete_me);
+
+ BookmarkBarNode* parent = node->GetParent();
+ DCHECK(parent);
+ int index = parent->IndexOfChild(node.get());
+ parent->Remove(index);
+ RemoveNode(node.get());
+
+ 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));
+}
+
+BookmarkBarNode* BookmarkBarModel::AddNode(BookmarkBarNode* parent,
+ 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;
+ }
+ FOR_EACH_OBSERVER(BookmarkBarModelObserver, observers_,
+ BookmarkNodeAdded(this, parent, index));
+ return node;
+}
+
+BookmarkBarNode* BookmarkBarModel::GetNodeByGroupID(
+ BookmarkBarNode* node,
+ history::UIStarID group_id) {
+ if (node->GetType() == history::StarredEntry::USER_GROUP &&
+ node->GetGroupID() == group_id) {
+ return node;
+ }
+ for (int i = 0; i < node->GetChildCount(); ++i) {
+ BookmarkBarNode* result = GetNodeByGroupID(node->GetChild(i), group_id);
+ if (result)
+ return result;
+ }
+ return NULL;
+}
+
+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());
+}
+
+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);
+}
+
+BookmarkBarNode* BookmarkBarModel::CreateRootNodeFromStarredEntry(
+ const history::StarredEntry& entry) {
+ DCHECK(entry.type == history::StarredEntry::BOOKMARK_BAR ||
+ entry.type == history::StarredEntry::OTHER);
+ BookmarkBarNode* node = new BookmarkBarNode(this);
+ 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,
+ scoped_refptr<RefCountedBytes> data,
+ bool expired,
+ GURL icon_url) {
+ SkBitmap fav_icon;
+ if (know_favicon && data.get() &&
+ PNGDecoder::Decode(&data->data, &fav_icon)) {
+ BookmarkBarNode* node =
+ load_consumer_.GetClientData(
+ profile_->GetHistoryService(Profile::EXPLICIT_ACCESS), handle);
+ DCHECK(node);
+ node->favicon_ = fav_icon;
+ node->favicon_load_handle_ = 0;
+ FavIconLoaded(node);
+ }
+}
+
+void BookmarkBarModel::LoadFavIcon(BookmarkBarNode* node) {
+ if (node->GetType() != history::StarredEntry::URL)
+ return;
+
+ DCHECK(node->GetURL().is_valid());
+ HistoryService* history_service =
+ profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
+ if (!history_service)
+ return;
+
+ HistoryService::Handle handle = history_service->GetFavIconForURL(
+ node->GetURL(), &load_consumer_,
+ NewCallback(this, &BookmarkBarModel::OnFavIconDataAvailable));
+ load_consumer_.SetClientData(history_service, handle, node);
+}
+
+void BookmarkBarModel::CancelPendingFavIconLoadRequests(BookmarkBarNode* node) {
+ if (node->favicon_load_handle_) {
+ HistoryService* history =
+ profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
+ if (history)
+ history->CancelRequest(node->favicon_load_handle_);
+ node->favicon_load_handle_ = 0;
+ }
+}
+
+// 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 (count == 0) {
+ nodes->push_back(parent);
+ } else {
+ std::vector<BookmarkBarNode*>::iterator i =
+ std::upper_bound(nodes->begin(), nodes->end(), parent,
+ &MoreRecentlyModified);
+ if (nodes->size() < count || i != nodes->end()) {
+ nodes->insert(i, parent);
+ while (nodes->size() > count)
+ nodes->pop_back();
+ }
+ }
+ } // else case, the root node, which we don't care about or imported nodes
+ // (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) {
+ GetMostRecentlyModifiedGroupNodes(child, count, nodes);
+ }
+ }
+}
+
+void BookmarkBarModel::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ switch (type) {
+ case NOTIFY_STARRED_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();
+ i != favicon_details->urls.end(); ++i) {
+ BookmarkBarNode* node = GetNodeByURL(*i);
+ if (node) {
+ // Got an updated favicon, for a URL, do a new request.
+ node->InvalidateFavicon();
+ CancelPendingFavIconLoadRequests(node);
+ FOR_EACH_OBSERVER(BookmarkBarModelObserver, observers_,
+ BookmarkNodeChanged(this, node));
+ }
+ }
+ break;
+ }
+
+ default:
+ NOTREACHED();
+ break;
+ }
+}