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 30 class BookmarkIndex; 31 class BookmarkLoadDetails; 32 class BookmarkModel; 33 class BookmarkStorage; 34 class Profile; 35 36 namespace bookmark_utils { 37 struct 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 // 45 class 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. GetURL()62 const GURL& GetURL() const { return url_; } 63 // Sets the URL to the given value. SetURL(const GURL & url)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. id()69 int64 id() const { return id_; } 70 // Sets the id to the given value. set_id(int64 id)71 void set_id(int64 id) { id_ = id; } 72 73 // Returns the type of this node. type()74 BookmarkNode::Type type() const { return type_; } set_type(BookmarkNode::Type type)75 void set_type(BookmarkNode::Type type) { type_ = type; } 76 77 // Returns the time the bookmark/folder was added. date_added()78 const base::Time& date_added() const { return date_added_; } 79 // Sets the time the bookmark/folder was added. set_date_added(const base::Time & date)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). date_folder_modified()84 const base::Time& date_folder_modified() const { 85 return date_folder_modified_; 86 } 87 // Sets the last time the folder was modified. set_date_folder_modified(const base::Time & date)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. is_folder()94 bool is_folder() const { return type_ != URL; } 95 96 // Is this a URL? is_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. favicon()103 const SkBitmap& favicon() const { return favicon_; } set_favicon(const SkBitmap & icon)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 is_favicon_loaded()109 bool is_favicon_loaded() const { return loaded_favicon_; } set_favicon_loaded(bool value)110 void set_favicon_loaded(bool value) { loaded_favicon_ = value; } 111 favicon_load_handle()112 HistoryService::Handle favicon_load_handle() const { 113 return favicon_load_handle_; 114 } set_favicon_load_handle(HistoryService::Handle handle)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 173 class 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. root_node()188 const BookmarkNode* root_node() { return &root_; } 189 190 // Returns the bookmark bar node. This is NULL until loaded. GetBookmarkBarNode()191 const BookmarkNode* GetBookmarkBarNode() { return bookmark_bar_node_; } 192 193 // Returns the 'other' node. This is NULL until loaded. other_node()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 AddObserver(BookmarkModelObserver * observer)200 void AddObserver(BookmarkModelObserver* observer) { 201 observers_.AddObserver(observer); 202 } 203 RemoveObserver(BookmarkModelObserver * observer)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 profile()308 Profile* profile() const { return profile_; } 309 is_root(const BookmarkNode * node)310 bool is_root(const BookmarkNode* node) const { return node == &root_; } is_bookmark_bar_node(const BookmarkNode * node)311 bool is_bookmark_bar_node(const BookmarkNode* node) const { 312 return node == bookmark_bar_node_; 313 } is_other_bookmarks_node(const BookmarkNode * node)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. is_permanent_node(const BookmarkNode * 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. file_changed()331 bool file_changed() const { return file_changed_; } 332 333 // Returns the next node ID. 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: operator()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. set_next_node_id(int64 id)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