blob: 661f7c50a86fbe4987708e3c8e588049d04b2ca3 [file] [log] [blame]
initial.commit09911bf2008-07-26 23:55:291// Copyright 2008, Google Inc.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8// * Redistributions of source code must retain the above copyright
9// notice, this list of conditions and the following disclaimer.
10// * Redistributions in binary form must reproduce the above
11// copyright notice, this list of conditions and the following disclaimer
12// in the documentation and/or other materials provided with the
13// distribution.
14// * Neither the name of Google Inc. nor the names of its
15// contributors may be used to endorse or promote products derived from
16// this software without specific prior written permission.
17//
18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30#include "chrome/browser/bookmark_bar_model.h"
31
32#include "base/gfx/png_decoder.h"
33#include "chrome/browser/profile.h"
34#include "generated_resources.h"
35
36// BookmarkBarNode ------------------------------------------------------------
37
38const SkBitmap& BookmarkBarNode::GetFavIcon() {
39 if (!loaded_favicon_) {
40 loaded_favicon_ = true;
41 model_->LoadFavIcon(this);
42 }
43 return favicon_;
44}
45
46BookmarkBarNode::BookmarkBarNode(BookmarkBarModel* model)
47 : model_(model),
48 group_id_(0),
49 star_id_(0),
50 loaded_favicon_(false),
51 favicon_load_handle_(0),
52 type_(history::StarredEntry::BOOKMARK_BAR),
53 date_added_(Time::Now()) {
54 DCHECK(model_);
55}
56
57void BookmarkBarNode::Reset(const history::StarredEntry& entry) {
58 // We should either have no id, or the id of the new entry should match
59 // this.
60 DCHECK(!star_id_ || star_id_ == entry.id);
61 star_id_ = entry.id;
62 group_id_ = entry.group_id;
63 url_ = entry.url;
64 favicon_ = SkBitmap();
65 loaded_favicon_ = false;
66 favicon_load_handle_ = 0;
67 type_ = entry.type;
68 date_added_ = entry.date_added;
69 date_group_modified_ = entry.date_group_modified;
70 SetTitle(entry.title);
71}
72
73void BookmarkBarNode::SetURL(const GURL& url) {
74 DCHECK(favicon_load_handle_ == 0);
75 loaded_favicon_ = false;
76 favicon_load_handle_ = 0;
77 url_ = url;
78 favicon_ .reset();
79}
80
81history::StarredEntry BookmarkBarNode::GetEntry() {
82 history::StarredEntry entry;
83 entry.id = GetStarID();
84 entry.group_id = group_id_;
85 entry.url = GetURL();
86 entry.title = GetTitle();
87 entry.type = type_;
88 entry.date_added = date_added_;
89 entry.date_group_modified = date_group_modified_;
90 // Only set the parent and visual order if we have a valid parent (the root
91 // node is not in the db and has a group_id of 0).
92 if (GetParent() && GetParent()->group_id_) {
93 entry.visual_order = GetParent()->IndexOfChild(this);
94 entry.parent_group_id = GetParent()->GetGroupID();
95 }
96 return entry;
97}
98
99// BookmarkBarModel -----------------------------------------------------------
100
101BookmarkBarModel::BookmarkBarModel(Profile* profile)
102 : profile_(profile),
103 loaded_(false),
104#pragma warning(suppress: 4355) // Okay to pass "this" here.
105 root_(this),
106 // See declaration for description.
107 next_group_id_(HistoryService::kBookmarkBarID + 1),
108 bookmark_bar_node_(NULL),
109 other_node_(NULL) {
110 if (!profile) {
111 // Profile is NULL during testing.
112 CreateBookmarkBarNode();
113 CreateOtherBookmarksNode();
114 AddRootChildren(NULL);
115 loaded_ = true;
116 return;
117 }
118
119 // Notifications we want.
120 NotificationService::current()->AddObserver(
121 this, NOTIFY_STARRED_FAVICON_CHANGED, Source<Profile>(profile_));
122
123 // Request the entries from the database.
124 HistoryService* history_service =
125 profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
126 if (!history_service)
127 return;
128
129 // Request the entries on the bookmark bar.
130 history_service->GetAllStarredEntries(&load_consumer_,
131 NewCallback(this, &BookmarkBarModel::OnGotStarredEntries));
132}
133
134BookmarkBarModel::~BookmarkBarModel() {
135 if (profile_) {
136 NotificationService::current()->RemoveObserver(
137 this, NOTIFY_STARRED_FAVICON_CHANGED, Source<Profile>(profile_));
138 }
139}
140
141BookmarkBarNode* BookmarkBarModel::GetParentForNewNodes() {
142 std::vector<BookmarkBarNode*> nodes;
143
144 GetMostRecentlyModifiedGroupNodes(&root_, 1, &nodes);
145 return nodes.empty() ? bookmark_bar_node_ : nodes[0];
146}
147
148std::vector<BookmarkBarNode*> BookmarkBarModel::GetMostRecentlyModifiedGroups(
149 size_t max_count) {
150 std::vector<BookmarkBarNode*> nodes;
151 GetMostRecentlyModifiedGroupNodes(&root_, max_count, &nodes);
152
153 if (nodes.size() < max_count) {
154 // Add the bookmark bar and other nodes if there is space.
155 if (find(nodes.begin(), nodes.end(), bookmark_bar_node_) == nodes.end())
156 nodes.push_back(bookmark_bar_node_);
157
158 if (nodes.size() < max_count &&
159 find(nodes.begin(), nodes.end(), other_node_) == nodes.end()) {
160 nodes.push_back(other_node_);
161 }
162 }
163 return nodes;
164}
165
166void BookmarkBarModel::Remove(BookmarkBarNode* parent, int index) {
167#ifndef NDEBUG
168 CheckIndex(parent, index, false);
169#endif
170 if (parent != &root_) {
171 RemoveAndDeleteNode(parent->GetChild(index));
172 } else {
173 NOTREACHED(); // Can't remove from the root.
174 }
175}
176
177void BookmarkBarModel::RemoveFromBookmarkBar(BookmarkBarNode* node) {
178 if (!node->HasAncestor(bookmark_bar_node_))
179 return;
180
181 if (node != &root_ && node != bookmark_bar_node_ && node != other_node_) {
182 Move(node, other_node_, other_node_->GetChildCount());
183 } else {
184 NOTREACHED(); // Can't move the root, bookmark bar or other nodes.
185 }
186}
187
188void BookmarkBarModel::Move(BookmarkBarNode* node,
189 BookmarkBarNode* new_parent,
190 int index) {
191 DCHECK(node && new_parent);
192 DCHECK(node->GetParent());
193#ifndef NDEBUG
194 CheckIndex(new_parent, index, true);
195#endif
196
197 if (new_parent == &root_ || node == &root_ || node == bookmark_bar_node_ ||
198 node == other_node_) {
199 NOTREACHED();
200 return;
201 }
202
203 if (new_parent->HasAncestor(node)) {
204 // Can't make an ancestor of the node be a child of the node.
205 NOTREACHED();
206 return;
207 }
208
209 SetDateGroupModified(new_parent, Time::Now());
210
211 BookmarkBarNode* old_parent = node->GetParent();
212 int old_index = old_parent->IndexOfChild(node);
213
214 if (old_parent == new_parent &&
215 (index == old_index || index == old_index + 1)) {
216 // Node is already in this position, nothing to do.
217 return;
218 }
219
220 if (old_parent == new_parent && index > old_index)
221 index--;
222 new_parent->Add(index, node);
223
224 HistoryService* history = !profile_ ? NULL :
225 profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
226 if (history)
227 history->UpdateStarredEntry(node->GetEntry());
228 FOR_EACH_OBSERVER(BookmarkBarModelObserver, observers_,
229 BookmarkNodeMoved(this, old_parent, old_index,
230 new_parent, index));
231}
232
233void BookmarkBarModel::SetTitle(BookmarkBarNode* node,
234 const std::wstring& title) {
235 DCHECK(node);
236 if (node->GetTitle() == title)
237 return;
238 node->SetTitle(title);
239 HistoryService* history = !profile_ ? NULL :
240 profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
241 if (history)
242 history->UpdateStarredEntry(node->GetEntry());
243 FOR_EACH_OBSERVER(BookmarkBarModelObserver, observers_,
244 BookmarkNodeChanged(this, node));
245}
246
247BookmarkBarNode* BookmarkBarModel::GetNodeByURL(const GURL& url) {
248 BookmarkBarNode tmp_node(this);
249 tmp_node.url_ = url;
250 NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.find(&tmp_node);
251 return (i != nodes_ordered_by_url_set_.end()) ? *i : NULL;
252}
253
254BookmarkBarNode* BookmarkBarModel::GetNodeByGroupID(
255 history::UIStarID group_id) {
256 // TODO(sky): TreeNode needs a method that visits all nodes using a predicate.
257 return GetNodeByGroupID(&root_, group_id);
258}
259
260BookmarkBarNode* BookmarkBarModel::AddGroup(
261 BookmarkBarNode* parent,
262 int index,
263 const std::wstring& title) {
264 DCHECK(IsLoaded());
265#ifndef NDEBUG
266 CheckIndex(parent, index, true);
267#endif
268 if (parent == &root_) {
269 // Can't add to the root.
270 NOTREACHED();
271 return NULL;
272 }
273
274 BookmarkBarNode* new_node = new BookmarkBarNode(this);
275 new_node->group_id_ = next_group_id_++;
276 new_node->SetTitle(title);
277 new_node->type_ = history::StarredEntry::USER_GROUP;
278
279 return AddNode(parent, index, new_node);
280}
281
282BookmarkBarNode* BookmarkBarModel::AddURL(BookmarkBarNode* parent,
283 int index,
284 const std::wstring& title,
285 const GURL& url) {
286 return AddURLWithCreationTime(parent, index, title, url, Time::Now());
287}
288
289BookmarkBarNode* BookmarkBarModel::AddURLWithCreationTime(
290 BookmarkBarNode* parent,
291 int index,
292 const std::wstring& title,
293 const GURL& url,
294 const Time& creation_time) {
295 DCHECK(IsLoaded() && url.is_valid() && parent);
296 if (parent == &root_) {
297 // Can't add to the root.
298 NOTREACHED();
299 return NULL;
300 }
301#ifndef NDEBUG
302 CheckIndex(parent, index, true);
303#endif
304
305 BookmarkBarNode* existing_node = GetNodeByURL(url);
306 if (existing_node) {
307 Move(existing_node, parent, index);
308 SetTitle(existing_node, title);
309 return existing_node;
310 }
311
312 SetDateGroupModified(parent, creation_time);
313
314 BookmarkBarNode* new_node = new BookmarkBarNode(this);
315 new_node->SetTitle(title);
316 new_node->SetURL(url);
317 new_node->date_added_ = creation_time;
318 new_node->type_ = history::StarredEntry::URL;
319
320 nodes_ordered_by_url_set_.insert(new_node);
321
322 return AddNode(parent, index, new_node);
323}
324
325void BookmarkBarModel::SetURLStarred(const GURL& url,
326 const std::wstring& title,
327 bool is_starred) {
328 BookmarkBarNode* node = GetNodeByURL(url);
329 if (is_starred && !node) {
330 // Add the url.
331 BookmarkBarNode* parent = GetParentForNewNodes();
332 AddURL(parent, parent->GetChildCount(), title, url);
333 } else if (!is_starred && node) {
334 Remove(node->GetParent(), node->GetParent()->IndexOfChild(node));
335 }
336}
337
338void BookmarkBarModel::ResetDateGroupModified(BookmarkBarNode* node) {
339 SetDateGroupModified(node, Time());
340}
341
342void BookmarkBarModel::FavIconLoaded(BookmarkBarNode* node) {
343 // Send out notification to the observer.
344 FOR_EACH_OBSERVER(BookmarkBarModelObserver, observers_,
345 BookmarkNodeFavIconLoaded(this, node));
346}
347
348void BookmarkBarModel::RemoveNode(BookmarkBarNode* node) {
349 DCHECK(node && node != bookmark_bar_node_ && node != other_node_);
350 if (node->GetType() == history::StarredEntry::URL) {
351 NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.find(node);
352 DCHECK(i != nodes_ordered_by_url_set_.end());
353 nodes_ordered_by_url_set_.erase(i);
354 }
355
356 NodeToHandleMap::iterator i = node_to_handle_map_.find(node);
357 if (i != node_to_handle_map_.end()) {
358 HistoryService* history = !profile_ ? NULL :
359 profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
360 if (history)
361 request_consumer_.SetClientData(history, i->second, NULL);
362 node_to_handle_map_.erase(i);
363 }
364
365 CancelPendingFavIconLoadRequests(node);
366
367 // Recurse through children.
368 for (int i = node->GetChildCount() - 1; i >= 0; --i)
369 RemoveNode(node->GetChild(i));
370}
371
372void BookmarkBarModel::OnGotStarredEntries(
373 HistoryService::Handle,
374 std::vector<history::StarredEntry>* entries) {
375 if (loaded_) {
376 NOTREACHED();
377 return;
378 }
379
380 DCHECK(entries);
381 // Create tree nodes for each of the elements.
382 PopulateNodes(entries);
383
384 // Yes, we've finished loading.
385 loaded_ = true;
386
387 FOR_EACH_OBSERVER(BookmarkBarModelObserver, observers_, Loaded(this));
388
389 NotificationService::current()->Notify(
390 NOTIFY_BOOKMARK_MODEL_LOADED,
391 Source<Profile>(profile_),
392 NotificationService::NoDetails());
393}
394
395void BookmarkBarModel::PopulateNodes(
396 std::vector<history::StarredEntry>* entries) {
397 std::map<history::UIStarID,history::StarID> group_id_to_id_map;
398 IDToNodeMap id_to_node_map;
399
400 // Iterate through the entries building a mapping between group_id and id as
401 // well as creating the bookmark bar node and other node.
402 for (std::vector<history::StarredEntry>::const_iterator i = entries->begin();
403 i != entries->end(); ++i) {
404 if (i->type == history::StarredEntry::URL)
405 continue;
406
407 if (i->type == history::StarredEntry::OTHER)
408 other_node_ = CreateRootNodeFromStarredEntry(*i);
409 else if (i->type == history::StarredEntry::BOOKMARK_BAR)
410 bookmark_bar_node_ = CreateRootNodeFromStarredEntry(*i);
411
412 group_id_to_id_map[i->group_id] = i->id;
413 }
414
415 // Add the bookmark bar and other nodes to the root node.
416 AddRootChildren(&id_to_node_map);
417
418 // If the db was corrupt and we didn't get the bookmark bar/other nodes,
419 // AddRootChildren will create them. Update the map to make sure it includes
420 // these nodes.
421 group_id_to_id_map[other_node_->group_id_] = other_node_->star_id_;
422 group_id_to_id_map[bookmark_bar_node_->group_id_] =
423 bookmark_bar_node_->star_id_;
424
425 // Iterate through the entries again creating the nodes.
426 for (std::vector<history::StarredEntry>::iterator i = entries->begin();
427 i != entries->end(); ++i) {
428 if (!i->parent_group_id) {
429 DCHECK(i->type == history::StarredEntry::BOOKMARK_BAR ||
430 i->type == history::StarredEntry::OTHER);
431 // Ignore entries not parented to the bookmark bar.
432 continue;
433 }
434
435 BookmarkBarNode* node = id_to_node_map[i->id];
436 if (!node) {
437 // Creating a node results in creating the parent. As such, it is
438 // possible for the node representing a group to have been created before
439 // encountering the details.
440
441 // The created nodes are owned by the root node.
442 node = new BookmarkBarNode(this);
443 id_to_node_map[i->id] = node;
444 }
445 node->Reset(*i);
446
447 DCHECK(group_id_to_id_map.find(i->parent_group_id) !=
448 group_id_to_id_map.end());
449 history::StarID parent_id = group_id_to_id_map[i->parent_group_id];
450 BookmarkBarNode* parent = id_to_node_map[parent_id];
451 if (!parent) {
452 // Haven't encountered the parent yet, create it now.
453 parent = new BookmarkBarNode(this);
454 id_to_node_map[parent_id] = parent;
455 }
456
457 if (i->type == history::StarredEntry::URL)
458 nodes_ordered_by_url_set_.insert(node);
459 else
460 next_group_id_ = std::max(next_group_id_, i->group_id + 1);
461
462 // Add the node to its parent. entries is ordered by parent then
463 // visual order so that we know we maintain visual order by always adding
464 // to the end.
465 parent->Add(parent->GetChildCount(), node);
466 }
467}
468
469void BookmarkBarModel::OnCreatedEntry(HistoryService::Handle handle,
470 history::StarID id) {
471 BookmarkBarNode* node = request_consumer_.GetClientData(
472 profile_->GetHistoryService(Profile::EXPLICIT_ACCESS), handle);
473 // Node is NULL if the node was removed by the user before the request
474 // was processed.
475 if (node) {
476 DCHECK(!node->star_id_);
477 node->star_id_ = id;
478 DCHECK(node_to_handle_map_.find(node) != node_to_handle_map_.end());
479 node_to_handle_map_.erase(node_to_handle_map_.find(node));
480 }
481}
482
483void BookmarkBarModel::RemoveAndDeleteNode(BookmarkBarNode* delete_me) {
484 scoped_ptr<BookmarkBarNode> node(delete_me);
485
486 BookmarkBarNode* parent = node->GetParent();
487 DCHECK(parent);
488 int index = parent->IndexOfChild(node.get());
489 parent->Remove(index);
490 RemoveNode(node.get());
491
492 HistoryService* history = profile_ ?
493 profile_->GetHistoryService(Profile::EXPLICIT_ACCESS) : NULL;
494 if (history) {
495 if (node->GetType() == history::StarredEntry::URL) {
496 history->DeleteStarredURL(node->GetURL());
497 } else {
498 history->DeleteStarredGroup(node->GetGroupID());
499 }
500 }
501 FOR_EACH_OBSERVER(BookmarkBarModelObserver, observers_,
502 BookmarkNodeRemoved(this, parent, index));
503}
504
505BookmarkBarNode* BookmarkBarModel::AddNode(BookmarkBarNode* parent,
506 int index,
507 BookmarkBarNode* node) {
508 parent->Add(index, node);
509
510 // NOTE: As history calls us back when we invoke CreateStarredEntry, we have
511 // to be sure to invoke it after we've updated the nodes appropriately.
512 HistoryService* history = !profile_ ? NULL :
513 profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
514 if (history) {
515 HistoryService::Handle handle =
516 history->CreateStarredEntry(node->GetEntry(), &request_consumer_,
517 NewCallback(this, &BookmarkBarModel::OnCreatedEntry));
518 request_consumer_.SetClientData(history, handle, node);
519 node_to_handle_map_[node] = handle;
520 }
521 FOR_EACH_OBSERVER(BookmarkBarModelObserver, observers_,
522 BookmarkNodeAdded(this, parent, index));
523 return node;
524}
525
526BookmarkBarNode* BookmarkBarModel::GetNodeByGroupID(
527 BookmarkBarNode* node,
528 history::UIStarID group_id) {
529 if (node->GetType() == history::StarredEntry::USER_GROUP &&
530 node->GetGroupID() == group_id) {
531 return node;
532 }
533 for (int i = 0; i < node->GetChildCount(); ++i) {
534 BookmarkBarNode* result = GetNodeByGroupID(node->GetChild(i), group_id);
535 if (result)
536 return result;
537 }
538 return NULL;
539}
540
541void BookmarkBarModel::SetDateGroupModified(BookmarkBarNode* parent,
542 const Time time) {
543 DCHECK(parent);
544 parent->date_group_modified_ = time;
545 HistoryService* history = profile_ ?
546 profile_->GetHistoryService(Profile::EXPLICIT_ACCESS) : NULL;
547 if (history)
548 history->UpdateStarredEntry(parent->GetEntry());
549}
550
551void BookmarkBarModel::CreateBookmarkBarNode() {
552 history::StarredEntry entry;
553 entry.id = HistoryService::kBookmarkBarID;
554 entry.group_id = HistoryService::kBookmarkBarID;
555 entry.type = history::StarredEntry::BOOKMARK_BAR;
556 bookmark_bar_node_ = CreateRootNodeFromStarredEntry(entry);
557}
558
559void BookmarkBarModel::CreateOtherBookmarksNode() {
560 history::StarredEntry entry;
561 entry.id = HistoryService::kBookmarkBarID;
562 for (NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.begin();
563 i != nodes_ordered_by_url_set_.end(); ++i) {
564 entry.id = std::max(entry.id, (*i)->GetStarID());
565 }
566 entry.id++;
567 entry.group_id = next_group_id_++;
568 entry.type = history::StarredEntry::OTHER;
569 other_node_ = CreateRootNodeFromStarredEntry(entry);
570}
571
572BookmarkBarNode* BookmarkBarModel::CreateRootNodeFromStarredEntry(
573 const history::StarredEntry& entry) {
574 DCHECK(entry.type == history::StarredEntry::BOOKMARK_BAR ||
575 entry.type == history::StarredEntry::OTHER);
576 BookmarkBarNode* node = new BookmarkBarNode(this);
577 node->Reset(entry);
578 if (entry.type == history::StarredEntry::BOOKMARK_BAR)
579 node->SetTitle(l10n_util::GetString(IDS_BOOMARK_BAR_FOLDER_NAME));
580 else
581 node->SetTitle(l10n_util::GetString(IDS_BOOMARK_BAR_OTHER_FOLDER_NAME));
582 next_group_id_ = std::max(next_group_id_, entry.group_id + 1);
583 return node;
584}
585
586void BookmarkBarModel::AddRootChildren(IDToNodeMap* id_to_node_map) {
587 // When invoked we should have created the bookmark bar and other nodes. If
588 // not, it indicates the db couldn't be loaded correctly or is corrupt.
589 // Force creation so that bookmark bar model is still usable.
590 if (!bookmark_bar_node_) {
591 LOG(WARNING) << "No bookmark bar entry in the database. This indicates "
592 "bookmarks database couldn't be loaded or is corrupt.";
593 CreateBookmarkBarNode();
594 }
595 if (!other_node_) {
596 LOG(WARNING) << "No other folders bookmark bar entry in the database. This "
597 "indicates bookmarks database couldn't be loaded or is "
598 "corrupt.";
599 CreateOtherBookmarksNode();
600 }
601
602 if (id_to_node_map) {
603 (*id_to_node_map)[other_node_->GetStarID()] = other_node_;
604 (*id_to_node_map)[bookmark_bar_node_->GetStarID()] = bookmark_bar_node_;
605 }
606
607 // WARNING: order is important here, various places assume bookmark bar then
608 // other node.
609 root_.Add(0, bookmark_bar_node_);
610 root_.Add(1, other_node_);
611}
612
613void BookmarkBarModel::OnFavIconDataAvailable(
614 HistoryService::Handle handle,
615 bool know_favicon,
616 scoped_refptr<RefCountedBytes> data,
617 bool expired,
618 GURL icon_url) {
619 SkBitmap fav_icon;
620 if (know_favicon && data.get() &&
621 PNGDecoder::Decode(&data->data, &fav_icon)) {
622 BookmarkBarNode* node =
623 load_consumer_.GetClientData(
624 profile_->GetHistoryService(Profile::EXPLICIT_ACCESS), handle);
625 DCHECK(node);
626 node->favicon_ = fav_icon;
627 node->favicon_load_handle_ = 0;
628 FavIconLoaded(node);
629 }
630}
631
632void BookmarkBarModel::LoadFavIcon(BookmarkBarNode* node) {
633 if (node->GetType() != history::StarredEntry::URL)
634 return;
635
636 DCHECK(node->GetURL().is_valid());
637 HistoryService* history_service =
638 profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
639 if (!history_service)
640 return;
641
642 HistoryService::Handle handle = history_service->GetFavIconForURL(
643 node->GetURL(), &load_consumer_,
644 NewCallback(this, &BookmarkBarModel::OnFavIconDataAvailable));
645 load_consumer_.SetClientData(history_service, handle, node);
646}
647
648void BookmarkBarModel::CancelPendingFavIconLoadRequests(BookmarkBarNode* node) {
649 if (node->favicon_load_handle_) {
650 HistoryService* history =
651 profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
652 if (history)
653 history->CancelRequest(node->favicon_load_handle_);
654 node->favicon_load_handle_ = 0;
655 }
656}
657
658// static
659bool BookmarkBarModel::MoreRecentlyModified(BookmarkBarNode* n1,
660 BookmarkBarNode* n2) {
661 return n1->date_group_modified_ > n2->date_group_modified_;
662}
663
664void BookmarkBarModel::GetMostRecentlyModifiedGroupNodes(
665 BookmarkBarNode* parent,
666 size_t count,
667 std::vector<BookmarkBarNode*>* nodes) {
668 if (parent->group_id_ && parent->date_group_modified_ > Time()) {
669 if (count == 0) {
670 nodes->push_back(parent);
671 } else {
672 std::vector<BookmarkBarNode*>::iterator i =
673 std::upper_bound(nodes->begin(), nodes->end(), parent,
674 &MoreRecentlyModified);
675 if (nodes->size() < count || i != nodes->end()) {
676 nodes->insert(i, parent);
677 while (nodes->size() > count)
678 nodes->pop_back();
679 }
680 }
681 } // else case, the root node, which we don't care about or imported nodes
682 // (which have a time of 0).
683 for (int i = 0; i < parent->GetChildCount(); ++i) {
684 BookmarkBarNode* child = parent->GetChild(i);
685 if (child->GetType() != history::StarredEntry::URL) {
686 GetMostRecentlyModifiedGroupNodes(child, count, nodes);
687 }
688 }
689}
690
691void BookmarkBarModel::Observe(NotificationType type,
692 const NotificationSource& source,
693 const NotificationDetails& details) {
694 switch (type) {
695 case NOTIFY_STARRED_FAVICON_CHANGED: {
696 // Prevent the observers from getting confused for multiple favicon loads.
697 Details<history::FavIconChangeDetails> favicon_details(details);
698 for (std::set<GURL>::const_iterator i = favicon_details->urls.begin();
699 i != favicon_details->urls.end(); ++i) {
700 BookmarkBarNode* node = GetNodeByURL(*i);
701 if (node) {
702 // Got an updated favicon, for a URL, do a new request.
703 node->InvalidateFavicon();
704 CancelPendingFavIconLoadRequests(node);
705 FOR_EACH_OBSERVER(BookmarkBarModelObserver, observers_,
706 BookmarkNodeChanged(this, node));
707 }
708 }
709 break;
710 }
711
712 default:
713 NOTREACHED();
714 break;
715 }
716}