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