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