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 #ifndef COMPONENTS_BOOKMARKS_BROWSER_BOOKMARK_MODEL_H_ 6 #define COMPONENTS_BOOKMARKS_BROWSER_BOOKMARK_MODEL_H_ 7 8 #include <stddef.h> 9 #include <stdint.h> 10 11 #include <map> 12 #include <memory> 13 #include <set> 14 #include <string> 15 #include <vector> 16 17 #include "base/check_op.h" 18 #include "base/compiler_specific.h" 19 #include "base/macros.h" 20 #include "base/memory/scoped_refptr.h" 21 #include "base/memory/weak_ptr.h" 22 #include "base/observer_list.h" 23 #include "base/sequence_checker.h" 24 #include "base/strings/string16.h" 25 #include "components/bookmarks/browser/bookmark_client.h" 26 #include "components/bookmarks/browser/bookmark_node.h" 27 #include "components/bookmarks/browser/bookmark_undo_provider.h" 28 #include "components/keyed_service/core/keyed_service.h" 29 #include "ui/gfx/image/image.h" 30 #include "url/gurl.h" 31 32 class PrefService; 33 34 namespace base { 35 class FilePath; 36 } 37 38 namespace favicon_base { 39 struct FaviconImageResult; 40 } 41 42 namespace query_parser { 43 enum class MatchingAlgorithm; 44 } 45 46 namespace bookmarks { 47 48 class BookmarkCodecTest; 49 class BookmarkExpandedStateTracker; 50 class BookmarkLoadDetails; 51 class BookmarkModelObserver; 52 class BookmarkStorage; 53 class BookmarkUndoDelegate; 54 class ModelLoader; 55 class ScopedGroupBookmarkActions; 56 class TestBookmarkClient; 57 class TitledUrlIndex; 58 class UrlIndex; 59 60 struct UrlAndTitle; 61 struct TitledUrlMatch; 62 63 // BookmarkModel -------------------------------------------------------------- 64 65 // BookmarkModel provides a directed acyclic graph of URLs and folders. 66 // Three graphs are provided for the three entry points: those on the 'bookmarks 67 // bar', those in the 'other bookmarks' folder and those in the 'mobile' folder. 68 // 69 // An observer may be attached to observe relevant events. 70 // 71 // You should NOT directly create a BookmarkModel, instead go through the 72 // BookmarkModelFactory. 73 class BookmarkModel : public BookmarkUndoProvider, 74 public KeyedService { 75 public: 76 explicit BookmarkModel(std::unique_ptr<BookmarkClient> client); 77 ~BookmarkModel() override; 78 79 // Triggers the loading of bookmarks, which is an asynchronous operation with 80 // most heavy-lifting taking place in a background sequence. Upon completion, 81 // loaded() will return true and observers will be notified via 82 // BookmarkModelLoaded(). 83 void Load(PrefService* pref_service, const base::FilePath& profile_path); 84 85 // Returns true if the model finished loading. loaded()86 bool loaded() const { 87 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); 88 return loaded_; 89 } 90 91 // Returns the object responsible for tracking loading. 92 scoped_refptr<ModelLoader> model_loader(); 93 94 // Returns the root node. The 'bookmark bar' node and 'other' node are 95 // children of the root node. root_node()96 const BookmarkNode* root_node() const { 97 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); 98 return root_; 99 } 100 101 // Returns the 'bookmark bar' node. This is NULL until loaded. bookmark_bar_node()102 const BookmarkNode* bookmark_bar_node() const { 103 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); 104 return bookmark_bar_node_; 105 } 106 107 // Returns the 'other' node. This is NULL until loaded. other_node()108 const BookmarkNode* other_node() const { 109 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); 110 return other_node_; 111 } 112 113 // Returns the 'mobile' node. This is NULL until loaded. mobile_node()114 const BookmarkNode* mobile_node() const { 115 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); 116 return mobile_node_; 117 } 118 is_root_node(const BookmarkNode * node)119 bool is_root_node(const BookmarkNode* node) const { 120 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); 121 return node == root_; 122 } 123 124 // Returns whether the given |node| is one of the permanent nodes - root node, 125 // 'bookmark bar' node, 'other' node or 'mobile' node, or one of the root 126 // nodes supplied by the |client_|. is_permanent_node(const BookmarkNode * node)127 bool is_permanent_node(const BookmarkNode* node) const { 128 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); 129 return node && (node == root_ || node->parent() == root_); 130 } 131 132 void AddObserver(BookmarkModelObserver* observer); 133 void RemoveObserver(BookmarkModelObserver* observer); 134 135 // Notifies the observers that an extensive set of changes is about to happen, 136 // such as during import or sync, so they can delay any expensive UI updates 137 // until it's finished. 138 void BeginExtensiveChanges(); 139 void EndExtensiveChanges(); 140 141 // Returns true if this bookmark model is currently in a mode where extensive 142 // changes might happen, such as for import and sync. This is helpful for 143 // observers that are created after the mode has started, and want to check 144 // state during their own initializer, such as the NTP. IsDoingExtensiveChanges()145 bool IsDoingExtensiveChanges() const { return extensive_changes_ > 0; } 146 147 // Removes |node| from the model and deletes it. Removing a folder node 148 // recursively removes all nodes. Observers are notified immediately. |node| 149 // must not be a permanent node. 150 void Remove(const BookmarkNode* node); 151 152 // Removes all the non-permanent bookmark nodes that are editable by the user. 153 // Observers are only notified when all nodes have been removed. There is no 154 // notification for individual node removals. 155 void RemoveAllUserBookmarks(); 156 157 // Moves |node| to |new_parent| and inserts it at the given |index|. 158 void Move(const BookmarkNode* node, 159 const BookmarkNode* new_parent, 160 size_t index); 161 162 // Inserts a copy of |node| into |new_parent| at |index|. 163 void Copy(const BookmarkNode* node, 164 const BookmarkNode* new_parent, 165 size_t index); 166 167 // Returns the favicon for |node|. If the favicon has not yet been loaded, 168 // a load will be triggered and the observer of the model notified when done. 169 // This also means that, on return, the node's state is guaranteed to be 170 // either LOADED_FAVICON (if it was already loaded prior to the call) or 171 // LOADING_FAVICON (with the exception of folders, where the call is a no-op). 172 const gfx::Image& GetFavicon(const BookmarkNode* node); 173 174 // Sets the title of |node|. 175 void SetTitle(const BookmarkNode* node, const base::string16& title); 176 177 // Sets the URL of |node|. 178 void SetURL(const BookmarkNode* node, const GURL& url); 179 180 // Sets the date added time of |node|. 181 void SetDateAdded(const BookmarkNode* node, base::Time date_added); 182 183 // Returns the set of nodes with the |url|. 184 void GetNodesByURL(const GURL& url, std::vector<const BookmarkNode*>* nodes); 185 186 // Returns the most recently added user node for the |url|; urls from any 187 // nodes that are not editable by the user are never returned by this call. 188 // Returns NULL if |url| is not bookmarked. 189 const BookmarkNode* GetMostRecentlyAddedUserNodeForURL(const GURL& url); 190 191 // Returns true if there are bookmarks, otherwise returns false. 192 bool HasBookmarks(); 193 194 // Returns true is there is no user created bookmarks or folders. 195 bool HasNoUserCreatedBookmarksOrFolders(); 196 197 // Returns true if the specified URL is bookmarked. 198 bool IsBookmarked(const GURL& url); 199 200 // Returns, by reference in |bookmarks|, the set of bookmarked urls and their 201 // titles. This returns the unique set of URLs. For example, if two bookmarks 202 // reference the same URL only one entry is added not matter the titles are 203 // same or not. 204 void GetBookmarks(std::vector<UrlAndTitle>* urls); 205 206 // Adds a new folder node at the specified position with the given |guid| and 207 // |meta_info|. If a GUID is provided, it must be a valid version 4 GUID, 208 // otherwise a new one is generated to replace it. 209 const BookmarkNode* AddFolder( 210 const BookmarkNode* parent, 211 size_t index, 212 const base::string16& title, 213 const BookmarkNode::MetaInfoMap* meta_info = nullptr, 214 base::Optional<std::string> guid = base::nullopt); 215 216 // Adds a url at the specified position with the given |creation_time|, 217 // |meta_info| and |guid|. If a GUID is provided, it must be a valid version 4 218 // GUID, otherwise a new one is generated to replace it. 219 const BookmarkNode* AddURL( 220 const BookmarkNode* parent, 221 size_t index, 222 const base::string16& title, 223 const GURL& url, 224 const BookmarkNode::MetaInfoMap* meta_info = nullptr, 225 base::Optional<base::Time> creation_time = base::nullopt, 226 base::Optional<std::string> guid = base::nullopt); 227 228 // Sorts the children of |parent|, notifying observers by way of the 229 // BookmarkNodeChildrenReordered method. 230 void SortChildren(const BookmarkNode* parent); 231 232 // Order the children of |parent| as specified in |ordered_nodes|. This 233 // function should only be used to reorder the child nodes of |parent| and 234 // is not meant to move nodes between different parent. Notifies observers 235 // using the BookmarkNodeChildrenReordered method. 236 void ReorderChildren(const BookmarkNode* parent, 237 const std::vector<const BookmarkNode*>& ordered_nodes); 238 239 // Sets the date when the folder was modified. 240 void SetDateFolderModified(const BookmarkNode* node, const base::Time time); 241 242 // Resets the 'date modified' time of the node to 0. This is used during 243 // importing to exclude the newly created folders from showing up in the 244 // combobox of most recently modified folders. 245 void ResetDateFolderModified(const BookmarkNode* node); 246 247 // Returns up to |max_count| bookmarks containing each term from |query| in 248 // either the title, URL, or, if |match_ancestor_titles| is true, the titles 249 // of ancestors. |matching_algorithm| determines the algorithm used by 250 // QueryParser internally to parse |query|. 251 std::vector<TitledUrlMatch> GetBookmarksMatching( 252 const base::string16& query, 253 size_t max_count, 254 query_parser::MatchingAlgorithm matching_algorithm, 255 bool match_ancestor_titles = false); 256 257 // Sets the store to NULL, making it so the BookmarkModel does not persist 258 // any changes to disk. This is only useful during testing to speed up 259 // testing. 260 void ClearStore(); 261 262 // Returns the next node ID. next_node_id()263 int64_t next_node_id() const { return next_node_id_; } 264 265 // Returns the object responsible for tracking the set of expanded nodes in 266 // the bookmark editor. expanded_state_tracker()267 BookmarkExpandedStateTracker* expanded_state_tracker() { 268 return expanded_state_tracker_.get(); 269 } 270 271 // Sets/deletes meta info of |node|. 272 void SetNodeMetaInfo(const BookmarkNode* node, 273 const std::string& key, 274 const std::string& value); 275 void SetNodeMetaInfoMap(const BookmarkNode* node, 276 const BookmarkNode::MetaInfoMap& meta_info_map); 277 void DeleteNodeMetaInfo(const BookmarkNode* node, 278 const std::string& key); 279 280 // Adds |key| to the set of meta info keys that are not copied when a node is 281 // cloned. 282 void AddNonClonedKey(const std::string& key); 283 284 // Returns the set of meta info keys that should not be copied when a node is 285 // cloned. non_cloned_keys()286 const std::set<std::string>& non_cloned_keys() const { 287 return non_cloned_keys_; 288 } 289 290 // Notify BookmarkModel that the favicons for the given page URLs (e.g. 291 // http://www.google.com) and the given icon URL (e.g. 292 // http://www.google.com/favicon.ico) have changed. It is valid to call 293 // OnFaviconsChanged() with non-empty |page_urls| and an empty |icon_url| and 294 // vice versa. 295 void OnFaviconsChanged(const std::set<GURL>& page_urls, 296 const GURL& icon_url); 297 298 // Returns the client used by this BookmarkModel. client()299 BookmarkClient* client() const { return client_.get(); } 300 301 void SetUndoDelegate(BookmarkUndoDelegate* undo_delegate); 302 AsWeakPtr()303 base::WeakPtr<BookmarkModel> AsWeakPtr() { 304 return weak_factory_.GetWeakPtr(); 305 } 306 307 private: 308 friend class BookmarkCodecTest; 309 friend class BookmarkModelFaviconTest; 310 friend class BookmarkStorage; 311 friend class ScopedGroupBookmarkActions; 312 friend class TestBookmarkClient; 313 314 // BookmarkUndoProvider: 315 void RestoreRemovedNode(const BookmarkNode* parent, 316 size_t index, 317 std::unique_ptr<BookmarkNode> node) override; 318 319 // Notifies the observers for adding every descendant of |node|. 320 void NotifyNodeAddedForAllDescendants(const BookmarkNode* node); 321 322 // Removes the node from internal maps and recurses through all children. If 323 // the node is a url, its url is added to removed_urls. 324 // 325 // This does NOT delete the node. 326 void RemoveNodeFromIndexRecursive(BookmarkNode* node); 327 328 // Called when done loading. Updates internal state and notifies observers. 329 void DoneLoading(std::unique_ptr<BookmarkLoadDetails> details); 330 331 // Adds the |node| at |parent| in the specified |index| and notifies its 332 // observers. 333 BookmarkNode* AddNode(BookmarkNode* parent, 334 size_t index, 335 std::unique_ptr<BookmarkNode> node); 336 337 // Adds |node| to |index_| and recursively invokes this for all children. 338 void AddNodeToIndexRecursive(const BookmarkNode* node); 339 340 // Returns true if the parent and index are valid. 341 bool IsValidIndex(const BookmarkNode* parent, size_t index, bool allow_end); 342 343 // Notification that a favicon has finished loading. If we can decode the 344 // favicon, FaviconLoaded is invoked. 345 void OnFaviconDataAvailable( 346 BookmarkNode* node, 347 const favicon_base::FaviconImageResult& image_result); 348 349 // Invoked from the node to load the favicon. Requests the favicon from the 350 // favicon service. 351 void LoadFavicon(BookmarkNode* node); 352 353 // Called to notify the observers that the favicon has been loaded. 354 void FaviconLoaded(const BookmarkNode* node); 355 356 // If we're waiting on a favicon for node, the load request is canceled. 357 void CancelPendingFaviconLoadRequests(BookmarkNode* node); 358 359 // Notifies the observers that a set of changes initiated by a single user 360 // action is about to happen and has completed. 361 void BeginGroupedChanges(); 362 void EndGroupedChanges(); 363 364 // Generates and returns the next node ID. 365 int64_t generate_next_node_id(); 366 367 // Sets the maximum node ID to the given value. 368 // This is used by BookmarkCodec to report the maximum ID after it's done 369 // decoding since during decoding codec assigns node IDs. set_next_node_id(int64_t id)370 void set_next_node_id(int64_t id) { next_node_id_ = id; } 371 372 BookmarkUndoDelegate* undo_delegate() const; 373 374 std::unique_ptr<BookmarkClient> client_; 375 376 // Whether the initial set of data has been loaded. 377 bool loaded_ = false; 378 379 // See |root_| for details. 380 std::unique_ptr<BookmarkNode> owned_root_; 381 382 // The root node. This contains the bookmark bar node, the 'other' node and 383 // the mobile node as children. The value of |root_| is initially that of 384 // |owned_root_|. Once loading has completed, |owned_root_| is destroyed and 385 // this is set to url_index_->root(). |owned_root_| is done as lots of 386 // existing code assumes the root is non-null while loading. 387 BookmarkNode* root_ = nullptr; 388 389 BookmarkPermanentNode* bookmark_bar_node_ = nullptr; 390 BookmarkPermanentNode* other_node_ = nullptr; 391 BookmarkPermanentNode* mobile_node_ = nullptr; 392 393 // The maximum ID assigned to the bookmark nodes in the model. 394 int64_t next_node_id_ = 1; 395 396 // The observers. 397 base::ObserverList<BookmarkModelObserver>::Unchecked observers_; 398 399 // Used for loading favicons. 400 base::CancelableTaskTracker cancelable_task_tracker_; 401 402 // Writes bookmarks to disk. 403 std::unique_ptr<BookmarkStorage> store_; 404 405 std::unique_ptr<TitledUrlIndex> titled_url_index_; 406 407 // Owned by |model_loader_|. 408 // WARNING: in some tests this does *not* refer to 409 // |ModelLoader::history_bookmark_model_|. This is because some tests 410 // directly call DoneLoading(). 411 // TODO: this is confusing, fix tests not to circumvent ModelLoader. 412 scoped_refptr<UrlIndex> url_index_; 413 414 // See description of IsDoingExtensiveChanges above. 415 int extensive_changes_ = 0; 416 417 std::unique_ptr<BookmarkExpandedStateTracker> expanded_state_tracker_; 418 419 std::set<std::string> non_cloned_keys_; 420 421 BookmarkUndoDelegate* undo_delegate_ = nullptr; 422 std::unique_ptr<BookmarkUndoDelegate> empty_undo_delegate_; 423 424 scoped_refptr<ModelLoader> model_loader_; 425 426 SEQUENCE_CHECKER(sequence_checker_); 427 428 base::WeakPtrFactory<BookmarkModel> weak_factory_{this}; 429 430 DISALLOW_COPY_AND_ASSIGN(BookmarkModel); 431 }; 432 433 } // namespace bookmarks 434 435 #endif // COMPONENTS_BOOKMARKS_BROWSER_BOOKMARK_MODEL_H_ 436