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