michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef nsNavBookmarks_h_ michael@0: #define nsNavBookmarks_h_ michael@0: michael@0: #include "nsINavBookmarksService.h" michael@0: #include "nsIAnnotationService.h" michael@0: #include "nsITransaction.h" michael@0: #include "nsNavHistory.h" michael@0: #include "nsToolkitCompsCID.h" michael@0: #include "nsCategoryCache.h" michael@0: #include "nsTHashtable.h" michael@0: #include "nsWeakReference.h" michael@0: #include "mozilla/Attributes.h" michael@0: #include "prtime.h" michael@0: michael@0: class nsNavBookmarks; michael@0: class nsIOutputStream; michael@0: michael@0: namespace mozilla { michael@0: namespace places { michael@0: michael@0: enum BookmarkStatementId { michael@0: DB_FIND_REDIRECTED_BOOKMARK = 0 michael@0: , DB_GET_BOOKMARKS_FOR_URI michael@0: }; michael@0: michael@0: struct BookmarkData { michael@0: int64_t id; michael@0: nsCString url; michael@0: nsCString title; michael@0: int32_t position; michael@0: int64_t placeId; michael@0: int64_t parentId; michael@0: int64_t grandParentId; michael@0: int32_t type; michael@0: nsCString serviceCID; michael@0: PRTime dateAdded; michael@0: PRTime lastModified; michael@0: nsCString guid; michael@0: nsCString parentGuid; michael@0: }; michael@0: michael@0: struct ItemVisitData { michael@0: BookmarkData bookmark; michael@0: int64_t visitId; michael@0: uint32_t transitionType; michael@0: PRTime time; michael@0: }; michael@0: michael@0: struct ItemChangeData { michael@0: BookmarkData bookmark; michael@0: nsCString property; michael@0: bool isAnnotation; michael@0: nsCString newValue; michael@0: }; michael@0: michael@0: typedef void (nsNavBookmarks::*ItemVisitMethod)(const ItemVisitData&); michael@0: typedef void (nsNavBookmarks::*ItemChangeMethod)(const ItemChangeData&); michael@0: michael@0: class BookmarkKeyClass : public nsTrimInt64HashKey michael@0: { michael@0: public: michael@0: BookmarkKeyClass(const int64_t* aItemId) michael@0: : nsTrimInt64HashKey(aItemId) michael@0: , creationTime(PR_Now()) michael@0: { michael@0: } michael@0: BookmarkKeyClass(const BookmarkKeyClass& aOther) michael@0: : nsTrimInt64HashKey(aOther) michael@0: , creationTime(PR_Now()) michael@0: { michael@0: NS_NOTREACHED("Do not call me!"); michael@0: } michael@0: BookmarkData bookmark; michael@0: PRTime creationTime; michael@0: }; michael@0: michael@0: enum BookmarkDate { michael@0: DATE_ADDED = 0 michael@0: , LAST_MODIFIED michael@0: }; michael@0: michael@0: } // namespace places michael@0: } // namespace mozilla michael@0: michael@0: class nsNavBookmarks MOZ_FINAL : public nsINavBookmarksService michael@0: , public nsINavHistoryObserver michael@0: , public nsIAnnotationObserver michael@0: , public nsIObserver michael@0: , public nsSupportsWeakReference michael@0: { michael@0: public: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSINAVBOOKMARKSSERVICE michael@0: NS_DECL_NSINAVHISTORYOBSERVER michael@0: NS_DECL_NSIANNOTATIONOBSERVER michael@0: NS_DECL_NSIOBSERVER michael@0: michael@0: nsNavBookmarks(); michael@0: michael@0: /** michael@0: * Obtains the service's object. michael@0: */ michael@0: static already_AddRefed GetSingleton(); michael@0: michael@0: /** michael@0: * Initializes the service's object. This should only be called once. michael@0: */ michael@0: nsresult Init(); michael@0: michael@0: static nsNavBookmarks* GetBookmarksService() { michael@0: if (!gBookmarksService) { michael@0: nsCOMPtr serv = michael@0: do_GetService(NS_NAVBOOKMARKSSERVICE_CONTRACTID); michael@0: NS_ENSURE_TRUE(serv, nullptr); michael@0: NS_ASSERTION(gBookmarksService, michael@0: "Should have static instance pointer now"); michael@0: } michael@0: return gBookmarksService; michael@0: } michael@0: michael@0: typedef mozilla::places::BookmarkData BookmarkData; michael@0: typedef mozilla::places::BookmarkKeyClass BookmarkKeyClass; michael@0: typedef mozilla::places::ItemVisitData ItemVisitData; michael@0: typedef mozilla::places::ItemChangeData ItemChangeData; michael@0: typedef mozilla::places::BookmarkStatementId BookmarkStatementId; michael@0: michael@0: nsresult ResultNodeForContainer(int64_t aID, michael@0: nsNavHistoryQueryOptions* aOptions, michael@0: nsNavHistoryResultNode** aNode); michael@0: michael@0: // Find all the children of a folder, using the given query and options. michael@0: // For each child, a ResultNode is created and added to |children|. michael@0: // The results are ordered by folder position. michael@0: nsresult QueryFolderChildren(int64_t aFolderId, michael@0: nsNavHistoryQueryOptions* aOptions, michael@0: nsCOMArray* children); michael@0: michael@0: /** michael@0: * Turns aRow into a node and appends it to aChildren if it is appropriate to michael@0: * do so. michael@0: * michael@0: * @param aRow michael@0: * A Storage statement (in the case of synchronous execution) or row of michael@0: * a result set (in the case of asynchronous execution). michael@0: * @param aOptions michael@0: * The options of the parent folder node. michael@0: * @param aChildren michael@0: * The children of the parent folder node. michael@0: * @param aCurrentIndex michael@0: * The index of aRow within the results. When called on the first row, michael@0: * this should be set to -1. michael@0: */ michael@0: nsresult ProcessFolderNodeRow(mozIStorageValueArray* aRow, michael@0: nsNavHistoryQueryOptions* aOptions, michael@0: nsCOMArray* aChildren, michael@0: int32_t& aCurrentIndex); michael@0: michael@0: /** michael@0: * The async version of QueryFolderChildren. michael@0: * michael@0: * @param aNode michael@0: * The folder node that will receive the children. michael@0: * @param _pendingStmt michael@0: * The Storage pending statement that will be used to control async michael@0: * execution. michael@0: */ michael@0: nsresult QueryFolderChildrenAsync(nsNavHistoryFolderResultNode* aNode, michael@0: int64_t aFolderId, michael@0: mozIStoragePendingStatement** _pendingStmt); michael@0: michael@0: /** michael@0: * @return index of the new folder in aIndex, whether it was passed in or michael@0: * generated by autoincrement. michael@0: * michael@0: * @note If aFolder is -1, uses the autoincrement id for folder index. michael@0: * @note aTitle will be truncated to TITLE_LENGTH_MAX michael@0: */ michael@0: nsresult CreateContainerWithID(int64_t aId, int64_t aParent, michael@0: const nsACString& aTitle, michael@0: bool aIsBookmarkFolder, michael@0: int32_t* aIndex, michael@0: const nsACString& aGUID, michael@0: int64_t* aNewFolder); michael@0: michael@0: /** michael@0: * Fetches information about the specified id from the database. michael@0: * michael@0: * @param aItemId michael@0: * Id of the item to fetch information for. michael@0: * @param aBookmark michael@0: * BookmarkData to store the information. michael@0: */ michael@0: nsresult FetchItemInfo(int64_t aItemId, michael@0: BookmarkData& _bookmark); michael@0: michael@0: /** michael@0: * Notifies that a bookmark has been visited. michael@0: * michael@0: * @param aItemId michael@0: * The visited item id. michael@0: * @param aData michael@0: * Details about the new visit. michael@0: */ michael@0: void NotifyItemVisited(const ItemVisitData& aData); michael@0: michael@0: /** michael@0: * Notifies that a bookmark has changed. michael@0: * michael@0: * @param aItemId michael@0: * The changed item id. michael@0: * @param aData michael@0: * Details about the change. michael@0: */ michael@0: void NotifyItemChanged(const ItemChangeData& aData); michael@0: michael@0: /** michael@0: * Recursively builds an array of descendant folders inside a given folder. michael@0: * michael@0: * @param aFolderId michael@0: * The folder to fetch descendants from. michael@0: * @param aDescendantFoldersArray michael@0: * Output array to put descendant folders id. michael@0: */ michael@0: nsresult GetDescendantFolders(int64_t aFolderId, michael@0: nsTArray& aDescendantFoldersArray); michael@0: michael@0: private: michael@0: static nsNavBookmarks* gBookmarksService; michael@0: michael@0: ~nsNavBookmarks(); michael@0: michael@0: /** michael@0: * Locates the root items in the bookmarks folder hierarchy assigning folder michael@0: * ids to the root properties that are exposed through the service interface. michael@0: */ michael@0: nsresult ReadRoots(); michael@0: michael@0: nsresult AdjustIndices(int64_t aFolder, michael@0: int32_t aStartIndex, michael@0: int32_t aEndIndex, michael@0: int32_t aDelta); michael@0: michael@0: /** michael@0: * Fetches properties of a folder. michael@0: * michael@0: * @param aFolderId michael@0: * Folder to count children for. michael@0: * @param _folderCount michael@0: * Number of children in the folder. michael@0: * @param _guid michael@0: * Unique id of the folder. michael@0: * @param _parentId michael@0: * Id of the parent of the folder. michael@0: * michael@0: * @throws If folder does not exist. michael@0: */ michael@0: nsresult FetchFolderInfo(int64_t aFolderId, michael@0: int32_t* _folderCount, michael@0: nsACString& _guid, michael@0: int64_t* _parentId); michael@0: michael@0: nsresult GetLastChildId(int64_t aFolder, int64_t* aItemId); michael@0: michael@0: /** michael@0: * This is an handle to the Places database. michael@0: */ michael@0: nsRefPtr mDB; michael@0: michael@0: int32_t mItemCount; michael@0: michael@0: nsMaybeWeakPtrArray mObservers; michael@0: michael@0: int64_t mRoot; michael@0: int64_t mMenuRoot; michael@0: int64_t mTagsRoot; michael@0: int64_t mUnfiledRoot; michael@0: int64_t mToolbarRoot; michael@0: michael@0: inline bool IsRoot(int64_t aFolderId) { michael@0: return aFolderId == mRoot || aFolderId == mMenuRoot || michael@0: aFolderId == mTagsRoot || aFolderId == mUnfiledRoot || michael@0: aFolderId == mToolbarRoot; michael@0: } michael@0: michael@0: nsresult IsBookmarkedInDatabase(int64_t aBookmarkID, bool* aIsBookmarked); michael@0: michael@0: nsresult SetItemDateInternal(enum mozilla::places::BookmarkDate aDateType, michael@0: int64_t aItemId, michael@0: PRTime aValue); michael@0: michael@0: // Recursive method to build an array of folder's children michael@0: nsresult GetDescendantChildren(int64_t aFolderId, michael@0: const nsACString& aFolderGuid, michael@0: int64_t aGrandParentId, michael@0: nsTArray& aFolderChildrenArray); michael@0: michael@0: enum ItemType { michael@0: BOOKMARK = TYPE_BOOKMARK, michael@0: FOLDER = TYPE_FOLDER, michael@0: SEPARATOR = TYPE_SEPARATOR, michael@0: }; michael@0: michael@0: /** michael@0: * Helper to insert a bookmark in the database. michael@0: * michael@0: * @param aItemId michael@0: * The itemId to insert, pass -1 to generate a new one. michael@0: * @param aPlaceId michael@0: * The placeId to which this bookmark refers to, pass nullptr for michael@0: * items that don't refer to an URI (eg. folders, separators, ...). michael@0: * @param aItemType michael@0: * The type of the new bookmark, see TYPE_* constants. michael@0: * @param aParentId michael@0: * The itemId of the parent folder. michael@0: * @param aIndex michael@0: * The position inside the parent folder. michael@0: * @param aTitle michael@0: * The title for the new bookmark. michael@0: * Pass a void string to set a NULL title. michael@0: * @param aDateAdded michael@0: * The date for the insertion. michael@0: * @param [optional] aLastModified michael@0: * The last modified date for the insertion. michael@0: * It defaults to aDateAdded. michael@0: * michael@0: * @return The new item id that has been inserted. michael@0: * michael@0: * @note This will also update last modified date of the parent folder. michael@0: */ michael@0: nsresult InsertBookmarkInDB(int64_t aPlaceId, michael@0: enum ItemType aItemType, michael@0: int64_t aParentId, michael@0: int32_t aIndex, michael@0: const nsACString& aTitle, michael@0: PRTime aDateAdded, michael@0: PRTime aLastModified, michael@0: const nsACString& aParentGuid, michael@0: int64_t aGrandParentId, michael@0: nsIURI* aURI, michael@0: int64_t* _itemId, michael@0: nsACString& _guid); michael@0: michael@0: /** michael@0: * TArray version of getBookmarksIdForURI for ease of use in C++ code. michael@0: * Pass in a reference to a TArray; it will get filled with the michael@0: * resulting list of bookmark IDs. michael@0: * michael@0: * @param aURI michael@0: * URI to get bookmarks for. michael@0: * @param aResult michael@0: * Array of bookmark ids. michael@0: * @param aSkipTags michael@0: * If true ids of tags-as-bookmarks entries will be excluded. michael@0: */ michael@0: nsresult GetBookmarkIdsForURITArray(nsIURI* aURI, michael@0: nsTArray& aResult, michael@0: bool aSkipTags); michael@0: michael@0: nsresult GetBookmarksForURI(nsIURI* aURI, michael@0: nsTArray& _bookmarks); michael@0: michael@0: int64_t RecursiveFindRedirectedBookmark(int64_t aPlaceId); michael@0: michael@0: static const int32_t kGetChildrenIndex_Position; michael@0: static const int32_t kGetChildrenIndex_Type; michael@0: static const int32_t kGetChildrenIndex_PlaceID; michael@0: static const int32_t kGetChildrenIndex_Guid; michael@0: michael@0: class RemoveFolderTransaction MOZ_FINAL : public nsITransaction { michael@0: public: michael@0: RemoveFolderTransaction(int64_t aID) : mID(aID) {} michael@0: michael@0: NS_DECL_ISUPPORTS michael@0: michael@0: NS_IMETHOD DoTransaction() { michael@0: nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService(); michael@0: NS_ENSURE_TRUE(bookmarks, NS_ERROR_OUT_OF_MEMORY); michael@0: BookmarkData folder; michael@0: nsresult rv = bookmarks->FetchItemInfo(mID, folder); michael@0: // TODO (Bug 656935): store the BookmarkData struct instead. michael@0: mParent = folder.parentId; michael@0: mIndex = folder.position; michael@0: michael@0: rv = bookmarks->GetItemTitle(mID, mTitle); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return bookmarks->RemoveItem(mID); michael@0: } michael@0: michael@0: NS_IMETHOD UndoTransaction() { michael@0: nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService(); michael@0: NS_ENSURE_TRUE(bookmarks, NS_ERROR_OUT_OF_MEMORY); michael@0: int64_t newFolder; michael@0: return bookmarks->CreateContainerWithID(mID, mParent, mTitle, true, michael@0: &mIndex, EmptyCString(), &newFolder); michael@0: } michael@0: michael@0: NS_IMETHOD RedoTransaction() { michael@0: return DoTransaction(); michael@0: } michael@0: michael@0: NS_IMETHOD GetIsTransient(bool* aResult) { michael@0: *aResult = false; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHOD Merge(nsITransaction* aTransaction, bool* aResult) { michael@0: *aResult = false; michael@0: return NS_OK; michael@0: } michael@0: michael@0: private: michael@0: int64_t mID; michael@0: int64_t mParent; michael@0: nsCString mTitle; michael@0: int32_t mIndex; michael@0: }; michael@0: michael@0: // Used to enable and disable the observer notifications. michael@0: bool mCanNotify; michael@0: nsCategoryCache mCacheObservers; michael@0: michael@0: // Tracks whether we are in batch mode. michael@0: // Note: this is only tracking bookmarks batches, not history ones. michael@0: bool mBatching; michael@0: michael@0: /** michael@0: * Always call EnsureKeywordsHash() and check it for errors before actually michael@0: * using the hash. Internal keyword methods are already doing that. michael@0: */ michael@0: nsresult EnsureKeywordsHash(); michael@0: nsDataHashtable mBookmarkToKeywordHash; michael@0: bool mBookmarkToKeywordHashInitialized; michael@0: michael@0: /** michael@0: * This function must be called every time a bookmark is removed. michael@0: * michael@0: * @param aURI michael@0: * Uri to test. michael@0: */ michael@0: nsresult UpdateKeywordsHashForRemovedBookmark(int64_t aItemId); michael@0: michael@0: /** michael@0: * Cache for the last fetched BookmarkData entries. michael@0: * This is used to speed up repeated requests to the same item id. michael@0: */ michael@0: nsTHashtable mRecentBookmarksCache; michael@0: michael@0: /** michael@0: * Tracks bookmarks in the cache critical path. Items should not be michael@0: * added to the cache till they are removed from this hash. michael@0: */ michael@0: nsTHashtable mUncachableBookmarks; michael@0: }; michael@0: michael@0: #endif // nsNavBookmarks_h_