1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/components/places/nsNavBookmarks.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,2984 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "nsNavBookmarks.h" 1.10 + 1.11 +#include "nsNavHistory.h" 1.12 +#include "nsAnnotationService.h" 1.13 +#include "nsPlacesMacros.h" 1.14 +#include "Helpers.h" 1.15 + 1.16 +#include "nsAppDirectoryServiceDefs.h" 1.17 +#include "nsNetUtil.h" 1.18 +#include "nsUnicharUtils.h" 1.19 +#include "nsPrintfCString.h" 1.20 +#include "prprf.h" 1.21 +#include "mozilla/storage.h" 1.22 + 1.23 +#include "GeckoProfiler.h" 1.24 + 1.25 +#define BOOKMARKS_TO_KEYWORDS_INITIAL_CACHE_SIZE 64 1.26 +#define RECENT_BOOKMARKS_INITIAL_CACHE_SIZE 10 1.27 +// Threashold to expire old bookmarks if the initial cache size is exceeded. 1.28 +#define RECENT_BOOKMARKS_THRESHOLD PRTime((int64_t)1 * 60 * PR_USEC_PER_SEC) 1.29 + 1.30 +#define BEGIN_CRITICAL_BOOKMARK_CACHE_SECTION(_itemId_) \ 1.31 + mUncachableBookmarks.PutEntry(_itemId_); \ 1.32 + mRecentBookmarksCache.RemoveEntry(_itemId_) 1.33 + 1.34 +#define END_CRITICAL_BOOKMARK_CACHE_SECTION(_itemId_) \ 1.35 + MOZ_ASSERT(!mRecentBookmarksCache.GetEntry(_itemId_)); \ 1.36 + MOZ_ASSERT(mUncachableBookmarks.GetEntry(_itemId_)); \ 1.37 + mUncachableBookmarks.RemoveEntry(_itemId_) 1.38 + 1.39 +#define ADD_TO_BOOKMARK_CACHE(_itemId_, _data_) \ 1.40 + PR_BEGIN_MACRO \ 1.41 + ExpireNonrecentBookmarks(&mRecentBookmarksCache); \ 1.42 + if (!mUncachableBookmarks.GetEntry(_itemId_)) { \ 1.43 + BookmarkKeyClass* key = mRecentBookmarksCache.PutEntry(_itemId_); \ 1.44 + if (key) { \ 1.45 + key->bookmark = _data_; \ 1.46 + } \ 1.47 + } \ 1.48 + PR_END_MACRO 1.49 + 1.50 +#define TOPIC_PLACES_MAINTENANCE "places-maintenance-finished" 1.51 + 1.52 +using namespace mozilla; 1.53 + 1.54 +// These columns sit to the right of the kGetInfoIndex_* columns. 1.55 +const int32_t nsNavBookmarks::kGetChildrenIndex_Guid = 15; 1.56 +const int32_t nsNavBookmarks::kGetChildrenIndex_Position = 16; 1.57 +const int32_t nsNavBookmarks::kGetChildrenIndex_Type = 17; 1.58 +const int32_t nsNavBookmarks::kGetChildrenIndex_PlaceID = 18; 1.59 + 1.60 +using namespace mozilla::places; 1.61 + 1.62 +PLACES_FACTORY_SINGLETON_IMPLEMENTATION(nsNavBookmarks, gBookmarksService) 1.63 + 1.64 +#define BOOKMARKS_ANNO_PREFIX "bookmarks/" 1.65 +#define BOOKMARKS_TOOLBAR_FOLDER_ANNO NS_LITERAL_CSTRING(BOOKMARKS_ANNO_PREFIX "toolbarFolder") 1.66 +#define READ_ONLY_ANNO NS_LITERAL_CSTRING("placesInternal/READ_ONLY") 1.67 + 1.68 + 1.69 +namespace { 1.70 + 1.71 +struct keywordSearchData 1.72 +{ 1.73 + int64_t itemId; 1.74 + nsString keyword; 1.75 +}; 1.76 + 1.77 +PLDHashOperator 1.78 +SearchBookmarkForKeyword(nsTrimInt64HashKey::KeyType aKey, 1.79 + const nsString aValue, 1.80 + void* aUserArg) 1.81 +{ 1.82 + keywordSearchData* data = reinterpret_cast<keywordSearchData*>(aUserArg); 1.83 + if (data->keyword.Equals(aValue)) { 1.84 + data->itemId = aKey; 1.85 + return PL_DHASH_STOP; 1.86 + } 1.87 + return PL_DHASH_NEXT; 1.88 +} 1.89 + 1.90 +template<typename Method, typename DataType> 1.91 +class AsyncGetBookmarksForURI : public AsyncStatementCallback 1.92 +{ 1.93 +public: 1.94 + AsyncGetBookmarksForURI(nsNavBookmarks* aBookmarksSvc, 1.95 + Method aCallback, 1.96 + const DataType& aData) 1.97 + : mBookmarksSvc(aBookmarksSvc) 1.98 + , mCallback(aCallback) 1.99 + , mData(aData) 1.100 + { 1.101 + } 1.102 + 1.103 + void Init() 1.104 + { 1.105 + nsRefPtr<Database> DB = Database::GetDatabase(); 1.106 + if (DB) { 1.107 + nsCOMPtr<mozIStorageAsyncStatement> stmt = DB->GetAsyncStatement( 1.108 + "SELECT b.id, b.guid, b.parent, b.lastModified, t.guid, t.parent " 1.109 + "FROM moz_bookmarks b " 1.110 + "JOIN moz_bookmarks t on t.id = b.parent " 1.111 + "WHERE b.fk = (SELECT id FROM moz_places WHERE url = :page_url) " 1.112 + "ORDER BY b.lastModified DESC, b.id DESC " 1.113 + ); 1.114 + if (stmt) { 1.115 + (void)URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), 1.116 + mData.bookmark.url); 1.117 + nsCOMPtr<mozIStoragePendingStatement> pendingStmt; 1.118 + (void)stmt->ExecuteAsync(this, getter_AddRefs(pendingStmt)); 1.119 + } 1.120 + } 1.121 + } 1.122 + 1.123 + NS_IMETHOD HandleResult(mozIStorageResultSet* aResultSet) 1.124 + { 1.125 + nsCOMPtr<mozIStorageRow> row; 1.126 + while (NS_SUCCEEDED(aResultSet->GetNextRow(getter_AddRefs(row))) && row) { 1.127 + // Skip tags, for the use-cases of this async getter they are useless. 1.128 + int64_t grandParentId, tagsFolderId; 1.129 + nsresult rv = row->GetInt64(5, &grandParentId); 1.130 + NS_ENSURE_SUCCESS(rv, rv); 1.131 + rv = mBookmarksSvc->GetTagsFolder(&tagsFolderId); 1.132 + NS_ENSURE_SUCCESS(rv, rv); 1.133 + if (grandParentId == tagsFolderId) { 1.134 + continue; 1.135 + } 1.136 + 1.137 + mData.bookmark.grandParentId = grandParentId; 1.138 + rv = row->GetInt64(0, &mData.bookmark.id); 1.139 + NS_ENSURE_SUCCESS(rv, rv); 1.140 + rv = row->GetUTF8String(1, mData.bookmark.guid); 1.141 + NS_ENSURE_SUCCESS(rv, rv); 1.142 + rv = row->GetInt64(2, &mData.bookmark.parentId); 1.143 + NS_ENSURE_SUCCESS(rv, rv); 1.144 + // lastModified (3) should not be set for the use-cases of this getter. 1.145 + rv = row->GetUTF8String(4, mData.bookmark.parentGuid); 1.146 + NS_ENSURE_SUCCESS(rv, rv); 1.147 + 1.148 + if (mCallback) { 1.149 + ((*mBookmarksSvc).*mCallback)(mData); 1.150 + } 1.151 + } 1.152 + return NS_OK; 1.153 + } 1.154 + 1.155 +private: 1.156 + nsRefPtr<nsNavBookmarks> mBookmarksSvc; 1.157 + Method mCallback; 1.158 + DataType mData; 1.159 +}; 1.160 + 1.161 +static PLDHashOperator 1.162 +ExpireNonrecentBookmarksCallback(BookmarkKeyClass* aKey, 1.163 + void* userArg) 1.164 +{ 1.165 + int64_t* threshold = reinterpret_cast<int64_t*>(userArg); 1.166 + if (aKey->creationTime < *threshold) { 1.167 + return PL_DHASH_REMOVE; 1.168 + } 1.169 + return PL_DHASH_NEXT; 1.170 +} 1.171 + 1.172 +static void 1.173 +ExpireNonrecentBookmarks(nsTHashtable<BookmarkKeyClass>* hashTable) 1.174 +{ 1.175 + if (hashTable->Count() > RECENT_BOOKMARKS_INITIAL_CACHE_SIZE) { 1.176 + int64_t threshold = PR_Now() - RECENT_BOOKMARKS_THRESHOLD; 1.177 + (void)hashTable->EnumerateEntries(ExpireNonrecentBookmarksCallback, 1.178 + reinterpret_cast<void*>(&threshold)); 1.179 + } 1.180 +} 1.181 + 1.182 +static PLDHashOperator 1.183 +ExpireRecentBookmarksByParentCallback(BookmarkKeyClass* aKey, 1.184 + void* userArg) 1.185 +{ 1.186 + int64_t* parentId = reinterpret_cast<int64_t*>(userArg); 1.187 + if (aKey->bookmark.parentId == *parentId) { 1.188 + return PL_DHASH_REMOVE; 1.189 + } 1.190 + return PL_DHASH_NEXT; 1.191 +} 1.192 + 1.193 +static void 1.194 +ExpireRecentBookmarksByParent(nsTHashtable<BookmarkKeyClass>* hashTable, 1.195 + int64_t aParentId) 1.196 +{ 1.197 + (void)hashTable->EnumerateEntries(ExpireRecentBookmarksByParentCallback, 1.198 + reinterpret_cast<void*>(&aParentId)); 1.199 +} 1.200 + 1.201 +} // Anonymous namespace. 1.202 + 1.203 + 1.204 +nsNavBookmarks::nsNavBookmarks() 1.205 + : mItemCount(0) 1.206 + , mRoot(0) 1.207 + , mMenuRoot(0) 1.208 + , mTagsRoot(0) 1.209 + , mUnfiledRoot(0) 1.210 + , mToolbarRoot(0) 1.211 + , mCanNotify(false) 1.212 + , mCacheObservers("bookmark-observers") 1.213 + , mBatching(false) 1.214 + , mBookmarkToKeywordHash(BOOKMARKS_TO_KEYWORDS_INITIAL_CACHE_SIZE) 1.215 + , mBookmarkToKeywordHashInitialized(false) 1.216 + , mRecentBookmarksCache(RECENT_BOOKMARKS_INITIAL_CACHE_SIZE) 1.217 + , mUncachableBookmarks(RECENT_BOOKMARKS_INITIAL_CACHE_SIZE) 1.218 +{ 1.219 + NS_ASSERTION(!gBookmarksService, 1.220 + "Attempting to create two instances of the service!"); 1.221 + gBookmarksService = this; 1.222 +} 1.223 + 1.224 + 1.225 +nsNavBookmarks::~nsNavBookmarks() 1.226 +{ 1.227 + NS_ASSERTION(gBookmarksService == this, 1.228 + "Deleting a non-singleton instance of the service"); 1.229 + if (gBookmarksService == this) 1.230 + gBookmarksService = nullptr; 1.231 +} 1.232 + 1.233 + 1.234 +NS_IMPL_ISUPPORTS(nsNavBookmarks 1.235 +, nsINavBookmarksService 1.236 +, nsINavHistoryObserver 1.237 +, nsIAnnotationObserver 1.238 +, nsIObserver 1.239 +, nsISupportsWeakReference 1.240 +) 1.241 + 1.242 + 1.243 +nsresult 1.244 +nsNavBookmarks::Init() 1.245 +{ 1.246 + mDB = Database::GetDatabase(); 1.247 + NS_ENSURE_STATE(mDB); 1.248 + 1.249 + nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); 1.250 + if (os) { 1.251 + (void)os->AddObserver(this, TOPIC_PLACES_MAINTENANCE, true); 1.252 + (void)os->AddObserver(this, TOPIC_PLACES_SHUTDOWN, true); 1.253 + (void)os->AddObserver(this, TOPIC_PLACES_CONNECTION_CLOSED, true); 1.254 + } 1.255 + 1.256 + nsresult rv = ReadRoots(); 1.257 + NS_ENSURE_SUCCESS(rv, rv); 1.258 + 1.259 + mCanNotify = true; 1.260 + 1.261 + // Observe annotations. 1.262 + nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService(); 1.263 + NS_ENSURE_TRUE(annosvc, NS_ERROR_OUT_OF_MEMORY); 1.264 + annosvc->AddObserver(this); 1.265 + 1.266 + // Allows us to notify on title changes. MUST BE LAST so it is impossible 1.267 + // to fail after this call, or the history service will have a reference to 1.268 + // us and we won't go away. 1.269 + nsNavHistory* history = nsNavHistory::GetHistoryService(); 1.270 + NS_ENSURE_STATE(history); 1.271 + history->AddObserver(this, true); 1.272 + 1.273 + // DO NOT PUT STUFF HERE that can fail. See observer comment above. 1.274 + 1.275 + return NS_OK; 1.276 +} 1.277 + 1.278 +nsresult 1.279 +nsNavBookmarks::ReadRoots() 1.280 +{ 1.281 + nsCOMPtr<mozIStorageStatement> stmt; 1.282 + nsresult rv = mDB->MainConn()->CreateStatement(NS_LITERAL_CSTRING( 1.283 + "SELECT root_name, folder_id FROM moz_bookmarks_roots" 1.284 + ), getter_AddRefs(stmt)); 1.285 + NS_ENSURE_SUCCESS(rv, rv); 1.286 + 1.287 + bool hasResult; 1.288 + while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) { 1.289 + nsAutoCString rootName; 1.290 + rv = stmt->GetUTF8String(0, rootName); 1.291 + NS_ENSURE_SUCCESS(rv, rv); 1.292 + int64_t rootId; 1.293 + rv = stmt->GetInt64(1, &rootId); 1.294 + NS_ENSURE_SUCCESS(rv, rv); 1.295 + NS_ABORT_IF_FALSE(rootId != 0, "Root id is 0, that is an invalid value."); 1.296 + 1.297 + if (rootName.EqualsLiteral("places")) { 1.298 + mRoot = rootId; 1.299 + } 1.300 + else if (rootName.EqualsLiteral("menu")) { 1.301 + mMenuRoot = rootId; 1.302 + } 1.303 + else if (rootName.EqualsLiteral("toolbar")) { 1.304 + mToolbarRoot = rootId; 1.305 + } 1.306 + else if (rootName.EqualsLiteral("tags")) { 1.307 + mTagsRoot = rootId; 1.308 + } 1.309 + else if (rootName.EqualsLiteral("unfiled")) { 1.310 + mUnfiledRoot = rootId; 1.311 + } 1.312 + } 1.313 + 1.314 + if (!mRoot || !mMenuRoot || !mToolbarRoot || !mTagsRoot || !mUnfiledRoot) 1.315 + return NS_ERROR_FAILURE; 1.316 + 1.317 + return NS_OK; 1.318 +} 1.319 + 1.320 +// nsNavBookmarks::IsBookmarkedInDatabase 1.321 +// 1.322 +// This checks to see if the specified place_id is actually bookmarked. 1.323 + 1.324 +nsresult 1.325 +nsNavBookmarks::IsBookmarkedInDatabase(int64_t aPlaceId, 1.326 + bool* aIsBookmarked) 1.327 +{ 1.328 + nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement( 1.329 + "SELECT 1 FROM moz_bookmarks WHERE fk = :page_id" 1.330 + ); 1.331 + NS_ENSURE_STATE(stmt); 1.332 + mozStorageStatementScoper scoper(stmt); 1.333 + 1.334 + nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), aPlaceId); 1.335 + NS_ENSURE_SUCCESS(rv, rv); 1.336 + rv = stmt->ExecuteStep(aIsBookmarked); 1.337 + NS_ENSURE_SUCCESS(rv, rv); 1.338 + return NS_OK; 1.339 +} 1.340 + 1.341 + 1.342 +nsresult 1.343 +nsNavBookmarks::AdjustIndices(int64_t aFolderId, 1.344 + int32_t aStartIndex, 1.345 + int32_t aEndIndex, 1.346 + int32_t aDelta) 1.347 +{ 1.348 + NS_ASSERTION(aStartIndex >= 0 && aEndIndex <= INT32_MAX && 1.349 + aStartIndex <= aEndIndex, "Bad indices"); 1.350 + 1.351 + // Expire all cached items for this parent, since all positions are going to 1.352 + // change. 1.353 + ExpireRecentBookmarksByParent(&mRecentBookmarksCache, aFolderId); 1.354 + 1.355 + nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement( 1.356 + "UPDATE moz_bookmarks SET position = position + :delta " 1.357 + "WHERE parent = :parent " 1.358 + "AND position BETWEEN :from_index AND :to_index" 1.359 + ); 1.360 + NS_ENSURE_STATE(stmt); 1.361 + mozStorageStatementScoper scoper(stmt); 1.362 + 1.363 + nsresult rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("delta"), aDelta); 1.364 + NS_ENSURE_SUCCESS(rv, rv); 1.365 + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aFolderId); 1.366 + NS_ENSURE_SUCCESS(rv, rv); 1.367 + rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("from_index"), aStartIndex); 1.368 + NS_ENSURE_SUCCESS(rv, rv); 1.369 + rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("to_index"), aEndIndex); 1.370 + NS_ENSURE_SUCCESS(rv, rv); 1.371 + 1.372 + rv = stmt->Execute(); 1.373 + NS_ENSURE_SUCCESS(rv, rv); 1.374 + 1.375 + return NS_OK; 1.376 +} 1.377 + 1.378 + 1.379 +NS_IMETHODIMP 1.380 +nsNavBookmarks::GetPlacesRoot(int64_t* aRoot) 1.381 +{ 1.382 + *aRoot = mRoot; 1.383 + return NS_OK; 1.384 +} 1.385 + 1.386 + 1.387 +NS_IMETHODIMP 1.388 +nsNavBookmarks::GetBookmarksMenuFolder(int64_t* aRoot) 1.389 +{ 1.390 + *aRoot = mMenuRoot; 1.391 + return NS_OK; 1.392 +} 1.393 + 1.394 + 1.395 +NS_IMETHODIMP 1.396 +nsNavBookmarks::GetToolbarFolder(int64_t* aFolderId) 1.397 +{ 1.398 + *aFolderId = mToolbarRoot; 1.399 + return NS_OK; 1.400 +} 1.401 + 1.402 + 1.403 +NS_IMETHODIMP 1.404 +nsNavBookmarks::GetTagsFolder(int64_t* aRoot) 1.405 +{ 1.406 + *aRoot = mTagsRoot; 1.407 + return NS_OK; 1.408 +} 1.409 + 1.410 + 1.411 +NS_IMETHODIMP 1.412 +nsNavBookmarks::GetUnfiledBookmarksFolder(int64_t* aRoot) 1.413 +{ 1.414 + *aRoot = mUnfiledRoot; 1.415 + return NS_OK; 1.416 +} 1.417 + 1.418 + 1.419 +nsresult 1.420 +nsNavBookmarks::InsertBookmarkInDB(int64_t aPlaceId, 1.421 + enum ItemType aItemType, 1.422 + int64_t aParentId, 1.423 + int32_t aIndex, 1.424 + const nsACString& aTitle, 1.425 + PRTime aDateAdded, 1.426 + PRTime aLastModified, 1.427 + const nsACString& aParentGuid, 1.428 + int64_t aGrandParentId, 1.429 + nsIURI* aURI, 1.430 + int64_t* _itemId, 1.431 + nsACString& _guid) 1.432 +{ 1.433 + // Check for a valid itemId. 1.434 + MOZ_ASSERT(_itemId && (*_itemId == -1 || *_itemId > 0)); 1.435 + // Check for a valid placeId. 1.436 + MOZ_ASSERT(aPlaceId && (aPlaceId == -1 || aPlaceId > 0)); 1.437 + 1.438 + nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement( 1.439 + "INSERT INTO moz_bookmarks " 1.440 + "(id, fk, type, parent, position, title, " 1.441 + "dateAdded, lastModified, guid) " 1.442 + "VALUES (:item_id, :page_id, :item_type, :parent, :item_index, " 1.443 + ":item_title, :date_added, :last_modified, " 1.444 + "IFNULL(:item_guid, GENERATE_GUID()))" 1.445 + ); 1.446 + NS_ENSURE_STATE(stmt); 1.447 + mozStorageStatementScoper scoper(stmt); 1.448 + 1.449 + nsresult rv; 1.450 + if (*_itemId != -1) 1.451 + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), *_itemId); 1.452 + else 1.453 + rv = stmt->BindNullByName(NS_LITERAL_CSTRING("item_id")); 1.454 + NS_ENSURE_SUCCESS(rv, rv); 1.455 + 1.456 + if (aPlaceId != -1) 1.457 + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), aPlaceId); 1.458 + else 1.459 + rv = stmt->BindNullByName(NS_LITERAL_CSTRING("page_id")); 1.460 + NS_ENSURE_SUCCESS(rv, rv); 1.461 + 1.462 + rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("item_type"), aItemType); 1.463 + NS_ENSURE_SUCCESS(rv, rv); 1.464 + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aParentId); 1.465 + NS_ENSURE_SUCCESS(rv, rv); 1.466 + rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("item_index"), aIndex); 1.467 + NS_ENSURE_SUCCESS(rv, rv); 1.468 + 1.469 + // Support NULL titles. 1.470 + if (aTitle.IsVoid()) 1.471 + rv = stmt->BindNullByName(NS_LITERAL_CSTRING("item_title")); 1.472 + else 1.473 + rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("item_title"), aTitle); 1.474 + NS_ENSURE_SUCCESS(rv, rv); 1.475 + 1.476 + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("date_added"), aDateAdded); 1.477 + NS_ENSURE_SUCCESS(rv, rv); 1.478 + 1.479 + if (aLastModified) { 1.480 + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("last_modified"), 1.481 + aLastModified); 1.482 + } 1.483 + else { 1.484 + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("last_modified"), aDateAdded); 1.485 + } 1.486 + NS_ENSURE_SUCCESS(rv, rv); 1.487 + 1.488 + // Could use IsEmpty because our callers check for GUID validity, 1.489 + // but it doesn't hurt. 1.490 + if (_guid.Length() == 12) { 1.491 + MOZ_ASSERT(IsValidGUID(_guid)); 1.492 + rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("item_guid"), _guid); 1.493 + } 1.494 + else { 1.495 + rv = stmt->BindNullByName(NS_LITERAL_CSTRING("item_guid")); 1.496 + } 1.497 + NS_ENSURE_SUCCESS(rv, rv); 1.498 + 1.499 + rv = stmt->Execute(); 1.500 + NS_ENSURE_SUCCESS(rv, rv); 1.501 + 1.502 + if (*_itemId == -1) { 1.503 + // Get the newly inserted item id and GUID. 1.504 + nsCOMPtr<mozIStorageStatement> lastInsertIdStmt = mDB->GetStatement( 1.505 + "SELECT id, guid " 1.506 + "FROM moz_bookmarks " 1.507 + "ORDER BY ROWID DESC " 1.508 + "LIMIT 1" 1.509 + ); 1.510 + NS_ENSURE_STATE(lastInsertIdStmt); 1.511 + mozStorageStatementScoper lastInsertIdScoper(lastInsertIdStmt); 1.512 + 1.513 + bool hasResult; 1.514 + rv = lastInsertIdStmt->ExecuteStep(&hasResult); 1.515 + NS_ENSURE_SUCCESS(rv, rv); 1.516 + NS_ENSURE_TRUE(hasResult, NS_ERROR_UNEXPECTED); 1.517 + rv = lastInsertIdStmt->GetInt64(0, _itemId); 1.518 + NS_ENSURE_SUCCESS(rv, rv); 1.519 + rv = lastInsertIdStmt->GetUTF8String(1, _guid); 1.520 + NS_ENSURE_SUCCESS(rv, rv); 1.521 + } 1.522 + 1.523 + if (aParentId > 0) { 1.524 + // Update last modified date of the ancestors. 1.525 + // TODO (bug 408991): Doing this for all ancestors would be slow without a 1.526 + // nested tree, so for now update only the parent. 1.527 + rv = SetItemDateInternal(LAST_MODIFIED, aParentId, aDateAdded); 1.528 + NS_ENSURE_SUCCESS(rv, rv); 1.529 + } 1.530 + 1.531 + // Add a cache entry since we know everything about this bookmark. 1.532 + BookmarkData bookmark; 1.533 + bookmark.id = *_itemId; 1.534 + bookmark.guid.Assign(_guid); 1.535 + if (aTitle.IsVoid()) { 1.536 + bookmark.title.SetIsVoid(true); 1.537 + } 1.538 + else { 1.539 + bookmark.title.Assign(aTitle); 1.540 + } 1.541 + bookmark.position = aIndex; 1.542 + bookmark.placeId = aPlaceId; 1.543 + bookmark.parentId = aParentId; 1.544 + bookmark.type = aItemType; 1.545 + bookmark.dateAdded = aDateAdded; 1.546 + if (aLastModified) 1.547 + bookmark.lastModified = aLastModified; 1.548 + else 1.549 + bookmark.lastModified = aDateAdded; 1.550 + if (aURI) { 1.551 + rv = aURI->GetSpec(bookmark.url); 1.552 + NS_ENSURE_SUCCESS(rv, rv); 1.553 + } 1.554 + bookmark.parentGuid = aParentGuid; 1.555 + bookmark.grandParentId = aGrandParentId; 1.556 + 1.557 + ADD_TO_BOOKMARK_CACHE(*_itemId, bookmark); 1.558 + 1.559 + return NS_OK; 1.560 +} 1.561 + 1.562 + 1.563 +NS_IMETHODIMP 1.564 +nsNavBookmarks::InsertBookmark(int64_t aFolder, 1.565 + nsIURI* aURI, 1.566 + int32_t aIndex, 1.567 + const nsACString& aTitle, 1.568 + const nsACString& aGUID, 1.569 + int64_t* aNewBookmarkId) 1.570 +{ 1.571 + NS_ENSURE_ARG(aURI); 1.572 + NS_ENSURE_ARG_POINTER(aNewBookmarkId); 1.573 + NS_ENSURE_ARG_MIN(aIndex, nsINavBookmarksService::DEFAULT_INDEX); 1.574 + 1.575 + if (!aGUID.IsEmpty() && !IsValidGUID(aGUID)) 1.576 + return NS_ERROR_INVALID_ARG; 1.577 + 1.578 + mozStorageTransaction transaction(mDB->MainConn(), false); 1.579 + 1.580 + nsNavHistory* history = nsNavHistory::GetHistoryService(); 1.581 + NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY); 1.582 + int64_t placeId; 1.583 + nsAutoCString placeGuid; 1.584 + nsresult rv = history->GetOrCreateIdForPage(aURI, &placeId, placeGuid); 1.585 + NS_ENSURE_SUCCESS(rv, rv); 1.586 + 1.587 + // Get the correct index for insertion. This also ensures the parent exists. 1.588 + int32_t index, folderCount; 1.589 + int64_t grandParentId; 1.590 + nsAutoCString folderGuid; 1.591 + rv = FetchFolderInfo(aFolder, &folderCount, folderGuid, &grandParentId); 1.592 + NS_ENSURE_SUCCESS(rv, rv); 1.593 + if (aIndex == nsINavBookmarksService::DEFAULT_INDEX || 1.594 + aIndex >= folderCount) { 1.595 + index = folderCount; 1.596 + } 1.597 + else { 1.598 + index = aIndex; 1.599 + // Create space for the insertion. 1.600 + rv = AdjustIndices(aFolder, index, INT32_MAX, 1); 1.601 + NS_ENSURE_SUCCESS(rv, rv); 1.602 + } 1.603 + 1.604 + *aNewBookmarkId = -1; 1.605 + PRTime dateAdded = PR_Now(); 1.606 + nsAutoCString guid(aGUID); 1.607 + nsCString title; 1.608 + TruncateTitle(aTitle, title); 1.609 + 1.610 + rv = InsertBookmarkInDB(placeId, BOOKMARK, aFolder, index, title, dateAdded, 1.611 + 0, folderGuid, grandParentId, aURI, 1.612 + aNewBookmarkId, guid); 1.613 + NS_ENSURE_SUCCESS(rv, rv); 1.614 + 1.615 + // If not a tag, recalculate frecency for this entry, since it changed. 1.616 + if (grandParentId != mTagsRoot) { 1.617 + rv = history->UpdateFrecency(placeId); 1.618 + NS_ENSURE_SUCCESS(rv, rv); 1.619 + } 1.620 + 1.621 + rv = transaction.Commit(); 1.622 + NS_ENSURE_SUCCESS(rv, rv); 1.623 + 1.624 + NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers, 1.625 + nsINavBookmarkObserver, 1.626 + OnItemAdded(*aNewBookmarkId, aFolder, index, TYPE_BOOKMARK, 1.627 + aURI, title, dateAdded, guid, folderGuid)); 1.628 + 1.629 + // If the bookmark has been added to a tag container, notify all 1.630 + // bookmark-folder result nodes which contain a bookmark for the new 1.631 + // bookmark's url. 1.632 + if (grandParentId == mTagsRoot) { 1.633 + // Notify a tags change to all bookmarks for this URI. 1.634 + nsTArray<BookmarkData> bookmarks; 1.635 + rv = GetBookmarksForURI(aURI, bookmarks); 1.636 + NS_ENSURE_SUCCESS(rv, rv); 1.637 + 1.638 + for (uint32_t i = 0; i < bookmarks.Length(); ++i) { 1.639 + // Check that bookmarks doesn't include the current tag itemId. 1.640 + MOZ_ASSERT(bookmarks[i].id != *aNewBookmarkId); 1.641 + 1.642 + NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers, 1.643 + nsINavBookmarkObserver, 1.644 + OnItemChanged(bookmarks[i].id, 1.645 + NS_LITERAL_CSTRING("tags"), 1.646 + false, 1.647 + EmptyCString(), 1.648 + bookmarks[i].lastModified, 1.649 + TYPE_BOOKMARK, 1.650 + bookmarks[i].parentId, 1.651 + bookmarks[i].guid, 1.652 + bookmarks[i].parentGuid)); 1.653 + } 1.654 + } 1.655 + 1.656 + return NS_OK; 1.657 +} 1.658 + 1.659 + 1.660 +NS_IMETHODIMP 1.661 +nsNavBookmarks::RemoveItem(int64_t aItemId) 1.662 +{ 1.663 + PROFILER_LABEL("bookmarks", "RemoveItem"); 1.664 + NS_ENSURE_ARG(!IsRoot(aItemId)); 1.665 + 1.666 + BookmarkData bookmark; 1.667 + nsresult rv = FetchItemInfo(aItemId, bookmark); 1.668 + NS_ENSURE_SUCCESS(rv, rv); 1.669 + 1.670 + mozStorageTransaction transaction(mDB->MainConn(), false); 1.671 + 1.672 + // First, if not a tag, remove item annotations. 1.673 + if (bookmark.parentId != mTagsRoot && 1.674 + bookmark.grandParentId != mTagsRoot) { 1.675 + nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService(); 1.676 + NS_ENSURE_TRUE(annosvc, NS_ERROR_OUT_OF_MEMORY); 1.677 + rv = annosvc->RemoveItemAnnotations(bookmark.id); 1.678 + NS_ENSURE_SUCCESS(rv, rv); 1.679 + } 1.680 + 1.681 + if (bookmark.type == TYPE_FOLDER) { 1.682 + // Remove all of the folder's children. 1.683 + rv = RemoveFolderChildren(bookmark.id); 1.684 + NS_ENSURE_SUCCESS(rv, rv); 1.685 + } 1.686 + 1.687 + BEGIN_CRITICAL_BOOKMARK_CACHE_SECTION(bookmark.id); 1.688 + 1.689 + nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement( 1.690 + "DELETE FROM moz_bookmarks WHERE id = :item_id" 1.691 + ); 1.692 + NS_ENSURE_STATE(stmt); 1.693 + mozStorageStatementScoper scoper(stmt); 1.694 + 1.695 + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), bookmark.id); 1.696 + NS_ENSURE_SUCCESS(rv, rv); 1.697 + rv = stmt->Execute(); 1.698 + NS_ENSURE_SUCCESS(rv, rv); 1.699 + 1.700 + // Fix indices in the parent. 1.701 + if (bookmark.position != DEFAULT_INDEX) { 1.702 + rv = AdjustIndices(bookmark.parentId, 1.703 + bookmark.position + 1, INT32_MAX, -1); 1.704 + NS_ENSURE_SUCCESS(rv, rv); 1.705 + } 1.706 + 1.707 + bookmark.lastModified = PR_Now(); 1.708 + rv = SetItemDateInternal(LAST_MODIFIED, bookmark.parentId, 1.709 + bookmark.lastModified); 1.710 + NS_ENSURE_SUCCESS(rv, rv); 1.711 + 1.712 + rv = transaction.Commit(); 1.713 + NS_ENSURE_SUCCESS(rv, rv); 1.714 + 1.715 + END_CRITICAL_BOOKMARK_CACHE_SECTION(bookmark.id); 1.716 + 1.717 + nsCOMPtr<nsIURI> uri; 1.718 + if (bookmark.type == TYPE_BOOKMARK) { 1.719 + // If not a tag, recalculate frecency for this entry, since it changed. 1.720 + if (bookmark.grandParentId != mTagsRoot) { 1.721 + nsNavHistory* history = nsNavHistory::GetHistoryService(); 1.722 + NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY); 1.723 + rv = history->UpdateFrecency(bookmark.placeId); 1.724 + NS_ENSURE_SUCCESS(rv, rv); 1.725 + } 1.726 + 1.727 + rv = UpdateKeywordsHashForRemovedBookmark(aItemId); 1.728 + NS_ENSURE_SUCCESS(rv, rv); 1.729 + 1.730 + // A broken url should not interrupt the removal process. 1.731 + (void)NS_NewURI(getter_AddRefs(uri), bookmark.url); 1.732 + } 1.733 + 1.734 + NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers, 1.735 + nsINavBookmarkObserver, 1.736 + OnItemRemoved(bookmark.id, 1.737 + bookmark.parentId, 1.738 + bookmark.position, 1.739 + bookmark.type, 1.740 + uri, 1.741 + bookmark.guid, 1.742 + bookmark.parentGuid)); 1.743 + 1.744 + if (bookmark.type == TYPE_BOOKMARK && bookmark.grandParentId == mTagsRoot && 1.745 + uri) { 1.746 + // If the removed bookmark was child of a tag container, notify a tags 1.747 + // change to all bookmarks for this URI. 1.748 + nsTArray<BookmarkData> bookmarks; 1.749 + rv = GetBookmarksForURI(uri, bookmarks); 1.750 + NS_ENSURE_SUCCESS(rv, rv); 1.751 + 1.752 + for (uint32_t i = 0; i < bookmarks.Length(); ++i) { 1.753 + NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers, 1.754 + nsINavBookmarkObserver, 1.755 + OnItemChanged(bookmarks[i].id, 1.756 + NS_LITERAL_CSTRING("tags"), 1.757 + false, 1.758 + EmptyCString(), 1.759 + bookmarks[i].lastModified, 1.760 + TYPE_BOOKMARK, 1.761 + bookmarks[i].parentId, 1.762 + bookmarks[i].guid, 1.763 + bookmarks[i].parentGuid)); 1.764 + } 1.765 + 1.766 + } 1.767 + 1.768 + return NS_OK; 1.769 +} 1.770 + 1.771 + 1.772 +NS_IMETHODIMP 1.773 +nsNavBookmarks::CreateFolder(int64_t aParent, const nsACString& aName, 1.774 + int32_t aIndex, const nsACString& aGUID, 1.775 + int64_t* aNewFolder) 1.776 +{ 1.777 + // NOTE: aParent can be null for root creation, so not checked 1.778 + NS_ENSURE_ARG_POINTER(aNewFolder); 1.779 + 1.780 + if (!aGUID.IsEmpty() && !IsValidGUID(aGUID)) 1.781 + return NS_ERROR_INVALID_ARG; 1.782 + 1.783 + // CreateContainerWithID returns the index of the new folder, but that's not 1.784 + // used here. To avoid any risk of corrupting data should this function 1.785 + // be changed, we'll use a local variable to hold it. The true argument 1.786 + // will cause notifications to be sent to bookmark observers. 1.787 + int32_t localIndex = aIndex; 1.788 + nsresult rv = CreateContainerWithID(-1, aParent, aName, true, &localIndex, 1.789 + aGUID, aNewFolder); 1.790 + NS_ENSURE_SUCCESS(rv, rv); 1.791 + return NS_OK; 1.792 +} 1.793 + 1.794 +NS_IMETHODIMP 1.795 +nsNavBookmarks::GetFolderReadonly(int64_t aFolder, bool* aResult) 1.796 +{ 1.797 + NS_ENSURE_ARG_MIN(aFolder, 1); 1.798 + NS_ENSURE_ARG_POINTER(aResult); 1.799 + 1.800 + nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService(); 1.801 + NS_ENSURE_TRUE(annosvc, NS_ERROR_OUT_OF_MEMORY); 1.802 + nsresult rv = annosvc->ItemHasAnnotation(aFolder, READ_ONLY_ANNO, aResult); 1.803 + NS_ENSURE_SUCCESS(rv, rv); 1.804 + return NS_OK; 1.805 +} 1.806 + 1.807 + 1.808 +NS_IMETHODIMP 1.809 +nsNavBookmarks::SetFolderReadonly(int64_t aFolder, bool aReadOnly) 1.810 +{ 1.811 + NS_ENSURE_ARG_MIN(aFolder, 1); 1.812 + 1.813 + nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService(); 1.814 + NS_ENSURE_TRUE(annosvc, NS_ERROR_OUT_OF_MEMORY); 1.815 + nsresult rv; 1.816 + if (aReadOnly) { 1.817 + rv = annosvc->SetItemAnnotationInt32(aFolder, READ_ONLY_ANNO, 1, 0, 1.818 + nsAnnotationService::EXPIRE_NEVER); 1.819 + NS_ENSURE_SUCCESS(rv, rv); 1.820 + } 1.821 + else { 1.822 + bool hasAnno; 1.823 + rv = annosvc->ItemHasAnnotation(aFolder, READ_ONLY_ANNO, &hasAnno); 1.824 + NS_ENSURE_SUCCESS(rv, rv); 1.825 + if (hasAnno) { 1.826 + rv = annosvc->RemoveItemAnnotation(aFolder, READ_ONLY_ANNO); 1.827 + NS_ENSURE_SUCCESS(rv, rv); 1.828 + } 1.829 + } 1.830 + return NS_OK; 1.831 +} 1.832 + 1.833 + 1.834 +nsresult 1.835 +nsNavBookmarks::CreateContainerWithID(int64_t aItemId, 1.836 + int64_t aParent, 1.837 + const nsACString& aTitle, 1.838 + bool aIsBookmarkFolder, 1.839 + int32_t* aIndex, 1.840 + const nsACString& aGUID, 1.841 + int64_t* aNewFolder) 1.842 +{ 1.843 + NS_ENSURE_ARG_MIN(*aIndex, nsINavBookmarksService::DEFAULT_INDEX); 1.844 + 1.845 + // Get the correct index for insertion. This also ensures the parent exists. 1.846 + int32_t index, folderCount; 1.847 + int64_t grandParentId; 1.848 + nsAutoCString folderGuid; 1.849 + nsresult rv = FetchFolderInfo(aParent, &folderCount, folderGuid, &grandParentId); 1.850 + NS_ENSURE_SUCCESS(rv, rv); 1.851 + 1.852 + mozStorageTransaction transaction(mDB->MainConn(), false); 1.853 + 1.854 + if (*aIndex == nsINavBookmarksService::DEFAULT_INDEX || 1.855 + *aIndex >= folderCount) { 1.856 + index = folderCount; 1.857 + } else { 1.858 + index = *aIndex; 1.859 + // Create space for the insertion. 1.860 + rv = AdjustIndices(aParent, index, INT32_MAX, 1); 1.861 + NS_ENSURE_SUCCESS(rv, rv); 1.862 + } 1.863 + 1.864 + *aNewFolder = aItemId; 1.865 + PRTime dateAdded = PR_Now(); 1.866 + nsAutoCString guid(aGUID); 1.867 + nsCString title; 1.868 + TruncateTitle(aTitle, title); 1.869 + 1.870 + rv = InsertBookmarkInDB(-1, FOLDER, aParent, index, 1.871 + title, dateAdded, 0, folderGuid, grandParentId, 1.872 + nullptr, aNewFolder, guid); 1.873 + NS_ENSURE_SUCCESS(rv, rv); 1.874 + 1.875 + rv = transaction.Commit(); 1.876 + NS_ENSURE_SUCCESS(rv, rv); 1.877 + 1.878 + NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers, 1.879 + nsINavBookmarkObserver, 1.880 + OnItemAdded(*aNewFolder, aParent, index, FOLDER, 1.881 + nullptr, title, dateAdded, guid, folderGuid)); 1.882 + 1.883 + *aIndex = index; 1.884 + return NS_OK; 1.885 +} 1.886 + 1.887 + 1.888 +NS_IMETHODIMP 1.889 +nsNavBookmarks::InsertSeparator(int64_t aParent, 1.890 + int32_t aIndex, 1.891 + const nsACString& aGUID, 1.892 + int64_t* aNewItemId) 1.893 +{ 1.894 + NS_ENSURE_ARG_MIN(aParent, 1); 1.895 + NS_ENSURE_ARG_MIN(aIndex, nsINavBookmarksService::DEFAULT_INDEX); 1.896 + NS_ENSURE_ARG_POINTER(aNewItemId); 1.897 + 1.898 + if (!aGUID.IsEmpty() && !IsValidGUID(aGUID)) 1.899 + return NS_ERROR_INVALID_ARG; 1.900 + 1.901 + // Get the correct index for insertion. This also ensures the parent exists. 1.902 + int32_t index, folderCount; 1.903 + int64_t grandParentId; 1.904 + nsAutoCString folderGuid; 1.905 + nsresult rv = FetchFolderInfo(aParent, &folderCount, folderGuid, &grandParentId); 1.906 + NS_ENSURE_SUCCESS(rv, rv); 1.907 + 1.908 + mozStorageTransaction transaction(mDB->MainConn(), false); 1.909 + 1.910 + if (aIndex == nsINavBookmarksService::DEFAULT_INDEX || 1.911 + aIndex >= folderCount) { 1.912 + index = folderCount; 1.913 + } 1.914 + else { 1.915 + index = aIndex; 1.916 + // Create space for the insertion. 1.917 + rv = AdjustIndices(aParent, index, INT32_MAX, 1); 1.918 + NS_ENSURE_SUCCESS(rv, rv); 1.919 + } 1.920 + 1.921 + *aNewItemId = -1; 1.922 + // Set a NULL title rather than an empty string. 1.923 + nsCString voidString; 1.924 + voidString.SetIsVoid(true); 1.925 + nsAutoCString guid(aGUID); 1.926 + PRTime dateAdded = PR_Now(); 1.927 + rv = InsertBookmarkInDB(-1, SEPARATOR, aParent, index, voidString, dateAdded, 1.928 + 0, folderGuid, grandParentId, nullptr, 1.929 + aNewItemId, guid); 1.930 + NS_ENSURE_SUCCESS(rv, rv); 1.931 + 1.932 + rv = transaction.Commit(); 1.933 + NS_ENSURE_SUCCESS(rv, rv); 1.934 + 1.935 + NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers, 1.936 + nsINavBookmarkObserver, 1.937 + OnItemAdded(*aNewItemId, aParent, index, TYPE_SEPARATOR, 1.938 + nullptr, voidString, dateAdded, guid, folderGuid)); 1.939 + 1.940 + return NS_OK; 1.941 +} 1.942 + 1.943 + 1.944 +nsresult 1.945 +nsNavBookmarks::GetLastChildId(int64_t aFolderId, int64_t* aItemId) 1.946 +{ 1.947 + NS_ASSERTION(aFolderId > 0, "Invalid folder id"); 1.948 + *aItemId = -1; 1.949 + 1.950 + nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement( 1.951 + "SELECT id FROM moz_bookmarks WHERE parent = :parent " 1.952 + "ORDER BY position DESC LIMIT 1" 1.953 + ); 1.954 + NS_ENSURE_STATE(stmt); 1.955 + mozStorageStatementScoper scoper(stmt); 1.956 + 1.957 + nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aFolderId); 1.958 + NS_ENSURE_SUCCESS(rv, rv); 1.959 + bool found; 1.960 + rv = stmt->ExecuteStep(&found); 1.961 + NS_ENSURE_SUCCESS(rv, rv); 1.962 + if (found) { 1.963 + rv = stmt->GetInt64(0, aItemId); 1.964 + NS_ENSURE_SUCCESS(rv, rv); 1.965 + } 1.966 + 1.967 + return NS_OK; 1.968 +} 1.969 + 1.970 + 1.971 +NS_IMETHODIMP 1.972 +nsNavBookmarks::GetIdForItemAt(int64_t aFolder, 1.973 + int32_t aIndex, 1.974 + int64_t* aItemId) 1.975 +{ 1.976 + NS_ENSURE_ARG_MIN(aFolder, 1); 1.977 + NS_ENSURE_ARG_POINTER(aItemId); 1.978 + 1.979 + *aItemId = -1; 1.980 + 1.981 + nsresult rv; 1.982 + if (aIndex == nsINavBookmarksService::DEFAULT_INDEX) { 1.983 + // Get last item within aFolder. 1.984 + rv = GetLastChildId(aFolder, aItemId); 1.985 + NS_ENSURE_SUCCESS(rv, rv); 1.986 + } 1.987 + else { 1.988 + // Get the item in aFolder with position aIndex. 1.989 + nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement( 1.990 + "SELECT id, fk, type FROM moz_bookmarks " 1.991 + "WHERE parent = :parent AND position = :item_index" 1.992 + ); 1.993 + NS_ENSURE_STATE(stmt); 1.994 + mozStorageStatementScoper scoper(stmt); 1.995 + 1.996 + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aFolder); 1.997 + NS_ENSURE_SUCCESS(rv, rv); 1.998 + rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("item_index"), aIndex); 1.999 + NS_ENSURE_SUCCESS(rv, rv); 1.1000 + 1.1001 + bool found; 1.1002 + rv = stmt->ExecuteStep(&found); 1.1003 + NS_ENSURE_SUCCESS(rv, rv); 1.1004 + if (found) { 1.1005 + rv = stmt->GetInt64(0, aItemId); 1.1006 + NS_ENSURE_SUCCESS(rv, rv); 1.1007 + } 1.1008 + } 1.1009 + return NS_OK; 1.1010 +} 1.1011 + 1.1012 +NS_IMPL_ISUPPORTS(nsNavBookmarks::RemoveFolderTransaction, nsITransaction) 1.1013 + 1.1014 +NS_IMETHODIMP 1.1015 +nsNavBookmarks::GetRemoveFolderTransaction(int64_t aFolderId, nsITransaction** aResult) 1.1016 +{ 1.1017 + NS_ENSURE_ARG_MIN(aFolderId, 1); 1.1018 + NS_ENSURE_ARG_POINTER(aResult); 1.1019 + 1.1020 + // Create and initialize a RemoveFolderTransaction object that can be used to 1.1021 + // recreate the folder safely later. 1.1022 + 1.1023 + RemoveFolderTransaction* rft = 1.1024 + new RemoveFolderTransaction(aFolderId); 1.1025 + if (!rft) 1.1026 + return NS_ERROR_OUT_OF_MEMORY; 1.1027 + 1.1028 + NS_ADDREF(*aResult = rft); 1.1029 + return NS_OK; 1.1030 +} 1.1031 + 1.1032 + 1.1033 +nsresult 1.1034 +nsNavBookmarks::GetDescendantFolders(int64_t aFolderId, 1.1035 + nsTArray<int64_t>& aDescendantFoldersArray) { 1.1036 + nsresult rv; 1.1037 + // New descendant folders will be added from this index on. 1.1038 + uint32_t startIndex = aDescendantFoldersArray.Length(); 1.1039 + { 1.1040 + nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement( 1.1041 + "SELECT id " 1.1042 + "FROM moz_bookmarks " 1.1043 + "WHERE parent = :parent " 1.1044 + "AND type = :item_type " 1.1045 + ); 1.1046 + NS_ENSURE_STATE(stmt); 1.1047 + mozStorageStatementScoper scoper(stmt); 1.1048 + 1.1049 + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aFolderId); 1.1050 + NS_ENSURE_SUCCESS(rv, rv); 1.1051 + rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("item_type"), TYPE_FOLDER); 1.1052 + NS_ENSURE_SUCCESS(rv, rv); 1.1053 + 1.1054 + bool hasMore = false; 1.1055 + while (NS_SUCCEEDED(stmt->ExecuteStep(&hasMore)) && hasMore) { 1.1056 + int64_t itemId; 1.1057 + rv = stmt->GetInt64(0, &itemId); 1.1058 + NS_ENSURE_SUCCESS(rv, rv); 1.1059 + aDescendantFoldersArray.AppendElement(itemId); 1.1060 + } 1.1061 + } 1.1062 + 1.1063 + // Recursively call GetDescendantFolders for added folders. 1.1064 + // We start at startIndex since previous folders are checked 1.1065 + // by previous calls to this method. 1.1066 + uint32_t childCount = aDescendantFoldersArray.Length(); 1.1067 + for (uint32_t i = startIndex; i < childCount; ++i) { 1.1068 + GetDescendantFolders(aDescendantFoldersArray[i], aDescendantFoldersArray); 1.1069 + } 1.1070 + 1.1071 + return NS_OK; 1.1072 +} 1.1073 + 1.1074 + 1.1075 +nsresult 1.1076 +nsNavBookmarks::GetDescendantChildren(int64_t aFolderId, 1.1077 + const nsACString& aFolderGuid, 1.1078 + int64_t aGrandParentId, 1.1079 + nsTArray<BookmarkData>& aFolderChildrenArray) { 1.1080 + // New children will be added from this index on. 1.1081 + uint32_t startIndex = aFolderChildrenArray.Length(); 1.1082 + nsresult rv; 1.1083 + { 1.1084 + // Collect children informations. 1.1085 + // Select all children of a given folder, sorted by position. 1.1086 + // This is a LEFT JOIN because not all bookmarks types have a place. 1.1087 + // We construct a result where the first columns exactly match 1.1088 + // kGetInfoIndex_* order, and additionally contains columns for position, 1.1089 + // item_child, and folder_child from moz_bookmarks. 1.1090 + nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement( 1.1091 + "SELECT h.id, h.url, IFNULL(b.title, h.title), h.rev_host, h.visit_count, " 1.1092 + "h.last_visit_date, f.url, b.id, b.dateAdded, b.lastModified, " 1.1093 + "b.parent, null, h.frecency, h.hidden, h.guid, b.guid, " 1.1094 + "b.position, b.type, b.fk " 1.1095 + "FROM moz_bookmarks b " 1.1096 + "LEFT JOIN moz_places h ON b.fk = h.id " 1.1097 + "LEFT JOIN moz_favicons f ON h.favicon_id = f.id " 1.1098 + "WHERE b.parent = :parent " 1.1099 + "ORDER BY b.position ASC" 1.1100 + ); 1.1101 + NS_ENSURE_STATE(stmt); 1.1102 + mozStorageStatementScoper scoper(stmt); 1.1103 + 1.1104 + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aFolderId); 1.1105 + NS_ENSURE_SUCCESS(rv, rv); 1.1106 + 1.1107 + bool hasMore; 1.1108 + while (NS_SUCCEEDED(stmt->ExecuteStep(&hasMore)) && hasMore) { 1.1109 + BookmarkData child; 1.1110 + rv = stmt->GetInt64(nsNavHistory::kGetInfoIndex_ItemId, &child.id); 1.1111 + NS_ENSURE_SUCCESS(rv, rv); 1.1112 + child.parentId = aFolderId; 1.1113 + child.grandParentId = aGrandParentId; 1.1114 + child.parentGuid = aFolderGuid; 1.1115 + rv = stmt->GetInt32(kGetChildrenIndex_Type, &child.type); 1.1116 + NS_ENSURE_SUCCESS(rv, rv); 1.1117 + rv = stmt->GetInt64(kGetChildrenIndex_PlaceID, &child.placeId); 1.1118 + NS_ENSURE_SUCCESS(rv, rv); 1.1119 + rv = stmt->GetInt32(kGetChildrenIndex_Position, &child.position); 1.1120 + NS_ENSURE_SUCCESS(rv, rv); 1.1121 + rv = stmt->GetUTF8String(kGetChildrenIndex_Guid, child.guid); 1.1122 + NS_ENSURE_SUCCESS(rv, rv); 1.1123 + 1.1124 + if (child.type == TYPE_BOOKMARK) { 1.1125 + rv = stmt->GetUTF8String(nsNavHistory::kGetInfoIndex_URL, child.url); 1.1126 + NS_ENSURE_SUCCESS(rv, rv); 1.1127 + } 1.1128 + 1.1129 + // Append item to children's array. 1.1130 + aFolderChildrenArray.AppendElement(child); 1.1131 + } 1.1132 + } 1.1133 + 1.1134 + // Recursively call GetDescendantChildren for added folders. 1.1135 + // We start at startIndex since previous folders are checked 1.1136 + // by previous calls to this method. 1.1137 + uint32_t childCount = aFolderChildrenArray.Length(); 1.1138 + for (uint32_t i = startIndex; i < childCount; ++i) { 1.1139 + if (aFolderChildrenArray[i].type == TYPE_FOLDER) { 1.1140 + // nsTarray assumes that all children can be memmove()d, thus we can't 1.1141 + // just pass aFolderChildrenArray[i].guid to a method that will change 1.1142 + // the array itself. Otherwise, since it's passed by reference, after a 1.1143 + // memmove() it could point to garbage and cause intermittent crashes. 1.1144 + nsCString guid = aFolderChildrenArray[i].guid; 1.1145 + GetDescendantChildren(aFolderChildrenArray[i].id, 1.1146 + guid, 1.1147 + aFolderId, 1.1148 + aFolderChildrenArray); 1.1149 + } 1.1150 + } 1.1151 + 1.1152 + return NS_OK; 1.1153 +} 1.1154 + 1.1155 + 1.1156 +NS_IMETHODIMP 1.1157 +nsNavBookmarks::RemoveFolderChildren(int64_t aFolderId) 1.1158 +{ 1.1159 + PROFILER_LABEL("bookmarks", "RemoveFolderChilder"); 1.1160 + NS_ENSURE_ARG_MIN(aFolderId, 1); 1.1161 + NS_ENSURE_ARG(aFolderId != mRoot); 1.1162 + 1.1163 + BookmarkData folder; 1.1164 + nsresult rv = FetchItemInfo(aFolderId, folder); 1.1165 + NS_ENSURE_SUCCESS(rv, rv); 1.1166 + NS_ENSURE_ARG(folder.type == TYPE_FOLDER); 1.1167 + 1.1168 + // Fill folder children array recursively. 1.1169 + nsTArray<BookmarkData> folderChildrenArray; 1.1170 + rv = GetDescendantChildren(folder.id, folder.guid, folder.parentId, 1.1171 + folderChildrenArray); 1.1172 + NS_ENSURE_SUCCESS(rv, rv); 1.1173 + 1.1174 + // Build a string of folders whose children will be removed. 1.1175 + nsCString foldersToRemove; 1.1176 + for (uint32_t i = 0; i < folderChildrenArray.Length(); ++i) { 1.1177 + BookmarkData& child = folderChildrenArray[i]; 1.1178 + 1.1179 + if (child.type == TYPE_FOLDER) { 1.1180 + foldersToRemove.AppendLiteral(","); 1.1181 + foldersToRemove.AppendInt(child.id); 1.1182 + } 1.1183 + 1.1184 + BEGIN_CRITICAL_BOOKMARK_CACHE_SECTION(child.id); 1.1185 + } 1.1186 + 1.1187 + // Delete items from the database now. 1.1188 + mozStorageTransaction transaction(mDB->MainConn(), false); 1.1189 + 1.1190 + nsCOMPtr<mozIStorageStatement> deleteStatement = mDB->GetStatement( 1.1191 + NS_LITERAL_CSTRING( 1.1192 + "DELETE FROM moz_bookmarks " 1.1193 + "WHERE parent IN (:parent") + foldersToRemove + NS_LITERAL_CSTRING(")") 1.1194 + ); 1.1195 + NS_ENSURE_STATE(deleteStatement); 1.1196 + mozStorageStatementScoper deleteStatementScoper(deleteStatement); 1.1197 + 1.1198 + rv = deleteStatement->BindInt64ByName(NS_LITERAL_CSTRING("parent"), folder.id); 1.1199 + NS_ENSURE_SUCCESS(rv, rv); 1.1200 + rv = deleteStatement->Execute(); 1.1201 + NS_ENSURE_SUCCESS(rv, rv); 1.1202 + 1.1203 + // Clean up orphan items annotations. 1.1204 + rv = mDB->MainConn()->ExecuteSimpleSQL( 1.1205 + NS_LITERAL_CSTRING( 1.1206 + "DELETE FROM moz_items_annos " 1.1207 + "WHERE id IN (" 1.1208 + "SELECT a.id from moz_items_annos a " 1.1209 + "LEFT JOIN moz_bookmarks b ON a.item_id = b.id " 1.1210 + "WHERE b.id ISNULL)")); 1.1211 + NS_ENSURE_SUCCESS(rv, rv); 1.1212 + 1.1213 + // Set the lastModified date. 1.1214 + rv = SetItemDateInternal(LAST_MODIFIED, folder.id, PR_Now()); 1.1215 + NS_ENSURE_SUCCESS(rv, rv); 1.1216 + 1.1217 + for (uint32_t i = 0; i < folderChildrenArray.Length(); i++) { 1.1218 + BookmarkData& child = folderChildrenArray[i]; 1.1219 + if (child.type == TYPE_BOOKMARK) { 1.1220 + // If not a tag, recalculate frecency for this entry, since it changed. 1.1221 + if (child.grandParentId != mTagsRoot) { 1.1222 + nsNavHistory* history = nsNavHistory::GetHistoryService(); 1.1223 + NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY); 1.1224 + rv = history->UpdateFrecency(child.placeId); 1.1225 + NS_ENSURE_SUCCESS(rv, rv); 1.1226 + } 1.1227 + 1.1228 + rv = UpdateKeywordsHashForRemovedBookmark(child.id); 1.1229 + NS_ENSURE_SUCCESS(rv, rv); 1.1230 + } 1.1231 + END_CRITICAL_BOOKMARK_CACHE_SECTION(child.id); 1.1232 + } 1.1233 + 1.1234 + rv = transaction.Commit(); 1.1235 + NS_ENSURE_SUCCESS(rv, rv); 1.1236 + 1.1237 + // Call observers in reverse order to serve children before their parent. 1.1238 + for (int32_t i = folderChildrenArray.Length() - 1; i >= 0; --i) { 1.1239 + BookmarkData& child = folderChildrenArray[i]; 1.1240 + nsCOMPtr<nsIURI> uri; 1.1241 + if (child.type == TYPE_BOOKMARK) { 1.1242 + // A broken url should not interrupt the removal process. 1.1243 + (void)NS_NewURI(getter_AddRefs(uri), child.url); 1.1244 + } 1.1245 + 1.1246 + NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers, 1.1247 + nsINavBookmarkObserver, 1.1248 + OnItemRemoved(child.id, 1.1249 + child.parentId, 1.1250 + child.position, 1.1251 + child.type, 1.1252 + uri, 1.1253 + child.guid, 1.1254 + child.parentGuid)); 1.1255 + 1.1256 + if (child.type == TYPE_BOOKMARK && child.grandParentId == mTagsRoot && 1.1257 + uri) { 1.1258 + // If the removed bookmark was a child of a tag container, notify all 1.1259 + // bookmark-folder result nodes which contain a bookmark for the removed 1.1260 + // bookmark's url. 1.1261 + nsTArray<BookmarkData> bookmarks; 1.1262 + rv = GetBookmarksForURI(uri, bookmarks); 1.1263 + NS_ENSURE_SUCCESS(rv, rv); 1.1264 + 1.1265 + for (uint32_t i = 0; i < bookmarks.Length(); ++i) { 1.1266 + NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers, 1.1267 + nsINavBookmarkObserver, 1.1268 + OnItemChanged(bookmarks[i].id, 1.1269 + NS_LITERAL_CSTRING("tags"), 1.1270 + false, 1.1271 + EmptyCString(), 1.1272 + bookmarks[i].lastModified, 1.1273 + TYPE_BOOKMARK, 1.1274 + bookmarks[i].parentId, 1.1275 + bookmarks[i].guid, 1.1276 + bookmarks[i].parentGuid)); 1.1277 + } 1.1278 + } 1.1279 + } 1.1280 + 1.1281 + return NS_OK; 1.1282 +} 1.1283 + 1.1284 + 1.1285 +NS_IMETHODIMP 1.1286 +nsNavBookmarks::MoveItem(int64_t aItemId, int64_t aNewParent, int32_t aIndex) 1.1287 +{ 1.1288 + NS_ENSURE_ARG(!IsRoot(aItemId)); 1.1289 + NS_ENSURE_ARG_MIN(aItemId, 1); 1.1290 + NS_ENSURE_ARG_MIN(aNewParent, 1); 1.1291 + // -1 is append, but no other negative number is allowed. 1.1292 + NS_ENSURE_ARG_MIN(aIndex, -1); 1.1293 + // Disallow making an item its own parent. 1.1294 + NS_ENSURE_ARG(aItemId != aNewParent); 1.1295 + 1.1296 + mozStorageTransaction transaction(mDB->MainConn(), false); 1.1297 + 1.1298 + BookmarkData bookmark; 1.1299 + nsresult rv = FetchItemInfo(aItemId, bookmark); 1.1300 + NS_ENSURE_SUCCESS(rv, rv); 1.1301 + 1.1302 + // if parent and index are the same, nothing to do 1.1303 + if (bookmark.parentId == aNewParent && bookmark.position == aIndex) 1.1304 + return NS_OK; 1.1305 + 1.1306 + // Make sure aNewParent is not aFolder or a subfolder of aFolder. 1.1307 + // TODO: make this performant, maybe with a nested tree (bug 408991). 1.1308 + if (bookmark.type == TYPE_FOLDER) { 1.1309 + int64_t ancestorId = aNewParent; 1.1310 + 1.1311 + while (ancestorId) { 1.1312 + if (ancestorId == bookmark.id) { 1.1313 + return NS_ERROR_INVALID_ARG; 1.1314 + } 1.1315 + rv = GetFolderIdForItem(ancestorId, &ancestorId); 1.1316 + if (NS_FAILED(rv)) { 1.1317 + break; 1.1318 + } 1.1319 + } 1.1320 + } 1.1321 + 1.1322 + // calculate new index 1.1323 + int32_t newIndex, folderCount; 1.1324 + int64_t grandParentId; 1.1325 + nsAutoCString newParentGuid; 1.1326 + rv = FetchFolderInfo(aNewParent, &folderCount, newParentGuid, &grandParentId); 1.1327 + NS_ENSURE_SUCCESS(rv, rv); 1.1328 + if (aIndex == nsINavBookmarksService::DEFAULT_INDEX || 1.1329 + aIndex >= folderCount) { 1.1330 + newIndex = folderCount; 1.1331 + // If the parent remains the same, then the folder is really being moved 1.1332 + // to count - 1 (since it's being removed from the old position) 1.1333 + if (bookmark.parentId == aNewParent) { 1.1334 + --newIndex; 1.1335 + } 1.1336 + } else { 1.1337 + newIndex = aIndex; 1.1338 + 1.1339 + if (bookmark.parentId == aNewParent && newIndex > bookmark.position) { 1.1340 + // when an item is being moved lower in the same folder, the new index 1.1341 + // refers to the index before it was removed. Removal causes everything 1.1342 + // to shift up. 1.1343 + --newIndex; 1.1344 + } 1.1345 + } 1.1346 + 1.1347 + // this is like the previous check, except this covers if 1.1348 + // the specified index was -1 (append), and the calculated 1.1349 + // new index is the same as the existing index 1.1350 + if (aNewParent == bookmark.parentId && newIndex == bookmark.position) { 1.1351 + // Nothing to do! 1.1352 + return NS_OK; 1.1353 + } 1.1354 + 1.1355 + // adjust indices to account for the move 1.1356 + // do this before we update the parent/index fields 1.1357 + // or we'll re-adjust the index for the item we are moving 1.1358 + if (bookmark.parentId == aNewParent) { 1.1359 + // We can optimize the updates if moving within the same container. 1.1360 + // We only shift the items between the old and new positions, since the 1.1361 + // insertion will offset the deletion. 1.1362 + if (bookmark.position > newIndex) { 1.1363 + rv = AdjustIndices(bookmark.parentId, newIndex, bookmark.position - 1, 1); 1.1364 + } 1.1365 + else { 1.1366 + rv = AdjustIndices(bookmark.parentId, bookmark.position + 1, newIndex, -1); 1.1367 + } 1.1368 + NS_ENSURE_SUCCESS(rv, rv); 1.1369 + } 1.1370 + else { 1.1371 + // We're moving between containers, so this happens in two steps. 1.1372 + // First, fill the hole from the removal from the old parent. 1.1373 + rv = AdjustIndices(bookmark.parentId, bookmark.position + 1, INT32_MAX, -1); 1.1374 + NS_ENSURE_SUCCESS(rv, rv); 1.1375 + // Now, make room in the new parent for the insertion. 1.1376 + rv = AdjustIndices(aNewParent, newIndex, INT32_MAX, 1); 1.1377 + NS_ENSURE_SUCCESS(rv, rv); 1.1378 + } 1.1379 + 1.1380 + BEGIN_CRITICAL_BOOKMARK_CACHE_SECTION(bookmark.id); 1.1381 + 1.1382 + { 1.1383 + // Update parent and position. 1.1384 + nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement( 1.1385 + "UPDATE moz_bookmarks SET parent = :parent, position = :item_index " 1.1386 + "WHERE id = :item_id " 1.1387 + ); 1.1388 + NS_ENSURE_STATE(stmt); 1.1389 + mozStorageStatementScoper scoper(stmt); 1.1390 + 1.1391 + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aNewParent); 1.1392 + NS_ENSURE_SUCCESS(rv, rv); 1.1393 + rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("item_index"), newIndex); 1.1394 + NS_ENSURE_SUCCESS(rv, rv); 1.1395 + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), bookmark.id); 1.1396 + NS_ENSURE_SUCCESS(rv, rv); 1.1397 + rv = stmt->Execute(); 1.1398 + NS_ENSURE_SUCCESS(rv, rv); 1.1399 + } 1.1400 + 1.1401 + PRTime now = PR_Now(); 1.1402 + rv = SetItemDateInternal(LAST_MODIFIED, bookmark.parentId, now); 1.1403 + NS_ENSURE_SUCCESS(rv, rv); 1.1404 + rv = SetItemDateInternal(LAST_MODIFIED, aNewParent, now); 1.1405 + NS_ENSURE_SUCCESS(rv, rv); 1.1406 + 1.1407 + rv = transaction.Commit(); 1.1408 + NS_ENSURE_SUCCESS(rv, rv); 1.1409 + 1.1410 + END_CRITICAL_BOOKMARK_CACHE_SECTION(bookmark.id); 1.1411 + 1.1412 + NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers, 1.1413 + nsINavBookmarkObserver, 1.1414 + OnItemMoved(bookmark.id, 1.1415 + bookmark.parentId, 1.1416 + bookmark.position, 1.1417 + aNewParent, 1.1418 + newIndex, 1.1419 + bookmark.type, 1.1420 + bookmark.guid, 1.1421 + bookmark.parentGuid, 1.1422 + newParentGuid)); 1.1423 + return NS_OK; 1.1424 +} 1.1425 + 1.1426 +nsresult 1.1427 +nsNavBookmarks::FetchItemInfo(int64_t aItemId, 1.1428 + BookmarkData& _bookmark) 1.1429 +{ 1.1430 + // Check if the requested id is in the recent cache and avoid the database 1.1431 + // lookup if so. Invalidate the cache after getting data if requested. 1.1432 + BookmarkKeyClass* key = mRecentBookmarksCache.GetEntry(aItemId); 1.1433 + if (key) { 1.1434 + _bookmark = key->bookmark; 1.1435 + return NS_OK; 1.1436 + } 1.1437 + 1.1438 + // LEFT JOIN since not all bookmarks have an associated place. 1.1439 + nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement( 1.1440 + "SELECT b.id, h.url, b.title, b.position, b.fk, b.parent, b.type, " 1.1441 + "b.dateAdded, b.lastModified, b.guid, t.guid, t.parent " 1.1442 + "FROM moz_bookmarks b " 1.1443 + "LEFT JOIN moz_bookmarks t ON t.id = b.parent " 1.1444 + "LEFT JOIN moz_places h ON h.id = b.fk " 1.1445 + "WHERE b.id = :item_id" 1.1446 + ); 1.1447 + NS_ENSURE_STATE(stmt); 1.1448 + mozStorageStatementScoper scoper(stmt); 1.1449 + 1.1450 + nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), aItemId); 1.1451 + NS_ENSURE_SUCCESS(rv, rv); 1.1452 + 1.1453 + bool hasResult; 1.1454 + rv = stmt->ExecuteStep(&hasResult); 1.1455 + NS_ENSURE_SUCCESS(rv, rv); 1.1456 + if (!hasResult) { 1.1457 + return NS_ERROR_INVALID_ARG; 1.1458 + } 1.1459 + 1.1460 + _bookmark.id = aItemId; 1.1461 + rv = stmt->GetUTF8String(1, _bookmark.url); 1.1462 + NS_ENSURE_SUCCESS(rv, rv); 1.1463 + bool isNull; 1.1464 + rv = stmt->GetIsNull(2, &isNull); 1.1465 + NS_ENSURE_SUCCESS(rv, rv); 1.1466 + if (isNull) { 1.1467 + _bookmark.title.SetIsVoid(true); 1.1468 + } 1.1469 + else { 1.1470 + rv = stmt->GetUTF8String(2, _bookmark.title); 1.1471 + NS_ENSURE_SUCCESS(rv, rv); 1.1472 + } 1.1473 + rv = stmt->GetInt32(3, &_bookmark.position); 1.1474 + NS_ENSURE_SUCCESS(rv, rv); 1.1475 + rv = stmt->GetInt64(4, &_bookmark.placeId); 1.1476 + NS_ENSURE_SUCCESS(rv, rv); 1.1477 + rv = stmt->GetInt64(5, &_bookmark.parentId); 1.1478 + NS_ENSURE_SUCCESS(rv, rv); 1.1479 + rv = stmt->GetInt32(6, &_bookmark.type); 1.1480 + NS_ENSURE_SUCCESS(rv, rv); 1.1481 + rv = stmt->GetInt64(7, reinterpret_cast<int64_t*>(&_bookmark.dateAdded)); 1.1482 + NS_ENSURE_SUCCESS(rv, rv); 1.1483 + rv = stmt->GetInt64(8, reinterpret_cast<int64_t*>(&_bookmark.lastModified)); 1.1484 + NS_ENSURE_SUCCESS(rv, rv); 1.1485 + rv = stmt->GetUTF8String(9, _bookmark.guid); 1.1486 + NS_ENSURE_SUCCESS(rv, rv); 1.1487 + // Getting properties of the root would show no parent. 1.1488 + rv = stmt->GetIsNull(10, &isNull); 1.1489 + NS_ENSURE_SUCCESS(rv, rv); 1.1490 + if (!isNull) { 1.1491 + rv = stmt->GetUTF8String(10, _bookmark.parentGuid); 1.1492 + NS_ENSURE_SUCCESS(rv, rv); 1.1493 + rv = stmt->GetInt64(11, &_bookmark.grandParentId); 1.1494 + NS_ENSURE_SUCCESS(rv, rv); 1.1495 + } 1.1496 + else { 1.1497 + _bookmark.grandParentId = -1; 1.1498 + } 1.1499 + 1.1500 + ADD_TO_BOOKMARK_CACHE(aItemId, _bookmark); 1.1501 + 1.1502 + return NS_OK; 1.1503 +} 1.1504 + 1.1505 +nsresult 1.1506 +nsNavBookmarks::SetItemDateInternal(enum BookmarkDate aDateType, 1.1507 + int64_t aItemId, 1.1508 + PRTime aValue) 1.1509 +{ 1.1510 + nsCOMPtr<mozIStorageStatement> stmt; 1.1511 + if (aDateType == DATE_ADDED) { 1.1512 + // lastModified is set to the same value as dateAdded. We do this for 1.1513 + // performance reasons, since it will allow us to use an index to sort items 1.1514 + // by date. 1.1515 + stmt = mDB->GetStatement( 1.1516 + "UPDATE moz_bookmarks SET dateAdded = :date, lastModified = :date " 1.1517 + "WHERE id = :item_id" 1.1518 + ); 1.1519 + } 1.1520 + else { 1.1521 + stmt = mDB->GetStatement( 1.1522 + "UPDATE moz_bookmarks SET lastModified = :date WHERE id = :item_id" 1.1523 + ); 1.1524 + } 1.1525 + NS_ENSURE_STATE(stmt); 1.1526 + mozStorageStatementScoper scoper(stmt); 1.1527 + 1.1528 + nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("date"), aValue); 1.1529 + NS_ENSURE_SUCCESS(rv, rv); 1.1530 + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), aItemId); 1.1531 + NS_ENSURE_SUCCESS(rv, rv); 1.1532 + 1.1533 + rv = stmt->Execute(); 1.1534 + NS_ENSURE_SUCCESS(rv, rv); 1.1535 + 1.1536 + // Update the cache entry, if needed. 1.1537 + BookmarkKeyClass* key = mRecentBookmarksCache.GetEntry(aItemId); 1.1538 + if (key) { 1.1539 + if (aDateType == DATE_ADDED) { 1.1540 + key->bookmark.dateAdded = aValue; 1.1541 + } 1.1542 + // Set lastModified in both cases. 1.1543 + key->bookmark.lastModified = aValue; 1.1544 + } 1.1545 + 1.1546 + // note, we are not notifying the observers 1.1547 + // that the item has changed. 1.1548 + 1.1549 + return NS_OK; 1.1550 +} 1.1551 + 1.1552 + 1.1553 +NS_IMETHODIMP 1.1554 +nsNavBookmarks::SetItemDateAdded(int64_t aItemId, PRTime aDateAdded) 1.1555 +{ 1.1556 + NS_ENSURE_ARG_MIN(aItemId, 1); 1.1557 + 1.1558 + BookmarkData bookmark; 1.1559 + nsresult rv = FetchItemInfo(aItemId, bookmark); 1.1560 + NS_ENSURE_SUCCESS(rv, rv); 1.1561 + bookmark.dateAdded = aDateAdded; 1.1562 + 1.1563 + rv = SetItemDateInternal(DATE_ADDED, bookmark.id, bookmark.dateAdded); 1.1564 + NS_ENSURE_SUCCESS(rv, rv); 1.1565 + 1.1566 + // Note: mDBSetItemDateAdded also sets lastModified to aDateAdded. 1.1567 + NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers, 1.1568 + nsINavBookmarkObserver, 1.1569 + OnItemChanged(bookmark.id, 1.1570 + NS_LITERAL_CSTRING("dateAdded"), 1.1571 + false, 1.1572 + nsPrintfCString("%lld", bookmark.dateAdded), 1.1573 + bookmark.dateAdded, 1.1574 + bookmark.type, 1.1575 + bookmark.parentId, 1.1576 + bookmark.guid, 1.1577 + bookmark.parentGuid)); 1.1578 + return NS_OK; 1.1579 +} 1.1580 + 1.1581 + 1.1582 +NS_IMETHODIMP 1.1583 +nsNavBookmarks::GetItemDateAdded(int64_t aItemId, PRTime* _dateAdded) 1.1584 +{ 1.1585 + NS_ENSURE_ARG_MIN(aItemId, 1); 1.1586 + NS_ENSURE_ARG_POINTER(_dateAdded); 1.1587 + 1.1588 + BookmarkData bookmark; 1.1589 + nsresult rv = FetchItemInfo(aItemId, bookmark); 1.1590 + NS_ENSURE_SUCCESS(rv, rv); 1.1591 + 1.1592 + *_dateAdded = bookmark.dateAdded; 1.1593 + return NS_OK; 1.1594 +} 1.1595 + 1.1596 + 1.1597 +NS_IMETHODIMP 1.1598 +nsNavBookmarks::SetItemLastModified(int64_t aItemId, PRTime aLastModified) 1.1599 +{ 1.1600 + NS_ENSURE_ARG_MIN(aItemId, 1); 1.1601 + 1.1602 + BookmarkData bookmark; 1.1603 + nsresult rv = FetchItemInfo(aItemId, bookmark); 1.1604 + NS_ENSURE_SUCCESS(rv, rv); 1.1605 + bookmark.lastModified = aLastModified; 1.1606 + 1.1607 + rv = SetItemDateInternal(LAST_MODIFIED, bookmark.id, bookmark.lastModified); 1.1608 + NS_ENSURE_SUCCESS(rv, rv); 1.1609 + 1.1610 + // Note: mDBSetItemDateAdded also sets lastModified to aDateAdded. 1.1611 + NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers, 1.1612 + nsINavBookmarkObserver, 1.1613 + OnItemChanged(bookmark.id, 1.1614 + NS_LITERAL_CSTRING("lastModified"), 1.1615 + false, 1.1616 + nsPrintfCString("%lld", bookmark.lastModified), 1.1617 + bookmark.lastModified, 1.1618 + bookmark.type, 1.1619 + bookmark.parentId, 1.1620 + bookmark.guid, 1.1621 + bookmark.parentGuid)); 1.1622 + return NS_OK; 1.1623 +} 1.1624 + 1.1625 + 1.1626 +NS_IMETHODIMP 1.1627 +nsNavBookmarks::GetItemLastModified(int64_t aItemId, PRTime* _lastModified) 1.1628 +{ 1.1629 + NS_ENSURE_ARG_MIN(aItemId, 1); 1.1630 + NS_ENSURE_ARG_POINTER(_lastModified); 1.1631 + 1.1632 + BookmarkData bookmark; 1.1633 + nsresult rv = FetchItemInfo(aItemId, bookmark); 1.1634 + NS_ENSURE_SUCCESS(rv, rv); 1.1635 + 1.1636 + *_lastModified = bookmark.lastModified; 1.1637 + return NS_OK; 1.1638 +} 1.1639 + 1.1640 + 1.1641 +NS_IMETHODIMP 1.1642 +nsNavBookmarks::SetItemTitle(int64_t aItemId, const nsACString& aTitle) 1.1643 +{ 1.1644 + NS_ENSURE_ARG_MIN(aItemId, 1); 1.1645 + 1.1646 + BookmarkData bookmark; 1.1647 + nsresult rv = FetchItemInfo(aItemId, bookmark); 1.1648 + NS_ENSURE_SUCCESS(rv, rv); 1.1649 + 1.1650 + nsCOMPtr<mozIStorageStatement> statement = mDB->GetStatement( 1.1651 + "UPDATE moz_bookmarks SET title = :item_title, lastModified = :date " 1.1652 + "WHERE id = :item_id " 1.1653 + ); 1.1654 + NS_ENSURE_STATE(statement); 1.1655 + mozStorageStatementScoper scoper(statement); 1.1656 + 1.1657 + nsCString title; 1.1658 + TruncateTitle(aTitle, title); 1.1659 + 1.1660 + // Support setting a null title, we support this in insertBookmark. 1.1661 + if (title.IsVoid()) { 1.1662 + rv = statement->BindNullByName(NS_LITERAL_CSTRING("item_title")); 1.1663 + } 1.1664 + else { 1.1665 + rv = statement->BindUTF8StringByName(NS_LITERAL_CSTRING("item_title"), 1.1666 + title); 1.1667 + } 1.1668 + NS_ENSURE_SUCCESS(rv, rv); 1.1669 + bookmark.lastModified = PR_Now(); 1.1670 + rv = statement->BindInt64ByName(NS_LITERAL_CSTRING("date"), 1.1671 + bookmark.lastModified); 1.1672 + NS_ENSURE_SUCCESS(rv, rv); 1.1673 + rv = statement->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), bookmark.id); 1.1674 + NS_ENSURE_SUCCESS(rv, rv); 1.1675 + 1.1676 + rv = statement->Execute(); 1.1677 + NS_ENSURE_SUCCESS(rv, rv); 1.1678 + 1.1679 + // Update the cache entry, if needed. 1.1680 + BookmarkKeyClass* key = mRecentBookmarksCache.GetEntry(aItemId); 1.1681 + if (key) { 1.1682 + if (title.IsVoid()) { 1.1683 + key->bookmark.title.SetIsVoid(true); 1.1684 + } 1.1685 + else { 1.1686 + key->bookmark.title.Assign(title); 1.1687 + } 1.1688 + key->bookmark.lastModified = bookmark.lastModified; 1.1689 + } 1.1690 + 1.1691 + NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers, 1.1692 + nsINavBookmarkObserver, 1.1693 + OnItemChanged(bookmark.id, 1.1694 + NS_LITERAL_CSTRING("title"), 1.1695 + false, 1.1696 + title, 1.1697 + bookmark.lastModified, 1.1698 + bookmark.type, 1.1699 + bookmark.parentId, 1.1700 + bookmark.guid, 1.1701 + bookmark.parentGuid)); 1.1702 + return NS_OK; 1.1703 +} 1.1704 + 1.1705 + 1.1706 +NS_IMETHODIMP 1.1707 +nsNavBookmarks::GetItemTitle(int64_t aItemId, 1.1708 + nsACString& _title) 1.1709 +{ 1.1710 + NS_ENSURE_ARG_MIN(aItemId, 1); 1.1711 + 1.1712 + BookmarkData bookmark; 1.1713 + nsresult rv = FetchItemInfo(aItemId, bookmark); 1.1714 + NS_ENSURE_SUCCESS(rv, rv); 1.1715 + 1.1716 + _title = bookmark.title; 1.1717 + return NS_OK; 1.1718 +} 1.1719 + 1.1720 + 1.1721 +NS_IMETHODIMP 1.1722 +nsNavBookmarks::GetBookmarkURI(int64_t aItemId, 1.1723 + nsIURI** _URI) 1.1724 +{ 1.1725 + NS_ENSURE_ARG_MIN(aItemId, 1); 1.1726 + NS_ENSURE_ARG_POINTER(_URI); 1.1727 + 1.1728 + BookmarkData bookmark; 1.1729 + nsresult rv = FetchItemInfo(aItemId, bookmark); 1.1730 + NS_ENSURE_SUCCESS(rv, rv); 1.1731 + 1.1732 + rv = NS_NewURI(_URI, bookmark.url); 1.1733 + NS_ENSURE_SUCCESS(rv, rv); 1.1734 + 1.1735 + return NS_OK; 1.1736 +} 1.1737 + 1.1738 + 1.1739 +NS_IMETHODIMP 1.1740 +nsNavBookmarks::GetItemType(int64_t aItemId, uint16_t* _type) 1.1741 +{ 1.1742 + NS_ENSURE_ARG_MIN(aItemId, 1); 1.1743 + NS_ENSURE_ARG_POINTER(_type); 1.1744 + 1.1745 + BookmarkData bookmark; 1.1746 + nsresult rv = FetchItemInfo(aItemId, bookmark); 1.1747 + NS_ENSURE_SUCCESS(rv, rv); 1.1748 + 1.1749 + *_type = static_cast<uint16_t>(bookmark.type); 1.1750 + return NS_OK; 1.1751 +} 1.1752 + 1.1753 + 1.1754 +nsresult 1.1755 +nsNavBookmarks::ResultNodeForContainer(int64_t aItemId, 1.1756 + nsNavHistoryQueryOptions* aOptions, 1.1757 + nsNavHistoryResultNode** aNode) 1.1758 +{ 1.1759 + BookmarkData bookmark; 1.1760 + nsresult rv = FetchItemInfo(aItemId, bookmark); 1.1761 + NS_ENSURE_SUCCESS(rv, rv); 1.1762 + 1.1763 + if (bookmark.type == TYPE_FOLDER) { // TYPE_FOLDER 1.1764 + *aNode = new nsNavHistoryFolderResultNode(bookmark.title, 1.1765 + aOptions, 1.1766 + bookmark.id); 1.1767 + } 1.1768 + else { 1.1769 + return NS_ERROR_INVALID_ARG; 1.1770 + } 1.1771 + 1.1772 + (*aNode)->mDateAdded = bookmark.dateAdded; 1.1773 + (*aNode)->mLastModified = bookmark.lastModified; 1.1774 + (*aNode)->mBookmarkGuid = bookmark.guid; 1.1775 + 1.1776 + NS_ADDREF(*aNode); 1.1777 + return NS_OK; 1.1778 +} 1.1779 + 1.1780 + 1.1781 +nsresult 1.1782 +nsNavBookmarks::QueryFolderChildren( 1.1783 + int64_t aFolderId, 1.1784 + nsNavHistoryQueryOptions* aOptions, 1.1785 + nsCOMArray<nsNavHistoryResultNode>* aChildren) 1.1786 +{ 1.1787 + NS_ENSURE_ARG_POINTER(aOptions); 1.1788 + NS_ENSURE_ARG_POINTER(aChildren); 1.1789 + 1.1790 + // Select all children of a given folder, sorted by position. 1.1791 + // This is a LEFT JOIN because not all bookmarks types have a place. 1.1792 + // We construct a result where the first columns exactly match those returned 1.1793 + // by mDBGetURLPageInfo, and additionally contains columns for position, 1.1794 + // item_child, and folder_child from moz_bookmarks. 1.1795 + nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement( 1.1796 + "SELECT h.id, h.url, IFNULL(b.title, h.title), h.rev_host, h.visit_count, " 1.1797 + "h.last_visit_date, f.url, b.id, b.dateAdded, b.lastModified, " 1.1798 + "b.parent, null, h.frecency, h.hidden, h.guid, b.guid, " 1.1799 + "b.position, b.type, b.fk " 1.1800 + "FROM moz_bookmarks b " 1.1801 + "LEFT JOIN moz_places h ON b.fk = h.id " 1.1802 + "LEFT JOIN moz_favicons f ON h.favicon_id = f.id " 1.1803 + "WHERE b.parent = :parent " 1.1804 + "ORDER BY b.position ASC" 1.1805 + ); 1.1806 + NS_ENSURE_STATE(stmt); 1.1807 + mozStorageStatementScoper scoper(stmt); 1.1808 + 1.1809 + nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aFolderId); 1.1810 + NS_ENSURE_SUCCESS(rv, rv); 1.1811 + 1.1812 + nsCOMPtr<mozIStorageValueArray> row = do_QueryInterface(stmt, &rv); 1.1813 + NS_ENSURE_SUCCESS(rv, rv); 1.1814 + 1.1815 + int32_t index = -1; 1.1816 + bool hasResult; 1.1817 + while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) { 1.1818 + rv = ProcessFolderNodeRow(row, aOptions, aChildren, index); 1.1819 + NS_ENSURE_SUCCESS(rv, rv); 1.1820 + } 1.1821 + 1.1822 + return NS_OK; 1.1823 +} 1.1824 + 1.1825 + 1.1826 +nsresult 1.1827 +nsNavBookmarks::ProcessFolderNodeRow( 1.1828 + mozIStorageValueArray* aRow, 1.1829 + nsNavHistoryQueryOptions* aOptions, 1.1830 + nsCOMArray<nsNavHistoryResultNode>* aChildren, 1.1831 + int32_t& aCurrentIndex) 1.1832 +{ 1.1833 + NS_ENSURE_ARG_POINTER(aRow); 1.1834 + NS_ENSURE_ARG_POINTER(aOptions); 1.1835 + NS_ENSURE_ARG_POINTER(aChildren); 1.1836 + 1.1837 + // The results will be in order of aCurrentIndex. Even if we don't add a node 1.1838 + // because it was excluded, we need to count its index, so do that before 1.1839 + // doing anything else. 1.1840 + aCurrentIndex++; 1.1841 + 1.1842 + int32_t itemType; 1.1843 + nsresult rv = aRow->GetInt32(kGetChildrenIndex_Type, &itemType); 1.1844 + NS_ENSURE_SUCCESS(rv, rv); 1.1845 + int64_t id; 1.1846 + rv = aRow->GetInt64(nsNavHistory::kGetInfoIndex_ItemId, &id); 1.1847 + NS_ENSURE_SUCCESS(rv, rv); 1.1848 + 1.1849 + nsRefPtr<nsNavHistoryResultNode> node; 1.1850 + 1.1851 + if (itemType == TYPE_BOOKMARK) { 1.1852 + nsNavHistory* history = nsNavHistory::GetHistoryService(); 1.1853 + NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY); 1.1854 + rv = history->RowToResult(aRow, aOptions, getter_AddRefs(node)); 1.1855 + NS_ENSURE_SUCCESS(rv, rv); 1.1856 + 1.1857 + uint32_t nodeType; 1.1858 + node->GetType(&nodeType); 1.1859 + if ((nodeType == nsINavHistoryResultNode::RESULT_TYPE_QUERY && 1.1860 + aOptions->ExcludeQueries()) || 1.1861 + (nodeType != nsINavHistoryResultNode::RESULT_TYPE_QUERY && 1.1862 + nodeType != nsINavHistoryResultNode::RESULT_TYPE_FOLDER_SHORTCUT && 1.1863 + aOptions->ExcludeItems())) { 1.1864 + return NS_OK; 1.1865 + } 1.1866 + } 1.1867 + else if (itemType == TYPE_FOLDER) { 1.1868 + if (aOptions->ExcludeReadOnlyFolders()) { 1.1869 + // If the folder is read-only, skip it. 1.1870 + bool readOnly = false; 1.1871 + GetFolderReadonly(id, &readOnly); 1.1872 + if (readOnly) 1.1873 + return NS_OK; 1.1874 + } 1.1875 + 1.1876 + nsAutoCString title; 1.1877 + rv = aRow->GetUTF8String(nsNavHistory::kGetInfoIndex_Title, title); 1.1878 + NS_ENSURE_SUCCESS(rv, rv); 1.1879 + 1.1880 + node = new nsNavHistoryFolderResultNode(title, aOptions, id); 1.1881 + 1.1882 + rv = aRow->GetInt64(nsNavHistory::kGetInfoIndex_ItemDateAdded, 1.1883 + reinterpret_cast<int64_t*>(&node->mDateAdded)); 1.1884 + NS_ENSURE_SUCCESS(rv, rv); 1.1885 + rv = aRow->GetInt64(nsNavHistory::kGetInfoIndex_ItemLastModified, 1.1886 + reinterpret_cast<int64_t*>(&node->mLastModified)); 1.1887 + NS_ENSURE_SUCCESS(rv, rv); 1.1888 + } 1.1889 + else { 1.1890 + // This is a separator. 1.1891 + if (aOptions->ExcludeItems()) { 1.1892 + return NS_OK; 1.1893 + } 1.1894 + node = new nsNavHistorySeparatorResultNode(); 1.1895 + 1.1896 + node->mItemId = id; 1.1897 + rv = aRow->GetInt64(nsNavHistory::kGetInfoIndex_ItemDateAdded, 1.1898 + reinterpret_cast<int64_t*>(&node->mDateAdded)); 1.1899 + NS_ENSURE_SUCCESS(rv, rv); 1.1900 + rv = aRow->GetInt64(nsNavHistory::kGetInfoIndex_ItemLastModified, 1.1901 + reinterpret_cast<int64_t*>(&node->mLastModified)); 1.1902 + NS_ENSURE_SUCCESS(rv, rv); 1.1903 + } 1.1904 + 1.1905 + // Store the index of the node within this container. Note that this is not 1.1906 + // moz_bookmarks.position. 1.1907 + node->mBookmarkIndex = aCurrentIndex; 1.1908 + 1.1909 + rv = aRow->GetUTF8String(kGetChildrenIndex_Guid, node->mBookmarkGuid); 1.1910 + NS_ENSURE_SUCCESS(rv, rv); 1.1911 + 1.1912 + NS_ENSURE_TRUE(aChildren->AppendObject(node), NS_ERROR_OUT_OF_MEMORY); 1.1913 + 1.1914 + return NS_OK; 1.1915 +} 1.1916 + 1.1917 + 1.1918 +nsresult 1.1919 +nsNavBookmarks::QueryFolderChildrenAsync( 1.1920 + nsNavHistoryFolderResultNode* aNode, 1.1921 + int64_t aFolderId, 1.1922 + mozIStoragePendingStatement** _pendingStmt) 1.1923 +{ 1.1924 + NS_ENSURE_ARG_POINTER(aNode); 1.1925 + NS_ENSURE_ARG_POINTER(_pendingStmt); 1.1926 + 1.1927 + // Select all children of a given folder, sorted by position. 1.1928 + // This is a LEFT JOIN because not all bookmarks types have a place. 1.1929 + // We construct a result where the first columns exactly match those returned 1.1930 + // by mDBGetURLPageInfo, and additionally contains columns for position, 1.1931 + // item_child, and folder_child from moz_bookmarks. 1.1932 + nsCOMPtr<mozIStorageAsyncStatement> stmt = mDB->GetAsyncStatement( 1.1933 + "SELECT h.id, h.url, IFNULL(b.title, h.title), h.rev_host, h.visit_count, " 1.1934 + "h.last_visit_date, f.url, b.id, b.dateAdded, b.lastModified, " 1.1935 + "b.parent, null, h.frecency, h.hidden, h.guid, b.guid, " 1.1936 + "b.position, b.type, b.fk " 1.1937 + "FROM moz_bookmarks b " 1.1938 + "LEFT JOIN moz_places h ON b.fk = h.id " 1.1939 + "LEFT JOIN moz_favicons f ON h.favicon_id = f.id " 1.1940 + "WHERE b.parent = :parent " 1.1941 + "ORDER BY b.position ASC" 1.1942 + ); 1.1943 + NS_ENSURE_STATE(stmt); 1.1944 + 1.1945 + nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aFolderId); 1.1946 + NS_ENSURE_SUCCESS(rv, rv); 1.1947 + 1.1948 + nsCOMPtr<mozIStoragePendingStatement> pendingStmt; 1.1949 + rv = stmt->ExecuteAsync(aNode, getter_AddRefs(pendingStmt)); 1.1950 + NS_ENSURE_SUCCESS(rv, rv); 1.1951 + 1.1952 + NS_IF_ADDREF(*_pendingStmt = pendingStmt); 1.1953 + return NS_OK; 1.1954 +} 1.1955 + 1.1956 + 1.1957 +nsresult 1.1958 +nsNavBookmarks::FetchFolderInfo(int64_t aFolderId, 1.1959 + int32_t* _folderCount, 1.1960 + nsACString& _guid, 1.1961 + int64_t* _parentId) 1.1962 +{ 1.1963 + *_folderCount = 0; 1.1964 + *_parentId = -1; 1.1965 + 1.1966 + // This query has to always return results, so it can't be written as a join, 1.1967 + // though a left join of 2 subqueries would have the same cost. 1.1968 + nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement( 1.1969 + "SELECT count(*), " 1.1970 + "(SELECT guid FROM moz_bookmarks WHERE id = :parent), " 1.1971 + "(SELECT parent FROM moz_bookmarks WHERE id = :parent) " 1.1972 + "FROM moz_bookmarks " 1.1973 + "WHERE parent = :parent" 1.1974 + ); 1.1975 + NS_ENSURE_STATE(stmt); 1.1976 + mozStorageStatementScoper scoper(stmt); 1.1977 + 1.1978 + nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aFolderId); 1.1979 + NS_ENSURE_SUCCESS(rv, rv); 1.1980 + 1.1981 + bool hasResult; 1.1982 + rv = stmt->ExecuteStep(&hasResult); 1.1983 + NS_ENSURE_SUCCESS(rv, rv); 1.1984 + NS_ENSURE_TRUE(hasResult, NS_ERROR_UNEXPECTED); 1.1985 + 1.1986 + // Ensure that the folder we are looking for exists. 1.1987 + // Can't rely only on parent, since the root has parent 0, that doesn't exist. 1.1988 + bool isNull; 1.1989 + rv = stmt->GetIsNull(2, &isNull); 1.1990 + NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && (!isNull || aFolderId == 0), 1.1991 + NS_ERROR_INVALID_ARG); 1.1992 + 1.1993 + rv = stmt->GetInt32(0, _folderCount); 1.1994 + NS_ENSURE_SUCCESS(rv, rv); 1.1995 + if (!isNull) { 1.1996 + rv = stmt->GetUTF8String(1, _guid); 1.1997 + NS_ENSURE_SUCCESS(rv, rv); 1.1998 + rv = stmt->GetInt64(2, _parentId); 1.1999 + NS_ENSURE_SUCCESS(rv, rv); 1.2000 + } 1.2001 + 1.2002 + return NS_OK; 1.2003 +} 1.2004 + 1.2005 + 1.2006 +NS_IMETHODIMP 1.2007 +nsNavBookmarks::IsBookmarked(nsIURI* aURI, bool* aBookmarked) 1.2008 +{ 1.2009 + NS_ENSURE_ARG(aURI); 1.2010 + NS_ENSURE_ARG_POINTER(aBookmarked); 1.2011 + 1.2012 + nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement( 1.2013 + "SELECT 1 FROM moz_bookmarks b " 1.2014 + "JOIN moz_places h ON b.fk = h.id " 1.2015 + "WHERE h.url = :page_url" 1.2016 + ); 1.2017 + NS_ENSURE_STATE(stmt); 1.2018 + mozStorageStatementScoper scoper(stmt); 1.2019 + 1.2020 + nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), aURI); 1.2021 + NS_ENSURE_SUCCESS(rv, rv); 1.2022 + rv = stmt->ExecuteStep(aBookmarked); 1.2023 + NS_ENSURE_SUCCESS(rv, rv); 1.2024 + 1.2025 + return NS_OK; 1.2026 +} 1.2027 + 1.2028 + 1.2029 +NS_IMETHODIMP 1.2030 +nsNavBookmarks::GetBookmarkedURIFor(nsIURI* aURI, nsIURI** _retval) 1.2031 +{ 1.2032 + NS_ENSURE_ARG(aURI); 1.2033 + NS_ENSURE_ARG_POINTER(_retval); 1.2034 + 1.2035 + *_retval = nullptr; 1.2036 + 1.2037 + nsNavHistory* history = nsNavHistory::GetHistoryService(); 1.2038 + NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY); 1.2039 + int64_t placeId; 1.2040 + nsAutoCString placeGuid; 1.2041 + nsresult rv = history->GetIdForPage(aURI, &placeId, placeGuid); 1.2042 + NS_ENSURE_SUCCESS(rv, rv); 1.2043 + if (!placeId) { 1.2044 + // This URI is unknown, just return null. 1.2045 + return NS_OK; 1.2046 + } 1.2047 + 1.2048 + // Check if a bookmark exists in the redirects chain for this URI. 1.2049 + // The query will also check if the page is directly bookmarked, and return 1.2050 + // the first found bookmark in case. The check is directly on moz_bookmarks 1.2051 + // without special filtering. 1.2052 + // The next query finds the bookmarked ancestors in a redirects chain. 1.2053 + // It won't go further than 3 levels of redirects (a->b->c->your_place_id). 1.2054 + // To make this path 100% correct (up to any level) we would need either: 1.2055 + // - A separate hash, build through recursive querying of the database. 1.2056 + // This solution was previously implemented, but it had a negative effect 1.2057 + // on startup since at each startup we have to recursively query the 1.2058 + // database to rebuild a hash that is always the same across sessions. 1.2059 + // It must be updated at each visit and bookmarks change too. The code to 1.2060 + // manage it is complex and prone to errors, sometimes causing incorrect 1.2061 + // data fetches (for example wrong favicon for a redirected bookmark). 1.2062 + // - A better way to track redirects for a visit. 1.2063 + // We would need a separate table to track redirects, in the table we would 1.2064 + // have visit_id, redirect_session. To get all sources for 1.2065 + // a visit then we could just join this table and get all visit_id that 1.2066 + // are in the same redirect_session as our visit. This has the drawback 1.2067 + // that we can't ensure data integrity in the downgrade -> upgrade path, 1.2068 + // since an old version would not update the table on new visits. 1.2069 + // 1.2070 + // For most cases these levels of redirects should be fine though, it's hard 1.2071 + // to hit a page that is 4 or 5 levels of redirects below a bookmarked page. 1.2072 + // 1.2073 + // As a bonus the query also checks first if place_id is already a bookmark, 1.2074 + // so you don't have to check that apart. 1.2075 + 1.2076 + nsCString query = nsPrintfCString( 1.2077 + "SELECT url FROM moz_places WHERE id = ( " 1.2078 + "SELECT :page_id FROM moz_bookmarks WHERE fk = :page_id " 1.2079 + "UNION ALL " 1.2080 + "SELECT COALESCE(grandparent.place_id, parent.place_id) AS r_place_id " 1.2081 + "FROM moz_historyvisits dest " 1.2082 + "LEFT JOIN moz_historyvisits parent ON parent.id = dest.from_visit " 1.2083 + "AND dest.visit_type IN (%d, %d) " 1.2084 + "LEFT JOIN moz_historyvisits grandparent ON parent.from_visit = grandparent.id " 1.2085 + "AND parent.visit_type IN (%d, %d) " 1.2086 + "WHERE dest.place_id = :page_id " 1.2087 + "AND EXISTS(SELECT 1 FROM moz_bookmarks WHERE fk = r_place_id) " 1.2088 + "LIMIT 1 " 1.2089 + ")", 1.2090 + nsINavHistoryService::TRANSITION_REDIRECT_PERMANENT, 1.2091 + nsINavHistoryService::TRANSITION_REDIRECT_TEMPORARY, 1.2092 + nsINavHistoryService::TRANSITION_REDIRECT_PERMANENT, 1.2093 + nsINavHistoryService::TRANSITION_REDIRECT_TEMPORARY 1.2094 + ); 1.2095 + 1.2096 + nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(query); 1.2097 + NS_ENSURE_STATE(stmt); 1.2098 + mozStorageStatementScoper scoper(stmt); 1.2099 + 1.2100 + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), placeId); 1.2101 + NS_ENSURE_SUCCESS(rv, rv); 1.2102 + bool hasBookmarkedOrigin; 1.2103 + if (NS_SUCCEEDED(stmt->ExecuteStep(&hasBookmarkedOrigin)) && 1.2104 + hasBookmarkedOrigin) { 1.2105 + nsAutoCString spec; 1.2106 + rv = stmt->GetUTF8String(0, spec); 1.2107 + NS_ENSURE_SUCCESS(rv, rv); 1.2108 + rv = NS_NewURI(_retval, spec); 1.2109 + NS_ENSURE_SUCCESS(rv, rv); 1.2110 + } 1.2111 + 1.2112 + // If there is no bookmarked origin, we will just return null. 1.2113 + return NS_OK; 1.2114 +} 1.2115 + 1.2116 + 1.2117 +NS_IMETHODIMP 1.2118 +nsNavBookmarks::ChangeBookmarkURI(int64_t aBookmarkId, nsIURI* aNewURI) 1.2119 +{ 1.2120 + NS_ENSURE_ARG_MIN(aBookmarkId, 1); 1.2121 + NS_ENSURE_ARG(aNewURI); 1.2122 + 1.2123 + BookmarkData bookmark; 1.2124 + nsresult rv = FetchItemInfo(aBookmarkId, bookmark); 1.2125 + NS_ENSURE_SUCCESS(rv, rv); 1.2126 + NS_ENSURE_ARG(bookmark.type == TYPE_BOOKMARK); 1.2127 + 1.2128 + mozStorageTransaction transaction(mDB->MainConn(), false); 1.2129 + 1.2130 + nsNavHistory* history = nsNavHistory::GetHistoryService(); 1.2131 + NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY); 1.2132 + int64_t newPlaceId; 1.2133 + nsAutoCString newPlaceGuid; 1.2134 + rv = history->GetOrCreateIdForPage(aNewURI, &newPlaceId, newPlaceGuid); 1.2135 + NS_ENSURE_SUCCESS(rv, rv); 1.2136 + if (!newPlaceId) 1.2137 + return NS_ERROR_INVALID_ARG; 1.2138 + 1.2139 + BEGIN_CRITICAL_BOOKMARK_CACHE_SECTION(bookmark.id); 1.2140 + 1.2141 + nsCOMPtr<mozIStorageStatement> statement = mDB->GetStatement( 1.2142 + "UPDATE moz_bookmarks SET fk = :page_id, lastModified = :date " 1.2143 + "WHERE id = :item_id " 1.2144 + ); 1.2145 + NS_ENSURE_STATE(statement); 1.2146 + mozStorageStatementScoper scoper(statement); 1.2147 + 1.2148 + rv = statement->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), newPlaceId); 1.2149 + NS_ENSURE_SUCCESS(rv, rv); 1.2150 + bookmark.lastModified = PR_Now(); 1.2151 + rv = statement->BindInt64ByName(NS_LITERAL_CSTRING("date"), 1.2152 + bookmark.lastModified); 1.2153 + NS_ENSURE_SUCCESS(rv, rv); 1.2154 + rv = statement->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), bookmark.id); 1.2155 + NS_ENSURE_SUCCESS(rv, rv); 1.2156 + rv = statement->Execute(); 1.2157 + NS_ENSURE_SUCCESS(rv, rv); 1.2158 + 1.2159 + rv = transaction.Commit(); 1.2160 + NS_ENSURE_SUCCESS(rv, rv); 1.2161 + 1.2162 + END_CRITICAL_BOOKMARK_CACHE_SECTION(bookmark.id); 1.2163 + 1.2164 + rv = history->UpdateFrecency(newPlaceId); 1.2165 + NS_ENSURE_SUCCESS(rv, rv); 1.2166 + 1.2167 + // Upon changing the URI for a bookmark, update the frecency for the old 1.2168 + // place as well. 1.2169 + rv = history->UpdateFrecency(bookmark.placeId); 1.2170 + NS_ENSURE_SUCCESS(rv, rv); 1.2171 + 1.2172 + nsAutoCString spec; 1.2173 + rv = aNewURI->GetSpec(spec); 1.2174 + NS_ENSURE_SUCCESS(rv, rv); 1.2175 + 1.2176 + NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers, 1.2177 + nsINavBookmarkObserver, 1.2178 + OnItemChanged(bookmark.id, 1.2179 + NS_LITERAL_CSTRING("uri"), 1.2180 + false, 1.2181 + spec, 1.2182 + bookmark.lastModified, 1.2183 + bookmark.type, 1.2184 + bookmark.parentId, 1.2185 + bookmark.guid, 1.2186 + bookmark.parentGuid)); 1.2187 + return NS_OK; 1.2188 +} 1.2189 + 1.2190 + 1.2191 +NS_IMETHODIMP 1.2192 +nsNavBookmarks::GetFolderIdForItem(int64_t aItemId, int64_t* _parentId) 1.2193 +{ 1.2194 + NS_ENSURE_ARG_MIN(aItemId, 1); 1.2195 + NS_ENSURE_ARG_POINTER(_parentId); 1.2196 + 1.2197 + BookmarkData bookmark; 1.2198 + nsresult rv = FetchItemInfo(aItemId, bookmark); 1.2199 + NS_ENSURE_SUCCESS(rv, rv); 1.2200 + 1.2201 + // this should not happen, but see bug #400448 for details 1.2202 + NS_ENSURE_TRUE(bookmark.id != bookmark.parentId, NS_ERROR_UNEXPECTED); 1.2203 + 1.2204 + *_parentId = bookmark.parentId; 1.2205 + return NS_OK; 1.2206 +} 1.2207 + 1.2208 + 1.2209 +nsresult 1.2210 +nsNavBookmarks::GetBookmarkIdsForURITArray(nsIURI* aURI, 1.2211 + nsTArray<int64_t>& aResult, 1.2212 + bool aSkipTags) 1.2213 +{ 1.2214 + NS_ENSURE_ARG(aURI); 1.2215 + 1.2216 + // Double ordering covers possible lastModified ties, that could happen when 1.2217 + // importing, syncing or due to extensions. 1.2218 + // Note: not using a JOIN is cheaper in this case. 1.2219 + nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement( 1.2220 + "SELECT b.id, b.guid, b.parent, b.lastModified, t.guid, t.parent " 1.2221 + "FROM moz_bookmarks b " 1.2222 + "JOIN moz_bookmarks t on t.id = b.parent " 1.2223 + "WHERE b.fk = (SELECT id FROM moz_places WHERE url = :page_url) " 1.2224 + "ORDER BY b.lastModified DESC, b.id DESC " 1.2225 + ); 1.2226 + NS_ENSURE_STATE(stmt); 1.2227 + mozStorageStatementScoper scoper(stmt); 1.2228 + 1.2229 + nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), aURI); 1.2230 + NS_ENSURE_SUCCESS(rv, rv); 1.2231 + 1.2232 + bool more; 1.2233 + while (NS_SUCCEEDED((rv = stmt->ExecuteStep(&more))) && more) { 1.2234 + if (aSkipTags) { 1.2235 + // Skip tags, for the use-cases of this async getter they are useless. 1.2236 + int64_t grandParentId; 1.2237 + nsresult rv = stmt->GetInt64(5, &grandParentId); 1.2238 + NS_ENSURE_SUCCESS(rv, rv); 1.2239 + if (grandParentId == mTagsRoot) { 1.2240 + continue; 1.2241 + } 1.2242 + } 1.2243 + int64_t bookmarkId; 1.2244 + rv = stmt->GetInt64(0, &bookmarkId); 1.2245 + NS_ENSURE_SUCCESS(rv, rv); 1.2246 + NS_ENSURE_TRUE(aResult.AppendElement(bookmarkId), NS_ERROR_OUT_OF_MEMORY); 1.2247 + } 1.2248 + NS_ENSURE_SUCCESS(rv, rv); 1.2249 + 1.2250 + return NS_OK; 1.2251 +} 1.2252 + 1.2253 +nsresult 1.2254 +nsNavBookmarks::GetBookmarksForURI(nsIURI* aURI, 1.2255 + nsTArray<BookmarkData>& aBookmarks) 1.2256 +{ 1.2257 + NS_ENSURE_ARG(aURI); 1.2258 + 1.2259 + // Double ordering covers possible lastModified ties, that could happen when 1.2260 + // importing, syncing or due to extensions. 1.2261 + // Note: not using a JOIN is cheaper in this case. 1.2262 + nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement( 1.2263 + "SELECT b.id, b.guid, b.parent, b.lastModified, t.guid, t.parent " 1.2264 + "FROM moz_bookmarks b " 1.2265 + "JOIN moz_bookmarks t on t.id = b.parent " 1.2266 + "WHERE b.fk = (SELECT id FROM moz_places WHERE url = :page_url) " 1.2267 + "ORDER BY b.lastModified DESC, b.id DESC " 1.2268 + ); 1.2269 + NS_ENSURE_STATE(stmt); 1.2270 + mozStorageStatementScoper scoper(stmt); 1.2271 + 1.2272 + nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), aURI); 1.2273 + NS_ENSURE_SUCCESS(rv, rv); 1.2274 + 1.2275 + bool more; 1.2276 + nsAutoString tags; 1.2277 + while (NS_SUCCEEDED((rv = stmt->ExecuteStep(&more))) && more) { 1.2278 + // Skip tags. 1.2279 + int64_t grandParentId; 1.2280 + nsresult rv = stmt->GetInt64(5, &grandParentId); 1.2281 + NS_ENSURE_SUCCESS(rv, rv); 1.2282 + if (grandParentId == mTagsRoot) { 1.2283 + continue; 1.2284 + } 1.2285 + 1.2286 + BookmarkData bookmark; 1.2287 + bookmark.grandParentId = grandParentId; 1.2288 + rv = stmt->GetInt64(0, &bookmark.id); 1.2289 + NS_ENSURE_SUCCESS(rv, rv); 1.2290 + rv = stmt->GetUTF8String(1, bookmark.guid); 1.2291 + NS_ENSURE_SUCCESS(rv, rv); 1.2292 + rv = stmt->GetInt64(2, &bookmark.parentId); 1.2293 + NS_ENSURE_SUCCESS(rv, rv); 1.2294 + rv = stmt->GetInt64(3, reinterpret_cast<int64_t*>(&bookmark.lastModified)); 1.2295 + NS_ENSURE_SUCCESS(rv, rv); 1.2296 + rv = stmt->GetUTF8String(4, bookmark.parentGuid); 1.2297 + NS_ENSURE_SUCCESS(rv, rv); 1.2298 + 1.2299 + NS_ENSURE_TRUE(aBookmarks.AppendElement(bookmark), NS_ERROR_OUT_OF_MEMORY); 1.2300 + } 1.2301 + 1.2302 + return NS_OK; 1.2303 +} 1.2304 + 1.2305 +NS_IMETHODIMP 1.2306 +nsNavBookmarks::GetBookmarkIdsForURI(nsIURI* aURI, uint32_t* aCount, 1.2307 + int64_t** aBookmarks) 1.2308 +{ 1.2309 + NS_ENSURE_ARG(aURI); 1.2310 + NS_ENSURE_ARG_POINTER(aCount); 1.2311 + NS_ENSURE_ARG_POINTER(aBookmarks); 1.2312 + 1.2313 + *aCount = 0; 1.2314 + *aBookmarks = nullptr; 1.2315 + nsTArray<int64_t> bookmarks; 1.2316 + 1.2317 + // Get the information from the DB as a TArray 1.2318 + // TODO (bug 653816): make this API skip tags by default. 1.2319 + nsresult rv = GetBookmarkIdsForURITArray(aURI, bookmarks, false); 1.2320 + NS_ENSURE_SUCCESS(rv, rv); 1.2321 + 1.2322 + // Copy the results into a new array for output 1.2323 + if (bookmarks.Length()) { 1.2324 + *aBookmarks = 1.2325 + static_cast<int64_t*>(nsMemory::Alloc(sizeof(int64_t) * bookmarks.Length())); 1.2326 + if (!*aBookmarks) 1.2327 + return NS_ERROR_OUT_OF_MEMORY; 1.2328 + for (uint32_t i = 0; i < bookmarks.Length(); i ++) 1.2329 + (*aBookmarks)[i] = bookmarks[i]; 1.2330 + } 1.2331 + 1.2332 + *aCount = bookmarks.Length(); 1.2333 + return NS_OK; 1.2334 +} 1.2335 + 1.2336 + 1.2337 +NS_IMETHODIMP 1.2338 +nsNavBookmarks::GetItemIndex(int64_t aItemId, int32_t* _index) 1.2339 +{ 1.2340 + NS_ENSURE_ARG_MIN(aItemId, 1); 1.2341 + NS_ENSURE_ARG_POINTER(_index); 1.2342 + 1.2343 + BookmarkData bookmark; 1.2344 + nsresult rv = FetchItemInfo(aItemId, bookmark); 1.2345 + // With respect to the API. 1.2346 + if (NS_FAILED(rv)) { 1.2347 + *_index = -1; 1.2348 + return NS_OK; 1.2349 + } 1.2350 + 1.2351 + *_index = bookmark.position; 1.2352 + return NS_OK; 1.2353 +} 1.2354 + 1.2355 +NS_IMETHODIMP 1.2356 +nsNavBookmarks::SetItemIndex(int64_t aItemId, int32_t aNewIndex) 1.2357 +{ 1.2358 + NS_ENSURE_ARG_MIN(aItemId, 1); 1.2359 + NS_ENSURE_ARG_MIN(aNewIndex, 0); 1.2360 + 1.2361 + BookmarkData bookmark; 1.2362 + nsresult rv = FetchItemInfo(aItemId, bookmark); 1.2363 + NS_ENSURE_SUCCESS(rv, rv); 1.2364 + 1.2365 + // Ensure we are not going out of range. 1.2366 + int32_t folderCount; 1.2367 + int64_t grandParentId; 1.2368 + nsAutoCString folderGuid; 1.2369 + rv = FetchFolderInfo(bookmark.parentId, &folderCount, folderGuid, &grandParentId); 1.2370 + NS_ENSURE_SUCCESS(rv, rv); 1.2371 + NS_ENSURE_TRUE(aNewIndex < folderCount, NS_ERROR_INVALID_ARG); 1.2372 + // Check the parent's guid is the expected one. 1.2373 + MOZ_ASSERT(bookmark.parentGuid == folderGuid); 1.2374 + 1.2375 + BEGIN_CRITICAL_BOOKMARK_CACHE_SECTION(bookmark.id); 1.2376 + 1.2377 + nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement( 1.2378 + "UPDATE moz_bookmarks SET position = :item_index WHERE id = :item_id" 1.2379 + ); 1.2380 + NS_ENSURE_STATE(stmt); 1.2381 + mozStorageStatementScoper scoper(stmt); 1.2382 + 1.2383 + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), aItemId); 1.2384 + NS_ENSURE_SUCCESS(rv, rv); 1.2385 + rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("item_index"), aNewIndex); 1.2386 + NS_ENSURE_SUCCESS(rv, rv); 1.2387 + 1.2388 + rv = stmt->Execute(); 1.2389 + NS_ENSURE_SUCCESS(rv, rv); 1.2390 + 1.2391 + END_CRITICAL_BOOKMARK_CACHE_SECTION(bookmark.id); 1.2392 + 1.2393 + NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers, 1.2394 + nsINavBookmarkObserver, 1.2395 + OnItemMoved(bookmark.id, 1.2396 + bookmark.parentId, 1.2397 + bookmark.position, 1.2398 + bookmark.parentId, 1.2399 + aNewIndex, 1.2400 + bookmark.type, 1.2401 + bookmark.guid, 1.2402 + bookmark.parentGuid, 1.2403 + bookmark.parentGuid)); 1.2404 + 1.2405 + return NS_OK; 1.2406 +} 1.2407 + 1.2408 + 1.2409 +nsresult 1.2410 +nsNavBookmarks::UpdateKeywordsHashForRemovedBookmark(int64_t aItemId) 1.2411 +{ 1.2412 + nsAutoString keyword; 1.2413 + if (NS_SUCCEEDED(GetKeywordForBookmark(aItemId, keyword)) && 1.2414 + !keyword.IsEmpty()) { 1.2415 + nsresult rv = EnsureKeywordsHash(); 1.2416 + NS_ENSURE_SUCCESS(rv, rv); 1.2417 + mBookmarkToKeywordHash.Remove(aItemId); 1.2418 + 1.2419 + // If the keyword is unused, remove it from the database. 1.2420 + keywordSearchData searchData; 1.2421 + searchData.keyword.Assign(keyword); 1.2422 + searchData.itemId = -1; 1.2423 + mBookmarkToKeywordHash.EnumerateRead(SearchBookmarkForKeyword, &searchData); 1.2424 + if (searchData.itemId == -1) { 1.2425 + nsCOMPtr<mozIStorageAsyncStatement> stmt = mDB->GetAsyncStatement( 1.2426 + "DELETE FROM moz_keywords " 1.2427 + "WHERE keyword = :keyword " 1.2428 + "AND NOT EXISTS ( " 1.2429 + "SELECT id " 1.2430 + "FROM moz_bookmarks " 1.2431 + "WHERE keyword_id = moz_keywords.id " 1.2432 + ")" 1.2433 + ); 1.2434 + NS_ENSURE_STATE(stmt); 1.2435 + 1.2436 + rv = stmt->BindStringByName(NS_LITERAL_CSTRING("keyword"), keyword); 1.2437 + NS_ENSURE_SUCCESS(rv, rv); 1.2438 + nsCOMPtr<mozIStoragePendingStatement> pendingStmt; 1.2439 + rv = stmt->ExecuteAsync(nullptr, getter_AddRefs(pendingStmt)); 1.2440 + NS_ENSURE_SUCCESS(rv, rv); 1.2441 + } 1.2442 + } 1.2443 + return NS_OK; 1.2444 +} 1.2445 + 1.2446 + 1.2447 +NS_IMETHODIMP 1.2448 +nsNavBookmarks::SetKeywordForBookmark(int64_t aBookmarkId, 1.2449 + const nsAString& aUserCasedKeyword) 1.2450 +{ 1.2451 + NS_ENSURE_ARG_MIN(aBookmarkId, 1); 1.2452 + 1.2453 + // This also ensures the bookmark is valid. 1.2454 + BookmarkData bookmark; 1.2455 + nsresult rv = FetchItemInfo(aBookmarkId, bookmark); 1.2456 + NS_ENSURE_SUCCESS(rv, rv); 1.2457 + 1.2458 + rv = EnsureKeywordsHash(); 1.2459 + NS_ENSURE_SUCCESS(rv, rv); 1.2460 + 1.2461 + // Shortcuts are always lowercased internally. 1.2462 + nsAutoString keyword(aUserCasedKeyword); 1.2463 + ToLowerCase(keyword); 1.2464 + 1.2465 + // Check if bookmark was already associated to a keyword. 1.2466 + nsAutoString oldKeyword; 1.2467 + rv = GetKeywordForBookmark(bookmark.id, oldKeyword); 1.2468 + NS_ENSURE_SUCCESS(rv, rv); 1.2469 + 1.2470 + // Trying to set the same value or to remove a nonexistent keyword is a no-op. 1.2471 + if (keyword.Equals(oldKeyword) || (keyword.IsEmpty() && oldKeyword.IsEmpty())) 1.2472 + return NS_OK; 1.2473 + 1.2474 + mozStorageTransaction transaction(mDB->MainConn(), false); 1.2475 + 1.2476 + nsCOMPtr<mozIStorageStatement> updateBookmarkStmt = mDB->GetStatement( 1.2477 + "UPDATE moz_bookmarks " 1.2478 + "SET keyword_id = (SELECT id FROM moz_keywords WHERE keyword = :keyword), " 1.2479 + "lastModified = :date " 1.2480 + "WHERE id = :item_id " 1.2481 + ); 1.2482 + NS_ENSURE_STATE(updateBookmarkStmt); 1.2483 + mozStorageStatementScoper updateBookmarkScoper(updateBookmarkStmt); 1.2484 + 1.2485 + if (keyword.IsEmpty()) { 1.2486 + // Remove keyword association from the hash. 1.2487 + mBookmarkToKeywordHash.Remove(bookmark.id); 1.2488 + rv = updateBookmarkStmt->BindNullByName(NS_LITERAL_CSTRING("keyword")); 1.2489 + } 1.2490 + else { 1.2491 + // We are associating bookmark to a new keyword. Create a new keyword 1.2492 + // record if needed. 1.2493 + nsCOMPtr<mozIStorageStatement> newKeywordStmt = mDB->GetStatement( 1.2494 + "INSERT OR IGNORE INTO moz_keywords (keyword) VALUES (:keyword)" 1.2495 + ); 1.2496 + NS_ENSURE_STATE(newKeywordStmt); 1.2497 + mozStorageStatementScoper newKeywordScoper(newKeywordStmt); 1.2498 + 1.2499 + rv = newKeywordStmt->BindStringByName(NS_LITERAL_CSTRING("keyword"), 1.2500 + keyword); 1.2501 + NS_ENSURE_SUCCESS(rv, rv); 1.2502 + rv = newKeywordStmt->Execute(); 1.2503 + NS_ENSURE_SUCCESS(rv, rv); 1.2504 + 1.2505 + // Add new keyword association to the hash, removing the old one if needed. 1.2506 + if (!oldKeyword.IsEmpty()) 1.2507 + mBookmarkToKeywordHash.Remove(bookmark.id); 1.2508 + mBookmarkToKeywordHash.Put(bookmark.id, keyword); 1.2509 + rv = updateBookmarkStmt->BindStringByName(NS_LITERAL_CSTRING("keyword"), keyword); 1.2510 + } 1.2511 + NS_ENSURE_SUCCESS(rv, rv); 1.2512 + bookmark.lastModified = PR_Now(); 1.2513 + rv = updateBookmarkStmt->BindInt64ByName(NS_LITERAL_CSTRING("date"), 1.2514 + bookmark.lastModified); 1.2515 + NS_ENSURE_SUCCESS(rv, rv); 1.2516 + rv = updateBookmarkStmt->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), 1.2517 + bookmark.id); 1.2518 + NS_ENSURE_SUCCESS(rv, rv); 1.2519 + rv = updateBookmarkStmt->Execute(); 1.2520 + NS_ENSURE_SUCCESS(rv, rv); 1.2521 + 1.2522 + rv = transaction.Commit(); 1.2523 + NS_ENSURE_SUCCESS(rv, rv); 1.2524 + 1.2525 + // Update the cache entry, if needed. 1.2526 + BookmarkKeyClass* key = mRecentBookmarksCache.GetEntry(aBookmarkId); 1.2527 + if (key) { 1.2528 + key->bookmark.lastModified = bookmark.lastModified; 1.2529 + } 1.2530 + 1.2531 + NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers, 1.2532 + nsINavBookmarkObserver, 1.2533 + OnItemChanged(bookmark.id, 1.2534 + NS_LITERAL_CSTRING("keyword"), 1.2535 + false, 1.2536 + NS_ConvertUTF16toUTF8(keyword), 1.2537 + bookmark.lastModified, 1.2538 + bookmark.type, 1.2539 + bookmark.parentId, 1.2540 + bookmark.guid, 1.2541 + bookmark.parentGuid)); 1.2542 + 1.2543 + return NS_OK; 1.2544 +} 1.2545 + 1.2546 + 1.2547 +NS_IMETHODIMP 1.2548 +nsNavBookmarks::GetKeywordForURI(nsIURI* aURI, nsAString& aKeyword) 1.2549 +{ 1.2550 + NS_ENSURE_ARG(aURI); 1.2551 + aKeyword.Truncate(0); 1.2552 + 1.2553 + nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement( 1.2554 + "SELECT k.keyword " 1.2555 + "FROM moz_places h " 1.2556 + "JOIN moz_bookmarks b ON b.fk = h.id " 1.2557 + "JOIN moz_keywords k ON k.id = b.keyword_id " 1.2558 + "WHERE h.url = :page_url " 1.2559 + ); 1.2560 + NS_ENSURE_STATE(stmt); 1.2561 + mozStorageStatementScoper scoper(stmt); 1.2562 + 1.2563 + nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), aURI); 1.2564 + NS_ENSURE_SUCCESS(rv, rv); 1.2565 + 1.2566 + bool hasMore = false; 1.2567 + rv = stmt->ExecuteStep(&hasMore); 1.2568 + if (NS_FAILED(rv) || !hasMore) { 1.2569 + aKeyword.SetIsVoid(true); 1.2570 + return NS_OK; // not found: return void keyword string 1.2571 + } 1.2572 + 1.2573 + // found, get the keyword 1.2574 + rv = stmt->GetString(0, aKeyword); 1.2575 + NS_ENSURE_SUCCESS(rv, rv); 1.2576 + return NS_OK; 1.2577 +} 1.2578 + 1.2579 + 1.2580 +NS_IMETHODIMP 1.2581 +nsNavBookmarks::GetKeywordForBookmark(int64_t aBookmarkId, nsAString& aKeyword) 1.2582 +{ 1.2583 + NS_ENSURE_ARG_MIN(aBookmarkId, 1); 1.2584 + aKeyword.Truncate(0); 1.2585 + 1.2586 + nsresult rv = EnsureKeywordsHash(); 1.2587 + NS_ENSURE_SUCCESS(rv, rv); 1.2588 + 1.2589 + nsAutoString keyword; 1.2590 + if (!mBookmarkToKeywordHash.Get(aBookmarkId, &keyword)) { 1.2591 + aKeyword.SetIsVoid(true); 1.2592 + } 1.2593 + else { 1.2594 + aKeyword.Assign(keyword); 1.2595 + } 1.2596 + 1.2597 + return NS_OK; 1.2598 +} 1.2599 + 1.2600 + 1.2601 +NS_IMETHODIMP 1.2602 +nsNavBookmarks::GetURIForKeyword(const nsAString& aUserCasedKeyword, 1.2603 + nsIURI** aURI) 1.2604 +{ 1.2605 + NS_ENSURE_ARG_POINTER(aURI); 1.2606 + NS_ENSURE_TRUE(!aUserCasedKeyword.IsEmpty(), NS_ERROR_INVALID_ARG); 1.2607 + *aURI = nullptr; 1.2608 + 1.2609 + // Shortcuts are always lowercased internally. 1.2610 + nsAutoString keyword(aUserCasedKeyword); 1.2611 + ToLowerCase(keyword); 1.2612 + 1.2613 + nsresult rv = EnsureKeywordsHash(); 1.2614 + NS_ENSURE_SUCCESS(rv, rv); 1.2615 + 1.2616 + keywordSearchData searchData; 1.2617 + searchData.keyword.Assign(keyword); 1.2618 + searchData.itemId = -1; 1.2619 + mBookmarkToKeywordHash.EnumerateRead(SearchBookmarkForKeyword, &searchData); 1.2620 + 1.2621 + if (searchData.itemId == -1) { 1.2622 + // Not found. 1.2623 + return NS_OK; 1.2624 + } 1.2625 + 1.2626 + rv = GetBookmarkURI(searchData.itemId, aURI); 1.2627 + NS_ENSURE_SUCCESS(rv, rv); 1.2628 + 1.2629 + return NS_OK; 1.2630 +} 1.2631 + 1.2632 + 1.2633 +nsresult 1.2634 +nsNavBookmarks::EnsureKeywordsHash() { 1.2635 + if (mBookmarkToKeywordHashInitialized) { 1.2636 + return NS_OK; 1.2637 + } 1.2638 + mBookmarkToKeywordHashInitialized = true; 1.2639 + 1.2640 + nsCOMPtr<mozIStorageStatement> stmt; 1.2641 + nsresult rv = mDB->MainConn()->CreateStatement(NS_LITERAL_CSTRING( 1.2642 + "SELECT b.id, k.keyword " 1.2643 + "FROM moz_bookmarks b " 1.2644 + "JOIN moz_keywords k ON k.id = b.keyword_id " 1.2645 + ), getter_AddRefs(stmt)); 1.2646 + NS_ENSURE_SUCCESS(rv, rv); 1.2647 + 1.2648 + bool hasMore; 1.2649 + while (NS_SUCCEEDED(stmt->ExecuteStep(&hasMore)) && hasMore) { 1.2650 + int64_t itemId; 1.2651 + rv = stmt->GetInt64(0, &itemId); 1.2652 + NS_ENSURE_SUCCESS(rv, rv); 1.2653 + nsAutoString keyword; 1.2654 + rv = stmt->GetString(1, keyword); 1.2655 + NS_ENSURE_SUCCESS(rv, rv); 1.2656 + 1.2657 + mBookmarkToKeywordHash.Put(itemId, keyword); 1.2658 + } 1.2659 + 1.2660 + return NS_OK; 1.2661 +} 1.2662 + 1.2663 + 1.2664 +NS_IMETHODIMP 1.2665 +nsNavBookmarks::RunInBatchMode(nsINavHistoryBatchCallback* aCallback, 1.2666 + nsISupports* aUserData) { 1.2667 + PROFILER_LABEL("bookmarks", "RunInBatchMode"); 1.2668 + NS_ENSURE_ARG(aCallback); 1.2669 + 1.2670 + mBatching = true; 1.2671 + 1.2672 + // Just forward the request to history. History service must exist for 1.2673 + // bookmarks to work and we are observing it, thus batch notifications will be 1.2674 + // forwarded to bookmarks observers. 1.2675 + nsNavHistory* history = nsNavHistory::GetHistoryService(); 1.2676 + NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY); 1.2677 + nsresult rv = history->RunInBatchMode(aCallback, aUserData); 1.2678 + NS_ENSURE_SUCCESS(rv, rv); 1.2679 + 1.2680 + return NS_OK; 1.2681 +} 1.2682 + 1.2683 + 1.2684 +NS_IMETHODIMP 1.2685 +nsNavBookmarks::AddObserver(nsINavBookmarkObserver* aObserver, 1.2686 + bool aOwnsWeak) 1.2687 +{ 1.2688 + NS_ENSURE_ARG(aObserver); 1.2689 + return mObservers.AppendWeakElement(aObserver, aOwnsWeak); 1.2690 +} 1.2691 + 1.2692 + 1.2693 +NS_IMETHODIMP 1.2694 +nsNavBookmarks::RemoveObserver(nsINavBookmarkObserver* aObserver) 1.2695 +{ 1.2696 + return mObservers.RemoveWeakElement(aObserver); 1.2697 +} 1.2698 + 1.2699 +void 1.2700 +nsNavBookmarks::NotifyItemVisited(const ItemVisitData& aData) 1.2701 +{ 1.2702 + nsCOMPtr<nsIURI> uri; 1.2703 + (void)NS_NewURI(getter_AddRefs(uri), aData.bookmark.url); 1.2704 + NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers, 1.2705 + nsINavBookmarkObserver, 1.2706 + OnItemVisited(aData.bookmark.id, 1.2707 + aData.visitId, 1.2708 + aData.time, 1.2709 + aData.transitionType, 1.2710 + uri, 1.2711 + aData.bookmark.parentId, 1.2712 + aData.bookmark.guid, 1.2713 + aData.bookmark.parentGuid)); 1.2714 +} 1.2715 + 1.2716 +void 1.2717 +nsNavBookmarks::NotifyItemChanged(const ItemChangeData& aData) 1.2718 +{ 1.2719 + // A guid must always be defined. 1.2720 + MOZ_ASSERT(!aData.bookmark.guid.IsEmpty()); 1.2721 + NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers, 1.2722 + nsINavBookmarkObserver, 1.2723 + OnItemChanged(aData.bookmark.id, 1.2724 + aData.property, 1.2725 + aData.isAnnotation, 1.2726 + aData.newValue, 1.2727 + aData.bookmark.lastModified, 1.2728 + aData.bookmark.type, 1.2729 + aData.bookmark.parentId, 1.2730 + aData.bookmark.guid, 1.2731 + aData.bookmark.parentGuid)); 1.2732 +} 1.2733 + 1.2734 +//////////////////////////////////////////////////////////////////////////////// 1.2735 +//// nsIObserver 1.2736 + 1.2737 +NS_IMETHODIMP 1.2738 +nsNavBookmarks::Observe(nsISupports *aSubject, const char *aTopic, 1.2739 + const char16_t *aData) 1.2740 +{ 1.2741 + NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread"); 1.2742 + 1.2743 + if (strcmp(aTopic, TOPIC_PLACES_MAINTENANCE) == 0) { 1.2744 + // Maintenance can execute direct writes to the database, thus clear all 1.2745 + // the cached bookmarks. 1.2746 + mRecentBookmarksCache.Clear(); 1.2747 + } 1.2748 + else if (strcmp(aTopic, TOPIC_PLACES_SHUTDOWN) == 0) { 1.2749 + // Stop Observing annotations. 1.2750 + nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService(); 1.2751 + if (annosvc) { 1.2752 + annosvc->RemoveObserver(this); 1.2753 + } 1.2754 + } 1.2755 + else if (strcmp(aTopic, TOPIC_PLACES_CONNECTION_CLOSED) == 0) { 1.2756 + // Don't even try to notify observers from this point on, the category 1.2757 + // cache would init services that could try to use our APIs. 1.2758 + mCanNotify = false; 1.2759 + } 1.2760 + 1.2761 + return NS_OK; 1.2762 +} 1.2763 + 1.2764 +//////////////////////////////////////////////////////////////////////////////// 1.2765 +//// nsINavHistoryObserver 1.2766 + 1.2767 +NS_IMETHODIMP 1.2768 +nsNavBookmarks::OnBeginUpdateBatch() 1.2769 +{ 1.2770 + NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers, 1.2771 + nsINavBookmarkObserver, OnBeginUpdateBatch()); 1.2772 + return NS_OK; 1.2773 +} 1.2774 + 1.2775 + 1.2776 +NS_IMETHODIMP 1.2777 +nsNavBookmarks::OnEndUpdateBatch() 1.2778 +{ 1.2779 + if (mBatching) { 1.2780 + mBatching = false; 1.2781 + } 1.2782 + 1.2783 + NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers, 1.2784 + nsINavBookmarkObserver, OnEndUpdateBatch()); 1.2785 + return NS_OK; 1.2786 +} 1.2787 + 1.2788 + 1.2789 +NS_IMETHODIMP 1.2790 +nsNavBookmarks::OnVisit(nsIURI* aURI, int64_t aVisitId, PRTime aTime, 1.2791 + int64_t aSessionID, int64_t aReferringID, 1.2792 + uint32_t aTransitionType, const nsACString& aGUID, 1.2793 + bool aHidden) 1.2794 +{ 1.2795 + // If the page is bookmarked, notify observers for each associated bookmark. 1.2796 + ItemVisitData visitData; 1.2797 + nsresult rv = aURI->GetSpec(visitData.bookmark.url); 1.2798 + NS_ENSURE_SUCCESS(rv, rv); 1.2799 + visitData.visitId = aVisitId; 1.2800 + visitData.time = aTime; 1.2801 + visitData.transitionType = aTransitionType; 1.2802 + 1.2803 + nsRefPtr< AsyncGetBookmarksForURI<ItemVisitMethod, ItemVisitData> > notifier = 1.2804 + new AsyncGetBookmarksForURI<ItemVisitMethod, ItemVisitData>(this, &nsNavBookmarks::NotifyItemVisited, visitData); 1.2805 + notifier->Init(); 1.2806 + return NS_OK; 1.2807 +} 1.2808 + 1.2809 + 1.2810 +NS_IMETHODIMP 1.2811 +nsNavBookmarks::OnDeleteURI(nsIURI* aURI, 1.2812 + const nsACString& aGUID, 1.2813 + uint16_t aReason) 1.2814 +{ 1.2815 +#ifdef DEBUG 1.2816 + nsNavHistory* history = nsNavHistory::GetHistoryService(); 1.2817 + int64_t placeId; 1.2818 + nsAutoCString placeGuid; 1.2819 + NS_ABORT_IF_FALSE( 1.2820 + history && NS_SUCCEEDED(history->GetIdForPage(aURI, &placeId, placeGuid)) && !placeId, 1.2821 + "OnDeleteURI was notified for a page that still exists?" 1.2822 + ); 1.2823 +#endif 1.2824 + return NS_OK; 1.2825 +} 1.2826 + 1.2827 + 1.2828 +NS_IMETHODIMP 1.2829 +nsNavBookmarks::OnClearHistory() 1.2830 +{ 1.2831 + // TODO(bryner): we should notify on visited-time change for all URIs 1.2832 + return NS_OK; 1.2833 +} 1.2834 + 1.2835 + 1.2836 +NS_IMETHODIMP 1.2837 +nsNavBookmarks::OnTitleChanged(nsIURI* aURI, 1.2838 + const nsAString& aPageTitle, 1.2839 + const nsACString& aGUID) 1.2840 +{ 1.2841 + // NOOP. We don't consume page titles from moz_places anymore. 1.2842 + // Title-change notifications are sent from SetItemTitle. 1.2843 + return NS_OK; 1.2844 +} 1.2845 + 1.2846 + 1.2847 +NS_IMETHODIMP 1.2848 +nsNavBookmarks::OnFrecencyChanged(nsIURI* aURI, 1.2849 + int32_t aNewFrecency, 1.2850 + const nsACString& aGUID, 1.2851 + bool aHidden, 1.2852 + PRTime aLastVisitDate) 1.2853 +{ 1.2854 + return NS_OK; 1.2855 +} 1.2856 + 1.2857 + 1.2858 +NS_IMETHODIMP 1.2859 +nsNavBookmarks::OnManyFrecenciesChanged() 1.2860 +{ 1.2861 + return NS_OK; 1.2862 +} 1.2863 + 1.2864 + 1.2865 +NS_IMETHODIMP 1.2866 +nsNavBookmarks::OnPageChanged(nsIURI* aURI, 1.2867 + uint32_t aChangedAttribute, 1.2868 + const nsAString& aNewValue, 1.2869 + const nsACString& aGUID) 1.2870 +{ 1.2871 + nsresult rv; 1.2872 + if (aChangedAttribute == nsINavHistoryObserver::ATTRIBUTE_FAVICON) { 1.2873 + ItemChangeData changeData; 1.2874 + rv = aURI->GetSpec(changeData.bookmark.url); 1.2875 + NS_ENSURE_SUCCESS(rv, rv); 1.2876 + changeData.property = NS_LITERAL_CSTRING("favicon"); 1.2877 + changeData.isAnnotation = false; 1.2878 + changeData.newValue = NS_ConvertUTF16toUTF8(aNewValue); 1.2879 + changeData.bookmark.lastModified = 0; 1.2880 + changeData.bookmark.type = TYPE_BOOKMARK; 1.2881 + 1.2882 + // Favicons may be set to either pure URIs or to folder URIs 1.2883 + bool isPlaceURI; 1.2884 + rv = aURI->SchemeIs("place", &isPlaceURI); 1.2885 + NS_ENSURE_SUCCESS(rv, rv); 1.2886 + if (isPlaceURI) { 1.2887 + nsNavHistory* history = nsNavHistory::GetHistoryService(); 1.2888 + NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY); 1.2889 + 1.2890 + nsCOMArray<nsNavHistoryQuery> queries; 1.2891 + nsCOMPtr<nsNavHistoryQueryOptions> options; 1.2892 + rv = history->QueryStringToQueryArray(changeData.bookmark.url, 1.2893 + &queries, getter_AddRefs(options)); 1.2894 + NS_ENSURE_SUCCESS(rv, rv); 1.2895 + 1.2896 + if (queries.Count() == 1 && queries[0]->Folders().Length() == 1) { 1.2897 + // Fetch missing data. 1.2898 + rv = FetchItemInfo(queries[0]->Folders()[0], changeData.bookmark); 1.2899 + NS_ENSURE_SUCCESS(rv, rv); 1.2900 + NotifyItemChanged(changeData); 1.2901 + } 1.2902 + } 1.2903 + else { 1.2904 + nsRefPtr< AsyncGetBookmarksForURI<ItemChangeMethod, ItemChangeData> > notifier = 1.2905 + new AsyncGetBookmarksForURI<ItemChangeMethod, ItemChangeData>(this, &nsNavBookmarks::NotifyItemChanged, changeData); 1.2906 + notifier->Init(); 1.2907 + } 1.2908 + } 1.2909 + return NS_OK; 1.2910 +} 1.2911 + 1.2912 + 1.2913 +NS_IMETHODIMP 1.2914 +nsNavBookmarks::OnDeleteVisits(nsIURI* aURI, PRTime aVisitTime, 1.2915 + const nsACString& aGUID, 1.2916 + uint16_t aReason, uint32_t aTransitionType) 1.2917 +{ 1.2918 + // Notify "cleartime" only if all visits to the page have been removed. 1.2919 + if (!aVisitTime) { 1.2920 + // If the page is bookmarked, notify observers for each associated bookmark. 1.2921 + ItemChangeData changeData; 1.2922 + nsresult rv = aURI->GetSpec(changeData.bookmark.url); 1.2923 + NS_ENSURE_SUCCESS(rv, rv); 1.2924 + changeData.property = NS_LITERAL_CSTRING("cleartime"); 1.2925 + changeData.isAnnotation = false; 1.2926 + changeData.bookmark.lastModified = 0; 1.2927 + changeData.bookmark.type = TYPE_BOOKMARK; 1.2928 + 1.2929 + nsRefPtr< AsyncGetBookmarksForURI<ItemChangeMethod, ItemChangeData> > notifier = 1.2930 + new AsyncGetBookmarksForURI<ItemChangeMethod, ItemChangeData>(this, &nsNavBookmarks::NotifyItemChanged, changeData); 1.2931 + notifier->Init(); 1.2932 + } 1.2933 + return NS_OK; 1.2934 +} 1.2935 + 1.2936 + 1.2937 +// nsIAnnotationObserver 1.2938 + 1.2939 +NS_IMETHODIMP 1.2940 +nsNavBookmarks::OnPageAnnotationSet(nsIURI* aPage, const nsACString& aName) 1.2941 +{ 1.2942 + return NS_OK; 1.2943 +} 1.2944 + 1.2945 + 1.2946 +NS_IMETHODIMP 1.2947 +nsNavBookmarks::OnItemAnnotationSet(int64_t aItemId, const nsACString& aName) 1.2948 +{ 1.2949 + BookmarkData bookmark; 1.2950 + nsresult rv = FetchItemInfo(aItemId, bookmark); 1.2951 + NS_ENSURE_SUCCESS(rv, rv); 1.2952 + 1.2953 + bookmark.lastModified = PR_Now(); 1.2954 + rv = SetItemDateInternal(LAST_MODIFIED, bookmark.id, bookmark.lastModified); 1.2955 + NS_ENSURE_SUCCESS(rv, rv); 1.2956 + 1.2957 + NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers, 1.2958 + nsINavBookmarkObserver, 1.2959 + OnItemChanged(bookmark.id, 1.2960 + aName, 1.2961 + true, 1.2962 + EmptyCString(), 1.2963 + bookmark.lastModified, 1.2964 + bookmark.type, 1.2965 + bookmark.parentId, 1.2966 + bookmark.guid, 1.2967 + bookmark.parentGuid)); 1.2968 + return NS_OK; 1.2969 +} 1.2970 + 1.2971 + 1.2972 +NS_IMETHODIMP 1.2973 +nsNavBookmarks::OnPageAnnotationRemoved(nsIURI* aPage, const nsACString& aName) 1.2974 +{ 1.2975 + return NS_OK; 1.2976 +} 1.2977 + 1.2978 + 1.2979 +NS_IMETHODIMP 1.2980 +nsNavBookmarks::OnItemAnnotationRemoved(int64_t aItemId, const nsACString& aName) 1.2981 +{ 1.2982 + // As of now this is doing the same as OnItemAnnotationSet, so just forward 1.2983 + // the call. 1.2984 + nsresult rv = OnItemAnnotationSet(aItemId, aName); 1.2985 + NS_ENSURE_SUCCESS(rv, rv); 1.2986 + return NS_OK; 1.2987 +}