Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(150)

Side by Side Diff: components/bookmarks/core/browser/bookmark_model.cc

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

Powered by Google App Engine
This is Rietveld 408576698