1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef CHROME_BROWSER_BOOKMARKS_BOOKMARK_MODEL_H_
6#define CHROME_BROWSER_BOOKMARKS_BOOKMARK_MODEL_H_
7
8#include <set>
9#include <vector>
10
11#include "base/basictypes.h"
12#include "base/compiler_specific.h"
13#include "base/memory/ref_counted.h"
14#include "base/memory/scoped_ptr.h"
15#include "base/observer_list.h"
16#include "base/strings/string16.h"
17#include "base/synchronization/lock.h"
18#include "base/synchronization/waitable_event.h"
19#include "chrome/browser/bookmarks/bookmark_service.h"
20#include "chrome/common/cancelable_task_tracker.h"
21#include "components/browser_context_keyed_service/browser_context_keyed_service.h"
22#include "content/public/browser/notification_observer.h"
23#include "content/public/browser/notification_registrar.h"
24#include "ui/base/models/tree_node_model.h"
25#include "ui/gfx/image/image.h"
26#include "url/gurl.h"
27
28class BookmarkExpandedStateTracker;
29class BookmarkIndex;
30class BookmarkLoadDetails;
31class BookmarkModel;
32class BookmarkModelObserver;
33class BookmarkStorage;
34struct BookmarkTitleMatch;
35class Profile;
36
37namespace base {
38class SequencedTaskRunner;
39}
40
41namespace chrome {
42struct FaviconImageResult;
43}
44
45// BookmarkNode ---------------------------------------------------------------
46
47// BookmarkNode contains information about a starred entry: title, URL, favicon,
48// id and type. BookmarkNodes are returned from BookmarkModel.
49class BookmarkNode : public ui::TreeNode<BookmarkNode> {
50 public:
51  enum Type {
52    URL,
53    FOLDER,
54    BOOKMARK_BAR,
55    OTHER_NODE,
56    MOBILE
57  };
58
59  enum FaviconState {
60    INVALID_FAVICON,
61    LOADING_FAVICON,
62    LOADED_FAVICON,
63  };
64
65  // Creates a new node with an id of 0 and |url|.
66  explicit BookmarkNode(const GURL& url);
67  // Creates a new node with |id| and |url|.
68  BookmarkNode(int64 id, const GURL& url);
69
70  virtual ~BookmarkNode();
71
72  // Set the node's internal title. Note that this neither invokes observers
73  // nor updates any bookmark model this node may be in. For that functionality,
74  // BookmarkModel::SetTitle(..) should be used instead.
75  virtual void SetTitle(const string16& title) OVERRIDE;
76
77  // Returns an unique id for this node.
78  // For bookmark nodes that are managed by the bookmark model, the IDs are
79  // persisted across sessions.
80  int64 id() const { return id_; }
81  void set_id(int64 id) { id_ = id; }
82
83  const GURL& url() const { return url_; }
84  void set_url(const GURL& url) { url_ = url; }
85
86  // Returns the favicon's URL. Returns an empty URL if there is no favicon
87  // associated with this bookmark.
88  const GURL& icon_url() const { return icon_url_; }
89
90  Type type() const { return type_; }
91  void set_type(Type type) { type_ = type; }
92
93  // Returns the time the node was added.
94  const base::Time& date_added() const { return date_added_; }
95  void set_date_added(const base::Time& date) { date_added_ = date; }
96
97  // Returns the last time the folder was modified. This is only maintained
98  // for folders (including the bookmark bar and other folder).
99  const base::Time& date_folder_modified() const {
100    return date_folder_modified_;
101  }
102  void set_date_folder_modified(const base::Time& date) {
103    date_folder_modified_ = date;
104  }
105
106  // Convenience for testing if this node represents a folder. A folder is a
107  // node whose type is not URL.
108  bool is_folder() const { return type_ != URL; }
109  bool is_url() const { return type_ == URL; }
110
111  bool is_favicon_loaded() const { return favicon_state_ == LOADED_FAVICON; }
112
113  // Accessor method for controlling the visibility of a bookmark node/sub-tree.
114  // Note that visibility is not propagated down the tree hierarchy so if a
115  // parent node is marked as invisible, a child node may return "Visible". This
116  // function is primarily useful when traversing the model to generate a UI
117  // representation but we may want to suppress some nodes.
118  virtual bool IsVisible() const;
119
120  // Gets/sets/deletes value of |key| in the meta info represented by
121  // |meta_info_str_|. Return true if key is found in meta info for gets or
122  // meta info is changed indeed for sets/deletes.
123  bool GetMetaInfo(const std::string& key, std::string* value) const;
124  bool SetMetaInfo(const std::string& key, const std::string& value);
125  bool DeleteMetaInfo(const std::string& key);
126  void set_meta_info_str(const std::string& meta_info_str) {
127    meta_info_str_.reserve(meta_info_str.size());
128    meta_info_str_ = meta_info_str.substr(0);
129  }
130  const std::string& meta_info_str() const { return meta_info_str_; }
131
132  // TODO(sky): Consider adding last visit time here, it'll greatly simplify
133  // HistoryContentsProvider.
134
135 private:
136  friend class BookmarkModel;
137
138  // A helper function to initialize various fields during construction.
139  void Initialize(int64 id);
140
141  // Called when the favicon becomes invalid.
142  void InvalidateFavicon();
143
144  // Sets the favicon's URL.
145  void set_icon_url(const GURL& icon_url) {
146    icon_url_ = icon_url;
147  }
148
149  const gfx::Image& favicon() const { return favicon_; }
150  void set_favicon(const gfx::Image& icon) { favicon_ = icon; }
151
152  FaviconState favicon_state() const { return favicon_state_; }
153  void set_favicon_state(FaviconState state) { favicon_state_ = state; }
154
155  CancelableTaskTracker::TaskId favicon_load_task_id() const {
156    return favicon_load_task_id_;
157  }
158  void set_favicon_load_task_id(CancelableTaskTracker::TaskId id) {
159    favicon_load_task_id_ = id;
160  }
161
162  // The unique identifier for this node.
163  int64 id_;
164
165  // The URL of this node. BookmarkModel maintains maps off this URL, so changes
166  // to the URL must be done through the BookmarkModel.
167  GURL url_;
168
169  // The type of this node. See enum above.
170  Type type_;
171
172  // Date of when this node was created.
173  base::Time date_added_;
174
175  // Date of the last modification. Only used for folders.
176  base::Time date_folder_modified_;
177
178  // The favicon of this node.
179  gfx::Image favicon_;
180
181  // The URL of the node's favicon.
182  GURL icon_url_;
183
184  // The loading state of the favicon.
185  FaviconState favicon_state_;
186
187  // If not CancelableTaskTracker::kBadTaskId, it indicates we're loading the
188  // favicon and the task is tracked by CancelabelTaskTracker.
189  CancelableTaskTracker::TaskId favicon_load_task_id_;
190
191  // A JSON string representing a DictionaryValue that stores arbitrary meta
192  // information about the node. Use serialized format to save memory.
193  std::string meta_info_str_;
194
195  DISALLOW_COPY_AND_ASSIGN(BookmarkNode);
196};
197
198// BookmarkPermanentNode -------------------------------------------------------
199
200// Node used for the permanent folders (excluding the root).
201class BookmarkPermanentNode : public BookmarkNode {
202 public:
203  explicit BookmarkPermanentNode(int64 id);
204  virtual ~BookmarkPermanentNode();
205
206  // WARNING: this code is used for other projects. Contact noyau@ for details.
207  void set_visible(bool value) { visible_ = value; }
208
209  // BookmarkNode overrides:
210  virtual bool IsVisible() const OVERRIDE;
211
212 private:
213  bool visible_;
214
215  DISALLOW_COPY_AND_ASSIGN(BookmarkPermanentNode);
216};
217
218// BookmarkModel --------------------------------------------------------------
219
220// BookmarkModel provides a directed acyclic graph of URLs and folders.
221// Three graphs are provided for the three entry points: those on the 'bookmarks
222// bar', those in the 'other bookmarks' folder and those in the 'mobile' folder.
223//
224// An observer may be attached to observe relevant events.
225//
226// You should NOT directly create a BookmarkModel, instead go through the
227// BookmarkModelFactory.
228class BookmarkModel : public content::NotificationObserver,
229                      public BookmarkService,
230                      public BrowserContextKeyedService {
231 public:
232  explicit BookmarkModel(Profile* profile);
233  virtual ~BookmarkModel();
234
235  // Invoked prior to destruction to release any necessary resources.
236  virtual void Shutdown() OVERRIDE;
237
238  // Loads the bookmarks. This is called upon creation of the
239  // BookmarkModel. You need not invoke this directly.
240  // All load operations will be executed on |task_runner|.
241  void Load(const scoped_refptr<base::SequencedTaskRunner>& task_runner);
242
243  // Returns true if the model finished loading.
244  bool loaded() const { return loaded_; }
245
246  // Returns the root node. The 'bookmark bar' node and 'other' node are
247  // children of the root node.
248  const BookmarkNode* root_node() { return &root_; }
249
250  // Returns the 'bookmark bar' node. This is NULL until loaded.
251  const BookmarkNode* bookmark_bar_node() { return bookmark_bar_node_; }
252
253  // Returns the 'other' node. This is NULL until loaded.
254  const BookmarkNode* other_node() { return other_node_; }
255
256  // Returns the 'mobile' node. This is NULL until loaded.
257  const BookmarkNode* mobile_node() { return mobile_node_; }
258
259  bool is_root_node(const BookmarkNode* node) const { return node == &root_; }
260
261  // Returns whether the given |node| is one of the permanent nodes - root node,
262  // 'bookmark bar' node, 'other' node or 'mobile' node.
263  bool is_permanent_node(const BookmarkNode* node) const {
264    return node == &root_ ||
265           node == bookmark_bar_node_ ||
266           node == other_node_ ||
267           node == mobile_node_;
268  }
269
270  // Returns the parent the last node was added to. This never returns NULL
271  // (as long as the model is loaded).
272  const BookmarkNode* GetParentForNewNodes();
273
274  void AddObserver(BookmarkModelObserver* observer);
275  void RemoveObserver(BookmarkModelObserver* observer);
276
277  // Notifies the observers that an extensive set of changes is about to happen,
278  // such as during import or sync, so they can delay any expensive UI updates
279  // until it's finished.
280  void BeginExtensiveChanges();
281  void EndExtensiveChanges();
282
283  // Returns true if this bookmark model is currently in a mode where extensive
284  // changes might happen, such as for import and sync. This is helpful for
285  // observers that are created after the mode has started, and want to check
286  // state during their own initializer, such as the NTP.
287  bool IsDoingExtensiveChanges() const { return extensive_changes_ > 0; }
288
289  // Removes the node at the given |index| from |parent|. Removing a folder node
290  // recursively removes all nodes. Observers are notified immediately.
291  void Remove(const BookmarkNode* parent, int index);
292
293  // Removes all the non-permanent bookmark nodes. Observers are only notified
294  // when all nodes have been removed. There is no notification for individual
295  // node removals.
296  void RemoveAll();
297
298  // Moves |node| to |new_parent| and inserts it at the given |index|.
299  void Move(const BookmarkNode* node,
300            const BookmarkNode* new_parent,
301            int index);
302
303  // Inserts a copy of |node| into |new_parent| at |index|.
304  void Copy(const BookmarkNode* node,
305            const BookmarkNode* new_parent,
306            int index);
307
308  // Returns the favicon for |node|. If the favicon has not yet been
309  // loaded it is loaded and the observer of the model notified when done.
310  const gfx::Image& GetFavicon(const BookmarkNode* node);
311
312  // Sets the title of |node|.
313  void SetTitle(const BookmarkNode* node, const string16& title);
314
315  // Sets the URL of |node|.
316  void SetURL(const BookmarkNode* node, const GURL& url);
317
318  // Sets the date added time of |node|.
319  void SetDateAdded(const BookmarkNode* node, base::Time date_added);
320
321  // Returns the set of nodes with the |url|.
322  void GetNodesByURL(const GURL& url, std::vector<const BookmarkNode*>* nodes);
323
324  // Returns the most recently added node for the |url|. Returns NULL if |url|
325  // is not bookmarked.
326  const BookmarkNode* GetMostRecentlyAddedNodeForURL(const GURL& url);
327
328  // Returns true if there are bookmarks, otherwise returns false.
329  // This method is thread safe.
330  bool HasBookmarks();
331
332  // Returns true if there is a bookmark with the |url|.
333  // This method is thread safe.
334  // See BookmarkService for more details on this.
335  virtual bool IsBookmarked(const GURL& url) OVERRIDE;
336
337  // Returns all the bookmarked urls and their titles.
338  // This method is thread safe.
339  // See BookmarkService for more details on this.
340  virtual void GetBookmarks(
341      std::vector<BookmarkService::URLAndTitle>* urls) OVERRIDE;
342
343  // Blocks until loaded; this is NOT invoked on the main thread.
344  // See BookmarkService for more details on this.
345  virtual void BlockTillLoaded() OVERRIDE;
346
347  // Returns the node with |id|, or NULL if there is no node with |id|.
348  const BookmarkNode* GetNodeByID(int64 id) const;
349
350  // Adds a new folder node at the specified position.
351  const BookmarkNode* AddFolder(const BookmarkNode* parent,
352                                int index,
353                                const string16& title);
354
355  // Adds a url at the specified position.
356  const BookmarkNode* AddURL(const BookmarkNode* parent,
357                             int index,
358                             const string16& title,
359                             const GURL& url);
360
361  // Adds a url with a specific creation date.
362  const BookmarkNode* AddURLWithCreationTime(const BookmarkNode* parent,
363                                             int index,
364                                             const string16& title,
365                                             const GURL& url,
366                                             const base::Time& creation_time);
367
368  // Sorts the children of |parent|, notifying observers by way of the
369  // BookmarkNodeChildrenReordered method.
370  void SortChildren(const BookmarkNode* parent);
371
372  // Order the children of |parent| as specified in |ordered_nodes|.  This
373  // function should only be used to reorder the child nodes of |parent| and
374  // is not meant to move nodes between different parent. Notifies observers
375  // using the BookmarkNodeChildrenReordered method.
376  void ReorderChildren(const BookmarkNode* parent,
377                       const std::vector<const BookmarkNode*>& ordered_nodes);
378
379  // Sets the date when the folder was modified.
380  void SetDateFolderModified(const BookmarkNode* node, const base::Time time);
381
382  // Resets the 'date modified' time of the node to 0. This is used during
383  // importing to exclude the newly created folders from showing up in the
384  // combobox of most recently modified folders.
385  void ResetDateFolderModified(const BookmarkNode* node);
386
387  void GetBookmarksWithTitlesMatching(
388      const string16& text,
389      size_t max_count,
390      std::vector<BookmarkTitleMatch>* matches);
391
392  // Sets the store to NULL, making it so the BookmarkModel does not persist
393  // any changes to disk. This is only useful during testing to speed up
394  // testing.
395  void ClearStore();
396
397  // Returns the next node ID.
398  int64 next_node_id() const { return next_node_id_; }
399
400  // Returns the object responsible for tracking the set of expanded nodes in
401  // the bookmark editor.
402  BookmarkExpandedStateTracker* expanded_state_tracker() {
403    return expanded_state_tracker_.get();
404  }
405
406  // Sets the visibility of one of the permanent nodes. This is set by sync.
407  void SetPermanentNodeVisible(BookmarkNode::Type type, bool value);
408
409  // Sets/deletes meta info of |node|.
410  void SetNodeMetaInfo(const BookmarkNode* node,
411                       const std::string& key,
412                       const std::string& value);
413  void DeleteNodeMetaInfo(const BookmarkNode* node,
414                          const std::string& key);
415
416 private:
417  friend class BookmarkCodecTest;
418  friend class BookmarkModelTest;
419  friend class BookmarkStorage;
420
421  // Used to order BookmarkNodes by URL.
422  class NodeURLComparator {
423   public:
424    bool operator()(const BookmarkNode* n1, const BookmarkNode* n2) const {
425      return n1->url() < n2->url();
426    }
427  };
428
429  // Implementation of IsBookmarked. Before calling this the caller must obtain
430  // a lock on |url_lock_|.
431  bool IsBookmarkedNoLock(const GURL& url);
432
433  // Removes the node from internal maps and recurses through all children. If
434  // the node is a url, its url is added to removed_urls.
435  //
436  // This does NOT delete the node.
437  void RemoveNode(BookmarkNode* node, std::set<GURL>* removed_urls);
438
439  // Invoked when loading is finished. Sets |loaded_| and notifies observers.
440  // BookmarkModel takes ownership of |details|.
441  void DoneLoading(BookmarkLoadDetails* details);
442
443  // Populates |nodes_ordered_by_url_set_| from root.
444  void PopulateNodesByURL(BookmarkNode* node);
445
446  // Removes the node from its parent, but does not delete it. No notifications
447  // are sent. |removed_urls| is populated with the urls which no longer have
448  // any bookmarks associated with them.
449  // This method should be called after acquiring |url_lock_|.
450  void RemoveNodeAndGetRemovedUrls(BookmarkNode* node,
451                                   std::set<GURL>* removed_urls);
452
453  // Removes the node from its parent, sends notification, and deletes it.
454  // type specifies how the node should be removed.
455  void RemoveAndDeleteNode(BookmarkNode* delete_me);
456
457  // Notifies the history backend about urls of removed bookmarks.
458  void NotifyHistoryAboutRemovedBookmarks(
459      const std::set<GURL>& removed_bookmark_urls) const;
460
461  // Adds the |node| at |parent| in the specified |index| and notifies its
462  // observers.
463  BookmarkNode* AddNode(BookmarkNode* parent,
464                        int index,
465                        BookmarkNode* node);
466
467  // Implementation of GetNodeByID.
468  const BookmarkNode* GetNodeByID(const BookmarkNode* node, int64 id) const;
469
470  // Returns true if the parent and index are valid.
471  bool IsValidIndex(const BookmarkNode* parent, int index, bool allow_end);
472
473  // Creates one of the possible permanent nodes (bookmark bar node, other node
474  // and mobile node) from |type|.
475  BookmarkPermanentNode* CreatePermanentNode(BookmarkNode::Type type);
476
477  // Notification that a favicon has finished loading. If we can decode the
478  // favicon, FaviconLoaded is invoked.
479  void OnFaviconDataAvailable(BookmarkNode* node,
480                              const chrome::FaviconImageResult& image_result);
481
482  // Invoked from the node to load the favicon. Requests the favicon from the
483  // favicon service.
484  void LoadFavicon(BookmarkNode* node);
485
486  // Called to notify the observers that the favicon has been loaded.
487  void FaviconLoaded(const BookmarkNode* node);
488
489  // If we're waiting on a favicon for node, the load request is canceled.
490  void CancelPendingFaviconLoadRequests(BookmarkNode* node);
491
492  // content::NotificationObserver:
493  virtual void Observe(int type,
494                       const content::NotificationSource& source,
495                       const content::NotificationDetails& details) OVERRIDE;
496
497  // Generates and returns the next node ID.
498  int64 generate_next_node_id();
499
500  // Sets the maximum node ID to the given value.
501  // This is used by BookmarkCodec to report the maximum ID after it's done
502  // decoding since during decoding codec assigns node IDs.
503  void set_next_node_id(int64 id) { next_node_id_ = id; }
504
505  // Creates and returns a new BookmarkLoadDetails. It's up to the caller to
506  // delete the returned object.
507  BookmarkLoadDetails* CreateLoadDetails();
508
509  content::NotificationRegistrar registrar_;
510
511  Profile* profile_;
512
513  // Whether the initial set of data has been loaded.
514  bool loaded_;
515
516  // The root node. This contains the bookmark bar node and the 'other' node as
517  // children.
518  BookmarkNode root_;
519
520  BookmarkPermanentNode* bookmark_bar_node_;
521  BookmarkPermanentNode* other_node_;
522  BookmarkPermanentNode* mobile_node_;
523
524  // The maximum ID assigned to the bookmark nodes in the model.
525  int64 next_node_id_;
526
527  // The observers.
528  ObserverList<BookmarkModelObserver> observers_;
529
530  // Set of nodes ordered by URL. This is not a map to avoid copying the
531  // urls.
532  // WARNING: |nodes_ordered_by_url_set_| is accessed on multiple threads. As
533  // such, be sure and wrap all usage of it around |url_lock_|.
534  typedef std::multiset<BookmarkNode*, NodeURLComparator> NodesOrderedByURLSet;
535  NodesOrderedByURLSet nodes_ordered_by_url_set_;
536  base::Lock url_lock_;
537
538  // Used for loading favicons.
539  CancelableTaskTracker cancelable_task_tracker_;
540
541  // Reads/writes bookmarks to disk.
542  scoped_refptr<BookmarkStorage> store_;
543
544  scoped_ptr<BookmarkIndex> index_;
545
546  base::WaitableEvent loaded_signal_;
547
548  // See description of IsDoingExtensiveChanges above.
549  int extensive_changes_;
550
551  scoped_ptr<BookmarkExpandedStateTracker> expanded_state_tracker_;
552
553  DISALLOW_COPY_AND_ASSIGN(BookmarkModel);
554};
555
556#endif  // CHROME_BROWSER_BOOKMARKS_BOOKMARK_MODEL_H_
557