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