diff options
author | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 23:55:29 +0000 |
---|---|---|
committer | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 23:55:29 +0000 |
commit | 09911bf300f1a419907a9412154760efd0b7abc3 (patch) | |
tree | f131325fb4e2ad12c6d3504ab75b16dd92facfed /chrome/browser/bookmark_bar_model.cc | |
parent | 586acc5fe142f498261f52c66862fa417c3d52d2 (diff) | |
download | chromium_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.cc | 716 |
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; + } +} |