bookmark_model.cc revision 201ade2fbba22bfb27ae029f4d23fca6ded109a0
1// Copyright (c) 2010 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/browser/bookmarks/bookmark_model.h" 6 7#include "app/l10n_util.h" 8#include "app/l10n_util_collator.h" 9#include "base/callback.h" 10#include "base/scoped_vector.h" 11#include "build/build_config.h" 12#include "chrome/browser/bookmarks/bookmark_index.h" 13#include "chrome/browser/bookmarks/bookmark_utils.h" 14#include "chrome/browser/bookmarks/bookmark_storage.h" 15#include "chrome/browser/browser_process.h" 16#include "chrome/browser/history/history_notifications.h" 17#include "chrome/browser/prefs/pref_service.h" 18#include "chrome/browser/profile.h" 19#include "chrome/common/notification_service.h" 20#include "gfx/codec/png_codec.h" 21#include "grit/generated_resources.h" 22 23using base::Time; 24 25namespace { 26 27// Helper to get a mutable bookmark node. 28static BookmarkNode* AsMutable(const BookmarkNode* node) { 29 return const_cast<BookmarkNode*>(node); 30} 31 32} // anonymous namespace 33 34// BookmarkNode --------------------------------------------------------------- 35 36BookmarkNode::BookmarkNode(const GURL& url) 37 : url_(url) { 38 Initialize(0); 39} 40 41BookmarkNode::BookmarkNode(int64 id, const GURL& url) 42 : url_(url) { 43 Initialize(id); 44} 45 46BookmarkNode::~BookmarkNode() { 47} 48 49void BookmarkNode::Initialize(int64 id) { 50 id_ = id; 51 loaded_favicon_ = false; 52 favicon_load_handle_ = 0; 53 type_ = !url_.is_empty() ? URL : BOOKMARK_BAR; 54 date_added_ = Time::Now(); 55} 56 57void BookmarkNode::InvalidateFavicon() { 58 loaded_favicon_ = false; 59 favicon_ = SkBitmap(); 60} 61 62void BookmarkNode::Reset(const history::StarredEntry& entry) { 63 DCHECK(entry.type != history::StarredEntry::URL || entry.url == url_); 64 65 favicon_ = SkBitmap(); 66 switch (entry.type) { 67 case history::StarredEntry::URL: 68 type_ = BookmarkNode::URL; 69 break; 70 case history::StarredEntry::USER_GROUP: 71 type_ = BookmarkNode::FOLDER; 72 break; 73 case history::StarredEntry::BOOKMARK_BAR: 74 type_ = BookmarkNode::BOOKMARK_BAR; 75 break; 76 case history::StarredEntry::OTHER: 77 type_ = BookmarkNode::OTHER_NODE; 78 break; 79 default: 80 NOTREACHED(); 81 } 82 date_added_ = entry.date_added; 83 date_group_modified_ = entry.date_group_modified; 84 SetTitle(entry.title); 85} 86 87// BookmarkModel -------------------------------------------------------------- 88 89namespace { 90 91// Comparator used when sorting bookmarks. Folders are sorted first, then 92// bookmarks. 93class SortComparator : public std::binary_function<const BookmarkNode*, 94 const BookmarkNode*, 95 bool> { 96 public: 97 explicit SortComparator(icu::Collator* collator) : collator_(collator) { } 98 99 // Returns true if lhs preceeds rhs. 100 bool operator() (const BookmarkNode* n1, const BookmarkNode* n2) { 101 if (n1->type() == n2->type()) { 102 // Types are the same, compare the names. 103 if (!collator_) 104 return n1->GetTitle() < n2->GetTitle(); 105 return l10n_util::CompareStringWithCollator(collator_, 106 UTF16ToWideHack(n1->GetTitle()), UTF16ToWideHack(n2->GetTitle())) == 107 UCOL_LESS; 108 } 109 // Types differ, sort such that folders come first. 110 return n1->is_folder(); 111 } 112 113 private: 114 icu::Collator* collator_; 115}; 116 117} // namespace 118 119BookmarkModel::BookmarkModel(Profile* profile) 120 : profile_(profile), 121 loaded_(false), 122 file_changed_(false), 123 root_(GURL()), 124 bookmark_bar_node_(NULL), 125 other_node_(NULL), 126 next_node_id_(1), 127 observers_(ObserverList<BookmarkModelObserver>::NOTIFY_EXISTING_ONLY), 128 loaded_signal_(TRUE, FALSE) { 129 if (!profile_) { 130 // Profile is null during testing. 131 DoneLoading(CreateLoadDetails()); 132 } 133} 134 135BookmarkModel::~BookmarkModel() { 136 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, 137 BookmarkModelBeingDeleted(this)); 138 139 if (store_) { 140 // The store maintains a reference back to us. We need to tell it we're gone 141 // so that it doesn't try and invoke a method back on us again. 142 store_->BookmarkModelDeleted(); 143 } 144} 145 146void BookmarkModel::Load() { 147 if (store_.get()) { 148 // If the store is non-null, it means Load was already invoked. Load should 149 // only be invoked once. 150 NOTREACHED(); 151 return; 152 } 153 154 // Listen for changes to favicons so that we can update the favicon of the 155 // node appropriately. 156 registrar_.Add(this, NotificationType::FAVICON_CHANGED, 157 Source<Profile>(profile_)); 158 159 // Load the bookmarks. BookmarkStorage notifies us when done. 160 store_ = new BookmarkStorage(profile_, this); 161 store_->LoadBookmarks(CreateLoadDetails()); 162} 163 164const BookmarkNode* BookmarkModel::GetParentForNewNodes() { 165 std::vector<const BookmarkNode*> nodes = 166 bookmark_utils::GetMostRecentlyModifiedGroups(this, 1); 167 return nodes.empty() ? bookmark_bar_node_ : nodes[0]; 168} 169 170void BookmarkModel::Remove(const BookmarkNode* parent, int index) { 171 if (!loaded_ || !IsValidIndex(parent, index, false) || is_root(parent)) { 172 NOTREACHED(); 173 return; 174 } 175 RemoveAndDeleteNode(AsMutable(parent->GetChild(index))); 176} 177 178void BookmarkModel::Move(const BookmarkNode* node, 179 const BookmarkNode* new_parent, 180 int index) { 181 if (!loaded_ || !node || !IsValidIndex(new_parent, index, true) || 182 is_root(new_parent) || is_permanent_node(node)) { 183 NOTREACHED(); 184 return; 185 } 186 187 if (new_parent->HasAncestor(node)) { 188 // Can't make an ancestor of the node be a child of the node. 189 NOTREACHED(); 190 return; 191 } 192 193 SetDateGroupModified(new_parent, Time::Now()); 194 195 const BookmarkNode* old_parent = node->GetParent(); 196 int old_index = old_parent->IndexOfChild(node); 197 198 if (old_parent == new_parent && 199 (index == old_index || index == old_index + 1)) { 200 // Node is already in this position, nothing to do. 201 return; 202 } 203 204 if (old_parent == new_parent && index > old_index) 205 index--; 206 BookmarkNode* mutable_new_parent = AsMutable(new_parent); 207 mutable_new_parent->Add(index, AsMutable(node)); 208 209 if (store_.get()) 210 store_->ScheduleSave(); 211 212 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, 213 BookmarkNodeMoved(this, old_parent, old_index, 214 new_parent, index)); 215} 216 217void BookmarkModel::Copy(const BookmarkNode* node, 218 const BookmarkNode* new_parent, 219 int index) { 220 if (!loaded_ || !node || !IsValidIndex(new_parent, index, true) || 221 is_root(new_parent) || is_permanent_node(node)) { 222 NOTREACHED(); 223 return; 224 } 225 226 if (new_parent->HasAncestor(node)) { 227 // Can't make an ancestor of the node be a child of the node. 228 NOTREACHED(); 229 return; 230 } 231 232 SetDateGroupModified(new_parent, Time::Now()); 233 BookmarkNodeData drag_data_(node); 234 std::vector<BookmarkNodeData::Element> elements(drag_data_.elements); 235 // CloneBookmarkNode will use BookmarkModel methods to do the job, so we 236 // don't need to send notifications here. 237 bookmark_utils::CloneBookmarkNode(this, elements, new_parent, index); 238 239 if (store_.get()) 240 store_->ScheduleSave(); 241} 242 243const SkBitmap& BookmarkModel::GetFavIcon(const BookmarkNode* node) { 244 DCHECK(node); 245 if (!node->is_favicon_loaded()) { 246 BookmarkNode* mutable_node = AsMutable(node); 247 mutable_node->set_favicon_loaded(true); 248 LoadFavIcon(mutable_node); 249 } 250 return node->favicon(); 251} 252 253void BookmarkModel::SetTitle(const BookmarkNode* node, const string16& title) { 254 if (!node) { 255 NOTREACHED(); 256 return; 257 } 258 if (node->GetTitle() == title) 259 return; 260 261 if (node == bookmark_bar_node_ || node == other_node_) { 262 NOTREACHED(); 263 return; 264 } 265 266 // The title index doesn't support changing the title, instead we remove then 267 // add it back. 268 index_->Remove(node); 269 AsMutable(node)->SetTitle(title); 270 index_->Add(node); 271 272 if (store_.get()) 273 store_->ScheduleSave(); 274 275 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, 276 BookmarkNodeChanged(this, node)); 277} 278 279void BookmarkModel::SetURL(const BookmarkNode* node, const GURL& url) { 280 if (!node) { 281 NOTREACHED(); 282 return; 283 } 284 285 // We cannot change the URL of a folder. 286 if (node->is_folder()) { 287 NOTREACHED(); 288 return; 289 } 290 291 if (url == node->GetURL()) 292 return; 293 294 AsMutable(node)->InvalidateFavicon(); 295 CancelPendingFavIconLoadRequests(AsMutable(node)); 296 297 { 298 AutoLock url_lock(url_lock_); 299 NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.find( 300 AsMutable(node)); 301 DCHECK(i != nodes_ordered_by_url_set_.end()); 302 // i points to the first node with the URL, advance until we find the 303 // node we're removing. 304 while (*i != node) 305 ++i; 306 nodes_ordered_by_url_set_.erase(i); 307 308 AsMutable(node)->SetURL(url); 309 nodes_ordered_by_url_set_.insert(AsMutable(node)); 310 } 311 312 if (store_.get()) 313 store_->ScheduleSave(); 314 315 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, 316 BookmarkNodeChanged(this, node)); 317} 318 319bool BookmarkModel::IsLoaded() { 320 return loaded_; 321} 322 323void BookmarkModel::GetNodesByURL(const GURL& url, 324 std::vector<const BookmarkNode*>* nodes) { 325 AutoLock url_lock(url_lock_); 326 BookmarkNode tmp_node(url); 327 NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.find(&tmp_node); 328 while (i != nodes_ordered_by_url_set_.end() && (*i)->GetURL() == url) { 329 nodes->push_back(*i); 330 ++i; 331 } 332} 333 334const BookmarkNode* BookmarkModel::GetMostRecentlyAddedNodeForURL( 335 const GURL& url) { 336 std::vector<const BookmarkNode*> nodes; 337 GetNodesByURL(url, &nodes); 338 if (nodes.empty()) 339 return NULL; 340 341 std::sort(nodes.begin(), nodes.end(), &bookmark_utils::MoreRecentlyAdded); 342 return nodes.front(); 343} 344 345void BookmarkModel::GetBookmarks(std::vector<GURL>* urls) { 346 AutoLock url_lock(url_lock_); 347 const GURL* last_url = NULL; 348 for (NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.begin(); 349 i != nodes_ordered_by_url_set_.end(); ++i) { 350 const GURL* url = &((*i)->GetURL()); 351 // Only add unique URLs. 352 if (!last_url || *url != *last_url) 353 urls->push_back(*url); 354 last_url = url; 355 } 356} 357 358bool BookmarkModel::HasBookmarks() { 359 AutoLock url_lock(url_lock_); 360 return !nodes_ordered_by_url_set_.empty(); 361} 362 363bool BookmarkModel::IsBookmarked(const GURL& url) { 364 AutoLock url_lock(url_lock_); 365 return IsBookmarkedNoLock(url); 366} 367 368const BookmarkNode* BookmarkModel::GetNodeByID(int64 id) { 369 // TODO(sky): TreeNode needs a method that visits all nodes using a predicate. 370 return GetNodeByID(&root_, id); 371} 372 373const BookmarkNode* BookmarkModel::AddGroup(const BookmarkNode* parent, 374 int index, 375 const string16& title) { 376 if (!loaded_ || parent == &root_ || !IsValidIndex(parent, index, true)) { 377 // Can't add to the root. 378 NOTREACHED(); 379 return NULL; 380 } 381 382 BookmarkNode* new_node = new BookmarkNode(generate_next_node_id(), 383 GURL()); 384 new_node->set_date_group_modified(Time::Now()); 385 new_node->SetTitle(title); 386 new_node->set_type(BookmarkNode::FOLDER); 387 388 return AddNode(AsMutable(parent), index, new_node, false); 389} 390 391const BookmarkNode* BookmarkModel::AddURL(const BookmarkNode* parent, 392 int index, 393 const string16& title, 394 const GURL& url) { 395 return AddURLWithCreationTime(parent, index, title, url, Time::Now()); 396} 397 398const BookmarkNode* BookmarkModel::AddURLWithCreationTime( 399 const BookmarkNode* parent, 400 int index, 401 const string16& title, 402 const GURL& url, 403 const Time& creation_time) { 404 if (!loaded_ || !url.is_valid() || is_root(parent) || 405 !IsValidIndex(parent, index, true)) { 406 NOTREACHED(); 407 return NULL; 408 } 409 410 bool was_bookmarked = IsBookmarked(url); 411 412 SetDateGroupModified(parent, creation_time); 413 414 BookmarkNode* new_node = new BookmarkNode(generate_next_node_id(), url); 415 new_node->SetTitle(title); 416 new_node->set_date_added(creation_time); 417 new_node->set_type(BookmarkNode::URL); 418 419 { 420 // Only hold the lock for the duration of the insert. 421 AutoLock url_lock(url_lock_); 422 nodes_ordered_by_url_set_.insert(new_node); 423 } 424 425 return AddNode(AsMutable(parent), index, new_node, was_bookmarked); 426} 427 428void BookmarkModel::SortChildren(const BookmarkNode* parent) { 429 if (!parent || !parent->is_folder() || is_root(parent) || 430 parent->GetChildCount() <= 1) { 431 return; 432 } 433 434 UErrorCode error = U_ZERO_ERROR; 435 scoped_ptr<icu::Collator> collator( 436 icu::Collator::createInstance( 437 icu::Locale(g_browser_process->GetApplicationLocale().c_str()), 438 error)); 439 if (U_FAILURE(error)) 440 collator.reset(NULL); 441 BookmarkNode* mutable_parent = AsMutable(parent); 442 std::sort(mutable_parent->children().begin(), 443 mutable_parent->children().end(), 444 SortComparator(collator.get())); 445 446 if (store_.get()) 447 store_->ScheduleSave(); 448 449 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, 450 BookmarkNodeChildrenReordered(this, parent)); 451} 452 453void BookmarkModel::SetURLStarred(const GURL& url, 454 const string16& title, 455 bool is_starred) { 456 std::vector<const BookmarkNode*> bookmarks; 457 GetNodesByURL(url, &bookmarks); 458 bool bookmarks_exist = !bookmarks.empty(); 459 if (is_starred == bookmarks_exist) 460 return; // Nothing to do, state already matches. 461 462 if (is_starred) { 463 // Create a bookmark. 464 const BookmarkNode* parent = GetParentForNewNodes(); 465 AddURL(parent, parent->GetChildCount(), title, url); 466 } else { 467 // Remove all the bookmarks. 468 for (size_t i = 0; i < bookmarks.size(); ++i) { 469 const BookmarkNode* node = bookmarks[i]; 470 Remove(node->GetParent(), node->GetParent()->IndexOfChild(node)); 471 } 472 } 473} 474 475void BookmarkModel::SetDateGroupModified(const BookmarkNode* parent, 476 const Time time) { 477 DCHECK(parent); 478 AsMutable(parent)->set_date_group_modified(time); 479 480 if (store_.get()) 481 store_->ScheduleSave(); 482} 483 484void BookmarkModel::ResetDateGroupModified(const BookmarkNode* node) { 485 SetDateGroupModified(node, Time()); 486} 487 488void BookmarkModel::GetBookmarksWithTitlesMatching( 489 const string16& text, 490 size_t max_count, 491 std::vector<bookmark_utils::TitleMatch>* matches) { 492 if (!loaded_) 493 return; 494 495 index_->GetBookmarksWithTitlesMatching(text, max_count, matches); 496} 497 498void BookmarkModel::ClearStore() { 499 registrar_.RemoveAll(); 500 store_ = NULL; 501} 502 503bool BookmarkModel::IsBookmarkedNoLock(const GURL& url) { 504 BookmarkNode tmp_node(url); 505 return (nodes_ordered_by_url_set_.find(&tmp_node) != 506 nodes_ordered_by_url_set_.end()); 507} 508 509void BookmarkModel::FavIconLoaded(const BookmarkNode* node) { 510 // Send out notification to the observer. 511 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, 512 BookmarkNodeFavIconLoaded(this, node)); 513} 514 515void BookmarkModel::RemoveNode(BookmarkNode* node, 516 std::set<GURL>* removed_urls) { 517 if (!loaded_ || !node || is_permanent_node(node)) { 518 NOTREACHED(); 519 return; 520 } 521 522 if (node->type() == BookmarkNode::URL) { 523 // NOTE: this is called in such a way that url_lock_ is already held. As 524 // such, this doesn't explicitly grab the lock. 525 NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.find(node); 526 DCHECK(i != nodes_ordered_by_url_set_.end()); 527 // i points to the first node with the URL, advance until we find the 528 // node we're removing. 529 while (*i != node) 530 ++i; 531 nodes_ordered_by_url_set_.erase(i); 532 removed_urls->insert(node->GetURL()); 533 534 index_->Remove(node); 535 } 536 537 CancelPendingFavIconLoadRequests(node); 538 539 // Recurse through children. 540 for (int i = node->GetChildCount() - 1; i >= 0; --i) 541 RemoveNode(node->GetChild(i), removed_urls); 542} 543 544void BookmarkModel::DoneLoading( 545 BookmarkLoadDetails* details_delete_me) { 546 DCHECK(details_delete_me); 547 scoped_ptr<BookmarkLoadDetails> details(details_delete_me); 548 if (loaded_) { 549 // We should only ever be loaded once. 550 NOTREACHED(); 551 return; 552 } 553 554 next_node_id_ = details->max_id(); 555 if (details->computed_checksum() != details->stored_checksum()) 556 SetFileChanged(); 557 if (details->computed_checksum() != details->stored_checksum() || 558 details->ids_reassigned()) { 559 // If bookmarks file changed externally, the IDs may have changed 560 // externally. In that case, the decoder may have reassigned IDs to make 561 // them unique. So when the file has changed externally, we should save the 562 // bookmarks file to persist new IDs. 563 if (store_.get()) 564 store_->ScheduleSave(); 565 } 566 bookmark_bar_node_ = details->release_bb_node(); 567 other_node_ = details->release_other_folder_node(); 568 index_.reset(details->release_index()); 569 570 // WARNING: order is important here, various places assume bookmark bar then 571 // other node. 572 root_.Add(0, bookmark_bar_node_); 573 root_.Add(1, other_node_); 574 575 { 576 AutoLock url_lock(url_lock_); 577 // Update nodes_ordered_by_url_set_ from the nodes. 578 PopulateNodesByURL(&root_); 579 } 580 581 loaded_ = true; 582 583 loaded_signal_.Signal(); 584 585 // Notify our direct observers. 586 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, Loaded(this)); 587 588 // And generic notification. 589 NotificationService::current()->Notify( 590 NotificationType::BOOKMARK_MODEL_LOADED, 591 Source<Profile>(profile_), 592 NotificationService::NoDetails()); 593} 594 595void BookmarkModel::RemoveAndDeleteNode(BookmarkNode* delete_me) { 596 scoped_ptr<BookmarkNode> node(delete_me); 597 598 BookmarkNode* parent = AsMutable(node->GetParent()); 599 DCHECK(parent); 600 int index = parent->IndexOfChild(node.get()); 601 parent->Remove(index); 602 history::URLsStarredDetails details(false); 603 { 604 AutoLock url_lock(url_lock_); 605 RemoveNode(node.get(), &details.changed_urls); 606 607 // RemoveNode adds an entry to changed_urls for each node of type URL. As we 608 // allow duplicates we need to remove any entries that are still bookmarked. 609 for (std::set<GURL>::iterator i = details.changed_urls.begin(); 610 i != details.changed_urls.end(); ) { 611 if (IsBookmarkedNoLock(*i)) { 612 // When we erase the iterator pointing at the erasee is 613 // invalidated, so using i++ here within the "erase" call is 614 // important as it advances the iterator before passing the 615 // old value through to erase. 616 details.changed_urls.erase(i++); 617 } else { 618 ++i; 619 } 620 } 621 } 622 623 if (store_.get()) 624 store_->ScheduleSave(); 625 626 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, 627 BookmarkNodeRemoved(this, parent, index, node.get())); 628 629 if (details.changed_urls.empty()) { 630 // No point in sending out notification if the starred state didn't change. 631 return; 632 } 633 634 if (profile_) { 635 HistoryService* history = 636 profile_->GetHistoryService(Profile::EXPLICIT_ACCESS); 637 if (history) 638 history->URLsNoLongerBookmarked(details.changed_urls); 639 } 640 641 NotificationService::current()->Notify( 642 NotificationType::URLS_STARRED, 643 Source<Profile>(profile_), 644 Details<history::URLsStarredDetails>(&details)); 645} 646 647void BookmarkModel::BeginImportMode() { 648 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, 649 BookmarkImportBeginning(this)); 650} 651 652void BookmarkModel::EndImportMode() { 653 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, 654 BookmarkImportEnding(this)); 655} 656 657BookmarkNode* BookmarkModel::AddNode(BookmarkNode* parent, 658 int index, 659 BookmarkNode* node, 660 bool was_bookmarked) { 661 parent->Add(index, node); 662 663 if (store_.get()) 664 store_->ScheduleSave(); 665 666 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, 667 BookmarkNodeAdded(this, parent, index)); 668 669 index_->Add(node); 670 671 if (node->type() == BookmarkNode::URL && !was_bookmarked) { 672 history::URLsStarredDetails details(true); 673 details.changed_urls.insert(node->GetURL()); 674 NotificationService::current()->Notify( 675 NotificationType::URLS_STARRED, 676 Source<Profile>(profile_), 677 Details<history::URLsStarredDetails>(&details)); 678 } 679 return node; 680} 681 682void BookmarkModel::BlockTillLoaded() { 683 loaded_signal_.Wait(); 684} 685 686const BookmarkNode* BookmarkModel::GetNodeByID(const BookmarkNode* node, 687 int64 id) { 688 if (node->id() == id) 689 return node; 690 691 for (int i = 0, child_count = node->GetChildCount(); i < child_count; ++i) { 692 const BookmarkNode* result = GetNodeByID(node->GetChild(i), id); 693 if (result) 694 return result; 695 } 696 return NULL; 697} 698 699bool BookmarkModel::IsValidIndex(const BookmarkNode* parent, 700 int index, 701 bool allow_end) { 702 return (parent && parent->is_folder() && 703 (index >= 0 && (index < parent->GetChildCount() || 704 (allow_end && index == parent->GetChildCount())))); 705} 706 707BookmarkNode* BookmarkModel::CreateBookmarkNode() { 708 history::StarredEntry entry; 709 entry.type = history::StarredEntry::BOOKMARK_BAR; 710 return CreateRootNodeFromStarredEntry(entry); 711} 712 713BookmarkNode* BookmarkModel::CreateOtherBookmarksNode() { 714 history::StarredEntry entry; 715 entry.type = history::StarredEntry::OTHER; 716 return CreateRootNodeFromStarredEntry(entry); 717} 718 719BookmarkNode* BookmarkModel::CreateRootNodeFromStarredEntry( 720 const history::StarredEntry& entry) { 721 DCHECK(entry.type == history::StarredEntry::BOOKMARK_BAR || 722 entry.type == history::StarredEntry::OTHER); 723 BookmarkNode* node = new BookmarkNode(generate_next_node_id(), GURL()); 724 node->Reset(entry); 725 if (entry.type == history::StarredEntry::BOOKMARK_BAR) { 726 node->SetTitle(l10n_util::GetStringUTF16(IDS_BOOMARK_BAR_FOLDER_NAME)); 727 } else { 728 node->SetTitle( 729 l10n_util::GetStringUTF16(IDS_BOOMARK_BAR_OTHER_FOLDER_NAME)); 730 } 731 return node; 732} 733 734void BookmarkModel::OnFavIconDataAvailable( 735 FaviconService::Handle handle, 736 bool know_favicon, 737 scoped_refptr<RefCountedMemory> data, 738 bool expired, 739 GURL icon_url) { 740 SkBitmap fav_icon; 741 BookmarkNode* node = 742 load_consumer_.GetClientData( 743 profile_->GetFaviconService(Profile::EXPLICIT_ACCESS), handle); 744 DCHECK(node); 745 node->set_favicon_load_handle(0); 746 if (know_favicon && data.get() && data->size() && 747 gfx::PNGCodec::Decode(data->front(), data->size(), &fav_icon)) { 748 node->set_favicon(fav_icon); 749 FavIconLoaded(node); 750 } 751} 752 753void BookmarkModel::LoadFavIcon(BookmarkNode* node) { 754 if (node->type() != BookmarkNode::URL) 755 return; 756 757 DCHECK(node->GetURL().is_valid()); 758 FaviconService* favicon_service = 759 profile_->GetFaviconService(Profile::EXPLICIT_ACCESS); 760 if (!favicon_service) 761 return; 762 FaviconService::Handle handle = favicon_service->GetFaviconForURL( 763 node->GetURL(), &load_consumer_, 764 NewCallback(this, &BookmarkModel::OnFavIconDataAvailable)); 765 load_consumer_.SetClientData(favicon_service, handle, node); 766 node->set_favicon_load_handle(handle); 767} 768 769void BookmarkModel::CancelPendingFavIconLoadRequests(BookmarkNode* node) { 770 if (node->favicon_load_handle()) { 771 FaviconService* favicon_service = 772 profile_->GetFaviconService(Profile::EXPLICIT_ACCESS); 773 if (favicon_service) 774 favicon_service->CancelRequest(node->favicon_load_handle()); 775 node->set_favicon_load_handle(0); 776 } 777} 778 779void BookmarkModel::Observe(NotificationType type, 780 const NotificationSource& source, 781 const NotificationDetails& details) { 782 switch (type.value) { 783 case NotificationType::FAVICON_CHANGED: { 784 // Prevent the observers from getting confused for multiple favicon loads. 785 Details<history::FavIconChangeDetails> favicon_details(details); 786 for (std::set<GURL>::const_iterator i = favicon_details->urls.begin(); 787 i != favicon_details->urls.end(); ++i) { 788 std::vector<const BookmarkNode*> nodes; 789 GetNodesByURL(*i, &nodes); 790 for (size_t i = 0; i < nodes.size(); ++i) { 791 // Got an updated favicon, for a URL, do a new request. 792 BookmarkNode* node = AsMutable(nodes[i]); 793 node->InvalidateFavicon(); 794 CancelPendingFavIconLoadRequests(node); 795 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_, 796 BookmarkNodeChanged(this, node)); 797 } 798 } 799 break; 800 } 801 802 default: 803 NOTREACHED(); 804 break; 805 } 806} 807 808void BookmarkModel::PopulateNodesByURL(BookmarkNode* node) { 809 // NOTE: this is called with url_lock_ already held. As such, this doesn't 810 // explicitly grab the lock. 811 if (node->is_url()) 812 nodes_ordered_by_url_set_.insert(node); 813 for (int i = 0; i < node->GetChildCount(); ++i) 814 PopulateNodesByURL(node->GetChild(i)); 815} 816 817int64 BookmarkModel::generate_next_node_id() { 818 return next_node_id_++; 819} 820 821void BookmarkModel::SetFileChanged() { 822 file_changed_ = true; 823} 824 825BookmarkLoadDetails* BookmarkModel::CreateLoadDetails() { 826 BookmarkNode* bb_node = CreateBookmarkNode(); 827 BookmarkNode* other_folder_node = CreateOtherBookmarksNode(); 828 return new BookmarkLoadDetails( 829 bb_node, other_folder_node, new BookmarkIndex(profile()), next_node_id_); 830} 831