• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2012 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/bind.h"
11 #include "base/bind_helpers.h"
12 #include "base/i18n/string_compare.h"
13 #include "base/sequenced_task_runner.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "chrome/browser/bookmarks/bookmark_expanded_state_tracker.h"
17 #include "chrome/browser/bookmarks/bookmark_index.h"
18 #include "chrome/browser/bookmarks/bookmark_model_observer.h"
19 #include "chrome/browser/bookmarks/bookmark_storage.h"
20 #include "chrome/browser/bookmarks/bookmark_title_match.h"
21 #include "chrome/browser/bookmarks/bookmark_utils.h"
22 #include "chrome/browser/chrome_notification_types.h"
23 #include "chrome/browser/favicon/favicon_changed_details.h"
24 #include "chrome/browser/favicon/favicon_service.h"
25 #include "chrome/browser/favicon/favicon_service_factory.h"
26 #include "chrome/browser/history/history_service.h"
27 #include "chrome/browser/history/history_service_factory.h"
28 #include "chrome/browser/profiles/profile.h"
29 #include "chrome/common/favicon/favicon_types.h"
30 #include "content/public/browser/notification_details.h"
31 #include "content/public/browser/notification_source.h"
32 #include "grit/generated_resources.h"
33 #include "ui/base/l10n/l10n_util.h"
34 #include "ui/gfx/favicon_size.h"
35 #include "ui/gfx/image/image_util.h"
36 
37 using base::Time;
38 
39 namespace {
40 
41 // Helper to get a mutable bookmark node.
AsMutable(const BookmarkNode * node)42 BookmarkNode* AsMutable(const BookmarkNode* node) {
43   return const_cast<BookmarkNode*>(node);
44 }
45 
46 // Whitespace characters to strip from bookmark titles.
47 const char16 kInvalidChars[] = {
48   '\n', '\r', '\t',
49   0x2028,  // Line separator
50   0x2029,  // Paragraph separator
51   0
52 };
53 
54 }  // namespace
55 
56 // BookmarkNode ---------------------------------------------------------------
57 
58 const int64 BookmarkNode::kInvalidSyncTransactionVersion = -1;
59 
BookmarkNode(const GURL & url)60 BookmarkNode::BookmarkNode(const GURL& url)
61     : url_(url) {
62   Initialize(0);
63 }
64 
BookmarkNode(int64 id,const GURL & url)65 BookmarkNode::BookmarkNode(int64 id, const GURL& url)
66     : url_(url) {
67   Initialize(id);
68 }
69 
~BookmarkNode()70 BookmarkNode::~BookmarkNode() {
71 }
72 
SetTitle(const base::string16 & title)73 void BookmarkNode::SetTitle(const base::string16& title) {
74   // Replace newlines and other problematic whitespace characters in
75   // folder/bookmark names with spaces.
76   base::string16 trimmed_title;
77   base::ReplaceChars(title, kInvalidChars, ASCIIToUTF16(" "), &trimmed_title);
78   ui::TreeNode<BookmarkNode>::SetTitle(trimmed_title);
79 }
80 
IsVisible() const81 bool BookmarkNode::IsVisible() const {
82   return true;
83 }
84 
GetMetaInfo(const std::string & key,std::string * value) const85 bool BookmarkNode::GetMetaInfo(const std::string& key,
86                                std::string* value) const {
87   if (!meta_info_map_)
88     return false;
89 
90   MetaInfoMap::const_iterator it = meta_info_map_->find(key);
91   if (it == meta_info_map_->end())
92     return false;
93 
94   *value = it->second;
95   return true;
96 }
97 
SetMetaInfo(const std::string & key,const std::string & value)98 bool BookmarkNode::SetMetaInfo(const std::string& key,
99                                const std::string& value) {
100   if (!meta_info_map_)
101     meta_info_map_.reset(new MetaInfoMap);
102 
103   MetaInfoMap::iterator it = meta_info_map_->find(key);
104   if (it == meta_info_map_->end()) {
105     (*meta_info_map_)[key] = value;
106     return true;
107   }
108   // Key already in map, check if the value has changed.
109   if (it->second == value)
110     return false;
111   it->second = value;
112   return true;
113 }
114 
DeleteMetaInfo(const std::string & key)115 bool BookmarkNode::DeleteMetaInfo(const std::string& key) {
116   if (!meta_info_map_)
117     return false;
118   bool erased = meta_info_map_->erase(key) != 0;
119   if (meta_info_map_->empty())
120     meta_info_map_.reset();
121   return erased;
122 }
123 
SetMetaInfoMap(const MetaInfoMap & meta_info_map)124 void BookmarkNode::SetMetaInfoMap(const MetaInfoMap& meta_info_map) {
125   if (meta_info_map.empty())
126     meta_info_map_.reset();
127   else
128     meta_info_map_.reset(new MetaInfoMap(meta_info_map));
129 }
130 
GetMetaInfoMap() const131 const BookmarkNode::MetaInfoMap* BookmarkNode::GetMetaInfoMap() const {
132   return meta_info_map_.get();
133 }
134 
Initialize(int64 id)135 void BookmarkNode::Initialize(int64 id) {
136   id_ = id;
137   type_ = url_.is_empty() ? FOLDER : URL;
138   date_added_ = Time::Now();
139   favicon_state_ = INVALID_FAVICON;
140   favicon_load_task_id_ = CancelableTaskTracker::kBadTaskId;
141   meta_info_map_.reset();
142   sync_transaction_version_ = kInvalidSyncTransactionVersion;
143 }
144 
InvalidateFavicon()145 void BookmarkNode::InvalidateFavicon() {
146   icon_url_ = GURL();
147   favicon_ = gfx::Image();
148   favicon_state_ = INVALID_FAVICON;
149 }
150 
151 namespace {
152 
153 // Comparator used when sorting bookmarks. Folders are sorted first, then
154 // bookmarks.
155 class SortComparator : public std::binary_function<const BookmarkNode*,
156                                                    const BookmarkNode*,
157                                                    bool> {
158  public:
SortComparator(icu::Collator * collator)159   explicit SortComparator(icu::Collator* collator) : collator_(collator) {}
160 
161   // Returns true if |n1| preceeds |n2|.
operator ()(const BookmarkNode * n1,const BookmarkNode * n2)162   bool operator()(const BookmarkNode* n1, const BookmarkNode* n2) {
163     if (n1->type() == n2->type()) {
164       // Types are the same, compare the names.
165       if (!collator_)
166         return n1->GetTitle() < n2->GetTitle();
167       return base::i18n::CompareString16WithCollator(
168           collator_, n1->GetTitle(), n2->GetTitle()) == UCOL_LESS;
169     }
170     // Types differ, sort such that folders come first.
171     return n1->is_folder();
172   }
173 
174  private:
175   icu::Collator* collator_;
176 };
177 
178 }  // namespace
179 
180 // BookmarkPermanentNode -------------------------------------------------------
181 
BookmarkPermanentNode(int64 id)182 BookmarkPermanentNode::BookmarkPermanentNode(int64 id)
183     : BookmarkNode(id, GURL()),
184       visible_(true) {
185 }
186 
~BookmarkPermanentNode()187 BookmarkPermanentNode::~BookmarkPermanentNode() {
188 }
189 
IsVisible() const190 bool BookmarkPermanentNode::IsVisible() const {
191   return visible_ || !empty();
192 }
193 
194 // BookmarkModel --------------------------------------------------------------
195 
BookmarkModel(Profile * profile)196 BookmarkModel::BookmarkModel(Profile* profile)
197     : profile_(profile),
198       loaded_(false),
199       root_(GURL()),
200       bookmark_bar_node_(NULL),
201       other_node_(NULL),
202       mobile_node_(NULL),
203       next_node_id_(1),
204       observers_(ObserverList<BookmarkModelObserver>::NOTIFY_EXISTING_ONLY),
205       loaded_signal_(true, false),
206       extensive_changes_(0) {
207   if (!profile_) {
208     // Profile is null during testing.
209     DoneLoading(CreateLoadDetails());
210   }
211 }
212 
~BookmarkModel()213 BookmarkModel::~BookmarkModel() {
214   FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
215                     BookmarkModelBeingDeleted(this));
216 
217   if (store_.get()) {
218     // The store maintains a reference back to us. We need to tell it we're gone
219     // so that it doesn't try and invoke a method back on us again.
220     store_->BookmarkModelDeleted();
221   }
222 }
223 
Shutdown()224 void BookmarkModel::Shutdown() {
225   if (loaded_)
226     return;
227 
228   // See comment in HistoryService::ShutdownOnUIThread where this is invoked for
229   // details. It is also called when the BookmarkModel is deleted.
230   loaded_signal_.Signal();
231 }
232 
Load(const scoped_refptr<base::SequencedTaskRunner> & task_runner)233 void BookmarkModel::Load(
234     const scoped_refptr<base::SequencedTaskRunner>& task_runner) {
235   if (store_.get()) {
236     // If the store is non-null, it means Load was already invoked. Load should
237     // only be invoked once.
238     NOTREACHED();
239     return;
240   }
241 
242   expanded_state_tracker_.reset(
243       new BookmarkExpandedStateTracker(this, profile_->GetPrefs()));
244 
245   // Listen for changes to favicons so that we can update the favicon of the
246   // node appropriately.
247   registrar_.Add(this, chrome::NOTIFICATION_FAVICON_CHANGED,
248                  content::Source<Profile>(profile_));
249 
250   // Load the bookmarks. BookmarkStorage notifies us when done.
251   store_ = new BookmarkStorage(profile_, this, task_runner.get());
252   store_->LoadBookmarks(CreateLoadDetails());
253 }
254 
GetParentForNewNodes()255 const BookmarkNode* BookmarkModel::GetParentForNewNodes() {
256   std::vector<const BookmarkNode*> nodes =
257       bookmark_utils::GetMostRecentlyModifiedFolders(this, 1);
258   DCHECK(!nodes.empty());  // This list is always padded with default folders.
259   return nodes[0];
260 }
261 
AddObserver(BookmarkModelObserver * observer)262 void BookmarkModel::AddObserver(BookmarkModelObserver* observer) {
263   observers_.AddObserver(observer);
264 }
265 
RemoveObserver(BookmarkModelObserver * observer)266 void BookmarkModel::RemoveObserver(BookmarkModelObserver* observer) {
267   observers_.RemoveObserver(observer);
268 }
269 
BeginExtensiveChanges()270 void BookmarkModel::BeginExtensiveChanges() {
271   if (++extensive_changes_ == 1) {
272     FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
273                       ExtensiveBookmarkChangesBeginning(this));
274   }
275 }
276 
EndExtensiveChanges()277 void BookmarkModel::EndExtensiveChanges() {
278   --extensive_changes_;
279   DCHECK_GE(extensive_changes_, 0);
280   if (extensive_changes_ == 0) {
281     FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
282                       ExtensiveBookmarkChangesEnded(this));
283   }
284 }
285 
Remove(const BookmarkNode * parent,int index)286 void BookmarkModel::Remove(const BookmarkNode* parent, int index) {
287   if (!loaded_ || !IsValidIndex(parent, index, false) || is_root_node(parent)) {
288     NOTREACHED();
289     return;
290   }
291   RemoveAndDeleteNode(AsMutable(parent->GetChild(index)));
292 }
293 
RemoveAll()294 void BookmarkModel::RemoveAll() {
295   std::set<GURL> removed_urls;
296   ScopedVector<BookmarkNode> removed_nodes;
297 
298   FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
299                     OnWillRemoveAllBookmarks(this));
300 
301   BeginExtensiveChanges();
302   // Skip deleting permanent nodes. Permanent bookmark nodes are the root and
303   // its immediate children. For removing all non permanent nodes just remove
304   // all children of non-root permanent nodes.
305   {
306     base::AutoLock url_lock(url_lock_);
307     for (int i = 0; i < root_.child_count(); ++i) {
308       const BookmarkNode* permanent_node = root_.GetChild(i);
309       for (int j = permanent_node->child_count() - 1; j >= 0; --j) {
310         BookmarkNode* child_node = AsMutable(permanent_node->GetChild(j));
311         removed_nodes.push_back(child_node);
312         RemoveNodeAndGetRemovedUrls(child_node, &removed_urls);
313       }
314     }
315   }
316   EndExtensiveChanges();
317   if (store_.get())
318     store_->ScheduleSave();
319 
320   NotifyHistoryAboutRemovedBookmarks(removed_urls);
321 
322   FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
323                     BookmarkAllNodesRemoved(this));
324 }
325 
Move(const BookmarkNode * node,const BookmarkNode * new_parent,int index)326 void BookmarkModel::Move(const BookmarkNode* node,
327                          const BookmarkNode* new_parent,
328                          int index) {
329   if (!loaded_ || !node || !IsValidIndex(new_parent, index, true) ||
330       is_root_node(new_parent) || is_permanent_node(node)) {
331     NOTREACHED();
332     return;
333   }
334 
335   if (new_parent->HasAncestor(node)) {
336     // Can't make an ancestor of the node be a child of the node.
337     NOTREACHED();
338     return;
339   }
340 
341   const BookmarkNode* old_parent = node->parent();
342   int old_index = old_parent->GetIndexOf(node);
343 
344   if (old_parent == new_parent &&
345       (index == old_index || index == old_index + 1)) {
346     // Node is already in this position, nothing to do.
347     return;
348   }
349 
350   SetDateFolderModified(new_parent, Time::Now());
351 
352   if (old_parent == new_parent && index > old_index)
353     index--;
354   BookmarkNode* mutable_new_parent = AsMutable(new_parent);
355   mutable_new_parent->Add(AsMutable(node), index);
356 
357   if (store_.get())
358     store_->ScheduleSave();
359 
360   FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
361                     BookmarkNodeMoved(this, old_parent, old_index,
362                                       new_parent, index));
363 }
364 
Copy(const BookmarkNode * node,const BookmarkNode * new_parent,int index)365 void BookmarkModel::Copy(const BookmarkNode* node,
366                          const BookmarkNode* new_parent,
367                          int index) {
368   if (!loaded_ || !node || !IsValidIndex(new_parent, index, true) ||
369       is_root_node(new_parent) || is_permanent_node(node)) {
370     NOTREACHED();
371     return;
372   }
373 
374   if (new_parent->HasAncestor(node)) {
375     // Can't make an ancestor of the node be a child of the node.
376     NOTREACHED();
377     return;
378   }
379 
380   SetDateFolderModified(new_parent, Time::Now());
381   BookmarkNodeData drag_data(node);
382   std::vector<BookmarkNodeData::Element> elements(drag_data.elements);
383   // CloneBookmarkNode will use BookmarkModel methods to do the job, so we
384   // don't need to send notifications here.
385   bookmark_utils::CloneBookmarkNode(this, elements, new_parent, index, true);
386 
387   if (store_.get())
388     store_->ScheduleSave();
389 }
390 
GetFavicon(const BookmarkNode * node)391 const gfx::Image& BookmarkModel::GetFavicon(const BookmarkNode* node) {
392   DCHECK(node);
393   if (node->favicon_state() == BookmarkNode::INVALID_FAVICON) {
394     BookmarkNode* mutable_node = AsMutable(node);
395     mutable_node->set_favicon_state(BookmarkNode::LOADING_FAVICON);
396     LoadFavicon(mutable_node);
397   }
398   return node->favicon();
399 }
400 
SetTitle(const BookmarkNode * node,const base::string16 & title)401 void BookmarkModel::SetTitle(const BookmarkNode* node, const base::string16& title) {
402   if (!node) {
403     NOTREACHED();
404     return;
405   }
406   if (node->GetTitle() == title)
407     return;
408 
409   if (is_permanent_node(node)) {
410     NOTREACHED();
411     return;
412   }
413 
414   FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
415                     OnWillChangeBookmarkNode(this, node));
416 
417   // The title index doesn't support changing the title, instead we remove then
418   // add it back.
419   index_->Remove(node);
420   AsMutable(node)->SetTitle(title);
421   index_->Add(node);
422 
423   if (store_.get())
424     store_->ScheduleSave();
425 
426   FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
427                     BookmarkNodeChanged(this, node));
428 }
429 
SetURL(const BookmarkNode * node,const GURL & url)430 void BookmarkModel::SetURL(const BookmarkNode* node, const GURL& url) {
431   if (!node) {
432     NOTREACHED();
433     return;
434   }
435 
436   // We cannot change the URL of a folder.
437   if (node->is_folder()) {
438     NOTREACHED();
439     return;
440   }
441 
442   if (node->url() == url)
443     return;
444 
445   BookmarkNode* mutable_node = AsMutable(node);
446   mutable_node->InvalidateFavicon();
447   CancelPendingFaviconLoadRequests(mutable_node);
448 
449   FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
450                     OnWillChangeBookmarkNode(this, node));
451 
452   {
453     base::AutoLock url_lock(url_lock_);
454     NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.find(
455         mutable_node);
456     DCHECK(i != nodes_ordered_by_url_set_.end());
457     // i points to the first node with the URL, advance until we find the
458     // node we're removing.
459     while (*i != node)
460       ++i;
461     nodes_ordered_by_url_set_.erase(i);
462 
463     mutable_node->set_url(url);
464     nodes_ordered_by_url_set_.insert(mutable_node);
465   }
466 
467   if (store_.get())
468     store_->ScheduleSave();
469 
470   FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
471                     BookmarkNodeChanged(this, node));
472 }
473 
SetNodeMetaInfo(const BookmarkNode * node,const std::string & key,const std::string & value)474 void BookmarkModel::SetNodeMetaInfo(const BookmarkNode* node,
475                                     const std::string& key,
476                                     const std::string& value) {
477   std::string old_value;
478   if (node->GetMetaInfo(key, &old_value) && old_value == value)
479     return;
480 
481   FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
482                     OnWillChangeBookmarkMetaInfo(this, node));
483 
484   if (AsMutable(node)->SetMetaInfo(key, value) && store_.get())
485     store_->ScheduleSave();
486 
487   FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
488                     BookmarkMetaInfoChanged(this, node));
489 }
490 
SetNodeMetaInfoMap(const BookmarkNode * node,const BookmarkNode::MetaInfoMap & meta_info_map)491 void BookmarkModel::SetNodeMetaInfoMap(
492     const BookmarkNode* node,
493     const BookmarkNode::MetaInfoMap& meta_info_map) {
494   const BookmarkNode::MetaInfoMap* old_meta_info_map = node->GetMetaInfoMap();
495   if ((!old_meta_info_map && meta_info_map.empty()) ||
496       (old_meta_info_map && meta_info_map == *old_meta_info_map))
497     return;
498 
499   FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
500                     OnWillChangeBookmarkMetaInfo(this, node));
501 
502   AsMutable(node)->SetMetaInfoMap(meta_info_map);
503   if (store_.get())
504     store_->ScheduleSave();
505 
506   FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
507                     BookmarkMetaInfoChanged(this, node));
508 }
509 
DeleteNodeMetaInfo(const BookmarkNode * node,const std::string & key)510 void BookmarkModel::DeleteNodeMetaInfo(const BookmarkNode* node,
511                                        const std::string& key) {
512   const BookmarkNode::MetaInfoMap* meta_info_map = node->GetMetaInfoMap();
513   if (!meta_info_map || meta_info_map->find(key) == meta_info_map->end())
514     return;
515 
516   FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
517                     OnWillChangeBookmarkMetaInfo(this, node));
518 
519   if (AsMutable(node)->DeleteMetaInfo(key) && store_.get())
520     store_->ScheduleSave();
521 
522   FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
523                     BookmarkMetaInfoChanged(this, node));
524 }
525 
SetNodeSyncTransactionVersion(const BookmarkNode * node,int64 sync_transaction_version)526 void BookmarkModel::SetNodeSyncTransactionVersion(
527     const BookmarkNode* node,
528     int64 sync_transaction_version) {
529   if (sync_transaction_version == node->sync_transaction_version())
530     return;
531 
532   AsMutable(node)->set_sync_transaction_version(sync_transaction_version);
533   if (store_.get())
534     store_->ScheduleSave();
535 }
536 
SetDateAdded(const BookmarkNode * node,base::Time date_added)537 void BookmarkModel::SetDateAdded(const BookmarkNode* node,
538                                  base::Time date_added) {
539   if (!node) {
540     NOTREACHED();
541     return;
542   }
543 
544   if (node->date_added() == date_added)
545     return;
546 
547   if (is_permanent_node(node)) {
548     NOTREACHED();
549     return;
550   }
551 
552   AsMutable(node)->set_date_added(date_added);
553 
554   // Syncing might result in dates newer than the folder's last modified date.
555   if (date_added > node->parent()->date_folder_modified()) {
556     // Will trigger store_->ScheduleSave().
557     SetDateFolderModified(node->parent(), date_added);
558   } else if (store_.get()) {
559     store_->ScheduleSave();
560   }
561 }
562 
GetNodesByURL(const GURL & url,std::vector<const BookmarkNode * > * nodes)563 void BookmarkModel::GetNodesByURL(const GURL& url,
564                                   std::vector<const BookmarkNode*>* nodes) {
565   base::AutoLock url_lock(url_lock_);
566   BookmarkNode tmp_node(url);
567   NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.find(&tmp_node);
568   while (i != nodes_ordered_by_url_set_.end() && (*i)->url() == url) {
569     nodes->push_back(*i);
570     ++i;
571   }
572 }
573 
GetMostRecentlyAddedNodeForURL(const GURL & url)574 const BookmarkNode* BookmarkModel::GetMostRecentlyAddedNodeForURL(
575     const GURL& url) {
576   std::vector<const BookmarkNode*> nodes;
577   GetNodesByURL(url, &nodes);
578   if (nodes.empty())
579     return NULL;
580 
581   std::sort(nodes.begin(), nodes.end(), &bookmark_utils::MoreRecentlyAdded);
582   return nodes.front();
583 }
584 
HasBookmarks()585 bool BookmarkModel::HasBookmarks() {
586   base::AutoLock url_lock(url_lock_);
587   return !nodes_ordered_by_url_set_.empty();
588 }
589 
IsBookmarked(const GURL & url)590 bool BookmarkModel::IsBookmarked(const GURL& url) {
591   base::AutoLock url_lock(url_lock_);
592   return IsBookmarkedNoLock(url);
593 }
594 
GetBookmarks(std::vector<BookmarkService::URLAndTitle> * bookmarks)595 void BookmarkModel::GetBookmarks(
596     std::vector<BookmarkService::URLAndTitle>* bookmarks) {
597   base::AutoLock url_lock(url_lock_);
598   const GURL* last_url = NULL;
599   for (NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.begin();
600        i != nodes_ordered_by_url_set_.end(); ++i) {
601     const GURL* url = &((*i)->url());
602     // Only add unique URLs.
603     if (!last_url || *url != *last_url) {
604       BookmarkService::URLAndTitle bookmark;
605       bookmark.url = *url;
606       bookmark.title = (*i)->GetTitle();
607       bookmarks->push_back(bookmark);
608     }
609     last_url = url;
610   }
611 }
612 
BlockTillLoaded()613 void BookmarkModel::BlockTillLoaded() {
614   loaded_signal_.Wait();
615 }
616 
GetNodeByID(int64 id) const617 const BookmarkNode* BookmarkModel::GetNodeByID(int64 id) const {
618   // TODO(sky): TreeNode needs a method that visits all nodes using a predicate.
619   return GetNodeByID(&root_, id);
620 }
621 
AddFolder(const BookmarkNode * parent,int index,const base::string16 & title)622 const BookmarkNode* BookmarkModel::AddFolder(const BookmarkNode* parent,
623                                              int index,
624                                              const base::string16& title) {
625   if (!loaded_ || is_root_node(parent) || !IsValidIndex(parent, index, true)) {
626     // Can't add to the root.
627     NOTREACHED();
628     return NULL;
629   }
630 
631   BookmarkNode* new_node = new BookmarkNode(generate_next_node_id(), GURL());
632   new_node->set_date_folder_modified(Time::Now());
633   // Folders shouldn't have line breaks in their titles.
634   new_node->SetTitle(title);
635   new_node->set_type(BookmarkNode::FOLDER);
636 
637   return AddNode(AsMutable(parent), index, new_node);
638 }
639 
AddURL(const BookmarkNode * parent,int index,const base::string16 & title,const GURL & url)640 const BookmarkNode* BookmarkModel::AddURL(const BookmarkNode* parent,
641                                           int index,
642                                           const base::string16& title,
643                                           const GURL& url) {
644   return AddURLWithCreationTime(parent, index,
645                                 CollapseWhitespace(title, false),
646                                 url, Time::Now());
647 }
648 
AddURLWithCreationTime(const BookmarkNode * parent,int index,const base::string16 & title,const GURL & url,const Time & creation_time)649 const BookmarkNode* BookmarkModel::AddURLWithCreationTime(
650     const BookmarkNode* parent,
651     int index,
652     const base::string16& title,
653     const GURL& url,
654     const Time& creation_time) {
655   if (!loaded_ || !url.is_valid() || is_root_node(parent) ||
656       !IsValidIndex(parent, index, true)) {
657     NOTREACHED();
658     return NULL;
659   }
660 
661   // Syncing may result in dates newer than the last modified date.
662   if (creation_time > parent->date_folder_modified())
663     SetDateFolderModified(parent, creation_time);
664 
665   BookmarkNode* new_node = new BookmarkNode(generate_next_node_id(), url);
666   new_node->SetTitle(title);
667   new_node->set_date_added(creation_time);
668   new_node->set_type(BookmarkNode::URL);
669 
670   {
671     // Only hold the lock for the duration of the insert.
672     base::AutoLock url_lock(url_lock_);
673     nodes_ordered_by_url_set_.insert(new_node);
674   }
675 
676   return AddNode(AsMutable(parent), index, new_node);
677 }
678 
SortChildren(const BookmarkNode * parent)679 void BookmarkModel::SortChildren(const BookmarkNode* parent) {
680   if (!parent || !parent->is_folder() || is_root_node(parent) ||
681       parent->child_count() <= 1) {
682     return;
683   }
684 
685   FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
686                     OnWillReorderBookmarkNode(this, parent));
687 
688   UErrorCode error = U_ZERO_ERROR;
689   scoped_ptr<icu::Collator> collator(icu::Collator::createInstance(error));
690   if (U_FAILURE(error))
691     collator.reset(NULL);
692   BookmarkNode* mutable_parent = AsMutable(parent);
693   std::sort(mutable_parent->children().begin(),
694             mutable_parent->children().end(),
695             SortComparator(collator.get()));
696 
697   if (store_.get())
698     store_->ScheduleSave();
699 
700   FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
701                     BookmarkNodeChildrenReordered(this, parent));
702 }
703 
ReorderChildren(const BookmarkNode * parent,const std::vector<const BookmarkNode * > & ordered_nodes)704 void BookmarkModel::ReorderChildren(
705     const BookmarkNode* parent,
706     const std::vector<const BookmarkNode*>& ordered_nodes) {
707   // Ensure that all children in |parent| are in |ordered_nodes|.
708   DCHECK_EQ(static_cast<size_t>(parent->child_count()), ordered_nodes.size());
709   for (size_t i = 0; i < ordered_nodes.size(); ++i)
710     DCHECK_EQ(parent, ordered_nodes[i]->parent());
711 
712   FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
713                     OnWillReorderBookmarkNode(this, parent));
714 
715   AsMutable(parent)->SetChildren(
716       *(reinterpret_cast<const std::vector<BookmarkNode*>*>(&ordered_nodes)));
717 
718   if (store_.get())
719     store_->ScheduleSave();
720 
721   FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
722                     BookmarkNodeChildrenReordered(this, parent));
723 }
724 
SetDateFolderModified(const BookmarkNode * parent,const Time time)725 void BookmarkModel::SetDateFolderModified(const BookmarkNode* parent,
726                                           const Time time) {
727   DCHECK(parent);
728   AsMutable(parent)->set_date_folder_modified(time);
729 
730   if (store_.get())
731     store_->ScheduleSave();
732 }
733 
ResetDateFolderModified(const BookmarkNode * node)734 void BookmarkModel::ResetDateFolderModified(const BookmarkNode* node) {
735   SetDateFolderModified(node, Time());
736 }
737 
GetBookmarksWithTitlesMatching(const base::string16 & text,size_t max_count,std::vector<BookmarkTitleMatch> * matches)738 void BookmarkModel::GetBookmarksWithTitlesMatching(
739     const base::string16& text,
740     size_t max_count,
741     std::vector<BookmarkTitleMatch>* matches) {
742   if (!loaded_)
743     return;
744 
745   index_->GetBookmarksWithTitlesMatching(text, max_count, matches);
746 }
747 
ClearStore()748 void BookmarkModel::ClearStore() {
749   registrar_.RemoveAll();
750   store_ = NULL;
751 }
752 
SetPermanentNodeVisible(BookmarkNode::Type type,bool value)753 void BookmarkModel::SetPermanentNodeVisible(BookmarkNode::Type type,
754                                             bool value) {
755   DCHECK(loaded_);
756   switch (type) {
757     case BookmarkNode::BOOKMARK_BAR:
758       bookmark_bar_node_->set_visible(value);
759       break;
760     case BookmarkNode::OTHER_NODE:
761       other_node_->set_visible(value);
762       break;
763     case BookmarkNode::MOBILE:
764       mobile_node_->set_visible(value);
765       break;
766     default:
767       NOTREACHED();
768   }
769 }
770 
IsBookmarkedNoLock(const GURL & url)771 bool BookmarkModel::IsBookmarkedNoLock(const GURL& url) {
772   BookmarkNode tmp_node(url);
773   return (nodes_ordered_by_url_set_.find(&tmp_node) !=
774           nodes_ordered_by_url_set_.end());
775 }
776 
RemoveNode(BookmarkNode * node,std::set<GURL> * removed_urls)777 void BookmarkModel::RemoveNode(BookmarkNode* node,
778                                std::set<GURL>* removed_urls) {
779   if (!loaded_ || !node || is_permanent_node(node)) {
780     NOTREACHED();
781     return;
782   }
783 
784   url_lock_.AssertAcquired();
785   if (node->is_url()) {
786     // NOTE: this is called in such a way that url_lock_ is already held. As
787     // such, this doesn't explicitly grab the lock.
788     NodesOrderedByURLSet::iterator i = nodes_ordered_by_url_set_.find(node);
789     DCHECK(i != nodes_ordered_by_url_set_.end());
790     // i points to the first node with the URL, advance until we find the
791     // node we're removing.
792     while (*i != node)
793       ++i;
794     nodes_ordered_by_url_set_.erase(i);
795     removed_urls->insert(node->url());
796 
797     index_->Remove(node);
798   }
799 
800   CancelPendingFaviconLoadRequests(node);
801 
802   // Recurse through children.
803   for (int i = node->child_count() - 1; i >= 0; --i)
804     RemoveNode(node->GetChild(i), removed_urls);
805 }
806 
DoneLoading(BookmarkLoadDetails * details_delete_me)807 void BookmarkModel::DoneLoading(BookmarkLoadDetails* details_delete_me) {
808   DCHECK(details_delete_me);
809   scoped_ptr<BookmarkLoadDetails> details(details_delete_me);
810   if (loaded_) {
811     // We should only ever be loaded once.
812     NOTREACHED();
813     return;
814   }
815 
816   next_node_id_ = details->max_id();
817   if (details->computed_checksum() != details->stored_checksum() ||
818       details->ids_reassigned()) {
819     // If bookmarks file changed externally, the IDs may have changed
820     // externally. In that case, the decoder may have reassigned IDs to make
821     // them unique. So when the file has changed externally, we should save the
822     // bookmarks file to persist new IDs.
823     if (store_.get())
824       store_->ScheduleSave();
825   }
826   bookmark_bar_node_ = details->release_bb_node();
827   other_node_ = details->release_other_folder_node();
828   mobile_node_ = details->release_mobile_folder_node();
829   index_.reset(details->release_index());
830 
831   // WARNING: order is important here, various places assume the order is
832   // constant.
833   root_.Add(bookmark_bar_node_, 0);
834   root_.Add(other_node_, 1);
835   root_.Add(mobile_node_, 2);
836 
837   root_.SetMetaInfoMap(details->model_meta_info_map());
838   root_.set_sync_transaction_version(details->model_sync_transaction_version());
839 
840   {
841     base::AutoLock url_lock(url_lock_);
842     // Update nodes_ordered_by_url_set_ from the nodes.
843     PopulateNodesByURL(&root_);
844   }
845 
846   loaded_ = true;
847 
848   loaded_signal_.Signal();
849 
850   // Notify our direct observers.
851   FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
852                     Loaded(this, details->ids_reassigned()));
853 }
854 
RemoveAndDeleteNode(BookmarkNode * delete_me)855 void BookmarkModel::RemoveAndDeleteNode(BookmarkNode* delete_me) {
856   scoped_ptr<BookmarkNode> node(delete_me);
857 
858   const BookmarkNode* parent = node->parent();
859   DCHECK(parent);
860   int index = parent->GetIndexOf(node.get());
861 
862   FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
863                     OnWillRemoveBookmarks(this, parent, index, node.get()));
864 
865   std::set<GURL> removed_urls;
866   {
867     base::AutoLock url_lock(url_lock_);
868     RemoveNodeAndGetRemovedUrls(node.get(), &removed_urls);
869   }
870 
871   if (store_.get())
872     store_->ScheduleSave();
873 
874   NotifyHistoryAboutRemovedBookmarks(removed_urls);
875 
876   FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
877                     BookmarkNodeRemoved(this, parent, index, node.get()));
878 }
879 
RemoveNodeAndGetRemovedUrls(BookmarkNode * node,std::set<GURL> * removed_urls)880 void BookmarkModel::RemoveNodeAndGetRemovedUrls(BookmarkNode* node,
881                                                 std::set<GURL>* removed_urls) {
882   // NOTE: this method should be always called with |url_lock_| held.
883   // This method does not explicitly acquires a lock.
884   url_lock_.AssertAcquired();
885   DCHECK(removed_urls);
886   BookmarkNode* parent = AsMutable(node->parent());
887   DCHECK(parent);
888   parent->Remove(node);
889   RemoveNode(node, removed_urls);
890   // RemoveNode adds an entry to removed_urls for each node of type URL. As we
891   // allow duplicates we need to remove any entries that are still bookmarked.
892   for (std::set<GURL>::iterator i = removed_urls->begin();
893        i != removed_urls->end();) {
894     if (IsBookmarkedNoLock(*i)) {
895       // When we erase the iterator pointing at the erasee is
896       // invalidated, so using i++ here within the "erase" call is
897       // important as it advances the iterator before passing the
898       // old value through to erase.
899       removed_urls->erase(i++);
900     } else {
901       ++i;
902     }
903   }
904 }
905 
NotifyHistoryAboutRemovedBookmarks(const std::set<GURL> & removed_bookmark_urls) const906 void BookmarkModel::NotifyHistoryAboutRemovedBookmarks(
907     const std::set<GURL>& removed_bookmark_urls) const {
908   if (removed_bookmark_urls.empty()) {
909     // No point in sending out notification if the starred state didn't change.
910     return;
911   }
912 
913   if (profile_) {
914     HistoryService* history =
915         HistoryServiceFactory::GetForProfile(profile_,
916                                              Profile::EXPLICIT_ACCESS);
917     if (history)
918       history->URLsNoLongerBookmarked(removed_bookmark_urls);
919   }
920 }
921 
AddNode(BookmarkNode * parent,int index,BookmarkNode * node)922 BookmarkNode* BookmarkModel::AddNode(BookmarkNode* parent,
923                                      int index,
924                                      BookmarkNode* node) {
925   parent->Add(node, index);
926 
927   if (store_.get())
928     store_->ScheduleSave();
929 
930   FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
931                     BookmarkNodeAdded(this, parent, index));
932 
933   index_->Add(node);
934 
935   return node;
936 }
937 
GetNodeByID(const BookmarkNode * node,int64 id) const938 const BookmarkNode* BookmarkModel::GetNodeByID(const BookmarkNode* node,
939                                                int64 id) const {
940   if (node->id() == id)
941     return node;
942 
943   for (int i = 0, child_count = node->child_count(); i < child_count; ++i) {
944     const BookmarkNode* result = GetNodeByID(node->GetChild(i), id);
945     if (result)
946       return result;
947   }
948   return NULL;
949 }
950 
IsValidIndex(const BookmarkNode * parent,int index,bool allow_end)951 bool BookmarkModel::IsValidIndex(const BookmarkNode* parent,
952                                  int index,
953                                  bool allow_end) {
954   return (parent && parent->is_folder() &&
955           (index >= 0 && (index < parent->child_count() ||
956                           (allow_end && index == parent->child_count()))));
957 }
958 
CreatePermanentNode(BookmarkNode::Type type)959 BookmarkPermanentNode* BookmarkModel::CreatePermanentNode(
960     BookmarkNode::Type type) {
961   DCHECK(type == BookmarkNode::BOOKMARK_BAR ||
962          type == BookmarkNode::OTHER_NODE ||
963          type == BookmarkNode::MOBILE);
964   BookmarkPermanentNode* node =
965       new BookmarkPermanentNode(generate_next_node_id());
966   if (type == BookmarkNode::MOBILE)
967     node->set_visible(false);  // Mobile node is initially hidden.
968 
969   int title_id;
970   switch (type) {
971     case BookmarkNode::BOOKMARK_BAR:
972       title_id = IDS_BOOKMARK_BAR_FOLDER_NAME;
973       break;
974     case BookmarkNode::OTHER_NODE:
975       title_id = IDS_BOOKMARK_BAR_OTHER_FOLDER_NAME;
976       break;
977     case BookmarkNode::MOBILE:
978       title_id = IDS_BOOKMARK_BAR_MOBILE_FOLDER_NAME;
979       break;
980     default:
981       NOTREACHED();
982       title_id = IDS_BOOKMARK_BAR_FOLDER_NAME;
983       break;
984   }
985   node->SetTitle(l10n_util::GetStringUTF16(title_id));
986   node->set_type(type);
987   return node;
988 }
989 
OnFaviconDataAvailable(BookmarkNode * node,const chrome::FaviconImageResult & image_result)990 void BookmarkModel::OnFaviconDataAvailable(
991     BookmarkNode* node,
992     const chrome::FaviconImageResult& image_result) {
993   DCHECK(node);
994   node->set_favicon_load_task_id(CancelableTaskTracker::kBadTaskId);
995   node->set_favicon_state(BookmarkNode::LOADED_FAVICON);
996   if (!image_result.image.IsEmpty()) {
997     node->set_favicon(image_result.image);
998     node->set_icon_url(image_result.icon_url);
999     FaviconLoaded(node);
1000   }
1001 }
1002 
LoadFavicon(BookmarkNode * node)1003 void BookmarkModel::LoadFavicon(BookmarkNode* node) {
1004   if (node->is_folder())
1005     return;
1006 
1007   DCHECK(node->url().is_valid());
1008   FaviconService* favicon_service = FaviconServiceFactory::GetForProfile(
1009       profile_, Profile::EXPLICIT_ACCESS);
1010   if (!favicon_service)
1011     return;
1012   FaviconService::Handle handle = favicon_service->GetFaviconImageForURL(
1013       FaviconService::FaviconForURLParams(node->url(),
1014                                           chrome::FAVICON,
1015                                           gfx::kFaviconSize),
1016       base::Bind(&BookmarkModel::OnFaviconDataAvailable,
1017                  base::Unretained(this), node),
1018       &cancelable_task_tracker_);
1019   node->set_favicon_load_task_id(handle);
1020 }
1021 
FaviconLoaded(const BookmarkNode * node)1022 void BookmarkModel::FaviconLoaded(const BookmarkNode* node) {
1023   FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
1024                     BookmarkNodeFaviconChanged(this, node));
1025 }
1026 
CancelPendingFaviconLoadRequests(BookmarkNode * node)1027 void BookmarkModel::CancelPendingFaviconLoadRequests(BookmarkNode* node) {
1028   if (node->favicon_load_task_id() != CancelableTaskTracker::kBadTaskId) {
1029     cancelable_task_tracker_.TryCancel(node->favicon_load_task_id());
1030     node->set_favicon_load_task_id(CancelableTaskTracker::kBadTaskId);
1031   }
1032 }
1033 
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)1034 void BookmarkModel::Observe(int type,
1035                             const content::NotificationSource& source,
1036                             const content::NotificationDetails& details) {
1037   switch (type) {
1038     case chrome::NOTIFICATION_FAVICON_CHANGED: {
1039       // Prevent the observers from getting confused for multiple favicon loads.
1040       content::Details<FaviconChangedDetails> favicon_details(details);
1041       for (std::set<GURL>::const_iterator i = favicon_details->urls.begin();
1042            i != favicon_details->urls.end(); ++i) {
1043         std::vector<const BookmarkNode*> nodes;
1044         GetNodesByURL(*i, &nodes);
1045         for (size_t i = 0; i < nodes.size(); ++i) {
1046           // Got an updated favicon, for a URL, do a new request.
1047           BookmarkNode* node = AsMutable(nodes[i]);
1048           node->InvalidateFavicon();
1049           CancelPendingFaviconLoadRequests(node);
1050           FOR_EACH_OBSERVER(BookmarkModelObserver, observers_,
1051                             BookmarkNodeFaviconChanged(this, node));
1052         }
1053       }
1054       break;
1055     }
1056 
1057     default:
1058       NOTREACHED();
1059       break;
1060   }
1061 }
1062 
PopulateNodesByURL(BookmarkNode * node)1063 void BookmarkModel::PopulateNodesByURL(BookmarkNode* node) {
1064   // NOTE: this is called with url_lock_ already held. As such, this doesn't
1065   // explicitly grab the lock.
1066   if (node->is_url())
1067     nodes_ordered_by_url_set_.insert(node);
1068   for (int i = 0; i < node->child_count(); ++i)
1069     PopulateNodesByURL(node->GetChild(i));
1070 }
1071 
generate_next_node_id()1072 int64 BookmarkModel::generate_next_node_id() {
1073   return next_node_id_++;
1074 }
1075 
CreateLoadDetails()1076 BookmarkLoadDetails* BookmarkModel::CreateLoadDetails() {
1077   BookmarkPermanentNode* bb_node =
1078       CreatePermanentNode(BookmarkNode::BOOKMARK_BAR);
1079   BookmarkPermanentNode* other_node =
1080       CreatePermanentNode(BookmarkNode::OTHER_NODE);
1081   BookmarkPermanentNode* mobile_node =
1082       CreatePermanentNode(BookmarkNode::MOBILE);
1083   return new BookmarkLoadDetails(bb_node, other_node, mobile_node,
1084                                  new BookmarkIndex(profile_),
1085                                  next_node_id_);
1086 }
1087