Clean up extension confirmation prompts and make them consistent between Views and...
[chromium-blink-merge.git] / components / bookmarks / browser / bookmark_model.cc
blobcf969748c6fb5b5751c50bfade02a6d224f790d7
1 // Copyright 2014 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.
5 #include "components/bookmarks/browser/bookmark_model.h"
7 #include <algorithm>
8 #include <functional>
10 #include "base/bind.h"
11 #include "base/bind_helpers.h"
12 #include "base/i18n/string_compare.h"
13 #include "base/logging.h"
14 #include "base/macros.h"
15 #include "base/profiler/scoped_tracker.h"
16 #include "base/strings/string_util.h"
17 #include "components/bookmarks/browser/bookmark_expanded_state_tracker.h"
18 #include "components/bookmarks/browser/bookmark_index.h"
19 #include "components/bookmarks/browser/bookmark_match.h"
20 #include "components/bookmarks/browser/bookmark_model_observer.h"
21 #include "components/bookmarks/browser/bookmark_node_data.h"
22 #include "components/bookmarks/browser/bookmark_storage.h"
23 #include "components/bookmarks/browser/bookmark_utils.h"
24 #include "components/favicon_base/favicon_types.h"
25 #include "grit/components_strings.h"
26 #include "ui/base/l10n/l10n_util.h"
27 #include "ui/gfx/favicon_size.h"
29 using base::Time;
31 namespace bookmarks {
33 namespace {
35 // Helper to get a mutable bookmark node.
36 BookmarkNode* AsMutable(const BookmarkNode* node) {
37 return const_cast<BookmarkNode*>(node);
40 // Helper to get a mutable permanent bookmark node.
41 BookmarkPermanentNode* AsMutable(const BookmarkPermanentNode* node) {
42 return const_cast<BookmarkPermanentNode*>(node);
45 // Comparator used when sorting permanent nodes. Nodes that are initially
46 // visible are sorted before nodes that are initially hidden.
47 class VisibilityComparator
48 : public std::binary_function<const BookmarkPermanentNode*,
49 const BookmarkPermanentNode*,
50 bool> {
51 public:
52 explicit VisibilityComparator(BookmarkClient* client) : client_(client) {}
54 // Returns true if |n1| preceeds |n2|.
55 bool operator()(const BookmarkPermanentNode* n1,
56 const BookmarkPermanentNode* n2) {
57 bool n1_visible = client_->IsPermanentNodeVisible(n1);
58 bool n2_visible = client_->IsPermanentNodeVisible(n2);
59 return n1_visible != n2_visible && n1_visible;
62 private:
63 BookmarkClient* client_;
66 // Comparator used when sorting bookmarks. Folders are sorted first, then
67 // bookmarks.
68 class SortComparator : public std::binary_function<const BookmarkNode*,
69 const BookmarkNode*,
70 bool> {
71 public:
72 explicit SortComparator(icu::Collator* collator) : collator_(collator) {}
74 // Returns true if |n1| preceeds |n2|.
75 bool operator()(const BookmarkNode* n1, const BookmarkNode* n2) {
76 if (n1->type() == n2->type()) {
77 // Types are the same, compare the names.
78 if (!collator_)
79 return n1->GetTitle() < n2->GetTitle();
80 return base::i18n::CompareString16WithCollator(
81 *collator_, n1->GetTitle(), n2->GetTitle()) == UCOL_LESS;
83 // Types differ, sort such that folders come first.
84 return n1->is_folder();
87 private:
88 icu::Collator* collator_;
91 } // namespace
93 // BookmarkModel --------------------------------------------------------------
95 BookmarkModel::BookmarkModel(BookmarkClient* client)
96 : client_(client),
97 loaded_(false),
98 root_(GURL()),
99 bookmark_bar_node_(NULL),
100 other_node_(NULL),
101 mobile_node_(NULL),
102 next_node_id_(1),
103 observers_(
104 base::ObserverList<BookmarkModelObserver>::NOTIFY_EXISTING_ONLY),
105 loaded_signal_(true, false),
106 extensive_changes_(0) {
107 DCHECK(client_);
110 BookmarkModel::~BookmarkModel() {
111 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
112 BookmarkModelBeingDeleted(this));
114 if (store_.get()) {
115 // The store maintains a reference back to us. We need to tell it we're gone
116 // so that it doesn't try and invoke a method back on us again.
117 store_->BookmarkModelDeleted();
121 void BookmarkModel::Shutdown() {
122 if (loaded_)
123 return;
125 // See comment in HistoryService::ShutdownOnUIThread where this is invoked for
126 // details. It is also called when the BookmarkModel is deleted.
127 loaded_signal_.Signal();
130 void BookmarkModel::Load(
131 PrefService* pref_service,
132 const std::string& accept_languages,
133 const base::FilePath& profile_path,
134 const scoped_refptr<base::SequencedTaskRunner>& io_task_runner,
135 const scoped_refptr<base::SequencedTaskRunner>& ui_task_runner) {
136 if (store_.get()) {
137 // If the store is non-null, it means Load was already invoked. Load should
138 // only be invoked once.
139 NOTREACHED();
140 return;
143 expanded_state_tracker_.reset(
144 new BookmarkExpandedStateTracker(this, pref_service));
146 // Load the bookmarks. BookmarkStorage notifies us when done.
147 store_ .reset(new BookmarkStorage(this, profile_path, io_task_runner.get()));
148 store_->LoadBookmarks(CreateLoadDetails(accept_languages), ui_task_runner);
151 const BookmarkNode* BookmarkModel::GetParentForNewNodes() {
152 std::vector<const BookmarkNode*> nodes =
153 GetMostRecentlyModifiedUserFolders(this, 1);
154 DCHECK(!nodes.empty()); // This list is always padded with default folders.
155 return nodes[0];
158 void BookmarkModel::AddObserver(BookmarkModelObserver* observer) {
159 observers_.AddObserver(observer);
162 void BookmarkModel::RemoveObserver(BookmarkModelObserver* observer) {
163 observers_.RemoveObserver(observer);
166 void BookmarkModel::BeginExtensiveChanges() {
167 if (++extensive_changes_ == 1) {
168 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
169 ExtensiveBookmarkChangesBeginning(this));
173 void BookmarkModel::EndExtensiveChanges() {
174 --extensive_changes_;
175 DCHECK_GE(extensive_changes_, 0);
176 if (extensive_changes_ == 0) {
177 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
178 ExtensiveBookmarkChangesEnded(this));
182 void BookmarkModel::BeginGroupedChanges() {
183 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
184 GroupedBookmarkChangesBeginning(this));
187 void BookmarkModel::EndGroupedChanges() {
188 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
189 GroupedBookmarkChangesEnded(this));
192 void BookmarkModel::Remove(const BookmarkNode* node) {
193 DCHECK(loaded_);
194 DCHECK(node);
195 DCHECK(!is_root_node(node));
196 RemoveAndDeleteNode(AsMutable(node));
199 void BookmarkModel::RemoveAllUserBookmarks() {
200 std::set<GURL> removed_urls;
201 ScopedVector<BookmarkNode> removed_nodes;
203 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
204 OnWillRemoveAllUserBookmarks(this));
206 BeginExtensiveChanges();
207 // Skip deleting permanent nodes. Permanent bookmark nodes are the root and
208 // its immediate children. For removing all non permanent nodes just remove
209 // all children of non-root permanent nodes.
211 base::AutoLock url_lock(url_lock_);
212 for (int i = 0; i < root_.child_count(); ++i) {
213 BookmarkNode* permanent_node = root_.GetChild(i);
215 if (!client_->CanBeEditedByUser(permanent_node))
216 continue;
218 for (int j = permanent_node->child_count() - 1; j >= 0; --j) {
219 BookmarkNode* child_node = permanent_node->GetChild(j);
220 removed_nodes.push_back(child_node);
221 RemoveNodeAndGetRemovedUrls(child_node, &removed_urls);
225 EndExtensiveChanges();
226 if (store_.get())
227 store_->ScheduleSave();
229 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
230 BookmarkAllUserNodesRemoved(this, removed_urls));
233 void BookmarkModel::Move(const BookmarkNode* node,
234 const BookmarkNode* new_parent,
235 int index) {
236 if (!loaded_ || !node || !IsValidIndex(new_parent, index, true) ||
237 is_root_node(new_parent) || is_permanent_node(node)) {
238 NOTREACHED();
239 return;
242 if (new_parent->HasAncestor(node)) {
243 // Can't make an ancestor of the node be a child of the node.
244 NOTREACHED();
245 return;
248 const BookmarkNode* old_parent = node->parent();
249 int old_index = old_parent->GetIndexOf(node);
251 if (old_parent == new_parent &&
252 (index == old_index || index == old_index + 1)) {
253 // Node is already in this position, nothing to do.
254 return;
257 SetDateFolderModified(new_parent, Time::Now());
259 if (old_parent == new_parent && index > old_index)
260 index--;
261 BookmarkNode* mutable_new_parent = AsMutable(new_parent);
262 mutable_new_parent->Add(AsMutable(node), index);
264 if (store_.get())
265 store_->ScheduleSave();
267 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
268 BookmarkNodeMoved(this, old_parent, old_index,
269 new_parent, index));
272 void BookmarkModel::Copy(const BookmarkNode* node,
273 const BookmarkNode* new_parent,
274 int index) {
275 if (!loaded_ || !node || !IsValidIndex(new_parent, index, true) ||
276 is_root_node(new_parent) || is_permanent_node(node)) {
277 NOTREACHED();
278 return;
281 if (new_parent->HasAncestor(node)) {
282 // Can't make an ancestor of the node be a child of the node.
283 NOTREACHED();
284 return;
287 SetDateFolderModified(new_parent, Time::Now());
288 BookmarkNodeData drag_data(node);
289 // CloneBookmarkNode will use BookmarkModel methods to do the job, so we
290 // don't need to send notifications here.
291 CloneBookmarkNode(this, drag_data.elements, new_parent, index, true);
293 if (store_.get())
294 store_->ScheduleSave();
297 const gfx::Image& BookmarkModel::GetFavicon(const BookmarkNode* node) {
298 DCHECK(node);
299 if (node->favicon_state() == BookmarkNode::INVALID_FAVICON) {
300 BookmarkNode* mutable_node = AsMutable(node);
301 LoadFavicon(mutable_node,
302 client_->PreferTouchIcon() ? favicon_base::TOUCH_ICON
303 : favicon_base::FAVICON);
305 return node->favicon();
308 favicon_base::IconType BookmarkModel::GetFaviconType(const BookmarkNode* node) {
309 DCHECK(node);
310 return node->favicon_type();
313 void BookmarkModel::SetTitle(const BookmarkNode* node,
314 const base::string16& title) {
315 DCHECK(node);
317 if (node->GetTitle() == title)
318 return;
320 if (is_permanent_node(node) && !client_->CanSetPermanentNodeTitle(node)) {
321 NOTREACHED();
322 return;
325 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
326 OnWillChangeBookmarkNode(this, node));
328 // The title index doesn't support changing the title, instead we remove then
329 // add it back.
330 index_->Remove(node);
331 AsMutable(node)->SetTitle(title);
332 index_->Add(node);
334 if (store_.get())
335 store_->ScheduleSave();
337 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
338 BookmarkNodeChanged(this, node));
341 void BookmarkModel::SetURL(const BookmarkNode* node, const GURL& url) {
342 DCHECK(node && !node->is_folder());
344 if (node->url() == url)
345 return;
347 BookmarkNode* mutable_node = AsMutable(node);
348 mutable_node->InvalidateFavicon();
349 CancelPendingFaviconLoadRequests(mutable_node);
351 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
352 OnWillChangeBookmarkNode(this, node));
355 base::AutoLock url_lock(url_lock_);
356 RemoveNodeFromInternalMaps(mutable_node);
357 mutable_node->set_url(url);
358 AddNodeToInternalMaps(mutable_node);
361 if (store_.get())
362 store_->ScheduleSave();
364 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
365 BookmarkNodeChanged(this, node));
368 void BookmarkModel::SetNodeMetaInfo(const BookmarkNode* node,
369 const std::string& key,
370 const std::string& value) {
371 std::string old_value;
372 if (node->GetMetaInfo(key, &old_value) && old_value == value)
373 return;
375 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
376 OnWillChangeBookmarkMetaInfo(this, node));
378 if (AsMutable(node)->SetMetaInfo(key, value) && store_.get())
379 store_->ScheduleSave();
381 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
382 BookmarkMetaInfoChanged(this, node));
385 void BookmarkModel::SetNodeMetaInfoMap(
386 const BookmarkNode* node,
387 const BookmarkNode::MetaInfoMap& meta_info_map) {
388 const BookmarkNode::MetaInfoMap* old_meta_info_map = node->GetMetaInfoMap();
389 if ((!old_meta_info_map && meta_info_map.empty()) ||
390 (old_meta_info_map && meta_info_map == *old_meta_info_map))
391 return;
393 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
394 OnWillChangeBookmarkMetaInfo(this, node));
396 AsMutable(node)->SetMetaInfoMap(meta_info_map);
397 if (store_.get())
398 store_->ScheduleSave();
400 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
401 BookmarkMetaInfoChanged(this, node));
404 void BookmarkModel::DeleteNodeMetaInfo(const BookmarkNode* node,
405 const std::string& key) {
406 const BookmarkNode::MetaInfoMap* meta_info_map = node->GetMetaInfoMap();
407 if (!meta_info_map || meta_info_map->find(key) == meta_info_map->end())
408 return;
410 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
411 OnWillChangeBookmarkMetaInfo(this, node));
413 if (AsMutable(node)->DeleteMetaInfo(key) && store_.get())
414 store_->ScheduleSave();
416 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
417 BookmarkMetaInfoChanged(this, node));
420 void BookmarkModel::AddNonClonedKey(const std::string& key) {
421 non_cloned_keys_.insert(key);
424 void BookmarkModel::SetNodeSyncTransactionVersion(
425 const BookmarkNode* node,
426 int64 sync_transaction_version) {
427 DCHECK(client_->CanSyncNode(node));
429 if (sync_transaction_version == node->sync_transaction_version())
430 return;
432 AsMutable(node)->set_sync_transaction_version(sync_transaction_version);
433 if (store_.get())
434 store_->ScheduleSave();
437 void BookmarkModel::OnFaviconChanged(const std::set<GURL>& urls) {
438 // Ignore events if |Load| has not been called yet.
439 if (!store_)
440 return;
442 // Prevent the observers from getting confused for multiple favicon loads.
443 for (std::set<GURL>::const_iterator i = urls.begin(); i != urls.end(); ++i) {
444 std::vector<const BookmarkNode*> nodes;
445 GetNodesByURL(*i, &nodes);
446 for (size_t i = 0; i < nodes.size(); ++i) {
447 // Got an updated favicon, for a URL, do a new request.
448 BookmarkNode* node = AsMutable(nodes[i]);
449 node->InvalidateFavicon();
450 CancelPendingFaviconLoadRequests(node);
451 FOR_EACH_OBSERVER(BookmarkModelObserver,
452 observers_,
453 BookmarkNodeFaviconChanged(this, node));
458 void BookmarkModel::SetDateAdded(const BookmarkNode* node, Time date_added) {
459 DCHECK(node && !is_permanent_node(node));
461 if (node->date_added() == date_added)
462 return;
464 AsMutable(node)->set_date_added(date_added);
466 // Syncing might result in dates newer than the folder's last modified date.
467 if (date_added > node->parent()->date_folder_modified()) {
468 // Will trigger store_->ScheduleSave().
469 SetDateFolderModified(node->parent(), date_added);
470 } else if (store_.get()) {
471 store_->ScheduleSave();
475 void BookmarkModel::GetNodesByURL(const GURL& url,
476 std::vector<const BookmarkNode*>* nodes) {
477 base::AutoLock url_lock(url_lock_);
478 BookmarkNode tmp_node(url);
479 NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.find(&tmp_node);
480 while (i != nodes_ordered_by_url_set_.end() && (*i)->url() == url) {
481 nodes->push_back(*i);
482 ++i;
486 const BookmarkNode* BookmarkModel::GetMostRecentlyAddedUserNodeForURL(
487 const GURL& url) {
488 std::vector<const BookmarkNode*> nodes;
489 GetNodesByURL(url, &nodes);
490 std::sort(nodes.begin(), nodes.end(), &MoreRecentlyAdded);
492 // Look for the first node that the user can edit.
493 for (size_t i = 0; i < nodes.size(); ++i) {
494 if (client_->CanBeEditedByUser(nodes[i]))
495 return nodes[i];
498 return NULL;
501 bool BookmarkModel::HasBookmarks() {
502 base::AutoLock url_lock(url_lock_);
503 return !nodes_ordered_by_url_set_.empty();
506 bool BookmarkModel::IsBookmarked(const GURL& url) {
507 base::AutoLock url_lock(url_lock_);
508 return IsBookmarkedNoLock(url);
511 void BookmarkModel::GetBookmarks(
512 std::vector<BookmarkModel::URLAndTitle>* bookmarks) {
513 base::AutoLock url_lock(url_lock_);
514 const GURL* last_url = NULL;
515 for (NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.begin();
516 i != nodes_ordered_by_url_set_.end(); ++i) {
517 const GURL* url = &((*i)->url());
518 // Only add unique URLs.
519 if (!last_url || *url != *last_url) {
520 BookmarkModel::URLAndTitle bookmark;
521 bookmark.url = *url;
522 bookmark.title = (*i)->GetTitle();
523 bookmarks->push_back(bookmark);
525 last_url = url;
529 void BookmarkModel::BlockTillLoaded() {
530 loaded_signal_.Wait();
533 const BookmarkNode* BookmarkModel::AddFolder(const BookmarkNode* parent,
534 int index,
535 const base::string16& title) {
536 return AddFolderWithMetaInfo(parent, index, title, NULL);
538 const BookmarkNode* BookmarkModel::AddFolderWithMetaInfo(
539 const BookmarkNode* parent,
540 int index,
541 const base::string16& title,
542 const BookmarkNode::MetaInfoMap* meta_info) {
543 if (!loaded_ || is_root_node(parent) || !IsValidIndex(parent, index, true)) {
544 // Can't add to the root.
545 NOTREACHED();
546 return NULL;
549 BookmarkNode* new_node = new BookmarkNode(generate_next_node_id(), GURL());
550 new_node->set_date_folder_modified(Time::Now());
551 // Folders shouldn't have line breaks in their titles.
552 new_node->SetTitle(title);
553 new_node->set_type(BookmarkNode::FOLDER);
554 if (meta_info)
555 new_node->SetMetaInfoMap(*meta_info);
557 return AddNode(AsMutable(parent), index, new_node);
560 const BookmarkNode* BookmarkModel::AddURL(const BookmarkNode* parent,
561 int index,
562 const base::string16& title,
563 const GURL& url) {
564 return AddURLWithCreationTimeAndMetaInfo(
565 parent,
566 index,
567 base::CollapseWhitespace(title, false),
568 url,
569 Time::Now(),
570 NULL);
573 const BookmarkNode* BookmarkModel::AddURLWithCreationTimeAndMetaInfo(
574 const BookmarkNode* parent,
575 int index,
576 const base::string16& title,
577 const GURL& url,
578 const Time& creation_time,
579 const BookmarkNode::MetaInfoMap* meta_info) {
580 if (!loaded_ || !url.is_valid() || is_root_node(parent) ||
581 !IsValidIndex(parent, index, true)) {
582 NOTREACHED();
583 return NULL;
586 // Syncing may result in dates newer than the last modified date.
587 if (creation_time > parent->date_folder_modified())
588 SetDateFolderModified(parent, creation_time);
590 BookmarkNode* new_node = new BookmarkNode(generate_next_node_id(), url);
591 new_node->SetTitle(title);
592 new_node->set_date_added(creation_time);
593 new_node->set_type(BookmarkNode::URL);
594 if (meta_info)
595 new_node->SetMetaInfoMap(*meta_info);
597 return AddNode(AsMutable(parent), index, new_node);
600 void BookmarkModel::SortChildren(const BookmarkNode* parent) {
601 DCHECK(client_->CanBeEditedByUser(parent));
603 if (!parent || !parent->is_folder() || is_root_node(parent) ||
604 parent->child_count() <= 1) {
605 return;
608 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
609 OnWillReorderBookmarkNode(this, parent));
611 UErrorCode error = U_ZERO_ERROR;
612 scoped_ptr<icu::Collator> collator(icu::Collator::createInstance(error));
613 if (U_FAILURE(error))
614 collator.reset(NULL);
615 BookmarkNode* mutable_parent = AsMutable(parent);
616 std::sort(mutable_parent->children().begin(),
617 mutable_parent->children().end(),
618 SortComparator(collator.get()));
620 if (store_.get())
621 store_->ScheduleSave();
623 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
624 BookmarkNodeChildrenReordered(this, parent));
627 void BookmarkModel::ReorderChildren(
628 const BookmarkNode* parent,
629 const std::vector<const BookmarkNode*>& ordered_nodes) {
630 DCHECK(client_->CanBeEditedByUser(parent));
632 // Ensure that all children in |parent| are in |ordered_nodes|.
633 DCHECK_EQ(static_cast<size_t>(parent->child_count()), ordered_nodes.size());
634 for (size_t i = 0; i < ordered_nodes.size(); ++i)
635 DCHECK_EQ(parent, ordered_nodes[i]->parent());
637 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
638 OnWillReorderBookmarkNode(this, parent));
640 AsMutable(parent)->SetChildren(
641 *(reinterpret_cast<const std::vector<BookmarkNode*>*>(&ordered_nodes)));
643 if (store_.get())
644 store_->ScheduleSave();
646 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
647 BookmarkNodeChildrenReordered(this, parent));
650 void BookmarkModel::SetDateFolderModified(const BookmarkNode* parent,
651 const Time time) {
652 DCHECK(parent);
653 AsMutable(parent)->set_date_folder_modified(time);
655 if (store_.get())
656 store_->ScheduleSave();
659 void BookmarkModel::ResetDateFolderModified(const BookmarkNode* node) {
660 SetDateFolderModified(node, Time());
663 void BookmarkModel::GetBookmarksMatching(const base::string16& text,
664 size_t max_count,
665 std::vector<BookmarkMatch>* matches) {
666 GetBookmarksMatching(text, max_count,
667 query_parser::MatchingAlgorithm::DEFAULT, matches);
670 void BookmarkModel::GetBookmarksMatching(
671 const base::string16& text,
672 size_t max_count,
673 query_parser::MatchingAlgorithm matching_algorithm,
674 std::vector<BookmarkMatch>* matches) {
675 if (!loaded_)
676 return;
678 index_->GetBookmarksMatching(text, max_count, matching_algorithm, matches);
681 void BookmarkModel::ClearStore() {
682 store_.reset();
685 void BookmarkModel::SetPermanentNodeVisible(BookmarkNode::Type type,
686 bool value) {
687 BookmarkPermanentNode* node = AsMutable(PermanentNode(type));
688 node->set_visible(value || client_->IsPermanentNodeVisible(node));
691 const BookmarkPermanentNode* BookmarkModel::PermanentNode(
692 BookmarkNode::Type type) {
693 DCHECK(loaded_);
694 switch (type) {
695 case BookmarkNode::BOOKMARK_BAR:
696 return bookmark_bar_node_;
697 case BookmarkNode::OTHER_NODE:
698 return other_node_;
699 case BookmarkNode::MOBILE:
700 return mobile_node_;
701 default:
702 NOTREACHED();
703 return NULL;
707 bool BookmarkModel::IsBookmarkedNoLock(const GURL& url) {
708 BookmarkNode tmp_node(url);
709 return (nodes_ordered_by_url_set_.find(&tmp_node) !=
710 nodes_ordered_by_url_set_.end());
713 void BookmarkModel::RemoveNode(BookmarkNode* node,
714 std::set<GURL>* removed_urls) {
715 if (!loaded_ || !node || is_permanent_node(node)) {
716 NOTREACHED();
717 return;
720 url_lock_.AssertAcquired();
721 if (node->is_url()) {
722 RemoveNodeFromInternalMaps(node);
723 removed_urls->insert(node->url());
726 CancelPendingFaviconLoadRequests(node);
728 // Recurse through children.
729 for (int i = node->child_count() - 1; i >= 0; --i)
730 RemoveNode(node->GetChild(i), removed_urls);
733 void BookmarkModel::DoneLoading(scoped_ptr<BookmarkLoadDetails> details) {
734 DCHECK(details);
735 if (loaded_) {
736 // We should only ever be loaded once.
737 NOTREACHED();
738 return;
741 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/467179
742 // is fixed.
743 tracked_objects::ScopedTracker tracking_profile1(
744 FROM_HERE_WITH_EXPLICIT_FUNCTION("467179 BookmarkModel::DoneLoading1"));
746 next_node_id_ = details->max_id();
747 if (details->computed_checksum() != details->stored_checksum() ||
748 details->ids_reassigned()) {
749 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/467179
750 // is fixed.
751 tracked_objects::ScopedTracker tracking_profile2(
752 FROM_HERE_WITH_EXPLICIT_FUNCTION("467179 BookmarkModel::DoneLoading2"));
754 // If bookmarks file changed externally, the IDs may have changed
755 // externally. In that case, the decoder may have reassigned IDs to make
756 // them unique. So when the file has changed externally, we should save the
757 // bookmarks file to persist new IDs.
758 if (store_.get())
759 store_->ScheduleSave();
761 bookmark_bar_node_ = details->release_bb_node();
762 other_node_ = details->release_other_folder_node();
763 mobile_node_ = details->release_mobile_folder_node();
764 index_.reset(details->release_index());
766 // Get any extra nodes and take ownership of them at the |root_|.
767 std::vector<BookmarkPermanentNode*> extra_nodes;
768 details->release_extra_nodes(&extra_nodes);
770 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/467179
771 // is fixed.
772 tracked_objects::ScopedTracker tracking_profile3(
773 FROM_HERE_WITH_EXPLICIT_FUNCTION("467179 BookmarkModel::DoneLoading3"));
775 // WARNING: order is important here, various places assume the order is
776 // constant (but can vary between embedders with the initial visibility
777 // of permanent nodes).
778 std::vector<BookmarkPermanentNode*> root_children;
779 root_children.push_back(bookmark_bar_node_);
780 root_children.push_back(other_node_);
781 root_children.push_back(mobile_node_);
782 for (size_t i = 0; i < extra_nodes.size(); ++i)
783 root_children.push_back(extra_nodes[i]);
785 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/467179
786 // is fixed.
787 tracked_objects::ScopedTracker tracking_profile4(
788 FROM_HERE_WITH_EXPLICIT_FUNCTION("467179 BookmarkModel::DoneLoading4"));
790 std::stable_sort(root_children.begin(),
791 root_children.end(),
792 VisibilityComparator(client_));
793 for (size_t i = 0; i < root_children.size(); ++i)
794 root_.Add(root_children[i], static_cast<int>(i));
796 root_.SetMetaInfoMap(details->model_meta_info_map());
797 root_.set_sync_transaction_version(details->model_sync_transaction_version());
799 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/467179
800 // is fixed.
801 tracked_objects::ScopedTracker tracking_profile5(
802 FROM_HERE_WITH_EXPLICIT_FUNCTION("467179 BookmarkModel::DoneLoading5"));
805 base::AutoLock url_lock(url_lock_);
806 // Update nodes_ordered_by_url_set_ from the nodes.
807 PopulateNodesByURL(&root_);
810 loaded_ = true;
812 loaded_signal_.Signal();
814 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/467179
815 // is fixed.
816 tracked_objects::ScopedTracker tracking_profile6(
817 FROM_HERE_WITH_EXPLICIT_FUNCTION("467179 BookmarkModel::DoneLoading6"));
819 // Notify our direct observers.
820 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
821 BookmarkModelLoaded(this, details->ids_reassigned()));
824 void BookmarkModel::RemoveAndDeleteNode(BookmarkNode* delete_me) {
825 scoped_ptr<BookmarkNode> node(delete_me);
827 const BookmarkNode* parent = node->parent();
828 DCHECK(parent);
829 int index = parent->GetIndexOf(node.get());
830 DCHECK_NE(-1, index);
832 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
833 OnWillRemoveBookmarks(this, parent, index, node.get()));
835 std::set<GURL> removed_urls;
837 base::AutoLock url_lock(url_lock_);
838 RemoveNodeAndGetRemovedUrls(node.get(), &removed_urls);
841 if (store_.get())
842 store_->ScheduleSave();
844 FOR_EACH_OBSERVER(
845 BookmarkModelObserver,
846 observers_,
847 BookmarkNodeRemoved(this, parent, index, node.get(), removed_urls));
850 void BookmarkModel::RemoveNodeFromInternalMaps(BookmarkNode* node) {
851 index_->Remove(node);
852 // NOTE: this is called in such a way that url_lock_ is already held. As
853 // such, this doesn't explicitly grab the lock.
854 url_lock_.AssertAcquired();
855 NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.find(node);
856 DCHECK(i != nodes_ordered_by_url_set_.end());
857 // i points to the first node with the URL, advance until we find the
858 // node we're removing.
859 while (*i != node)
860 ++i;
861 nodes_ordered_by_url_set_.erase(i);
864 void BookmarkModel::RemoveNodeAndGetRemovedUrls(BookmarkNode* node,
865 std::set<GURL>* removed_urls) {
866 // NOTE: this method should be always called with |url_lock_| held.
867 // This method does not explicitly acquires a lock.
868 url_lock_.AssertAcquired();
869 DCHECK(removed_urls);
870 BookmarkNode* parent = node->parent();
871 DCHECK(parent);
872 parent->Remove(node);
873 RemoveNode(node, removed_urls);
874 // RemoveNode adds an entry to removed_urls for each node of type URL. As we
875 // allow duplicates we need to remove any entries that are still bookmarked.
876 for (std::set<GURL>::iterator i = removed_urls->begin();
877 i != removed_urls->end();) {
878 if (IsBookmarkedNoLock(*i)) {
879 // When we erase the iterator pointing at the erasee is
880 // invalidated, so using i++ here within the "erase" call is
881 // important as it advances the iterator before passing the
882 // old value through to erase.
883 removed_urls->erase(i++);
884 } else {
885 ++i;
890 BookmarkNode* BookmarkModel::AddNode(BookmarkNode* parent,
891 int index,
892 BookmarkNode* node) {
893 parent->Add(node, index);
895 if (store_.get())
896 store_->ScheduleSave();
898 if (node->type() == BookmarkNode::URL) {
899 base::AutoLock url_lock(url_lock_);
900 AddNodeToInternalMaps(node);
901 } else {
902 index_->Add(node);
905 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
906 BookmarkNodeAdded(this, parent, index));
908 return node;
911 void BookmarkModel::AddNodeToInternalMaps(BookmarkNode* node) {
912 index_->Add(node);
913 url_lock_.AssertAcquired();
914 nodes_ordered_by_url_set_.insert(node);
917 bool BookmarkModel::IsValidIndex(const BookmarkNode* parent,
918 int index,
919 bool allow_end) {
920 return (parent && parent->is_folder() &&
921 (index >= 0 && (index < parent->child_count() ||
922 (allow_end && index == parent->child_count()))));
925 BookmarkPermanentNode* BookmarkModel::CreatePermanentNode(
926 BookmarkNode::Type type) {
927 DCHECK(type == BookmarkNode::BOOKMARK_BAR ||
928 type == BookmarkNode::OTHER_NODE ||
929 type == BookmarkNode::MOBILE);
930 BookmarkPermanentNode* node =
931 new BookmarkPermanentNode(generate_next_node_id());
932 node->set_type(type);
933 node->set_visible(client_->IsPermanentNodeVisible(node));
935 int title_id;
936 switch (type) {
937 case BookmarkNode::BOOKMARK_BAR:
938 title_id = IDS_BOOKMARK_BAR_FOLDER_NAME;
939 break;
940 case BookmarkNode::OTHER_NODE:
941 title_id = IDS_BOOKMARK_BAR_OTHER_FOLDER_NAME;
942 break;
943 case BookmarkNode::MOBILE:
944 title_id = IDS_BOOKMARK_BAR_MOBILE_FOLDER_NAME;
945 break;
946 default:
947 NOTREACHED();
948 title_id = IDS_BOOKMARK_BAR_FOLDER_NAME;
949 break;
951 node->SetTitle(l10n_util::GetStringUTF16(title_id));
952 return node;
955 void BookmarkModel::OnFaviconDataAvailable(
956 BookmarkNode* node,
957 favicon_base::IconType icon_type,
958 const favicon_base::FaviconImageResult& image_result) {
959 DCHECK(node);
960 node->set_favicon_load_task_id(base::CancelableTaskTracker::kBadTaskId);
961 node->set_favicon_state(BookmarkNode::LOADED_FAVICON);
962 if (!image_result.image.IsEmpty()) {
963 node->set_favicon_type(icon_type);
964 node->set_favicon(image_result.image);
965 node->set_icon_url(image_result.icon_url);
966 FaviconLoaded(node);
967 } else if (icon_type == favicon_base::TOUCH_ICON) {
968 // Couldn't load the touch icon, fallback to the regular favicon.
969 DCHECK(client_->PreferTouchIcon());
970 LoadFavicon(node, favicon_base::FAVICON);
974 void BookmarkModel::LoadFavicon(BookmarkNode* node,
975 favicon_base::IconType icon_type) {
976 if (node->is_folder())
977 return;
979 DCHECK(node->url().is_valid());
980 node->set_favicon_state(BookmarkNode::LOADING_FAVICON);
981 base::CancelableTaskTracker::TaskId taskId =
982 client_->GetFaviconImageForPageURL(
983 node->url(),
984 icon_type,
985 base::Bind(
986 &BookmarkModel::OnFaviconDataAvailable,
987 base::Unretained(this),
988 node,
989 icon_type),
990 &cancelable_task_tracker_);
991 if (taskId != base::CancelableTaskTracker::kBadTaskId)
992 node->set_favicon_load_task_id(taskId);
995 void BookmarkModel::FaviconLoaded(const BookmarkNode* node) {
996 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
997 BookmarkNodeFaviconChanged(this, node));
1000 void BookmarkModel::CancelPendingFaviconLoadRequests(BookmarkNode* node) {
1001 if (node->favicon_load_task_id() != base::CancelableTaskTracker::kBadTaskId) {
1002 cancelable_task_tracker_.TryCancel(node->favicon_load_task_id());
1003 node->set_favicon_load_task_id(base::CancelableTaskTracker::kBadTaskId);
1007 void BookmarkModel::PopulateNodesByURL(BookmarkNode* node) {
1008 // NOTE: this is called with url_lock_ already held. As such, this doesn't
1009 // explicitly grab the lock.
1010 if (node->is_url())
1011 nodes_ordered_by_url_set_.insert(node);
1012 for (int i = 0; i < node->child_count(); ++i)
1013 PopulateNodesByURL(node->GetChild(i));
1016 int64 BookmarkModel::generate_next_node_id() {
1017 return next_node_id_++;
1020 scoped_ptr<BookmarkLoadDetails> BookmarkModel::CreateLoadDetails(
1021 const std::string& accept_languages) {
1022 BookmarkPermanentNode* bb_node =
1023 CreatePermanentNode(BookmarkNode::BOOKMARK_BAR);
1024 BookmarkPermanentNode* other_node =
1025 CreatePermanentNode(BookmarkNode::OTHER_NODE);
1026 BookmarkPermanentNode* mobile_node =
1027 CreatePermanentNode(BookmarkNode::MOBILE);
1028 return scoped_ptr<BookmarkLoadDetails>(new BookmarkLoadDetails(
1029 bb_node,
1030 other_node,
1031 mobile_node,
1032 client_->GetLoadExtraNodesCallback(),
1033 new BookmarkIndex(client_, accept_languages),
1034 next_node_id_));
1037 } // namespace bookmarks