Workaround for xkbcommon dead keys.
[chromium-blink-merge.git] / components / bookmarks / browser / bookmark_model.cc
blob8131ec1f49e7705396737355618200c012e4b122
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/strings/string_util.h"
16 #include "components/bookmarks/browser/bookmark_expanded_state_tracker.h"
17 #include "components/bookmarks/browser/bookmark_index.h"
18 #include "components/bookmarks/browser/bookmark_match.h"
19 #include "components/bookmarks/browser/bookmark_model_observer.h"
20 #include "components/bookmarks/browser/bookmark_node_data.h"
21 #include "components/bookmarks/browser/bookmark_storage.h"
22 #include "components/bookmarks/browser/bookmark_utils.h"
23 #include "components/favicon_base/favicon_types.h"
24 #include "grit/components_strings.h"
25 #include "ui/base/l10n/l10n_util.h"
26 #include "ui/gfx/favicon_size.h"
28 using base::Time;
29 using bookmarks::BookmarkClient;
30 using bookmarks::BookmarkExpandedStateTracker;
31 using bookmarks::BookmarkIndex;
32 using bookmarks::BookmarkLoadDetails;
33 using bookmarks::BookmarkMatch;
34 using bookmarks::BookmarkModelObserver;
35 using bookmarks::BookmarkNodeData;
36 using bookmarks::BookmarkStorage;
38 namespace {
40 // Helper to get a mutable bookmark node.
41 BookmarkNode* AsMutable(const BookmarkNode* node) {
42 return const_cast<BookmarkNode*>(node);
45 // Helper to get a mutable permanent bookmark node.
46 BookmarkPermanentNode* AsMutable(const BookmarkPermanentNode* node) {
47 return const_cast<BookmarkPermanentNode*>(node);
50 // Comparator used when sorting permanent nodes. Nodes that are initially
51 // visible are sorted before nodes that are initially hidden.
52 class VisibilityComparator
53 : public std::binary_function<const BookmarkPermanentNode*,
54 const BookmarkPermanentNode*,
55 bool> {
56 public:
57 explicit VisibilityComparator(BookmarkClient* client) : client_(client) {}
59 // Returns true if |n1| preceeds |n2|.
60 bool operator()(const BookmarkPermanentNode* n1,
61 const BookmarkPermanentNode* n2) {
62 bool n1_visible = client_->IsPermanentNodeVisible(n1);
63 bool n2_visible = client_->IsPermanentNodeVisible(n2);
64 return n1_visible != n2_visible && n1_visible;
67 private:
68 BookmarkClient* client_;
71 // Comparator used when sorting bookmarks. Folders are sorted first, then
72 // bookmarks.
73 class SortComparator : public std::binary_function<const BookmarkNode*,
74 const BookmarkNode*,
75 bool> {
76 public:
77 explicit SortComparator(icu::Collator* collator) : collator_(collator) {}
79 // Returns true if |n1| preceeds |n2|.
80 bool operator()(const BookmarkNode* n1, const BookmarkNode* n2) {
81 if (n1->type() == n2->type()) {
82 // Types are the same, compare the names.
83 if (!collator_)
84 return n1->GetTitle() < n2->GetTitle();
85 return base::i18n::CompareString16WithCollator(
86 collator_, n1->GetTitle(), n2->GetTitle()) == UCOL_LESS;
88 // Types differ, sort such that folders come first.
89 return n1->is_folder();
92 private:
93 icu::Collator* collator_;
96 } // namespace
98 // BookmarkModel --------------------------------------------------------------
100 BookmarkModel::BookmarkModel(BookmarkClient* client)
101 : client_(client),
102 loaded_(false),
103 root_(GURL()),
104 bookmark_bar_node_(NULL),
105 other_node_(NULL),
106 mobile_node_(NULL),
107 next_node_id_(1),
108 observers_(ObserverList<BookmarkModelObserver>::NOTIFY_EXISTING_ONLY),
109 loaded_signal_(true, false),
110 extensive_changes_(0) {
111 DCHECK(client_);
114 BookmarkModel::~BookmarkModel() {
115 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
116 BookmarkModelBeingDeleted(this));
118 if (store_.get()) {
119 // The store maintains a reference back to us. We need to tell it we're gone
120 // so that it doesn't try and invoke a method back on us again.
121 store_->BookmarkModelDeleted();
125 void BookmarkModel::Shutdown() {
126 if (loaded_)
127 return;
129 // See comment in HistoryService::ShutdownOnUIThread where this is invoked for
130 // details. It is also called when the BookmarkModel is deleted.
131 loaded_signal_.Signal();
134 void BookmarkModel::Load(
135 PrefService* pref_service,
136 const std::string& accept_languages,
137 const base::FilePath& profile_path,
138 const scoped_refptr<base::SequencedTaskRunner>& io_task_runner,
139 const scoped_refptr<base::SequencedTaskRunner>& ui_task_runner) {
140 if (store_.get()) {
141 // If the store is non-null, it means Load was already invoked. Load should
142 // only be invoked once.
143 NOTREACHED();
144 return;
147 expanded_state_tracker_.reset(
148 new BookmarkExpandedStateTracker(this, pref_service));
150 // Load the bookmarks. BookmarkStorage notifies us when done.
151 store_ .reset(new BookmarkStorage(this, profile_path, io_task_runner.get()));
152 store_->LoadBookmarks(CreateLoadDetails(accept_languages), ui_task_runner);
155 const BookmarkNode* BookmarkModel::GetParentForNewNodes() {
156 std::vector<const BookmarkNode*> nodes =
157 bookmarks::GetMostRecentlyModifiedUserFolders(this, 1);
158 DCHECK(!nodes.empty()); // This list is always padded with default folders.
159 return nodes[0];
162 void BookmarkModel::AddObserver(BookmarkModelObserver* observer) {
163 observers_.AddObserver(observer);
166 void BookmarkModel::RemoveObserver(BookmarkModelObserver* observer) {
167 observers_.RemoveObserver(observer);
170 void BookmarkModel::BeginExtensiveChanges() {
171 if (++extensive_changes_ == 1) {
172 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
173 ExtensiveBookmarkChangesBeginning(this));
177 void BookmarkModel::EndExtensiveChanges() {
178 --extensive_changes_;
179 DCHECK_GE(extensive_changes_, 0);
180 if (extensive_changes_ == 0) {
181 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
182 ExtensiveBookmarkChangesEnded(this));
186 void BookmarkModel::BeginGroupedChanges() {
187 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
188 GroupedBookmarkChangesBeginning(this));
191 void BookmarkModel::EndGroupedChanges() {
192 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
193 GroupedBookmarkChangesEnded(this));
196 void BookmarkModel::Remove(const BookmarkNode* parent, int index) {
197 if (!loaded_ || !IsValidIndex(parent, index, false) || is_root_node(parent)) {
198 NOTREACHED();
199 return;
201 RemoveAndDeleteNode(AsMutable(parent->GetChild(index)));
204 void BookmarkModel::RemoveAllUserBookmarks() {
205 std::set<GURL> removed_urls;
206 ScopedVector<BookmarkNode> removed_nodes;
208 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
209 OnWillRemoveAllUserBookmarks(this));
211 BeginExtensiveChanges();
212 // Skip deleting permanent nodes. Permanent bookmark nodes are the root and
213 // its immediate children. For removing all non permanent nodes just remove
214 // all children of non-root permanent nodes.
216 base::AutoLock url_lock(url_lock_);
217 for (int i = 0; i < root_.child_count(); ++i) {
218 BookmarkNode* permanent_node = root_.GetChild(i);
220 if (!client_->CanBeEditedByUser(permanent_node))
221 continue;
223 for (int j = permanent_node->child_count() - 1; j >= 0; --j) {
224 BookmarkNode* child_node = permanent_node->GetChild(j);
225 removed_nodes.push_back(child_node);
226 RemoveNodeAndGetRemovedUrls(child_node, &removed_urls);
230 EndExtensiveChanges();
231 if (store_.get())
232 store_->ScheduleSave();
234 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
235 BookmarkAllUserNodesRemoved(this, removed_urls));
238 void BookmarkModel::Move(const BookmarkNode* node,
239 const BookmarkNode* new_parent,
240 int index) {
241 if (!loaded_ || !node || !IsValidIndex(new_parent, index, true) ||
242 is_root_node(new_parent) || is_permanent_node(node)) {
243 NOTREACHED();
244 return;
247 if (new_parent->HasAncestor(node)) {
248 // Can't make an ancestor of the node be a child of the node.
249 NOTREACHED();
250 return;
253 const BookmarkNode* old_parent = node->parent();
254 int old_index = old_parent->GetIndexOf(node);
256 if (old_parent == new_parent &&
257 (index == old_index || index == old_index + 1)) {
258 // Node is already in this position, nothing to do.
259 return;
262 SetDateFolderModified(new_parent, Time::Now());
264 if (old_parent == new_parent && index > old_index)
265 index--;
266 BookmarkNode* mutable_new_parent = AsMutable(new_parent);
267 mutable_new_parent->Add(AsMutable(node), index);
269 if (store_.get())
270 store_->ScheduleSave();
272 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
273 BookmarkNodeMoved(this, old_parent, old_index,
274 new_parent, index));
277 void BookmarkModel::Copy(const BookmarkNode* node,
278 const BookmarkNode* new_parent,
279 int index) {
280 if (!loaded_ || !node || !IsValidIndex(new_parent, index, true) ||
281 is_root_node(new_parent) || is_permanent_node(node)) {
282 NOTREACHED();
283 return;
286 if (new_parent->HasAncestor(node)) {
287 // Can't make an ancestor of the node be a child of the node.
288 NOTREACHED();
289 return;
292 SetDateFolderModified(new_parent, Time::Now());
293 BookmarkNodeData drag_data(node);
294 std::vector<BookmarkNodeData::Element> elements(drag_data.elements);
295 // CloneBookmarkNode will use BookmarkModel methods to do the job, so we
296 // don't need to send notifications here.
297 bookmarks::CloneBookmarkNode(this, elements, new_parent, index, true);
299 if (store_.get())
300 store_->ScheduleSave();
303 const gfx::Image& BookmarkModel::GetFavicon(const BookmarkNode* node) {
304 DCHECK(node);
305 if (node->favicon_state() == BookmarkNode::INVALID_FAVICON) {
306 BookmarkNode* mutable_node = AsMutable(node);
307 LoadFavicon(mutable_node,
308 client_->PreferTouchIcon() ? favicon_base::TOUCH_ICON
309 : favicon_base::FAVICON);
311 return node->favicon();
314 favicon_base::IconType BookmarkModel::GetFaviconType(const BookmarkNode* node) {
315 DCHECK(node);
316 return node->favicon_type();
319 void BookmarkModel::SetTitle(const BookmarkNode* node,
320 const base::string16& title) {
321 if (!node) {
322 NOTREACHED();
323 return;
325 if (node->GetTitle() == title)
326 return;
328 if (is_permanent_node(node) && !client_->CanSetPermanentNodeTitle(node)) {
329 NOTREACHED();
330 return;
333 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
334 OnWillChangeBookmarkNode(this, node));
336 // The title index doesn't support changing the title, instead we remove then
337 // add it back.
338 index_->Remove(node);
339 AsMutable(node)->SetTitle(title);
340 index_->Add(node);
342 if (store_.get())
343 store_->ScheduleSave();
345 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
346 BookmarkNodeChanged(this, node));
349 void BookmarkModel::SetURL(const BookmarkNode* node, const GURL& url) {
350 if (!node) {
351 NOTREACHED();
352 return;
355 // We cannot change the URL of a folder.
356 if (node->is_folder()) {
357 NOTREACHED();
358 return;
361 if (node->url() == url)
362 return;
364 BookmarkNode* mutable_node = AsMutable(node);
365 mutable_node->InvalidateFavicon();
366 CancelPendingFaviconLoadRequests(mutable_node);
368 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
369 OnWillChangeBookmarkNode(this, node));
372 base::AutoLock url_lock(url_lock_);
373 RemoveNodeFromInternalMaps(mutable_node);
374 mutable_node->set_url(url);
375 AddNodeToInternalMaps(mutable_node);
378 if (store_.get())
379 store_->ScheduleSave();
381 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
382 BookmarkNodeChanged(this, node));
385 void BookmarkModel::SetNodeMetaInfo(const BookmarkNode* node,
386 const std::string& key,
387 const std::string& value) {
388 std::string old_value;
389 if (node->GetMetaInfo(key, &old_value) && old_value == value)
390 return;
392 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
393 OnWillChangeBookmarkMetaInfo(this, node));
395 if (AsMutable(node)->SetMetaInfo(key, value) && store_.get())
396 store_->ScheduleSave();
398 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
399 BookmarkMetaInfoChanged(this, node));
402 void BookmarkModel::SetNodeMetaInfoMap(
403 const BookmarkNode* node,
404 const BookmarkNode::MetaInfoMap& meta_info_map) {
405 const BookmarkNode::MetaInfoMap* old_meta_info_map = node->GetMetaInfoMap();
406 if ((!old_meta_info_map && meta_info_map.empty()) ||
407 (old_meta_info_map && meta_info_map == *old_meta_info_map))
408 return;
410 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
411 OnWillChangeBookmarkMetaInfo(this, node));
413 AsMutable(node)->SetMetaInfoMap(meta_info_map);
414 if (store_.get())
415 store_->ScheduleSave();
417 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
418 BookmarkMetaInfoChanged(this, node));
421 void BookmarkModel::DeleteNodeMetaInfo(const BookmarkNode* node,
422 const std::string& key) {
423 const BookmarkNode::MetaInfoMap* meta_info_map = node->GetMetaInfoMap();
424 if (!meta_info_map || meta_info_map->find(key) == meta_info_map->end())
425 return;
427 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
428 OnWillChangeBookmarkMetaInfo(this, node));
430 if (AsMutable(node)->DeleteMetaInfo(key) && store_.get())
431 store_->ScheduleSave();
433 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
434 BookmarkMetaInfoChanged(this, node));
437 void BookmarkModel::AddNonClonedKey(const std::string& key) {
438 non_cloned_keys_.insert(key);
441 void BookmarkModel::SetNodeSyncTransactionVersion(
442 const BookmarkNode* node,
443 int64 sync_transaction_version) {
444 DCHECK(client_->CanSyncNode(node));
446 if (sync_transaction_version == node->sync_transaction_version())
447 return;
449 AsMutable(node)->set_sync_transaction_version(sync_transaction_version);
450 if (store_.get())
451 store_->ScheduleSave();
454 void BookmarkModel::OnFaviconChanged(const std::set<GURL>& urls) {
455 // Ignore events if |Load| has not been called yet.
456 if (!store_)
457 return;
459 // Prevent the observers from getting confused for multiple favicon loads.
460 for (std::set<GURL>::const_iterator i = urls.begin(); i != urls.end(); ++i) {
461 std::vector<const BookmarkNode*> nodes;
462 GetNodesByURL(*i, &nodes);
463 for (size_t i = 0; i < nodes.size(); ++i) {
464 // Got an updated favicon, for a URL, do a new request.
465 BookmarkNode* node = AsMutable(nodes[i]);
466 node->InvalidateFavicon();
467 CancelPendingFaviconLoadRequests(node);
468 FOR_EACH_OBSERVER(BookmarkModelObserver,
469 observers_,
470 BookmarkNodeFaviconChanged(this, node));
475 void BookmarkModel::SetDateAdded(const BookmarkNode* node,
476 Time date_added) {
477 if (!node) {
478 NOTREACHED();
479 return;
482 if (node->date_added() == date_added)
483 return;
485 if (is_permanent_node(node)) {
486 NOTREACHED();
487 return;
490 AsMutable(node)->set_date_added(date_added);
492 // Syncing might result in dates newer than the folder's last modified date.
493 if (date_added > node->parent()->date_folder_modified()) {
494 // Will trigger store_->ScheduleSave().
495 SetDateFolderModified(node->parent(), date_added);
496 } else if (store_.get()) {
497 store_->ScheduleSave();
501 void BookmarkModel::GetNodesByURL(const GURL& url,
502 std::vector<const BookmarkNode*>* nodes) {
503 base::AutoLock url_lock(url_lock_);
504 BookmarkNode tmp_node(url);
505 NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.find(&tmp_node);
506 while (i != nodes_ordered_by_url_set_.end() && (*i)->url() == url) {
507 nodes->push_back(*i);
508 ++i;
512 const BookmarkNode* BookmarkModel::GetMostRecentlyAddedUserNodeForURL(
513 const GURL& url) {
514 std::vector<const BookmarkNode*> nodes;
515 GetNodesByURL(url, &nodes);
516 std::sort(nodes.begin(), nodes.end(), &bookmarks::MoreRecentlyAdded);
518 // Look for the first node that the user can edit.
519 for (size_t i = 0; i < nodes.size(); ++i) {
520 if (client_->CanBeEditedByUser(nodes[i]))
521 return nodes[i];
524 return NULL;
527 bool BookmarkModel::HasBookmarks() {
528 base::AutoLock url_lock(url_lock_);
529 return !nodes_ordered_by_url_set_.empty();
532 bool BookmarkModel::IsBookmarked(const GURL& url) {
533 base::AutoLock url_lock(url_lock_);
534 return IsBookmarkedNoLock(url);
537 void BookmarkModel::GetBookmarks(
538 std::vector<BookmarkModel::URLAndTitle>* bookmarks) {
539 base::AutoLock url_lock(url_lock_);
540 const GURL* last_url = NULL;
541 for (NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.begin();
542 i != nodes_ordered_by_url_set_.end(); ++i) {
543 const GURL* url = &((*i)->url());
544 // Only add unique URLs.
545 if (!last_url || *url != *last_url) {
546 BookmarkModel::URLAndTitle bookmark;
547 bookmark.url = *url;
548 bookmark.title = (*i)->GetTitle();
549 bookmarks->push_back(bookmark);
551 last_url = url;
555 void BookmarkModel::BlockTillLoaded() {
556 loaded_signal_.Wait();
559 const BookmarkNode* BookmarkModel::AddFolder(const BookmarkNode* parent,
560 int index,
561 const base::string16& title) {
562 return AddFolderWithMetaInfo(parent, index, title, NULL);
564 const BookmarkNode* BookmarkModel::AddFolderWithMetaInfo(
565 const BookmarkNode* parent,
566 int index,
567 const base::string16& title,
568 const BookmarkNode::MetaInfoMap* meta_info) {
569 if (!loaded_ || is_root_node(parent) || !IsValidIndex(parent, index, true)) {
570 // Can't add to the root.
571 NOTREACHED();
572 return NULL;
575 BookmarkNode* new_node = new BookmarkNode(generate_next_node_id(), GURL());
576 new_node->set_date_folder_modified(Time::Now());
577 // Folders shouldn't have line breaks in their titles.
578 new_node->SetTitle(title);
579 new_node->set_type(BookmarkNode::FOLDER);
580 if (meta_info)
581 new_node->SetMetaInfoMap(*meta_info);
583 return AddNode(AsMutable(parent), index, new_node);
586 const BookmarkNode* BookmarkModel::AddURL(const BookmarkNode* parent,
587 int index,
588 const base::string16& title,
589 const GURL& url) {
590 return AddURLWithCreationTimeAndMetaInfo(
591 parent,
592 index,
593 base::CollapseWhitespace(title, false),
594 url,
595 Time::Now(),
596 NULL);
599 const BookmarkNode* BookmarkModel::AddURLWithCreationTimeAndMetaInfo(
600 const BookmarkNode* parent,
601 int index,
602 const base::string16& title,
603 const GURL& url,
604 const Time& creation_time,
605 const BookmarkNode::MetaInfoMap* meta_info) {
606 if (!loaded_ || !url.is_valid() || is_root_node(parent) ||
607 !IsValidIndex(parent, index, true)) {
608 NOTREACHED();
609 return NULL;
612 // Syncing may result in dates newer than the last modified date.
613 if (creation_time > parent->date_folder_modified())
614 SetDateFolderModified(parent, creation_time);
616 BookmarkNode* new_node = new BookmarkNode(generate_next_node_id(), url);
617 new_node->SetTitle(title);
618 new_node->set_date_added(creation_time);
619 new_node->set_type(BookmarkNode::URL);
620 if (meta_info)
621 new_node->SetMetaInfoMap(*meta_info);
623 return AddNode(AsMutable(parent), index, new_node);
626 void BookmarkModel::SortChildren(const BookmarkNode* parent) {
627 DCHECK(client_->CanBeEditedByUser(parent));
629 if (!parent || !parent->is_folder() || is_root_node(parent) ||
630 parent->child_count() <= 1) {
631 return;
634 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
635 OnWillReorderBookmarkNode(this, parent));
637 UErrorCode error = U_ZERO_ERROR;
638 scoped_ptr<icu::Collator> collator(icu::Collator::createInstance(error));
639 if (U_FAILURE(error))
640 collator.reset(NULL);
641 BookmarkNode* mutable_parent = AsMutable(parent);
642 std::sort(mutable_parent->children().begin(),
643 mutable_parent->children().end(),
644 SortComparator(collator.get()));
646 if (store_.get())
647 store_->ScheduleSave();
649 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
650 BookmarkNodeChildrenReordered(this, parent));
653 void BookmarkModel::ReorderChildren(
654 const BookmarkNode* parent,
655 const std::vector<const BookmarkNode*>& ordered_nodes) {
656 DCHECK(client_->CanBeEditedByUser(parent));
658 // Ensure that all children in |parent| are in |ordered_nodes|.
659 DCHECK_EQ(static_cast<size_t>(parent->child_count()), ordered_nodes.size());
660 for (size_t i = 0; i < ordered_nodes.size(); ++i)
661 DCHECK_EQ(parent, ordered_nodes[i]->parent());
663 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
664 OnWillReorderBookmarkNode(this, parent));
666 AsMutable(parent)->SetChildren(
667 *(reinterpret_cast<const std::vector<BookmarkNode*>*>(&ordered_nodes)));
669 if (store_.get())
670 store_->ScheduleSave();
672 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
673 BookmarkNodeChildrenReordered(this, parent));
676 void BookmarkModel::SetDateFolderModified(const BookmarkNode* parent,
677 const Time time) {
678 DCHECK(parent);
679 AsMutable(parent)->set_date_folder_modified(time);
681 if (store_.get())
682 store_->ScheduleSave();
685 void BookmarkModel::ResetDateFolderModified(const BookmarkNode* node) {
686 SetDateFolderModified(node, Time());
689 void BookmarkModel::GetBookmarksMatching(const base::string16& text,
690 size_t max_count,
691 std::vector<BookmarkMatch>* matches) {
692 GetBookmarksMatching(text, max_count,
693 query_parser::MatchingAlgorithm::DEFAULT, matches);
696 void BookmarkModel::GetBookmarksMatching(
697 const base::string16& text,
698 size_t max_count,
699 query_parser::MatchingAlgorithm matching_algorithm,
700 std::vector<BookmarkMatch>* matches) {
701 if (!loaded_)
702 return;
704 index_->GetBookmarksMatching(text, max_count, matching_algorithm, matches);
707 void BookmarkModel::ClearStore() {
708 store_.reset();
711 void BookmarkModel::SetPermanentNodeVisible(BookmarkNode::Type type,
712 bool value) {
713 BookmarkPermanentNode* node = AsMutable(PermanentNode(type));
714 node->set_visible(value || client_->IsPermanentNodeVisible(node));
717 const BookmarkPermanentNode* BookmarkModel::PermanentNode(
718 BookmarkNode::Type type) {
719 DCHECK(loaded_);
720 switch (type) {
721 case BookmarkNode::BOOKMARK_BAR:
722 return bookmark_bar_node_;
723 case BookmarkNode::OTHER_NODE:
724 return other_node_;
725 case BookmarkNode::MOBILE:
726 return mobile_node_;
727 default:
728 NOTREACHED();
729 return NULL;
733 bool BookmarkModel::IsBookmarkedNoLock(const GURL& url) {
734 BookmarkNode tmp_node(url);
735 return (nodes_ordered_by_url_set_.find(&tmp_node) !=
736 nodes_ordered_by_url_set_.end());
739 void BookmarkModel::RemoveNode(BookmarkNode* node,
740 std::set<GURL>* removed_urls) {
741 if (!loaded_ || !node || is_permanent_node(node)) {
742 NOTREACHED();
743 return;
746 url_lock_.AssertAcquired();
747 if (node->is_url()) {
748 RemoveNodeFromInternalMaps(node);
749 removed_urls->insert(node->url());
752 CancelPendingFaviconLoadRequests(node);
754 // Recurse through children.
755 for (int i = node->child_count() - 1; i >= 0; --i)
756 RemoveNode(node->GetChild(i), removed_urls);
759 void BookmarkModel::DoneLoading(scoped_ptr<BookmarkLoadDetails> details) {
760 DCHECK(details);
761 if (loaded_) {
762 // We should only ever be loaded once.
763 NOTREACHED();
764 return;
767 next_node_id_ = details->max_id();
768 if (details->computed_checksum() != details->stored_checksum() ||
769 details->ids_reassigned()) {
770 // If bookmarks file changed externally, the IDs may have changed
771 // externally. In that case, the decoder may have reassigned IDs to make
772 // them unique. So when the file has changed externally, we should save the
773 // bookmarks file to persist new IDs.
774 if (store_.get())
775 store_->ScheduleSave();
777 bookmark_bar_node_ = details->release_bb_node();
778 other_node_ = details->release_other_folder_node();
779 mobile_node_ = details->release_mobile_folder_node();
780 index_.reset(details->release_index());
782 // Get any extra nodes and take ownership of them at the |root_|.
783 std::vector<BookmarkPermanentNode*> extra_nodes;
784 details->release_extra_nodes(&extra_nodes);
786 // WARNING: order is important here, various places assume the order is
787 // constant (but can vary between embedders with the initial visibility
788 // of permanent nodes).
789 std::vector<BookmarkPermanentNode*> root_children;
790 root_children.push_back(bookmark_bar_node_);
791 root_children.push_back(other_node_);
792 root_children.push_back(mobile_node_);
793 for (size_t i = 0; i < extra_nodes.size(); ++i)
794 root_children.push_back(extra_nodes[i]);
795 std::stable_sort(root_children.begin(),
796 root_children.end(),
797 VisibilityComparator(client_));
798 for (size_t i = 0; i < root_children.size(); ++i)
799 root_.Add(root_children[i], static_cast<int>(i));
801 root_.SetMetaInfoMap(details->model_meta_info_map());
802 root_.set_sync_transaction_version(details->model_sync_transaction_version());
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 // Notify our direct observers.
815 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
816 BookmarkModelLoaded(this, details->ids_reassigned()));
819 void BookmarkModel::RemoveAndDeleteNode(BookmarkNode* delete_me) {
820 scoped_ptr<BookmarkNode> node(delete_me);
822 const BookmarkNode* parent = node->parent();
823 DCHECK(parent);
824 int index = parent->GetIndexOf(node.get());
826 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
827 OnWillRemoveBookmarks(this, parent, index, node.get()));
829 std::set<GURL> removed_urls;
831 base::AutoLock url_lock(url_lock_);
832 RemoveNodeAndGetRemovedUrls(node.get(), &removed_urls);
835 if (store_.get())
836 store_->ScheduleSave();
838 FOR_EACH_OBSERVER(
839 BookmarkModelObserver,
840 observers_,
841 BookmarkNodeRemoved(this, parent, index, node.get(), removed_urls));
844 void BookmarkModel::RemoveNodeFromInternalMaps(BookmarkNode* node) {
845 index_->Remove(node);
846 // NOTE: this is called in such a way that url_lock_ is already held. As
847 // such, this doesn't explicitly grab the lock.
848 url_lock_.AssertAcquired();
849 NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.find(node);
850 DCHECK(i != nodes_ordered_by_url_set_.end());
851 // i points to the first node with the URL, advance until we find the
852 // node we're removing.
853 while (*i != node)
854 ++i;
855 nodes_ordered_by_url_set_.erase(i);
858 void BookmarkModel::RemoveNodeAndGetRemovedUrls(BookmarkNode* node,
859 std::set<GURL>* removed_urls) {
860 // NOTE: this method should be always called with |url_lock_| held.
861 // This method does not explicitly acquires a lock.
862 url_lock_.AssertAcquired();
863 DCHECK(removed_urls);
864 BookmarkNode* parent = AsMutable(node->parent());
865 DCHECK(parent);
866 parent->Remove(node);
867 RemoveNode(node, removed_urls);
868 // RemoveNode adds an entry to removed_urls for each node of type URL. As we
869 // allow duplicates we need to remove any entries that are still bookmarked.
870 for (std::set<GURL>::iterator i = removed_urls->begin();
871 i != removed_urls->end();) {
872 if (IsBookmarkedNoLock(*i)) {
873 // When we erase the iterator pointing at the erasee is
874 // invalidated, so using i++ here within the "erase" call is
875 // important as it advances the iterator before passing the
876 // old value through to erase.
877 removed_urls->erase(i++);
878 } else {
879 ++i;
884 BookmarkNode* BookmarkModel::AddNode(BookmarkNode* parent,
885 int index,
886 BookmarkNode* node) {
887 parent->Add(node, index);
889 if (store_.get())
890 store_->ScheduleSave();
892 if (node->type() == BookmarkNode::URL) {
893 base::AutoLock url_lock(url_lock_);
894 AddNodeToInternalMaps(node);
895 } else {
896 index_->Add(node);
899 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
900 BookmarkNodeAdded(this, parent, index));
902 return node;
905 void BookmarkModel::AddNodeToInternalMaps(BookmarkNode* node) {
906 index_->Add(node);
907 url_lock_.AssertAcquired();
908 nodes_ordered_by_url_set_.insert(node);
911 bool BookmarkModel::IsValidIndex(const BookmarkNode* parent,
912 int index,
913 bool allow_end) {
914 return (parent && parent->is_folder() &&
915 (index >= 0 && (index < parent->child_count() ||
916 (allow_end && index == parent->child_count()))));
919 BookmarkPermanentNode* BookmarkModel::CreatePermanentNode(
920 BookmarkNode::Type type) {
921 DCHECK(type == BookmarkNode::BOOKMARK_BAR ||
922 type == BookmarkNode::OTHER_NODE ||
923 type == BookmarkNode::MOBILE);
924 BookmarkPermanentNode* node =
925 new BookmarkPermanentNode(generate_next_node_id());
926 node->set_type(type);
927 node->set_visible(client_->IsPermanentNodeVisible(node));
929 int title_id;
930 switch (type) {
931 case BookmarkNode::BOOKMARK_BAR:
932 title_id = IDS_BOOKMARK_BAR_FOLDER_NAME;
933 break;
934 case BookmarkNode::OTHER_NODE:
935 title_id = IDS_BOOKMARK_BAR_OTHER_FOLDER_NAME;
936 break;
937 case BookmarkNode::MOBILE:
938 title_id = IDS_BOOKMARK_BAR_MOBILE_FOLDER_NAME;
939 break;
940 default:
941 NOTREACHED();
942 title_id = IDS_BOOKMARK_BAR_FOLDER_NAME;
943 break;
945 node->SetTitle(l10n_util::GetStringUTF16(title_id));
946 return node;
949 void BookmarkModel::OnFaviconDataAvailable(
950 BookmarkNode* node,
951 favicon_base::IconType icon_type,
952 const favicon_base::FaviconImageResult& image_result) {
953 DCHECK(node);
954 node->set_favicon_load_task_id(base::CancelableTaskTracker::kBadTaskId);
955 node->set_favicon_state(BookmarkNode::LOADED_FAVICON);
956 if (!image_result.image.IsEmpty()) {
957 node->set_favicon_type(icon_type);
958 node->set_favicon(image_result.image);
959 node->set_icon_url(image_result.icon_url);
960 FaviconLoaded(node);
961 } else if (icon_type == favicon_base::TOUCH_ICON) {
962 // Couldn't load the touch icon, fallback to the regular favicon.
963 DCHECK(client_->PreferTouchIcon());
964 LoadFavicon(node, favicon_base::FAVICON);
968 void BookmarkModel::LoadFavicon(BookmarkNode* node,
969 favicon_base::IconType icon_type) {
970 if (node->is_folder())
971 return;
973 DCHECK(node->url().is_valid());
974 node->set_favicon_state(BookmarkNode::LOADING_FAVICON);
975 base::CancelableTaskTracker::TaskId taskId =
976 client_->GetFaviconImageForPageURL(
977 node->url(),
978 icon_type,
979 base::Bind(
980 &BookmarkModel::OnFaviconDataAvailable,
981 base::Unretained(this),
982 node,
983 icon_type),
984 &cancelable_task_tracker_);
985 if (taskId != base::CancelableTaskTracker::kBadTaskId)
986 node->set_favicon_load_task_id(taskId);
989 void BookmarkModel::FaviconLoaded(const BookmarkNode* node) {
990 FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
991 BookmarkNodeFaviconChanged(this, node));
994 void BookmarkModel::CancelPendingFaviconLoadRequests(BookmarkNode* node) {
995 if (node->favicon_load_task_id() != base::CancelableTaskTracker::kBadTaskId) {
996 cancelable_task_tracker_.TryCancel(node->favicon_load_task_id());
997 node->set_favicon_load_task_id(base::CancelableTaskTracker::kBadTaskId);
1001 void BookmarkModel::PopulateNodesByURL(BookmarkNode* node) {
1002 // NOTE: this is called with url_lock_ already held. As such, this doesn't
1003 // explicitly grab the lock.
1004 if (node->is_url())
1005 nodes_ordered_by_url_set_.insert(node);
1006 for (int i = 0; i < node->child_count(); ++i)
1007 PopulateNodesByURL(node->GetChild(i));
1010 int64 BookmarkModel::generate_next_node_id() {
1011 return next_node_id_++;
1014 scoped_ptr<BookmarkLoadDetails> BookmarkModel::CreateLoadDetails(
1015 const std::string& accept_languages) {
1016 BookmarkPermanentNode* bb_node =
1017 CreatePermanentNode(BookmarkNode::BOOKMARK_BAR);
1018 BookmarkPermanentNode* other_node =
1019 CreatePermanentNode(BookmarkNode::OTHER_NODE);
1020 BookmarkPermanentNode* mobile_node =
1021 CreatePermanentNode(BookmarkNode::MOBILE);
1022 return scoped_ptr<BookmarkLoadDetails>(new BookmarkLoadDetails(
1023 bb_node,
1024 other_node,
1025 mobile_node,
1026 client_->GetLoadExtraNodesCallback(),
1027 new BookmarkIndex(client_, accept_languages),
1028 next_node_id_));