1// Copyright (c) 2011 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#pragma once
8
9#include "build/build_config.h"
10
11#include <set>
12#include <vector>
13
14#include "base/observer_list.h"
15#include "base/string16.h"
16#include "base/synchronization/lock.h"
17#include "base/synchronization/waitable_event.h"
18#include "chrome/browser/bookmarks/bookmark_model_observer.h"
19#include "chrome/browser/bookmarks/bookmark_service.h"
20#include "chrome/browser/favicon_service.h"
21#include "chrome/browser/history/history.h"
22#include "chrome/browser/history/history_types.h"
23#include "content/browser/cancelable_request.h"
24#include "content/common/notification_registrar.h"
25#include "googleurl/src/gurl.h"
26#include "testing/gtest/include/gtest/gtest_prod.h"
27#include "third_party/skia/include/core/SkBitmap.h"
28#include "ui/base/models/tree_node_model.h"
29
30class BookmarkIndex;
31class BookmarkLoadDetails;
32class BookmarkModel;
33class BookmarkStorage;
34class Profile;
35
36namespace bookmark_utils {
37struct TitleMatch;
38}
39
40// BookmarkNode ---------------------------------------------------------------
41
42// BookmarkNode contains information about a starred entry: title, URL, favicon,
43// star id and type. BookmarkNodes are returned from a BookmarkModel.
44//
45class BookmarkNode : public ui::TreeNode<BookmarkNode> {
46  friend class BookmarkModel;
47
48 public:
49  enum Type {
50    URL,
51    FOLDER,
52    BOOKMARK_BAR,
53    OTHER_NODE
54  };
55  // Creates a new node with the specified url and id of 0
56  explicit BookmarkNode(const GURL& url);
57  // Creates a new node with the specified url and id.
58  BookmarkNode(int64 id, const GURL& url);
59  virtual ~BookmarkNode();
60
61  // Returns the URL.
62  const GURL& GetURL() const { return url_; }
63  // Sets the URL to the given value.
64  void SetURL(const GURL& url) { url_ = url; }
65
66  // Returns a unique id for this node.
67  // For bookmark nodes that are managed by the bookmark model, the IDs are
68  // persisted across sessions.
69  int64 id() const { return id_; }
70  // Sets the id to the given value.
71  void set_id(int64 id) { id_ = id; }
72
73  // Returns the type of this node.
74  BookmarkNode::Type type() const { return type_; }
75  void set_type(BookmarkNode::Type type) { type_ = type; }
76
77  // Returns the time the bookmark/folder was added.
78  const base::Time& date_added() const { return date_added_; }
79  // Sets the time the bookmark/folder was added.
80  void set_date_added(const base::Time& date) { date_added_ = date; }
81
82  // Returns the last time the folder was modified. This is only maintained
83  // for folders (including the bookmark and other folder).
84  const base::Time& date_folder_modified() const {
85    return date_folder_modified_;
86  }
87  // Sets the last time the folder was modified.
88  void set_date_folder_modified(const base::Time& date) {
89    date_folder_modified_ = date;
90  }
91
92  // Convenience for testing if this nodes represents a folder. A folder is a
93  // node whose type is not URL.
94  bool is_folder() const { return type_ != URL; }
95
96  // Is this a URL?
97  bool is_url() const { return type_ == URL; }
98
99  // Returns the favicon. In nearly all cases you should use the method
100  // BookmarkModel::GetFavicon rather than this. BookmarkModel::GetFavicon
101  // takes care of loading the favicon if it isn't already loaded, where as
102  // this does not.
103  const SkBitmap& favicon() const { return favicon_; }
104  void set_favicon(const SkBitmap& icon) { favicon_ = icon; }
105
106  // The following methods are used by the bookmark model, and are not
107  // really useful outside of it.
108
109  bool is_favicon_loaded() const { return loaded_favicon_; }
110  void set_favicon_loaded(bool value) { loaded_favicon_ = value; }
111
112  HistoryService::Handle favicon_load_handle() const {
113    return favicon_load_handle_;
114  }
115  void set_favicon_load_handle(HistoryService::Handle handle) {
116    favicon_load_handle_ = handle;
117  }
118
119  // Called when the favicon becomes invalid.
120  void InvalidateFavicon();
121
122  // Resets the properties of the node from the supplied entry.
123  // This is used by the bookmark model and not really useful outside of it.
124  void Reset(const history::StarredEntry& entry);
125
126  // TODO(sky): Consider adding last visit time here, it'll greatly simplify
127  // HistoryContentsProvider.
128
129 private:
130  // helper to initialize various fields during construction.
131  void Initialize(int64 id);
132
133  // Unique identifier for this node.
134  int64 id_;
135
136  // Whether the favicon has been loaded.
137  bool loaded_favicon_;
138
139  // The favicon.
140  SkBitmap favicon_;
141
142  // If non-zero, it indicates we're loading the favicon and this is the handle
143  // from the HistoryService.
144  HistoryService::Handle favicon_load_handle_;
145
146  // The URL. BookmarkModel maintains maps off this URL, it is important that
147  // changes to the URL is done through the bookmark model.
148  GURL url_;
149
150  // Type of node.
151  BookmarkNode::Type type_;
152
153  // Date we were created.
154  base::Time date_added_;
155
156  // Time last modified. Only used for folders.
157  base::Time date_folder_modified_;
158
159  DISALLOW_COPY_AND_ASSIGN(BookmarkNode);
160};
161
162// BookmarkModel --------------------------------------------------------------
163
164// BookmarkModel provides a directed acyclic graph of the starred entries
165// and folders. Two graphs are provided for the two entry points: those on
166// the bookmark bar, and those in the other folder.
167//
168// An observer may be attached to observer relevant events.
169//
170// You should NOT directly create a BookmarkModel, instead go through the
171// Profile.
172
173class BookmarkModel : public NotificationObserver, public BookmarkService {
174  friend class BookmarkCodecTest;
175  friend class BookmarkModelTest;
176  friend class BookmarkStorage;
177
178 public:
179  explicit BookmarkModel(Profile* profile);
180  virtual ~BookmarkModel();
181
182  // Loads the bookmarks. This is called by Profile upon creation of the
183  // BookmarkModel. You need not invoke this directly.
184  void Load();
185
186  // Returns the root node. The bookmark bar node and other node are children of
187  // the root node.
188  const BookmarkNode* root_node() { return &root_; }
189
190  // Returns the bookmark bar node. This is NULL until loaded.
191  const BookmarkNode* GetBookmarkBarNode() { return bookmark_bar_node_; }
192
193  // Returns the 'other' node. This is NULL until loaded.
194  const BookmarkNode* other_node() { return other_node_; }
195
196  // Returns the parent the last node was added to. This never returns NULL
197  // (as long as the model is loaded).
198  const BookmarkNode* GetParentForNewNodes();
199
200  void AddObserver(BookmarkModelObserver* observer) {
201    observers_.AddObserver(observer);
202  }
203
204  void RemoveObserver(BookmarkModelObserver* observer) {
205    observers_.RemoveObserver(observer);
206  }
207
208  // Notify the observes that an import is about to happen, so they can
209  // delay any expensive UI updates until it is finished.
210  void BeginImportMode();
211  void EndImportMode();
212
213  // Unstars or deletes the specified entry. Removing a folder entry recursively
214  // unstars all nodes. Observers are notified immediately.
215  void Remove(const BookmarkNode* parent, int index);
216
217  // Moves the specified entry to a new location.
218  void Move(const BookmarkNode* node,
219            const BookmarkNode* new_parent,
220            int index);
221
222  // Duplicates a bookmark node and inserts it at a new location.
223  void Copy(const BookmarkNode* node,
224            const BookmarkNode* new_parent,
225            int index);
226
227  // Returns the favicon for |node|. If the favicon has not yet been
228  // loaded it is loaded and the observer of the model notified when done.
229  const SkBitmap& GetFavicon(const BookmarkNode* node);
230
231  // Sets the title of the specified node.
232  void SetTitle(const BookmarkNode* node, const string16& title);
233
234  // Sets the URL of the specified bookmark node.
235  void SetURL(const BookmarkNode* node, const GURL& url);
236
237  // Returns true if the model finished loading.
238  virtual bool IsLoaded();
239
240  // Returns the set of nodes with the specified URL.
241  void GetNodesByURL(const GURL& url, std::vector<const BookmarkNode*>* nodes);
242
243  // Returns the most recently added node for the url. Returns NULL if url is
244  // not bookmarked.
245  const BookmarkNode* GetMostRecentlyAddedNodeForURL(const GURL& url);
246
247  // Returns all the bookmarked urls. This method is thread safe.
248  virtual void GetBookmarks(std::vector<GURL>* urls);
249
250  // Returns true if there are bookmarks, otherwise returns false. This method
251  // is thread safe.
252  bool HasBookmarks();
253
254  // Returns true if there is a bookmark for the specified URL. This method is
255  // thread safe. See BookmarkService for more details on this.
256  virtual bool IsBookmarked(const GURL& url);
257
258  // Blocks until loaded; this is NOT invoked on the main thread. See
259  // BookmarkService for more details on this.
260  virtual void BlockTillLoaded();
261
262  // Returns the node with the specified id, or NULL if there is no node with
263  // the specified id.
264  const BookmarkNode* GetNodeByID(int64 id);
265
266  // Adds a new folder node at the specified position.
267  const BookmarkNode* AddFolder(const BookmarkNode* parent,
268                                int index,
269                                const string16& title);
270
271  // Adds a url at the specified position.
272  const BookmarkNode* AddURL(const BookmarkNode* parent,
273                             int index,
274                             const string16& title,
275                             const GURL& url);
276
277  // Adds a url with a specific creation date.
278  const BookmarkNode* AddURLWithCreationTime(const BookmarkNode* parent,
279                                             int index,
280                                             const string16& title,
281                                             const GURL& url,
282                                             const base::Time& creation_time);
283
284  // Sorts the children of |parent|, notifying observers by way of the
285  // BookmarkNodeChildrenReordered method.
286  void SortChildren(const BookmarkNode* parent);
287
288  // This is the convenience that makes sure the url is starred or not starred.
289  // If is_starred is false, all bookmarks for URL are removed. If is_starred is
290  // true and there are no bookmarks for url, a bookmark is created.
291  void SetURLStarred(const GURL& url,
292                     const string16& title,
293                     bool is_starred);
294
295  // Sets the date modified time of the specified node.
296  void SetDateFolderModified(const BookmarkNode* parent, const base::Time time);
297
298  // Resets the 'date modified' time of the node to 0. This is used during
299  // importing to exclude the newly created folders from showing up in the
300  // combobox of most recently modified folders.
301  void ResetDateFolderModified(const BookmarkNode* node);
302
303  void GetBookmarksWithTitlesMatching(
304      const string16& text,
305      size_t max_count,
306      std::vector<bookmark_utils::TitleMatch>* matches);
307
308  Profile* profile() const { return profile_; }
309
310  bool is_root(const BookmarkNode* node) const { return node == &root_; }
311  bool is_bookmark_bar_node(const BookmarkNode* node) const {
312    return node == bookmark_bar_node_;
313  }
314  bool is_other_bookmarks_node(const BookmarkNode* node) const {
315    return node == other_node_;
316  }
317  // Returns whether the given node is one of the permanent nodes - root node,
318  // bookmark bar node or other bookmarks node.
319  bool is_permanent_node(const BookmarkNode* node) const {
320    return is_root(node) ||
321           is_bookmark_bar_node(node) ||
322           is_other_bookmarks_node(node);
323  }
324
325  // Sets the store to NULL, making it so the BookmarkModel does not persist
326  // any changes to disk. This is only useful during testing to speed up
327  // testing.
328  void ClearStore();
329
330  // Returns whether the bookmarks file changed externally.
331  bool file_changed() const { return file_changed_; }
332
333  // Returns the next node ID.
334  int64 next_node_id() const { return next_node_id_; }
335
336 private:
337  // Used to order BookmarkNodes by URL.
338  class NodeURLComparator {
339   public:
340    bool operator()(const BookmarkNode* n1, const BookmarkNode* n2) const {
341      return n1->GetURL() < n2->GetURL();
342    }
343  };
344
345  // Implementation of IsBookmarked. Before calling this the caller must
346  // obtain a lock on url_lock_.
347  bool IsBookmarkedNoLock(const GURL& url);
348
349  // Overriden to notify the observer the favicon has been loaded.
350  void FaviconLoaded(const BookmarkNode* node);
351
352  // Removes the node from internal maps and recurses through all children. If
353  // the node is a url, its url is added to removed_urls.
354  //
355  // This does NOT delete the node.
356  void RemoveNode(BookmarkNode* node, std::set<GURL>* removed_urls);
357
358  // Invoked when loading is finished. Sets loaded_ and notifies observers.
359  // BookmarkModel takes ownership of |details|.
360  void DoneLoading(BookmarkLoadDetails* details);
361
362  // Populates nodes_ordered_by_url_set_ from root.
363  void PopulateNodesByURL(BookmarkNode* node);
364
365  // Removes the node from its parent, sends notification, and deletes it.
366  // type specifies how the node should be removed.
367  void RemoveAndDeleteNode(BookmarkNode* delete_me);
368
369  // Adds the node at the specified position and sends notification. If
370  // was_bookmarked is true, it indicates a bookmark already existed for the
371  // URL.
372  BookmarkNode* AddNode(BookmarkNode* parent,
373                        int index,
374                        BookmarkNode* node,
375                        bool was_bookmarked);
376
377  // Implementation of GetNodeByID.
378  const BookmarkNode* GetNodeByID(const BookmarkNode* node, int64 id);
379
380  // Returns true if the parent and index are valid.
381  bool IsValidIndex(const BookmarkNode* parent, int index, bool allow_end);
382
383  // Creates the bookmark bar/other nodes. These call into
384  // CreateRootNodeFromStarredEntry.
385  BookmarkNode* CreateBookmarkNode();
386  BookmarkNode* CreateOtherBookmarksNode();
387
388  // Creates a root node (either the bookmark bar node or other node) from the
389  // specified starred entry.
390  BookmarkNode* CreateRootNodeFromStarredEntry(
391      const history::StarredEntry& entry);
392
393  // Notification that a favicon has finished loading. If we can decode the
394  // favicon, FaviconLoaded is invoked.
395  void OnFaviconDataAvailable(FaviconService::Handle handle,
396                              history::FaviconData favicon);
397
398  // Invoked from the node to load the favicon. Requests the favicon from the
399  // favicon service.
400  void LoadFavicon(BookmarkNode* node);
401
402  // If we're waiting on a favicon for node, the load request is canceled.
403  void CancelPendingFaviconLoadRequests(BookmarkNode* node);
404
405  // NotificationObserver.
406  virtual void Observe(NotificationType type,
407                       const NotificationSource& source,
408                       const NotificationDetails& details);
409
410  // Generates and returns the next node ID.
411  int64 generate_next_node_id();
412
413  // Sets the maximum node ID to the given value.
414  // This is used by BookmarkCodec to report the maximum ID after it's done
415  // decoding since during decoding codec assigns node IDs.
416  void set_next_node_id(int64 id) { next_node_id_ = id; }
417
418  // Records that the bookmarks file was changed externally.
419  void SetFileChanged();
420
421  // Creates and returns a new BookmarkLoadDetails. It's up to the caller to
422  // delete the returned object.
423  BookmarkLoadDetails* CreateLoadDetails();
424
425  NotificationRegistrar registrar_;
426
427  Profile* profile_;
428
429  // Whether the initial set of data has been loaded.
430  bool loaded_;
431
432  // Whether the bookmarks file was changed externally. This is set after
433  // loading is complete and once set the value never changes.
434  bool file_changed_;
435
436  // The root node. This contains the bookmark bar node and the 'other' node as
437  // children.
438  BookmarkNode root_;
439
440  BookmarkNode* bookmark_bar_node_;
441  BookmarkNode* other_node_;
442
443  // The maximum ID assigned to the bookmark nodes in the model.
444  int64 next_node_id_;
445
446  // The observers.
447  ObserverList<BookmarkModelObserver> observers_;
448
449  // Set of nodes ordered by URL. This is not a map to avoid copying the
450  // urls.
451  // WARNING: nodes_ordered_by_url_set_ is accessed on multiple threads. As
452  // such, be sure and wrap all usage of it around url_lock_.
453  typedef std::multiset<BookmarkNode*, NodeURLComparator> NodesOrderedByURLSet;
454  NodesOrderedByURLSet nodes_ordered_by_url_set_;
455  base::Lock url_lock_;
456
457  // Used for loading favicons and the empty history request.
458  CancelableRequestConsumerTSimple<BookmarkNode*> load_consumer_;
459
460  // Reads/writes bookmarks to disk.
461  scoped_refptr<BookmarkStorage> store_;
462
463  scoped_ptr<BookmarkIndex> index_;
464
465  base::WaitableEvent loaded_signal_;
466
467  DISALLOW_COPY_AND_ASSIGN(BookmarkModel);
468};
469
470#endif  // CHROME_BROWSER_BOOKMARKS_BOOKMARK_MODEL_H_
471