|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 #include "nsNavBookmarks.h" |
|
7 |
|
8 #include "nsNavHistory.h" |
|
9 #include "nsAnnotationService.h" |
|
10 #include "nsPlacesMacros.h" |
|
11 #include "Helpers.h" |
|
12 |
|
13 #include "nsAppDirectoryServiceDefs.h" |
|
14 #include "nsNetUtil.h" |
|
15 #include "nsUnicharUtils.h" |
|
16 #include "nsPrintfCString.h" |
|
17 #include "prprf.h" |
|
18 #include "mozilla/storage.h" |
|
19 |
|
20 #include "GeckoProfiler.h" |
|
21 |
|
22 #define BOOKMARKS_TO_KEYWORDS_INITIAL_CACHE_SIZE 64 |
|
23 #define RECENT_BOOKMARKS_INITIAL_CACHE_SIZE 10 |
|
24 // Threashold to expire old bookmarks if the initial cache size is exceeded. |
|
25 #define RECENT_BOOKMARKS_THRESHOLD PRTime((int64_t)1 * 60 * PR_USEC_PER_SEC) |
|
26 |
|
27 #define BEGIN_CRITICAL_BOOKMARK_CACHE_SECTION(_itemId_) \ |
|
28 mUncachableBookmarks.PutEntry(_itemId_); \ |
|
29 mRecentBookmarksCache.RemoveEntry(_itemId_) |
|
30 |
|
31 #define END_CRITICAL_BOOKMARK_CACHE_SECTION(_itemId_) \ |
|
32 MOZ_ASSERT(!mRecentBookmarksCache.GetEntry(_itemId_)); \ |
|
33 MOZ_ASSERT(mUncachableBookmarks.GetEntry(_itemId_)); \ |
|
34 mUncachableBookmarks.RemoveEntry(_itemId_) |
|
35 |
|
36 #define ADD_TO_BOOKMARK_CACHE(_itemId_, _data_) \ |
|
37 PR_BEGIN_MACRO \ |
|
38 ExpireNonrecentBookmarks(&mRecentBookmarksCache); \ |
|
39 if (!mUncachableBookmarks.GetEntry(_itemId_)) { \ |
|
40 BookmarkKeyClass* key = mRecentBookmarksCache.PutEntry(_itemId_); \ |
|
41 if (key) { \ |
|
42 key->bookmark = _data_; \ |
|
43 } \ |
|
44 } \ |
|
45 PR_END_MACRO |
|
46 |
|
47 #define TOPIC_PLACES_MAINTENANCE "places-maintenance-finished" |
|
48 |
|
49 using namespace mozilla; |
|
50 |
|
51 // These columns sit to the right of the kGetInfoIndex_* columns. |
|
52 const int32_t nsNavBookmarks::kGetChildrenIndex_Guid = 15; |
|
53 const int32_t nsNavBookmarks::kGetChildrenIndex_Position = 16; |
|
54 const int32_t nsNavBookmarks::kGetChildrenIndex_Type = 17; |
|
55 const int32_t nsNavBookmarks::kGetChildrenIndex_PlaceID = 18; |
|
56 |
|
57 using namespace mozilla::places; |
|
58 |
|
59 PLACES_FACTORY_SINGLETON_IMPLEMENTATION(nsNavBookmarks, gBookmarksService) |
|
60 |
|
61 #define BOOKMARKS_ANNO_PREFIX "bookmarks/" |
|
62 #define BOOKMARKS_TOOLBAR_FOLDER_ANNO NS_LITERAL_CSTRING(BOOKMARKS_ANNO_PREFIX "toolbarFolder") |
|
63 #define READ_ONLY_ANNO NS_LITERAL_CSTRING("placesInternal/READ_ONLY") |
|
64 |
|
65 |
|
66 namespace { |
|
67 |
|
68 struct keywordSearchData |
|
69 { |
|
70 int64_t itemId; |
|
71 nsString keyword; |
|
72 }; |
|
73 |
|
74 PLDHashOperator |
|
75 SearchBookmarkForKeyword(nsTrimInt64HashKey::KeyType aKey, |
|
76 const nsString aValue, |
|
77 void* aUserArg) |
|
78 { |
|
79 keywordSearchData* data = reinterpret_cast<keywordSearchData*>(aUserArg); |
|
80 if (data->keyword.Equals(aValue)) { |
|
81 data->itemId = aKey; |
|
82 return PL_DHASH_STOP; |
|
83 } |
|
84 return PL_DHASH_NEXT; |
|
85 } |
|
86 |
|
87 template<typename Method, typename DataType> |
|
88 class AsyncGetBookmarksForURI : public AsyncStatementCallback |
|
89 { |
|
90 public: |
|
91 AsyncGetBookmarksForURI(nsNavBookmarks* aBookmarksSvc, |
|
92 Method aCallback, |
|
93 const DataType& aData) |
|
94 : mBookmarksSvc(aBookmarksSvc) |
|
95 , mCallback(aCallback) |
|
96 , mData(aData) |
|
97 { |
|
98 } |
|
99 |
|
100 void Init() |
|
101 { |
|
102 nsRefPtr<Database> DB = Database::GetDatabase(); |
|
103 if (DB) { |
|
104 nsCOMPtr<mozIStorageAsyncStatement> stmt = DB->GetAsyncStatement( |
|
105 "SELECT b.id, b.guid, b.parent, b.lastModified, t.guid, t.parent " |
|
106 "FROM moz_bookmarks b " |
|
107 "JOIN moz_bookmarks t on t.id = b.parent " |
|
108 "WHERE b.fk = (SELECT id FROM moz_places WHERE url = :page_url) " |
|
109 "ORDER BY b.lastModified DESC, b.id DESC " |
|
110 ); |
|
111 if (stmt) { |
|
112 (void)URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), |
|
113 mData.bookmark.url); |
|
114 nsCOMPtr<mozIStoragePendingStatement> pendingStmt; |
|
115 (void)stmt->ExecuteAsync(this, getter_AddRefs(pendingStmt)); |
|
116 } |
|
117 } |
|
118 } |
|
119 |
|
120 NS_IMETHOD HandleResult(mozIStorageResultSet* aResultSet) |
|
121 { |
|
122 nsCOMPtr<mozIStorageRow> row; |
|
123 while (NS_SUCCEEDED(aResultSet->GetNextRow(getter_AddRefs(row))) && row) { |
|
124 // Skip tags, for the use-cases of this async getter they are useless. |
|
125 int64_t grandParentId, tagsFolderId; |
|
126 nsresult rv = row->GetInt64(5, &grandParentId); |
|
127 NS_ENSURE_SUCCESS(rv, rv); |
|
128 rv = mBookmarksSvc->GetTagsFolder(&tagsFolderId); |
|
129 NS_ENSURE_SUCCESS(rv, rv); |
|
130 if (grandParentId == tagsFolderId) { |
|
131 continue; |
|
132 } |
|
133 |
|
134 mData.bookmark.grandParentId = grandParentId; |
|
135 rv = row->GetInt64(0, &mData.bookmark.id); |
|
136 NS_ENSURE_SUCCESS(rv, rv); |
|
137 rv = row->GetUTF8String(1, mData.bookmark.guid); |
|
138 NS_ENSURE_SUCCESS(rv, rv); |
|
139 rv = row->GetInt64(2, &mData.bookmark.parentId); |
|
140 NS_ENSURE_SUCCESS(rv, rv); |
|
141 // lastModified (3) should not be set for the use-cases of this getter. |
|
142 rv = row->GetUTF8String(4, mData.bookmark.parentGuid); |
|
143 NS_ENSURE_SUCCESS(rv, rv); |
|
144 |
|
145 if (mCallback) { |
|
146 ((*mBookmarksSvc).*mCallback)(mData); |
|
147 } |
|
148 } |
|
149 return NS_OK; |
|
150 } |
|
151 |
|
152 private: |
|
153 nsRefPtr<nsNavBookmarks> mBookmarksSvc; |
|
154 Method mCallback; |
|
155 DataType mData; |
|
156 }; |
|
157 |
|
158 static PLDHashOperator |
|
159 ExpireNonrecentBookmarksCallback(BookmarkKeyClass* aKey, |
|
160 void* userArg) |
|
161 { |
|
162 int64_t* threshold = reinterpret_cast<int64_t*>(userArg); |
|
163 if (aKey->creationTime < *threshold) { |
|
164 return PL_DHASH_REMOVE; |
|
165 } |
|
166 return PL_DHASH_NEXT; |
|
167 } |
|
168 |
|
169 static void |
|
170 ExpireNonrecentBookmarks(nsTHashtable<BookmarkKeyClass>* hashTable) |
|
171 { |
|
172 if (hashTable->Count() > RECENT_BOOKMARKS_INITIAL_CACHE_SIZE) { |
|
173 int64_t threshold = PR_Now() - RECENT_BOOKMARKS_THRESHOLD; |
|
174 (void)hashTable->EnumerateEntries(ExpireNonrecentBookmarksCallback, |
|
175 reinterpret_cast<void*>(&threshold)); |
|
176 } |
|
177 } |
|
178 |
|
179 static PLDHashOperator |
|
180 ExpireRecentBookmarksByParentCallback(BookmarkKeyClass* aKey, |
|
181 void* userArg) |
|
182 { |
|
183 int64_t* parentId = reinterpret_cast<int64_t*>(userArg); |
|
184 if (aKey->bookmark.parentId == *parentId) { |
|
185 return PL_DHASH_REMOVE; |
|
186 } |
|
187 return PL_DHASH_NEXT; |
|
188 } |
|
189 |
|
190 static void |
|
191 ExpireRecentBookmarksByParent(nsTHashtable<BookmarkKeyClass>* hashTable, |
|
192 int64_t aParentId) |
|
193 { |
|
194 (void)hashTable->EnumerateEntries(ExpireRecentBookmarksByParentCallback, |
|
195 reinterpret_cast<void*>(&aParentId)); |
|
196 } |
|
197 |
|
198 } // Anonymous namespace. |
|
199 |
|
200 |
|
201 nsNavBookmarks::nsNavBookmarks() |
|
202 : mItemCount(0) |
|
203 , mRoot(0) |
|
204 , mMenuRoot(0) |
|
205 , mTagsRoot(0) |
|
206 , mUnfiledRoot(0) |
|
207 , mToolbarRoot(0) |
|
208 , mCanNotify(false) |
|
209 , mCacheObservers("bookmark-observers") |
|
210 , mBatching(false) |
|
211 , mBookmarkToKeywordHash(BOOKMARKS_TO_KEYWORDS_INITIAL_CACHE_SIZE) |
|
212 , mBookmarkToKeywordHashInitialized(false) |
|
213 , mRecentBookmarksCache(RECENT_BOOKMARKS_INITIAL_CACHE_SIZE) |
|
214 , mUncachableBookmarks(RECENT_BOOKMARKS_INITIAL_CACHE_SIZE) |
|
215 { |
|
216 NS_ASSERTION(!gBookmarksService, |
|
217 "Attempting to create two instances of the service!"); |
|
218 gBookmarksService = this; |
|
219 } |
|
220 |
|
221 |
|
222 nsNavBookmarks::~nsNavBookmarks() |
|
223 { |
|
224 NS_ASSERTION(gBookmarksService == this, |
|
225 "Deleting a non-singleton instance of the service"); |
|
226 if (gBookmarksService == this) |
|
227 gBookmarksService = nullptr; |
|
228 } |
|
229 |
|
230 |
|
231 NS_IMPL_ISUPPORTS(nsNavBookmarks |
|
232 , nsINavBookmarksService |
|
233 , nsINavHistoryObserver |
|
234 , nsIAnnotationObserver |
|
235 , nsIObserver |
|
236 , nsISupportsWeakReference |
|
237 ) |
|
238 |
|
239 |
|
240 nsresult |
|
241 nsNavBookmarks::Init() |
|
242 { |
|
243 mDB = Database::GetDatabase(); |
|
244 NS_ENSURE_STATE(mDB); |
|
245 |
|
246 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); |
|
247 if (os) { |
|
248 (void)os->AddObserver(this, TOPIC_PLACES_MAINTENANCE, true); |
|
249 (void)os->AddObserver(this, TOPIC_PLACES_SHUTDOWN, true); |
|
250 (void)os->AddObserver(this, TOPIC_PLACES_CONNECTION_CLOSED, true); |
|
251 } |
|
252 |
|
253 nsresult rv = ReadRoots(); |
|
254 NS_ENSURE_SUCCESS(rv, rv); |
|
255 |
|
256 mCanNotify = true; |
|
257 |
|
258 // Observe annotations. |
|
259 nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService(); |
|
260 NS_ENSURE_TRUE(annosvc, NS_ERROR_OUT_OF_MEMORY); |
|
261 annosvc->AddObserver(this); |
|
262 |
|
263 // Allows us to notify on title changes. MUST BE LAST so it is impossible |
|
264 // to fail after this call, or the history service will have a reference to |
|
265 // us and we won't go away. |
|
266 nsNavHistory* history = nsNavHistory::GetHistoryService(); |
|
267 NS_ENSURE_STATE(history); |
|
268 history->AddObserver(this, true); |
|
269 |
|
270 // DO NOT PUT STUFF HERE that can fail. See observer comment above. |
|
271 |
|
272 return NS_OK; |
|
273 } |
|
274 |
|
275 nsresult |
|
276 nsNavBookmarks::ReadRoots() |
|
277 { |
|
278 nsCOMPtr<mozIStorageStatement> stmt; |
|
279 nsresult rv = mDB->MainConn()->CreateStatement(NS_LITERAL_CSTRING( |
|
280 "SELECT root_name, folder_id FROM moz_bookmarks_roots" |
|
281 ), getter_AddRefs(stmt)); |
|
282 NS_ENSURE_SUCCESS(rv, rv); |
|
283 |
|
284 bool hasResult; |
|
285 while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) { |
|
286 nsAutoCString rootName; |
|
287 rv = stmt->GetUTF8String(0, rootName); |
|
288 NS_ENSURE_SUCCESS(rv, rv); |
|
289 int64_t rootId; |
|
290 rv = stmt->GetInt64(1, &rootId); |
|
291 NS_ENSURE_SUCCESS(rv, rv); |
|
292 NS_ABORT_IF_FALSE(rootId != 0, "Root id is 0, that is an invalid value."); |
|
293 |
|
294 if (rootName.EqualsLiteral("places")) { |
|
295 mRoot = rootId; |
|
296 } |
|
297 else if (rootName.EqualsLiteral("menu")) { |
|
298 mMenuRoot = rootId; |
|
299 } |
|
300 else if (rootName.EqualsLiteral("toolbar")) { |
|
301 mToolbarRoot = rootId; |
|
302 } |
|
303 else if (rootName.EqualsLiteral("tags")) { |
|
304 mTagsRoot = rootId; |
|
305 } |
|
306 else if (rootName.EqualsLiteral("unfiled")) { |
|
307 mUnfiledRoot = rootId; |
|
308 } |
|
309 } |
|
310 |
|
311 if (!mRoot || !mMenuRoot || !mToolbarRoot || !mTagsRoot || !mUnfiledRoot) |
|
312 return NS_ERROR_FAILURE; |
|
313 |
|
314 return NS_OK; |
|
315 } |
|
316 |
|
317 // nsNavBookmarks::IsBookmarkedInDatabase |
|
318 // |
|
319 // This checks to see if the specified place_id is actually bookmarked. |
|
320 |
|
321 nsresult |
|
322 nsNavBookmarks::IsBookmarkedInDatabase(int64_t aPlaceId, |
|
323 bool* aIsBookmarked) |
|
324 { |
|
325 nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement( |
|
326 "SELECT 1 FROM moz_bookmarks WHERE fk = :page_id" |
|
327 ); |
|
328 NS_ENSURE_STATE(stmt); |
|
329 mozStorageStatementScoper scoper(stmt); |
|
330 |
|
331 nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), aPlaceId); |
|
332 NS_ENSURE_SUCCESS(rv, rv); |
|
333 rv = stmt->ExecuteStep(aIsBookmarked); |
|
334 NS_ENSURE_SUCCESS(rv, rv); |
|
335 return NS_OK; |
|
336 } |
|
337 |
|
338 |
|
339 nsresult |
|
340 nsNavBookmarks::AdjustIndices(int64_t aFolderId, |
|
341 int32_t aStartIndex, |
|
342 int32_t aEndIndex, |
|
343 int32_t aDelta) |
|
344 { |
|
345 NS_ASSERTION(aStartIndex >= 0 && aEndIndex <= INT32_MAX && |
|
346 aStartIndex <= aEndIndex, "Bad indices"); |
|
347 |
|
348 // Expire all cached items for this parent, since all positions are going to |
|
349 // change. |
|
350 ExpireRecentBookmarksByParent(&mRecentBookmarksCache, aFolderId); |
|
351 |
|
352 nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement( |
|
353 "UPDATE moz_bookmarks SET position = position + :delta " |
|
354 "WHERE parent = :parent " |
|
355 "AND position BETWEEN :from_index AND :to_index" |
|
356 ); |
|
357 NS_ENSURE_STATE(stmt); |
|
358 mozStorageStatementScoper scoper(stmt); |
|
359 |
|
360 nsresult rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("delta"), aDelta); |
|
361 NS_ENSURE_SUCCESS(rv, rv); |
|
362 rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aFolderId); |
|
363 NS_ENSURE_SUCCESS(rv, rv); |
|
364 rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("from_index"), aStartIndex); |
|
365 NS_ENSURE_SUCCESS(rv, rv); |
|
366 rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("to_index"), aEndIndex); |
|
367 NS_ENSURE_SUCCESS(rv, rv); |
|
368 |
|
369 rv = stmt->Execute(); |
|
370 NS_ENSURE_SUCCESS(rv, rv); |
|
371 |
|
372 return NS_OK; |
|
373 } |
|
374 |
|
375 |
|
376 NS_IMETHODIMP |
|
377 nsNavBookmarks::GetPlacesRoot(int64_t* aRoot) |
|
378 { |
|
379 *aRoot = mRoot; |
|
380 return NS_OK; |
|
381 } |
|
382 |
|
383 |
|
384 NS_IMETHODIMP |
|
385 nsNavBookmarks::GetBookmarksMenuFolder(int64_t* aRoot) |
|
386 { |
|
387 *aRoot = mMenuRoot; |
|
388 return NS_OK; |
|
389 } |
|
390 |
|
391 |
|
392 NS_IMETHODIMP |
|
393 nsNavBookmarks::GetToolbarFolder(int64_t* aFolderId) |
|
394 { |
|
395 *aFolderId = mToolbarRoot; |
|
396 return NS_OK; |
|
397 } |
|
398 |
|
399 |
|
400 NS_IMETHODIMP |
|
401 nsNavBookmarks::GetTagsFolder(int64_t* aRoot) |
|
402 { |
|
403 *aRoot = mTagsRoot; |
|
404 return NS_OK; |
|
405 } |
|
406 |
|
407 |
|
408 NS_IMETHODIMP |
|
409 nsNavBookmarks::GetUnfiledBookmarksFolder(int64_t* aRoot) |
|
410 { |
|
411 *aRoot = mUnfiledRoot; |
|
412 return NS_OK; |
|
413 } |
|
414 |
|
415 |
|
416 nsresult |
|
417 nsNavBookmarks::InsertBookmarkInDB(int64_t aPlaceId, |
|
418 enum ItemType aItemType, |
|
419 int64_t aParentId, |
|
420 int32_t aIndex, |
|
421 const nsACString& aTitle, |
|
422 PRTime aDateAdded, |
|
423 PRTime aLastModified, |
|
424 const nsACString& aParentGuid, |
|
425 int64_t aGrandParentId, |
|
426 nsIURI* aURI, |
|
427 int64_t* _itemId, |
|
428 nsACString& _guid) |
|
429 { |
|
430 // Check for a valid itemId. |
|
431 MOZ_ASSERT(_itemId && (*_itemId == -1 || *_itemId > 0)); |
|
432 // Check for a valid placeId. |
|
433 MOZ_ASSERT(aPlaceId && (aPlaceId == -1 || aPlaceId > 0)); |
|
434 |
|
435 nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement( |
|
436 "INSERT INTO moz_bookmarks " |
|
437 "(id, fk, type, parent, position, title, " |
|
438 "dateAdded, lastModified, guid) " |
|
439 "VALUES (:item_id, :page_id, :item_type, :parent, :item_index, " |
|
440 ":item_title, :date_added, :last_modified, " |
|
441 "IFNULL(:item_guid, GENERATE_GUID()))" |
|
442 ); |
|
443 NS_ENSURE_STATE(stmt); |
|
444 mozStorageStatementScoper scoper(stmt); |
|
445 |
|
446 nsresult rv; |
|
447 if (*_itemId != -1) |
|
448 rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), *_itemId); |
|
449 else |
|
450 rv = stmt->BindNullByName(NS_LITERAL_CSTRING("item_id")); |
|
451 NS_ENSURE_SUCCESS(rv, rv); |
|
452 |
|
453 if (aPlaceId != -1) |
|
454 rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), aPlaceId); |
|
455 else |
|
456 rv = stmt->BindNullByName(NS_LITERAL_CSTRING("page_id")); |
|
457 NS_ENSURE_SUCCESS(rv, rv); |
|
458 |
|
459 rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("item_type"), aItemType); |
|
460 NS_ENSURE_SUCCESS(rv, rv); |
|
461 rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aParentId); |
|
462 NS_ENSURE_SUCCESS(rv, rv); |
|
463 rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("item_index"), aIndex); |
|
464 NS_ENSURE_SUCCESS(rv, rv); |
|
465 |
|
466 // Support NULL titles. |
|
467 if (aTitle.IsVoid()) |
|
468 rv = stmt->BindNullByName(NS_LITERAL_CSTRING("item_title")); |
|
469 else |
|
470 rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("item_title"), aTitle); |
|
471 NS_ENSURE_SUCCESS(rv, rv); |
|
472 |
|
473 rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("date_added"), aDateAdded); |
|
474 NS_ENSURE_SUCCESS(rv, rv); |
|
475 |
|
476 if (aLastModified) { |
|
477 rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("last_modified"), |
|
478 aLastModified); |
|
479 } |
|
480 else { |
|
481 rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("last_modified"), aDateAdded); |
|
482 } |
|
483 NS_ENSURE_SUCCESS(rv, rv); |
|
484 |
|
485 // Could use IsEmpty because our callers check for GUID validity, |
|
486 // but it doesn't hurt. |
|
487 if (_guid.Length() == 12) { |
|
488 MOZ_ASSERT(IsValidGUID(_guid)); |
|
489 rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("item_guid"), _guid); |
|
490 } |
|
491 else { |
|
492 rv = stmt->BindNullByName(NS_LITERAL_CSTRING("item_guid")); |
|
493 } |
|
494 NS_ENSURE_SUCCESS(rv, rv); |
|
495 |
|
496 rv = stmt->Execute(); |
|
497 NS_ENSURE_SUCCESS(rv, rv); |
|
498 |
|
499 if (*_itemId == -1) { |
|
500 // Get the newly inserted item id and GUID. |
|
501 nsCOMPtr<mozIStorageStatement> lastInsertIdStmt = mDB->GetStatement( |
|
502 "SELECT id, guid " |
|
503 "FROM moz_bookmarks " |
|
504 "ORDER BY ROWID DESC " |
|
505 "LIMIT 1" |
|
506 ); |
|
507 NS_ENSURE_STATE(lastInsertIdStmt); |
|
508 mozStorageStatementScoper lastInsertIdScoper(lastInsertIdStmt); |
|
509 |
|
510 bool hasResult; |
|
511 rv = lastInsertIdStmt->ExecuteStep(&hasResult); |
|
512 NS_ENSURE_SUCCESS(rv, rv); |
|
513 NS_ENSURE_TRUE(hasResult, NS_ERROR_UNEXPECTED); |
|
514 rv = lastInsertIdStmt->GetInt64(0, _itemId); |
|
515 NS_ENSURE_SUCCESS(rv, rv); |
|
516 rv = lastInsertIdStmt->GetUTF8String(1, _guid); |
|
517 NS_ENSURE_SUCCESS(rv, rv); |
|
518 } |
|
519 |
|
520 if (aParentId > 0) { |
|
521 // Update last modified date of the ancestors. |
|
522 // TODO (bug 408991): Doing this for all ancestors would be slow without a |
|
523 // nested tree, so for now update only the parent. |
|
524 rv = SetItemDateInternal(LAST_MODIFIED, aParentId, aDateAdded); |
|
525 NS_ENSURE_SUCCESS(rv, rv); |
|
526 } |
|
527 |
|
528 // Add a cache entry since we know everything about this bookmark. |
|
529 BookmarkData bookmark; |
|
530 bookmark.id = *_itemId; |
|
531 bookmark.guid.Assign(_guid); |
|
532 if (aTitle.IsVoid()) { |
|
533 bookmark.title.SetIsVoid(true); |
|
534 } |
|
535 else { |
|
536 bookmark.title.Assign(aTitle); |
|
537 } |
|
538 bookmark.position = aIndex; |
|
539 bookmark.placeId = aPlaceId; |
|
540 bookmark.parentId = aParentId; |
|
541 bookmark.type = aItemType; |
|
542 bookmark.dateAdded = aDateAdded; |
|
543 if (aLastModified) |
|
544 bookmark.lastModified = aLastModified; |
|
545 else |
|
546 bookmark.lastModified = aDateAdded; |
|
547 if (aURI) { |
|
548 rv = aURI->GetSpec(bookmark.url); |
|
549 NS_ENSURE_SUCCESS(rv, rv); |
|
550 } |
|
551 bookmark.parentGuid = aParentGuid; |
|
552 bookmark.grandParentId = aGrandParentId; |
|
553 |
|
554 ADD_TO_BOOKMARK_CACHE(*_itemId, bookmark); |
|
555 |
|
556 return NS_OK; |
|
557 } |
|
558 |
|
559 |
|
560 NS_IMETHODIMP |
|
561 nsNavBookmarks::InsertBookmark(int64_t aFolder, |
|
562 nsIURI* aURI, |
|
563 int32_t aIndex, |
|
564 const nsACString& aTitle, |
|
565 const nsACString& aGUID, |
|
566 int64_t* aNewBookmarkId) |
|
567 { |
|
568 NS_ENSURE_ARG(aURI); |
|
569 NS_ENSURE_ARG_POINTER(aNewBookmarkId); |
|
570 NS_ENSURE_ARG_MIN(aIndex, nsINavBookmarksService::DEFAULT_INDEX); |
|
571 |
|
572 if (!aGUID.IsEmpty() && !IsValidGUID(aGUID)) |
|
573 return NS_ERROR_INVALID_ARG; |
|
574 |
|
575 mozStorageTransaction transaction(mDB->MainConn(), false); |
|
576 |
|
577 nsNavHistory* history = nsNavHistory::GetHistoryService(); |
|
578 NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY); |
|
579 int64_t placeId; |
|
580 nsAutoCString placeGuid; |
|
581 nsresult rv = history->GetOrCreateIdForPage(aURI, &placeId, placeGuid); |
|
582 NS_ENSURE_SUCCESS(rv, rv); |
|
583 |
|
584 // Get the correct index for insertion. This also ensures the parent exists. |
|
585 int32_t index, folderCount; |
|
586 int64_t grandParentId; |
|
587 nsAutoCString folderGuid; |
|
588 rv = FetchFolderInfo(aFolder, &folderCount, folderGuid, &grandParentId); |
|
589 NS_ENSURE_SUCCESS(rv, rv); |
|
590 if (aIndex == nsINavBookmarksService::DEFAULT_INDEX || |
|
591 aIndex >= folderCount) { |
|
592 index = folderCount; |
|
593 } |
|
594 else { |
|
595 index = aIndex; |
|
596 // Create space for the insertion. |
|
597 rv = AdjustIndices(aFolder, index, INT32_MAX, 1); |
|
598 NS_ENSURE_SUCCESS(rv, rv); |
|
599 } |
|
600 |
|
601 *aNewBookmarkId = -1; |
|
602 PRTime dateAdded = PR_Now(); |
|
603 nsAutoCString guid(aGUID); |
|
604 nsCString title; |
|
605 TruncateTitle(aTitle, title); |
|
606 |
|
607 rv = InsertBookmarkInDB(placeId, BOOKMARK, aFolder, index, title, dateAdded, |
|
608 0, folderGuid, grandParentId, aURI, |
|
609 aNewBookmarkId, guid); |
|
610 NS_ENSURE_SUCCESS(rv, rv); |
|
611 |
|
612 // If not a tag, recalculate frecency for this entry, since it changed. |
|
613 if (grandParentId != mTagsRoot) { |
|
614 rv = history->UpdateFrecency(placeId); |
|
615 NS_ENSURE_SUCCESS(rv, rv); |
|
616 } |
|
617 |
|
618 rv = transaction.Commit(); |
|
619 NS_ENSURE_SUCCESS(rv, rv); |
|
620 |
|
621 NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers, |
|
622 nsINavBookmarkObserver, |
|
623 OnItemAdded(*aNewBookmarkId, aFolder, index, TYPE_BOOKMARK, |
|
624 aURI, title, dateAdded, guid, folderGuid)); |
|
625 |
|
626 // If the bookmark has been added to a tag container, notify all |
|
627 // bookmark-folder result nodes which contain a bookmark for the new |
|
628 // bookmark's url. |
|
629 if (grandParentId == mTagsRoot) { |
|
630 // Notify a tags change to all bookmarks for this URI. |
|
631 nsTArray<BookmarkData> bookmarks; |
|
632 rv = GetBookmarksForURI(aURI, bookmarks); |
|
633 NS_ENSURE_SUCCESS(rv, rv); |
|
634 |
|
635 for (uint32_t i = 0; i < bookmarks.Length(); ++i) { |
|
636 // Check that bookmarks doesn't include the current tag itemId. |
|
637 MOZ_ASSERT(bookmarks[i].id != *aNewBookmarkId); |
|
638 |
|
639 NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers, |
|
640 nsINavBookmarkObserver, |
|
641 OnItemChanged(bookmarks[i].id, |
|
642 NS_LITERAL_CSTRING("tags"), |
|
643 false, |
|
644 EmptyCString(), |
|
645 bookmarks[i].lastModified, |
|
646 TYPE_BOOKMARK, |
|
647 bookmarks[i].parentId, |
|
648 bookmarks[i].guid, |
|
649 bookmarks[i].parentGuid)); |
|
650 } |
|
651 } |
|
652 |
|
653 return NS_OK; |
|
654 } |
|
655 |
|
656 |
|
657 NS_IMETHODIMP |
|
658 nsNavBookmarks::RemoveItem(int64_t aItemId) |
|
659 { |
|
660 PROFILER_LABEL("bookmarks", "RemoveItem"); |
|
661 NS_ENSURE_ARG(!IsRoot(aItemId)); |
|
662 |
|
663 BookmarkData bookmark; |
|
664 nsresult rv = FetchItemInfo(aItemId, bookmark); |
|
665 NS_ENSURE_SUCCESS(rv, rv); |
|
666 |
|
667 mozStorageTransaction transaction(mDB->MainConn(), false); |
|
668 |
|
669 // First, if not a tag, remove item annotations. |
|
670 if (bookmark.parentId != mTagsRoot && |
|
671 bookmark.grandParentId != mTagsRoot) { |
|
672 nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService(); |
|
673 NS_ENSURE_TRUE(annosvc, NS_ERROR_OUT_OF_MEMORY); |
|
674 rv = annosvc->RemoveItemAnnotations(bookmark.id); |
|
675 NS_ENSURE_SUCCESS(rv, rv); |
|
676 } |
|
677 |
|
678 if (bookmark.type == TYPE_FOLDER) { |
|
679 // Remove all of the folder's children. |
|
680 rv = RemoveFolderChildren(bookmark.id); |
|
681 NS_ENSURE_SUCCESS(rv, rv); |
|
682 } |
|
683 |
|
684 BEGIN_CRITICAL_BOOKMARK_CACHE_SECTION(bookmark.id); |
|
685 |
|
686 nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement( |
|
687 "DELETE FROM moz_bookmarks WHERE id = :item_id" |
|
688 ); |
|
689 NS_ENSURE_STATE(stmt); |
|
690 mozStorageStatementScoper scoper(stmt); |
|
691 |
|
692 rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), bookmark.id); |
|
693 NS_ENSURE_SUCCESS(rv, rv); |
|
694 rv = stmt->Execute(); |
|
695 NS_ENSURE_SUCCESS(rv, rv); |
|
696 |
|
697 // Fix indices in the parent. |
|
698 if (bookmark.position != DEFAULT_INDEX) { |
|
699 rv = AdjustIndices(bookmark.parentId, |
|
700 bookmark.position + 1, INT32_MAX, -1); |
|
701 NS_ENSURE_SUCCESS(rv, rv); |
|
702 } |
|
703 |
|
704 bookmark.lastModified = PR_Now(); |
|
705 rv = SetItemDateInternal(LAST_MODIFIED, bookmark.parentId, |
|
706 bookmark.lastModified); |
|
707 NS_ENSURE_SUCCESS(rv, rv); |
|
708 |
|
709 rv = transaction.Commit(); |
|
710 NS_ENSURE_SUCCESS(rv, rv); |
|
711 |
|
712 END_CRITICAL_BOOKMARK_CACHE_SECTION(bookmark.id); |
|
713 |
|
714 nsCOMPtr<nsIURI> uri; |
|
715 if (bookmark.type == TYPE_BOOKMARK) { |
|
716 // If not a tag, recalculate frecency for this entry, since it changed. |
|
717 if (bookmark.grandParentId != mTagsRoot) { |
|
718 nsNavHistory* history = nsNavHistory::GetHistoryService(); |
|
719 NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY); |
|
720 rv = history->UpdateFrecency(bookmark.placeId); |
|
721 NS_ENSURE_SUCCESS(rv, rv); |
|
722 } |
|
723 |
|
724 rv = UpdateKeywordsHashForRemovedBookmark(aItemId); |
|
725 NS_ENSURE_SUCCESS(rv, rv); |
|
726 |
|
727 // A broken url should not interrupt the removal process. |
|
728 (void)NS_NewURI(getter_AddRefs(uri), bookmark.url); |
|
729 } |
|
730 |
|
731 NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers, |
|
732 nsINavBookmarkObserver, |
|
733 OnItemRemoved(bookmark.id, |
|
734 bookmark.parentId, |
|
735 bookmark.position, |
|
736 bookmark.type, |
|
737 uri, |
|
738 bookmark.guid, |
|
739 bookmark.parentGuid)); |
|
740 |
|
741 if (bookmark.type == TYPE_BOOKMARK && bookmark.grandParentId == mTagsRoot && |
|
742 uri) { |
|
743 // If the removed bookmark was child of a tag container, notify a tags |
|
744 // change to all bookmarks for this URI. |
|
745 nsTArray<BookmarkData> bookmarks; |
|
746 rv = GetBookmarksForURI(uri, bookmarks); |
|
747 NS_ENSURE_SUCCESS(rv, rv); |
|
748 |
|
749 for (uint32_t i = 0; i < bookmarks.Length(); ++i) { |
|
750 NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers, |
|
751 nsINavBookmarkObserver, |
|
752 OnItemChanged(bookmarks[i].id, |
|
753 NS_LITERAL_CSTRING("tags"), |
|
754 false, |
|
755 EmptyCString(), |
|
756 bookmarks[i].lastModified, |
|
757 TYPE_BOOKMARK, |
|
758 bookmarks[i].parentId, |
|
759 bookmarks[i].guid, |
|
760 bookmarks[i].parentGuid)); |
|
761 } |
|
762 |
|
763 } |
|
764 |
|
765 return NS_OK; |
|
766 } |
|
767 |
|
768 |
|
769 NS_IMETHODIMP |
|
770 nsNavBookmarks::CreateFolder(int64_t aParent, const nsACString& aName, |
|
771 int32_t aIndex, const nsACString& aGUID, |
|
772 int64_t* aNewFolder) |
|
773 { |
|
774 // NOTE: aParent can be null for root creation, so not checked |
|
775 NS_ENSURE_ARG_POINTER(aNewFolder); |
|
776 |
|
777 if (!aGUID.IsEmpty() && !IsValidGUID(aGUID)) |
|
778 return NS_ERROR_INVALID_ARG; |
|
779 |
|
780 // CreateContainerWithID returns the index of the new folder, but that's not |
|
781 // used here. To avoid any risk of corrupting data should this function |
|
782 // be changed, we'll use a local variable to hold it. The true argument |
|
783 // will cause notifications to be sent to bookmark observers. |
|
784 int32_t localIndex = aIndex; |
|
785 nsresult rv = CreateContainerWithID(-1, aParent, aName, true, &localIndex, |
|
786 aGUID, aNewFolder); |
|
787 NS_ENSURE_SUCCESS(rv, rv); |
|
788 return NS_OK; |
|
789 } |
|
790 |
|
791 NS_IMETHODIMP |
|
792 nsNavBookmarks::GetFolderReadonly(int64_t aFolder, bool* aResult) |
|
793 { |
|
794 NS_ENSURE_ARG_MIN(aFolder, 1); |
|
795 NS_ENSURE_ARG_POINTER(aResult); |
|
796 |
|
797 nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService(); |
|
798 NS_ENSURE_TRUE(annosvc, NS_ERROR_OUT_OF_MEMORY); |
|
799 nsresult rv = annosvc->ItemHasAnnotation(aFolder, READ_ONLY_ANNO, aResult); |
|
800 NS_ENSURE_SUCCESS(rv, rv); |
|
801 return NS_OK; |
|
802 } |
|
803 |
|
804 |
|
805 NS_IMETHODIMP |
|
806 nsNavBookmarks::SetFolderReadonly(int64_t aFolder, bool aReadOnly) |
|
807 { |
|
808 NS_ENSURE_ARG_MIN(aFolder, 1); |
|
809 |
|
810 nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService(); |
|
811 NS_ENSURE_TRUE(annosvc, NS_ERROR_OUT_OF_MEMORY); |
|
812 nsresult rv; |
|
813 if (aReadOnly) { |
|
814 rv = annosvc->SetItemAnnotationInt32(aFolder, READ_ONLY_ANNO, 1, 0, |
|
815 nsAnnotationService::EXPIRE_NEVER); |
|
816 NS_ENSURE_SUCCESS(rv, rv); |
|
817 } |
|
818 else { |
|
819 bool hasAnno; |
|
820 rv = annosvc->ItemHasAnnotation(aFolder, READ_ONLY_ANNO, &hasAnno); |
|
821 NS_ENSURE_SUCCESS(rv, rv); |
|
822 if (hasAnno) { |
|
823 rv = annosvc->RemoveItemAnnotation(aFolder, READ_ONLY_ANNO); |
|
824 NS_ENSURE_SUCCESS(rv, rv); |
|
825 } |
|
826 } |
|
827 return NS_OK; |
|
828 } |
|
829 |
|
830 |
|
831 nsresult |
|
832 nsNavBookmarks::CreateContainerWithID(int64_t aItemId, |
|
833 int64_t aParent, |
|
834 const nsACString& aTitle, |
|
835 bool aIsBookmarkFolder, |
|
836 int32_t* aIndex, |
|
837 const nsACString& aGUID, |
|
838 int64_t* aNewFolder) |
|
839 { |
|
840 NS_ENSURE_ARG_MIN(*aIndex, nsINavBookmarksService::DEFAULT_INDEX); |
|
841 |
|
842 // Get the correct index for insertion. This also ensures the parent exists. |
|
843 int32_t index, folderCount; |
|
844 int64_t grandParentId; |
|
845 nsAutoCString folderGuid; |
|
846 nsresult rv = FetchFolderInfo(aParent, &folderCount, folderGuid, &grandParentId); |
|
847 NS_ENSURE_SUCCESS(rv, rv); |
|
848 |
|
849 mozStorageTransaction transaction(mDB->MainConn(), false); |
|
850 |
|
851 if (*aIndex == nsINavBookmarksService::DEFAULT_INDEX || |
|
852 *aIndex >= folderCount) { |
|
853 index = folderCount; |
|
854 } else { |
|
855 index = *aIndex; |
|
856 // Create space for the insertion. |
|
857 rv = AdjustIndices(aParent, index, INT32_MAX, 1); |
|
858 NS_ENSURE_SUCCESS(rv, rv); |
|
859 } |
|
860 |
|
861 *aNewFolder = aItemId; |
|
862 PRTime dateAdded = PR_Now(); |
|
863 nsAutoCString guid(aGUID); |
|
864 nsCString title; |
|
865 TruncateTitle(aTitle, title); |
|
866 |
|
867 rv = InsertBookmarkInDB(-1, FOLDER, aParent, index, |
|
868 title, dateAdded, 0, folderGuid, grandParentId, |
|
869 nullptr, aNewFolder, guid); |
|
870 NS_ENSURE_SUCCESS(rv, rv); |
|
871 |
|
872 rv = transaction.Commit(); |
|
873 NS_ENSURE_SUCCESS(rv, rv); |
|
874 |
|
875 NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers, |
|
876 nsINavBookmarkObserver, |
|
877 OnItemAdded(*aNewFolder, aParent, index, FOLDER, |
|
878 nullptr, title, dateAdded, guid, folderGuid)); |
|
879 |
|
880 *aIndex = index; |
|
881 return NS_OK; |
|
882 } |
|
883 |
|
884 |
|
885 NS_IMETHODIMP |
|
886 nsNavBookmarks::InsertSeparator(int64_t aParent, |
|
887 int32_t aIndex, |
|
888 const nsACString& aGUID, |
|
889 int64_t* aNewItemId) |
|
890 { |
|
891 NS_ENSURE_ARG_MIN(aParent, 1); |
|
892 NS_ENSURE_ARG_MIN(aIndex, nsINavBookmarksService::DEFAULT_INDEX); |
|
893 NS_ENSURE_ARG_POINTER(aNewItemId); |
|
894 |
|
895 if (!aGUID.IsEmpty() && !IsValidGUID(aGUID)) |
|
896 return NS_ERROR_INVALID_ARG; |
|
897 |
|
898 // Get the correct index for insertion. This also ensures the parent exists. |
|
899 int32_t index, folderCount; |
|
900 int64_t grandParentId; |
|
901 nsAutoCString folderGuid; |
|
902 nsresult rv = FetchFolderInfo(aParent, &folderCount, folderGuid, &grandParentId); |
|
903 NS_ENSURE_SUCCESS(rv, rv); |
|
904 |
|
905 mozStorageTransaction transaction(mDB->MainConn(), false); |
|
906 |
|
907 if (aIndex == nsINavBookmarksService::DEFAULT_INDEX || |
|
908 aIndex >= folderCount) { |
|
909 index = folderCount; |
|
910 } |
|
911 else { |
|
912 index = aIndex; |
|
913 // Create space for the insertion. |
|
914 rv = AdjustIndices(aParent, index, INT32_MAX, 1); |
|
915 NS_ENSURE_SUCCESS(rv, rv); |
|
916 } |
|
917 |
|
918 *aNewItemId = -1; |
|
919 // Set a NULL title rather than an empty string. |
|
920 nsCString voidString; |
|
921 voidString.SetIsVoid(true); |
|
922 nsAutoCString guid(aGUID); |
|
923 PRTime dateAdded = PR_Now(); |
|
924 rv = InsertBookmarkInDB(-1, SEPARATOR, aParent, index, voidString, dateAdded, |
|
925 0, folderGuid, grandParentId, nullptr, |
|
926 aNewItemId, guid); |
|
927 NS_ENSURE_SUCCESS(rv, rv); |
|
928 |
|
929 rv = transaction.Commit(); |
|
930 NS_ENSURE_SUCCESS(rv, rv); |
|
931 |
|
932 NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers, |
|
933 nsINavBookmarkObserver, |
|
934 OnItemAdded(*aNewItemId, aParent, index, TYPE_SEPARATOR, |
|
935 nullptr, voidString, dateAdded, guid, folderGuid)); |
|
936 |
|
937 return NS_OK; |
|
938 } |
|
939 |
|
940 |
|
941 nsresult |
|
942 nsNavBookmarks::GetLastChildId(int64_t aFolderId, int64_t* aItemId) |
|
943 { |
|
944 NS_ASSERTION(aFolderId > 0, "Invalid folder id"); |
|
945 *aItemId = -1; |
|
946 |
|
947 nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement( |
|
948 "SELECT id FROM moz_bookmarks WHERE parent = :parent " |
|
949 "ORDER BY position DESC LIMIT 1" |
|
950 ); |
|
951 NS_ENSURE_STATE(stmt); |
|
952 mozStorageStatementScoper scoper(stmt); |
|
953 |
|
954 nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aFolderId); |
|
955 NS_ENSURE_SUCCESS(rv, rv); |
|
956 bool found; |
|
957 rv = stmt->ExecuteStep(&found); |
|
958 NS_ENSURE_SUCCESS(rv, rv); |
|
959 if (found) { |
|
960 rv = stmt->GetInt64(0, aItemId); |
|
961 NS_ENSURE_SUCCESS(rv, rv); |
|
962 } |
|
963 |
|
964 return NS_OK; |
|
965 } |
|
966 |
|
967 |
|
968 NS_IMETHODIMP |
|
969 nsNavBookmarks::GetIdForItemAt(int64_t aFolder, |
|
970 int32_t aIndex, |
|
971 int64_t* aItemId) |
|
972 { |
|
973 NS_ENSURE_ARG_MIN(aFolder, 1); |
|
974 NS_ENSURE_ARG_POINTER(aItemId); |
|
975 |
|
976 *aItemId = -1; |
|
977 |
|
978 nsresult rv; |
|
979 if (aIndex == nsINavBookmarksService::DEFAULT_INDEX) { |
|
980 // Get last item within aFolder. |
|
981 rv = GetLastChildId(aFolder, aItemId); |
|
982 NS_ENSURE_SUCCESS(rv, rv); |
|
983 } |
|
984 else { |
|
985 // Get the item in aFolder with position aIndex. |
|
986 nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement( |
|
987 "SELECT id, fk, type FROM moz_bookmarks " |
|
988 "WHERE parent = :parent AND position = :item_index" |
|
989 ); |
|
990 NS_ENSURE_STATE(stmt); |
|
991 mozStorageStatementScoper scoper(stmt); |
|
992 |
|
993 rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aFolder); |
|
994 NS_ENSURE_SUCCESS(rv, rv); |
|
995 rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("item_index"), aIndex); |
|
996 NS_ENSURE_SUCCESS(rv, rv); |
|
997 |
|
998 bool found; |
|
999 rv = stmt->ExecuteStep(&found); |
|
1000 NS_ENSURE_SUCCESS(rv, rv); |
|
1001 if (found) { |
|
1002 rv = stmt->GetInt64(0, aItemId); |
|
1003 NS_ENSURE_SUCCESS(rv, rv); |
|
1004 } |
|
1005 } |
|
1006 return NS_OK; |
|
1007 } |
|
1008 |
|
1009 NS_IMPL_ISUPPORTS(nsNavBookmarks::RemoveFolderTransaction, nsITransaction) |
|
1010 |
|
1011 NS_IMETHODIMP |
|
1012 nsNavBookmarks::GetRemoveFolderTransaction(int64_t aFolderId, nsITransaction** aResult) |
|
1013 { |
|
1014 NS_ENSURE_ARG_MIN(aFolderId, 1); |
|
1015 NS_ENSURE_ARG_POINTER(aResult); |
|
1016 |
|
1017 // Create and initialize a RemoveFolderTransaction object that can be used to |
|
1018 // recreate the folder safely later. |
|
1019 |
|
1020 RemoveFolderTransaction* rft = |
|
1021 new RemoveFolderTransaction(aFolderId); |
|
1022 if (!rft) |
|
1023 return NS_ERROR_OUT_OF_MEMORY; |
|
1024 |
|
1025 NS_ADDREF(*aResult = rft); |
|
1026 return NS_OK; |
|
1027 } |
|
1028 |
|
1029 |
|
1030 nsresult |
|
1031 nsNavBookmarks::GetDescendantFolders(int64_t aFolderId, |
|
1032 nsTArray<int64_t>& aDescendantFoldersArray) { |
|
1033 nsresult rv; |
|
1034 // New descendant folders will be added from this index on. |
|
1035 uint32_t startIndex = aDescendantFoldersArray.Length(); |
|
1036 { |
|
1037 nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement( |
|
1038 "SELECT id " |
|
1039 "FROM moz_bookmarks " |
|
1040 "WHERE parent = :parent " |
|
1041 "AND type = :item_type " |
|
1042 ); |
|
1043 NS_ENSURE_STATE(stmt); |
|
1044 mozStorageStatementScoper scoper(stmt); |
|
1045 |
|
1046 rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aFolderId); |
|
1047 NS_ENSURE_SUCCESS(rv, rv); |
|
1048 rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("item_type"), TYPE_FOLDER); |
|
1049 NS_ENSURE_SUCCESS(rv, rv); |
|
1050 |
|
1051 bool hasMore = false; |
|
1052 while (NS_SUCCEEDED(stmt->ExecuteStep(&hasMore)) && hasMore) { |
|
1053 int64_t itemId; |
|
1054 rv = stmt->GetInt64(0, &itemId); |
|
1055 NS_ENSURE_SUCCESS(rv, rv); |
|
1056 aDescendantFoldersArray.AppendElement(itemId); |
|
1057 } |
|
1058 } |
|
1059 |
|
1060 // Recursively call GetDescendantFolders for added folders. |
|
1061 // We start at startIndex since previous folders are checked |
|
1062 // by previous calls to this method. |
|
1063 uint32_t childCount = aDescendantFoldersArray.Length(); |
|
1064 for (uint32_t i = startIndex; i < childCount; ++i) { |
|
1065 GetDescendantFolders(aDescendantFoldersArray[i], aDescendantFoldersArray); |
|
1066 } |
|
1067 |
|
1068 return NS_OK; |
|
1069 } |
|
1070 |
|
1071 |
|
1072 nsresult |
|
1073 nsNavBookmarks::GetDescendantChildren(int64_t aFolderId, |
|
1074 const nsACString& aFolderGuid, |
|
1075 int64_t aGrandParentId, |
|
1076 nsTArray<BookmarkData>& aFolderChildrenArray) { |
|
1077 // New children will be added from this index on. |
|
1078 uint32_t startIndex = aFolderChildrenArray.Length(); |
|
1079 nsresult rv; |
|
1080 { |
|
1081 // Collect children informations. |
|
1082 // Select all children of a given folder, sorted by position. |
|
1083 // This is a LEFT JOIN because not all bookmarks types have a place. |
|
1084 // We construct a result where the first columns exactly match |
|
1085 // kGetInfoIndex_* order, and additionally contains columns for position, |
|
1086 // item_child, and folder_child from moz_bookmarks. |
|
1087 nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement( |
|
1088 "SELECT h.id, h.url, IFNULL(b.title, h.title), h.rev_host, h.visit_count, " |
|
1089 "h.last_visit_date, f.url, b.id, b.dateAdded, b.lastModified, " |
|
1090 "b.parent, null, h.frecency, h.hidden, h.guid, b.guid, " |
|
1091 "b.position, b.type, b.fk " |
|
1092 "FROM moz_bookmarks b " |
|
1093 "LEFT JOIN moz_places h ON b.fk = h.id " |
|
1094 "LEFT JOIN moz_favicons f ON h.favicon_id = f.id " |
|
1095 "WHERE b.parent = :parent " |
|
1096 "ORDER BY b.position ASC" |
|
1097 ); |
|
1098 NS_ENSURE_STATE(stmt); |
|
1099 mozStorageStatementScoper scoper(stmt); |
|
1100 |
|
1101 rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aFolderId); |
|
1102 NS_ENSURE_SUCCESS(rv, rv); |
|
1103 |
|
1104 bool hasMore; |
|
1105 while (NS_SUCCEEDED(stmt->ExecuteStep(&hasMore)) && hasMore) { |
|
1106 BookmarkData child; |
|
1107 rv = stmt->GetInt64(nsNavHistory::kGetInfoIndex_ItemId, &child.id); |
|
1108 NS_ENSURE_SUCCESS(rv, rv); |
|
1109 child.parentId = aFolderId; |
|
1110 child.grandParentId = aGrandParentId; |
|
1111 child.parentGuid = aFolderGuid; |
|
1112 rv = stmt->GetInt32(kGetChildrenIndex_Type, &child.type); |
|
1113 NS_ENSURE_SUCCESS(rv, rv); |
|
1114 rv = stmt->GetInt64(kGetChildrenIndex_PlaceID, &child.placeId); |
|
1115 NS_ENSURE_SUCCESS(rv, rv); |
|
1116 rv = stmt->GetInt32(kGetChildrenIndex_Position, &child.position); |
|
1117 NS_ENSURE_SUCCESS(rv, rv); |
|
1118 rv = stmt->GetUTF8String(kGetChildrenIndex_Guid, child.guid); |
|
1119 NS_ENSURE_SUCCESS(rv, rv); |
|
1120 |
|
1121 if (child.type == TYPE_BOOKMARK) { |
|
1122 rv = stmt->GetUTF8String(nsNavHistory::kGetInfoIndex_URL, child.url); |
|
1123 NS_ENSURE_SUCCESS(rv, rv); |
|
1124 } |
|
1125 |
|
1126 // Append item to children's array. |
|
1127 aFolderChildrenArray.AppendElement(child); |
|
1128 } |
|
1129 } |
|
1130 |
|
1131 // Recursively call GetDescendantChildren for added folders. |
|
1132 // We start at startIndex since previous folders are checked |
|
1133 // by previous calls to this method. |
|
1134 uint32_t childCount = aFolderChildrenArray.Length(); |
|
1135 for (uint32_t i = startIndex; i < childCount; ++i) { |
|
1136 if (aFolderChildrenArray[i].type == TYPE_FOLDER) { |
|
1137 // nsTarray assumes that all children can be memmove()d, thus we can't |
|
1138 // just pass aFolderChildrenArray[i].guid to a method that will change |
|
1139 // the array itself. Otherwise, since it's passed by reference, after a |
|
1140 // memmove() it could point to garbage and cause intermittent crashes. |
|
1141 nsCString guid = aFolderChildrenArray[i].guid; |
|
1142 GetDescendantChildren(aFolderChildrenArray[i].id, |
|
1143 guid, |
|
1144 aFolderId, |
|
1145 aFolderChildrenArray); |
|
1146 } |
|
1147 } |
|
1148 |
|
1149 return NS_OK; |
|
1150 } |
|
1151 |
|
1152 |
|
1153 NS_IMETHODIMP |
|
1154 nsNavBookmarks::RemoveFolderChildren(int64_t aFolderId) |
|
1155 { |
|
1156 PROFILER_LABEL("bookmarks", "RemoveFolderChilder"); |
|
1157 NS_ENSURE_ARG_MIN(aFolderId, 1); |
|
1158 NS_ENSURE_ARG(aFolderId != mRoot); |
|
1159 |
|
1160 BookmarkData folder; |
|
1161 nsresult rv = FetchItemInfo(aFolderId, folder); |
|
1162 NS_ENSURE_SUCCESS(rv, rv); |
|
1163 NS_ENSURE_ARG(folder.type == TYPE_FOLDER); |
|
1164 |
|
1165 // Fill folder children array recursively. |
|
1166 nsTArray<BookmarkData> folderChildrenArray; |
|
1167 rv = GetDescendantChildren(folder.id, folder.guid, folder.parentId, |
|
1168 folderChildrenArray); |
|
1169 NS_ENSURE_SUCCESS(rv, rv); |
|
1170 |
|
1171 // Build a string of folders whose children will be removed. |
|
1172 nsCString foldersToRemove; |
|
1173 for (uint32_t i = 0; i < folderChildrenArray.Length(); ++i) { |
|
1174 BookmarkData& child = folderChildrenArray[i]; |
|
1175 |
|
1176 if (child.type == TYPE_FOLDER) { |
|
1177 foldersToRemove.AppendLiteral(","); |
|
1178 foldersToRemove.AppendInt(child.id); |
|
1179 } |
|
1180 |
|
1181 BEGIN_CRITICAL_BOOKMARK_CACHE_SECTION(child.id); |
|
1182 } |
|
1183 |
|
1184 // Delete items from the database now. |
|
1185 mozStorageTransaction transaction(mDB->MainConn(), false); |
|
1186 |
|
1187 nsCOMPtr<mozIStorageStatement> deleteStatement = mDB->GetStatement( |
|
1188 NS_LITERAL_CSTRING( |
|
1189 "DELETE FROM moz_bookmarks " |
|
1190 "WHERE parent IN (:parent") + foldersToRemove + NS_LITERAL_CSTRING(")") |
|
1191 ); |
|
1192 NS_ENSURE_STATE(deleteStatement); |
|
1193 mozStorageStatementScoper deleteStatementScoper(deleteStatement); |
|
1194 |
|
1195 rv = deleteStatement->BindInt64ByName(NS_LITERAL_CSTRING("parent"), folder.id); |
|
1196 NS_ENSURE_SUCCESS(rv, rv); |
|
1197 rv = deleteStatement->Execute(); |
|
1198 NS_ENSURE_SUCCESS(rv, rv); |
|
1199 |
|
1200 // Clean up orphan items annotations. |
|
1201 rv = mDB->MainConn()->ExecuteSimpleSQL( |
|
1202 NS_LITERAL_CSTRING( |
|
1203 "DELETE FROM moz_items_annos " |
|
1204 "WHERE id IN (" |
|
1205 "SELECT a.id from moz_items_annos a " |
|
1206 "LEFT JOIN moz_bookmarks b ON a.item_id = b.id " |
|
1207 "WHERE b.id ISNULL)")); |
|
1208 NS_ENSURE_SUCCESS(rv, rv); |
|
1209 |
|
1210 // Set the lastModified date. |
|
1211 rv = SetItemDateInternal(LAST_MODIFIED, folder.id, PR_Now()); |
|
1212 NS_ENSURE_SUCCESS(rv, rv); |
|
1213 |
|
1214 for (uint32_t i = 0; i < folderChildrenArray.Length(); i++) { |
|
1215 BookmarkData& child = folderChildrenArray[i]; |
|
1216 if (child.type == TYPE_BOOKMARK) { |
|
1217 // If not a tag, recalculate frecency for this entry, since it changed. |
|
1218 if (child.grandParentId != mTagsRoot) { |
|
1219 nsNavHistory* history = nsNavHistory::GetHistoryService(); |
|
1220 NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY); |
|
1221 rv = history->UpdateFrecency(child.placeId); |
|
1222 NS_ENSURE_SUCCESS(rv, rv); |
|
1223 } |
|
1224 |
|
1225 rv = UpdateKeywordsHashForRemovedBookmark(child.id); |
|
1226 NS_ENSURE_SUCCESS(rv, rv); |
|
1227 } |
|
1228 END_CRITICAL_BOOKMARK_CACHE_SECTION(child.id); |
|
1229 } |
|
1230 |
|
1231 rv = transaction.Commit(); |
|
1232 NS_ENSURE_SUCCESS(rv, rv); |
|
1233 |
|
1234 // Call observers in reverse order to serve children before their parent. |
|
1235 for (int32_t i = folderChildrenArray.Length() - 1; i >= 0; --i) { |
|
1236 BookmarkData& child = folderChildrenArray[i]; |
|
1237 nsCOMPtr<nsIURI> uri; |
|
1238 if (child.type == TYPE_BOOKMARK) { |
|
1239 // A broken url should not interrupt the removal process. |
|
1240 (void)NS_NewURI(getter_AddRefs(uri), child.url); |
|
1241 } |
|
1242 |
|
1243 NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers, |
|
1244 nsINavBookmarkObserver, |
|
1245 OnItemRemoved(child.id, |
|
1246 child.parentId, |
|
1247 child.position, |
|
1248 child.type, |
|
1249 uri, |
|
1250 child.guid, |
|
1251 child.parentGuid)); |
|
1252 |
|
1253 if (child.type == TYPE_BOOKMARK && child.grandParentId == mTagsRoot && |
|
1254 uri) { |
|
1255 // If the removed bookmark was a child of a tag container, notify all |
|
1256 // bookmark-folder result nodes which contain a bookmark for the removed |
|
1257 // bookmark's url. |
|
1258 nsTArray<BookmarkData> bookmarks; |
|
1259 rv = GetBookmarksForURI(uri, bookmarks); |
|
1260 NS_ENSURE_SUCCESS(rv, rv); |
|
1261 |
|
1262 for (uint32_t i = 0; i < bookmarks.Length(); ++i) { |
|
1263 NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers, |
|
1264 nsINavBookmarkObserver, |
|
1265 OnItemChanged(bookmarks[i].id, |
|
1266 NS_LITERAL_CSTRING("tags"), |
|
1267 false, |
|
1268 EmptyCString(), |
|
1269 bookmarks[i].lastModified, |
|
1270 TYPE_BOOKMARK, |
|
1271 bookmarks[i].parentId, |
|
1272 bookmarks[i].guid, |
|
1273 bookmarks[i].parentGuid)); |
|
1274 } |
|
1275 } |
|
1276 } |
|
1277 |
|
1278 return NS_OK; |
|
1279 } |
|
1280 |
|
1281 |
|
1282 NS_IMETHODIMP |
|
1283 nsNavBookmarks::MoveItem(int64_t aItemId, int64_t aNewParent, int32_t aIndex) |
|
1284 { |
|
1285 NS_ENSURE_ARG(!IsRoot(aItemId)); |
|
1286 NS_ENSURE_ARG_MIN(aItemId, 1); |
|
1287 NS_ENSURE_ARG_MIN(aNewParent, 1); |
|
1288 // -1 is append, but no other negative number is allowed. |
|
1289 NS_ENSURE_ARG_MIN(aIndex, -1); |
|
1290 // Disallow making an item its own parent. |
|
1291 NS_ENSURE_ARG(aItemId != aNewParent); |
|
1292 |
|
1293 mozStorageTransaction transaction(mDB->MainConn(), false); |
|
1294 |
|
1295 BookmarkData bookmark; |
|
1296 nsresult rv = FetchItemInfo(aItemId, bookmark); |
|
1297 NS_ENSURE_SUCCESS(rv, rv); |
|
1298 |
|
1299 // if parent and index are the same, nothing to do |
|
1300 if (bookmark.parentId == aNewParent && bookmark.position == aIndex) |
|
1301 return NS_OK; |
|
1302 |
|
1303 // Make sure aNewParent is not aFolder or a subfolder of aFolder. |
|
1304 // TODO: make this performant, maybe with a nested tree (bug 408991). |
|
1305 if (bookmark.type == TYPE_FOLDER) { |
|
1306 int64_t ancestorId = aNewParent; |
|
1307 |
|
1308 while (ancestorId) { |
|
1309 if (ancestorId == bookmark.id) { |
|
1310 return NS_ERROR_INVALID_ARG; |
|
1311 } |
|
1312 rv = GetFolderIdForItem(ancestorId, &ancestorId); |
|
1313 if (NS_FAILED(rv)) { |
|
1314 break; |
|
1315 } |
|
1316 } |
|
1317 } |
|
1318 |
|
1319 // calculate new index |
|
1320 int32_t newIndex, folderCount; |
|
1321 int64_t grandParentId; |
|
1322 nsAutoCString newParentGuid; |
|
1323 rv = FetchFolderInfo(aNewParent, &folderCount, newParentGuid, &grandParentId); |
|
1324 NS_ENSURE_SUCCESS(rv, rv); |
|
1325 if (aIndex == nsINavBookmarksService::DEFAULT_INDEX || |
|
1326 aIndex >= folderCount) { |
|
1327 newIndex = folderCount; |
|
1328 // If the parent remains the same, then the folder is really being moved |
|
1329 // to count - 1 (since it's being removed from the old position) |
|
1330 if (bookmark.parentId == aNewParent) { |
|
1331 --newIndex; |
|
1332 } |
|
1333 } else { |
|
1334 newIndex = aIndex; |
|
1335 |
|
1336 if (bookmark.parentId == aNewParent && newIndex > bookmark.position) { |
|
1337 // when an item is being moved lower in the same folder, the new index |
|
1338 // refers to the index before it was removed. Removal causes everything |
|
1339 // to shift up. |
|
1340 --newIndex; |
|
1341 } |
|
1342 } |
|
1343 |
|
1344 // this is like the previous check, except this covers if |
|
1345 // the specified index was -1 (append), and the calculated |
|
1346 // new index is the same as the existing index |
|
1347 if (aNewParent == bookmark.parentId && newIndex == bookmark.position) { |
|
1348 // Nothing to do! |
|
1349 return NS_OK; |
|
1350 } |
|
1351 |
|
1352 // adjust indices to account for the move |
|
1353 // do this before we update the parent/index fields |
|
1354 // or we'll re-adjust the index for the item we are moving |
|
1355 if (bookmark.parentId == aNewParent) { |
|
1356 // We can optimize the updates if moving within the same container. |
|
1357 // We only shift the items between the old and new positions, since the |
|
1358 // insertion will offset the deletion. |
|
1359 if (bookmark.position > newIndex) { |
|
1360 rv = AdjustIndices(bookmark.parentId, newIndex, bookmark.position - 1, 1); |
|
1361 } |
|
1362 else { |
|
1363 rv = AdjustIndices(bookmark.parentId, bookmark.position + 1, newIndex, -1); |
|
1364 } |
|
1365 NS_ENSURE_SUCCESS(rv, rv); |
|
1366 } |
|
1367 else { |
|
1368 // We're moving between containers, so this happens in two steps. |
|
1369 // First, fill the hole from the removal from the old parent. |
|
1370 rv = AdjustIndices(bookmark.parentId, bookmark.position + 1, INT32_MAX, -1); |
|
1371 NS_ENSURE_SUCCESS(rv, rv); |
|
1372 // Now, make room in the new parent for the insertion. |
|
1373 rv = AdjustIndices(aNewParent, newIndex, INT32_MAX, 1); |
|
1374 NS_ENSURE_SUCCESS(rv, rv); |
|
1375 } |
|
1376 |
|
1377 BEGIN_CRITICAL_BOOKMARK_CACHE_SECTION(bookmark.id); |
|
1378 |
|
1379 { |
|
1380 // Update parent and position. |
|
1381 nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement( |
|
1382 "UPDATE moz_bookmarks SET parent = :parent, position = :item_index " |
|
1383 "WHERE id = :item_id " |
|
1384 ); |
|
1385 NS_ENSURE_STATE(stmt); |
|
1386 mozStorageStatementScoper scoper(stmt); |
|
1387 |
|
1388 rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aNewParent); |
|
1389 NS_ENSURE_SUCCESS(rv, rv); |
|
1390 rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("item_index"), newIndex); |
|
1391 NS_ENSURE_SUCCESS(rv, rv); |
|
1392 rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), bookmark.id); |
|
1393 NS_ENSURE_SUCCESS(rv, rv); |
|
1394 rv = stmt->Execute(); |
|
1395 NS_ENSURE_SUCCESS(rv, rv); |
|
1396 } |
|
1397 |
|
1398 PRTime now = PR_Now(); |
|
1399 rv = SetItemDateInternal(LAST_MODIFIED, bookmark.parentId, now); |
|
1400 NS_ENSURE_SUCCESS(rv, rv); |
|
1401 rv = SetItemDateInternal(LAST_MODIFIED, aNewParent, now); |
|
1402 NS_ENSURE_SUCCESS(rv, rv); |
|
1403 |
|
1404 rv = transaction.Commit(); |
|
1405 NS_ENSURE_SUCCESS(rv, rv); |
|
1406 |
|
1407 END_CRITICAL_BOOKMARK_CACHE_SECTION(bookmark.id); |
|
1408 |
|
1409 NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers, |
|
1410 nsINavBookmarkObserver, |
|
1411 OnItemMoved(bookmark.id, |
|
1412 bookmark.parentId, |
|
1413 bookmark.position, |
|
1414 aNewParent, |
|
1415 newIndex, |
|
1416 bookmark.type, |
|
1417 bookmark.guid, |
|
1418 bookmark.parentGuid, |
|
1419 newParentGuid)); |
|
1420 return NS_OK; |
|
1421 } |
|
1422 |
|
1423 nsresult |
|
1424 nsNavBookmarks::FetchItemInfo(int64_t aItemId, |
|
1425 BookmarkData& _bookmark) |
|
1426 { |
|
1427 // Check if the requested id is in the recent cache and avoid the database |
|
1428 // lookup if so. Invalidate the cache after getting data if requested. |
|
1429 BookmarkKeyClass* key = mRecentBookmarksCache.GetEntry(aItemId); |
|
1430 if (key) { |
|
1431 _bookmark = key->bookmark; |
|
1432 return NS_OK; |
|
1433 } |
|
1434 |
|
1435 // LEFT JOIN since not all bookmarks have an associated place. |
|
1436 nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement( |
|
1437 "SELECT b.id, h.url, b.title, b.position, b.fk, b.parent, b.type, " |
|
1438 "b.dateAdded, b.lastModified, b.guid, t.guid, t.parent " |
|
1439 "FROM moz_bookmarks b " |
|
1440 "LEFT JOIN moz_bookmarks t ON t.id = b.parent " |
|
1441 "LEFT JOIN moz_places h ON h.id = b.fk " |
|
1442 "WHERE b.id = :item_id" |
|
1443 ); |
|
1444 NS_ENSURE_STATE(stmt); |
|
1445 mozStorageStatementScoper scoper(stmt); |
|
1446 |
|
1447 nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), aItemId); |
|
1448 NS_ENSURE_SUCCESS(rv, rv); |
|
1449 |
|
1450 bool hasResult; |
|
1451 rv = stmt->ExecuteStep(&hasResult); |
|
1452 NS_ENSURE_SUCCESS(rv, rv); |
|
1453 if (!hasResult) { |
|
1454 return NS_ERROR_INVALID_ARG; |
|
1455 } |
|
1456 |
|
1457 _bookmark.id = aItemId; |
|
1458 rv = stmt->GetUTF8String(1, _bookmark.url); |
|
1459 NS_ENSURE_SUCCESS(rv, rv); |
|
1460 bool isNull; |
|
1461 rv = stmt->GetIsNull(2, &isNull); |
|
1462 NS_ENSURE_SUCCESS(rv, rv); |
|
1463 if (isNull) { |
|
1464 _bookmark.title.SetIsVoid(true); |
|
1465 } |
|
1466 else { |
|
1467 rv = stmt->GetUTF8String(2, _bookmark.title); |
|
1468 NS_ENSURE_SUCCESS(rv, rv); |
|
1469 } |
|
1470 rv = stmt->GetInt32(3, &_bookmark.position); |
|
1471 NS_ENSURE_SUCCESS(rv, rv); |
|
1472 rv = stmt->GetInt64(4, &_bookmark.placeId); |
|
1473 NS_ENSURE_SUCCESS(rv, rv); |
|
1474 rv = stmt->GetInt64(5, &_bookmark.parentId); |
|
1475 NS_ENSURE_SUCCESS(rv, rv); |
|
1476 rv = stmt->GetInt32(6, &_bookmark.type); |
|
1477 NS_ENSURE_SUCCESS(rv, rv); |
|
1478 rv = stmt->GetInt64(7, reinterpret_cast<int64_t*>(&_bookmark.dateAdded)); |
|
1479 NS_ENSURE_SUCCESS(rv, rv); |
|
1480 rv = stmt->GetInt64(8, reinterpret_cast<int64_t*>(&_bookmark.lastModified)); |
|
1481 NS_ENSURE_SUCCESS(rv, rv); |
|
1482 rv = stmt->GetUTF8String(9, _bookmark.guid); |
|
1483 NS_ENSURE_SUCCESS(rv, rv); |
|
1484 // Getting properties of the root would show no parent. |
|
1485 rv = stmt->GetIsNull(10, &isNull); |
|
1486 NS_ENSURE_SUCCESS(rv, rv); |
|
1487 if (!isNull) { |
|
1488 rv = stmt->GetUTF8String(10, _bookmark.parentGuid); |
|
1489 NS_ENSURE_SUCCESS(rv, rv); |
|
1490 rv = stmt->GetInt64(11, &_bookmark.grandParentId); |
|
1491 NS_ENSURE_SUCCESS(rv, rv); |
|
1492 } |
|
1493 else { |
|
1494 _bookmark.grandParentId = -1; |
|
1495 } |
|
1496 |
|
1497 ADD_TO_BOOKMARK_CACHE(aItemId, _bookmark); |
|
1498 |
|
1499 return NS_OK; |
|
1500 } |
|
1501 |
|
1502 nsresult |
|
1503 nsNavBookmarks::SetItemDateInternal(enum BookmarkDate aDateType, |
|
1504 int64_t aItemId, |
|
1505 PRTime aValue) |
|
1506 { |
|
1507 nsCOMPtr<mozIStorageStatement> stmt; |
|
1508 if (aDateType == DATE_ADDED) { |
|
1509 // lastModified is set to the same value as dateAdded. We do this for |
|
1510 // performance reasons, since it will allow us to use an index to sort items |
|
1511 // by date. |
|
1512 stmt = mDB->GetStatement( |
|
1513 "UPDATE moz_bookmarks SET dateAdded = :date, lastModified = :date " |
|
1514 "WHERE id = :item_id" |
|
1515 ); |
|
1516 } |
|
1517 else { |
|
1518 stmt = mDB->GetStatement( |
|
1519 "UPDATE moz_bookmarks SET lastModified = :date WHERE id = :item_id" |
|
1520 ); |
|
1521 } |
|
1522 NS_ENSURE_STATE(stmt); |
|
1523 mozStorageStatementScoper scoper(stmt); |
|
1524 |
|
1525 nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("date"), aValue); |
|
1526 NS_ENSURE_SUCCESS(rv, rv); |
|
1527 rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), aItemId); |
|
1528 NS_ENSURE_SUCCESS(rv, rv); |
|
1529 |
|
1530 rv = stmt->Execute(); |
|
1531 NS_ENSURE_SUCCESS(rv, rv); |
|
1532 |
|
1533 // Update the cache entry, if needed. |
|
1534 BookmarkKeyClass* key = mRecentBookmarksCache.GetEntry(aItemId); |
|
1535 if (key) { |
|
1536 if (aDateType == DATE_ADDED) { |
|
1537 key->bookmark.dateAdded = aValue; |
|
1538 } |
|
1539 // Set lastModified in both cases. |
|
1540 key->bookmark.lastModified = aValue; |
|
1541 } |
|
1542 |
|
1543 // note, we are not notifying the observers |
|
1544 // that the item has changed. |
|
1545 |
|
1546 return NS_OK; |
|
1547 } |
|
1548 |
|
1549 |
|
1550 NS_IMETHODIMP |
|
1551 nsNavBookmarks::SetItemDateAdded(int64_t aItemId, PRTime aDateAdded) |
|
1552 { |
|
1553 NS_ENSURE_ARG_MIN(aItemId, 1); |
|
1554 |
|
1555 BookmarkData bookmark; |
|
1556 nsresult rv = FetchItemInfo(aItemId, bookmark); |
|
1557 NS_ENSURE_SUCCESS(rv, rv); |
|
1558 bookmark.dateAdded = aDateAdded; |
|
1559 |
|
1560 rv = SetItemDateInternal(DATE_ADDED, bookmark.id, bookmark.dateAdded); |
|
1561 NS_ENSURE_SUCCESS(rv, rv); |
|
1562 |
|
1563 // Note: mDBSetItemDateAdded also sets lastModified to aDateAdded. |
|
1564 NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers, |
|
1565 nsINavBookmarkObserver, |
|
1566 OnItemChanged(bookmark.id, |
|
1567 NS_LITERAL_CSTRING("dateAdded"), |
|
1568 false, |
|
1569 nsPrintfCString("%lld", bookmark.dateAdded), |
|
1570 bookmark.dateAdded, |
|
1571 bookmark.type, |
|
1572 bookmark.parentId, |
|
1573 bookmark.guid, |
|
1574 bookmark.parentGuid)); |
|
1575 return NS_OK; |
|
1576 } |
|
1577 |
|
1578 |
|
1579 NS_IMETHODIMP |
|
1580 nsNavBookmarks::GetItemDateAdded(int64_t aItemId, PRTime* _dateAdded) |
|
1581 { |
|
1582 NS_ENSURE_ARG_MIN(aItemId, 1); |
|
1583 NS_ENSURE_ARG_POINTER(_dateAdded); |
|
1584 |
|
1585 BookmarkData bookmark; |
|
1586 nsresult rv = FetchItemInfo(aItemId, bookmark); |
|
1587 NS_ENSURE_SUCCESS(rv, rv); |
|
1588 |
|
1589 *_dateAdded = bookmark.dateAdded; |
|
1590 return NS_OK; |
|
1591 } |
|
1592 |
|
1593 |
|
1594 NS_IMETHODIMP |
|
1595 nsNavBookmarks::SetItemLastModified(int64_t aItemId, PRTime aLastModified) |
|
1596 { |
|
1597 NS_ENSURE_ARG_MIN(aItemId, 1); |
|
1598 |
|
1599 BookmarkData bookmark; |
|
1600 nsresult rv = FetchItemInfo(aItemId, bookmark); |
|
1601 NS_ENSURE_SUCCESS(rv, rv); |
|
1602 bookmark.lastModified = aLastModified; |
|
1603 |
|
1604 rv = SetItemDateInternal(LAST_MODIFIED, bookmark.id, bookmark.lastModified); |
|
1605 NS_ENSURE_SUCCESS(rv, rv); |
|
1606 |
|
1607 // Note: mDBSetItemDateAdded also sets lastModified to aDateAdded. |
|
1608 NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers, |
|
1609 nsINavBookmarkObserver, |
|
1610 OnItemChanged(bookmark.id, |
|
1611 NS_LITERAL_CSTRING("lastModified"), |
|
1612 false, |
|
1613 nsPrintfCString("%lld", bookmark.lastModified), |
|
1614 bookmark.lastModified, |
|
1615 bookmark.type, |
|
1616 bookmark.parentId, |
|
1617 bookmark.guid, |
|
1618 bookmark.parentGuid)); |
|
1619 return NS_OK; |
|
1620 } |
|
1621 |
|
1622 |
|
1623 NS_IMETHODIMP |
|
1624 nsNavBookmarks::GetItemLastModified(int64_t aItemId, PRTime* _lastModified) |
|
1625 { |
|
1626 NS_ENSURE_ARG_MIN(aItemId, 1); |
|
1627 NS_ENSURE_ARG_POINTER(_lastModified); |
|
1628 |
|
1629 BookmarkData bookmark; |
|
1630 nsresult rv = FetchItemInfo(aItemId, bookmark); |
|
1631 NS_ENSURE_SUCCESS(rv, rv); |
|
1632 |
|
1633 *_lastModified = bookmark.lastModified; |
|
1634 return NS_OK; |
|
1635 } |
|
1636 |
|
1637 |
|
1638 NS_IMETHODIMP |
|
1639 nsNavBookmarks::SetItemTitle(int64_t aItemId, const nsACString& aTitle) |
|
1640 { |
|
1641 NS_ENSURE_ARG_MIN(aItemId, 1); |
|
1642 |
|
1643 BookmarkData bookmark; |
|
1644 nsresult rv = FetchItemInfo(aItemId, bookmark); |
|
1645 NS_ENSURE_SUCCESS(rv, rv); |
|
1646 |
|
1647 nsCOMPtr<mozIStorageStatement> statement = mDB->GetStatement( |
|
1648 "UPDATE moz_bookmarks SET title = :item_title, lastModified = :date " |
|
1649 "WHERE id = :item_id " |
|
1650 ); |
|
1651 NS_ENSURE_STATE(statement); |
|
1652 mozStorageStatementScoper scoper(statement); |
|
1653 |
|
1654 nsCString title; |
|
1655 TruncateTitle(aTitle, title); |
|
1656 |
|
1657 // Support setting a null title, we support this in insertBookmark. |
|
1658 if (title.IsVoid()) { |
|
1659 rv = statement->BindNullByName(NS_LITERAL_CSTRING("item_title")); |
|
1660 } |
|
1661 else { |
|
1662 rv = statement->BindUTF8StringByName(NS_LITERAL_CSTRING("item_title"), |
|
1663 title); |
|
1664 } |
|
1665 NS_ENSURE_SUCCESS(rv, rv); |
|
1666 bookmark.lastModified = PR_Now(); |
|
1667 rv = statement->BindInt64ByName(NS_LITERAL_CSTRING("date"), |
|
1668 bookmark.lastModified); |
|
1669 NS_ENSURE_SUCCESS(rv, rv); |
|
1670 rv = statement->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), bookmark.id); |
|
1671 NS_ENSURE_SUCCESS(rv, rv); |
|
1672 |
|
1673 rv = statement->Execute(); |
|
1674 NS_ENSURE_SUCCESS(rv, rv); |
|
1675 |
|
1676 // Update the cache entry, if needed. |
|
1677 BookmarkKeyClass* key = mRecentBookmarksCache.GetEntry(aItemId); |
|
1678 if (key) { |
|
1679 if (title.IsVoid()) { |
|
1680 key->bookmark.title.SetIsVoid(true); |
|
1681 } |
|
1682 else { |
|
1683 key->bookmark.title.Assign(title); |
|
1684 } |
|
1685 key->bookmark.lastModified = bookmark.lastModified; |
|
1686 } |
|
1687 |
|
1688 NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers, |
|
1689 nsINavBookmarkObserver, |
|
1690 OnItemChanged(bookmark.id, |
|
1691 NS_LITERAL_CSTRING("title"), |
|
1692 false, |
|
1693 title, |
|
1694 bookmark.lastModified, |
|
1695 bookmark.type, |
|
1696 bookmark.parentId, |
|
1697 bookmark.guid, |
|
1698 bookmark.parentGuid)); |
|
1699 return NS_OK; |
|
1700 } |
|
1701 |
|
1702 |
|
1703 NS_IMETHODIMP |
|
1704 nsNavBookmarks::GetItemTitle(int64_t aItemId, |
|
1705 nsACString& _title) |
|
1706 { |
|
1707 NS_ENSURE_ARG_MIN(aItemId, 1); |
|
1708 |
|
1709 BookmarkData bookmark; |
|
1710 nsresult rv = FetchItemInfo(aItemId, bookmark); |
|
1711 NS_ENSURE_SUCCESS(rv, rv); |
|
1712 |
|
1713 _title = bookmark.title; |
|
1714 return NS_OK; |
|
1715 } |
|
1716 |
|
1717 |
|
1718 NS_IMETHODIMP |
|
1719 nsNavBookmarks::GetBookmarkURI(int64_t aItemId, |
|
1720 nsIURI** _URI) |
|
1721 { |
|
1722 NS_ENSURE_ARG_MIN(aItemId, 1); |
|
1723 NS_ENSURE_ARG_POINTER(_URI); |
|
1724 |
|
1725 BookmarkData bookmark; |
|
1726 nsresult rv = FetchItemInfo(aItemId, bookmark); |
|
1727 NS_ENSURE_SUCCESS(rv, rv); |
|
1728 |
|
1729 rv = NS_NewURI(_URI, bookmark.url); |
|
1730 NS_ENSURE_SUCCESS(rv, rv); |
|
1731 |
|
1732 return NS_OK; |
|
1733 } |
|
1734 |
|
1735 |
|
1736 NS_IMETHODIMP |
|
1737 nsNavBookmarks::GetItemType(int64_t aItemId, uint16_t* _type) |
|
1738 { |
|
1739 NS_ENSURE_ARG_MIN(aItemId, 1); |
|
1740 NS_ENSURE_ARG_POINTER(_type); |
|
1741 |
|
1742 BookmarkData bookmark; |
|
1743 nsresult rv = FetchItemInfo(aItemId, bookmark); |
|
1744 NS_ENSURE_SUCCESS(rv, rv); |
|
1745 |
|
1746 *_type = static_cast<uint16_t>(bookmark.type); |
|
1747 return NS_OK; |
|
1748 } |
|
1749 |
|
1750 |
|
1751 nsresult |
|
1752 nsNavBookmarks::ResultNodeForContainer(int64_t aItemId, |
|
1753 nsNavHistoryQueryOptions* aOptions, |
|
1754 nsNavHistoryResultNode** aNode) |
|
1755 { |
|
1756 BookmarkData bookmark; |
|
1757 nsresult rv = FetchItemInfo(aItemId, bookmark); |
|
1758 NS_ENSURE_SUCCESS(rv, rv); |
|
1759 |
|
1760 if (bookmark.type == TYPE_FOLDER) { // TYPE_FOLDER |
|
1761 *aNode = new nsNavHistoryFolderResultNode(bookmark.title, |
|
1762 aOptions, |
|
1763 bookmark.id); |
|
1764 } |
|
1765 else { |
|
1766 return NS_ERROR_INVALID_ARG; |
|
1767 } |
|
1768 |
|
1769 (*aNode)->mDateAdded = bookmark.dateAdded; |
|
1770 (*aNode)->mLastModified = bookmark.lastModified; |
|
1771 (*aNode)->mBookmarkGuid = bookmark.guid; |
|
1772 |
|
1773 NS_ADDREF(*aNode); |
|
1774 return NS_OK; |
|
1775 } |
|
1776 |
|
1777 |
|
1778 nsresult |
|
1779 nsNavBookmarks::QueryFolderChildren( |
|
1780 int64_t aFolderId, |
|
1781 nsNavHistoryQueryOptions* aOptions, |
|
1782 nsCOMArray<nsNavHistoryResultNode>* aChildren) |
|
1783 { |
|
1784 NS_ENSURE_ARG_POINTER(aOptions); |
|
1785 NS_ENSURE_ARG_POINTER(aChildren); |
|
1786 |
|
1787 // Select all children of a given folder, sorted by position. |
|
1788 // This is a LEFT JOIN because not all bookmarks types have a place. |
|
1789 // We construct a result where the first columns exactly match those returned |
|
1790 // by mDBGetURLPageInfo, and additionally contains columns for position, |
|
1791 // item_child, and folder_child from moz_bookmarks. |
|
1792 nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement( |
|
1793 "SELECT h.id, h.url, IFNULL(b.title, h.title), h.rev_host, h.visit_count, " |
|
1794 "h.last_visit_date, f.url, b.id, b.dateAdded, b.lastModified, " |
|
1795 "b.parent, null, h.frecency, h.hidden, h.guid, b.guid, " |
|
1796 "b.position, b.type, b.fk " |
|
1797 "FROM moz_bookmarks b " |
|
1798 "LEFT JOIN moz_places h ON b.fk = h.id " |
|
1799 "LEFT JOIN moz_favicons f ON h.favicon_id = f.id " |
|
1800 "WHERE b.parent = :parent " |
|
1801 "ORDER BY b.position ASC" |
|
1802 ); |
|
1803 NS_ENSURE_STATE(stmt); |
|
1804 mozStorageStatementScoper scoper(stmt); |
|
1805 |
|
1806 nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aFolderId); |
|
1807 NS_ENSURE_SUCCESS(rv, rv); |
|
1808 |
|
1809 nsCOMPtr<mozIStorageValueArray> row = do_QueryInterface(stmt, &rv); |
|
1810 NS_ENSURE_SUCCESS(rv, rv); |
|
1811 |
|
1812 int32_t index = -1; |
|
1813 bool hasResult; |
|
1814 while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) { |
|
1815 rv = ProcessFolderNodeRow(row, aOptions, aChildren, index); |
|
1816 NS_ENSURE_SUCCESS(rv, rv); |
|
1817 } |
|
1818 |
|
1819 return NS_OK; |
|
1820 } |
|
1821 |
|
1822 |
|
1823 nsresult |
|
1824 nsNavBookmarks::ProcessFolderNodeRow( |
|
1825 mozIStorageValueArray* aRow, |
|
1826 nsNavHistoryQueryOptions* aOptions, |
|
1827 nsCOMArray<nsNavHistoryResultNode>* aChildren, |
|
1828 int32_t& aCurrentIndex) |
|
1829 { |
|
1830 NS_ENSURE_ARG_POINTER(aRow); |
|
1831 NS_ENSURE_ARG_POINTER(aOptions); |
|
1832 NS_ENSURE_ARG_POINTER(aChildren); |
|
1833 |
|
1834 // The results will be in order of aCurrentIndex. Even if we don't add a node |
|
1835 // because it was excluded, we need to count its index, so do that before |
|
1836 // doing anything else. |
|
1837 aCurrentIndex++; |
|
1838 |
|
1839 int32_t itemType; |
|
1840 nsresult rv = aRow->GetInt32(kGetChildrenIndex_Type, &itemType); |
|
1841 NS_ENSURE_SUCCESS(rv, rv); |
|
1842 int64_t id; |
|
1843 rv = aRow->GetInt64(nsNavHistory::kGetInfoIndex_ItemId, &id); |
|
1844 NS_ENSURE_SUCCESS(rv, rv); |
|
1845 |
|
1846 nsRefPtr<nsNavHistoryResultNode> node; |
|
1847 |
|
1848 if (itemType == TYPE_BOOKMARK) { |
|
1849 nsNavHistory* history = nsNavHistory::GetHistoryService(); |
|
1850 NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY); |
|
1851 rv = history->RowToResult(aRow, aOptions, getter_AddRefs(node)); |
|
1852 NS_ENSURE_SUCCESS(rv, rv); |
|
1853 |
|
1854 uint32_t nodeType; |
|
1855 node->GetType(&nodeType); |
|
1856 if ((nodeType == nsINavHistoryResultNode::RESULT_TYPE_QUERY && |
|
1857 aOptions->ExcludeQueries()) || |
|
1858 (nodeType != nsINavHistoryResultNode::RESULT_TYPE_QUERY && |
|
1859 nodeType != nsINavHistoryResultNode::RESULT_TYPE_FOLDER_SHORTCUT && |
|
1860 aOptions->ExcludeItems())) { |
|
1861 return NS_OK; |
|
1862 } |
|
1863 } |
|
1864 else if (itemType == TYPE_FOLDER) { |
|
1865 if (aOptions->ExcludeReadOnlyFolders()) { |
|
1866 // If the folder is read-only, skip it. |
|
1867 bool readOnly = false; |
|
1868 GetFolderReadonly(id, &readOnly); |
|
1869 if (readOnly) |
|
1870 return NS_OK; |
|
1871 } |
|
1872 |
|
1873 nsAutoCString title; |
|
1874 rv = aRow->GetUTF8String(nsNavHistory::kGetInfoIndex_Title, title); |
|
1875 NS_ENSURE_SUCCESS(rv, rv); |
|
1876 |
|
1877 node = new nsNavHistoryFolderResultNode(title, aOptions, id); |
|
1878 |
|
1879 rv = aRow->GetInt64(nsNavHistory::kGetInfoIndex_ItemDateAdded, |
|
1880 reinterpret_cast<int64_t*>(&node->mDateAdded)); |
|
1881 NS_ENSURE_SUCCESS(rv, rv); |
|
1882 rv = aRow->GetInt64(nsNavHistory::kGetInfoIndex_ItemLastModified, |
|
1883 reinterpret_cast<int64_t*>(&node->mLastModified)); |
|
1884 NS_ENSURE_SUCCESS(rv, rv); |
|
1885 } |
|
1886 else { |
|
1887 // This is a separator. |
|
1888 if (aOptions->ExcludeItems()) { |
|
1889 return NS_OK; |
|
1890 } |
|
1891 node = new nsNavHistorySeparatorResultNode(); |
|
1892 |
|
1893 node->mItemId = id; |
|
1894 rv = aRow->GetInt64(nsNavHistory::kGetInfoIndex_ItemDateAdded, |
|
1895 reinterpret_cast<int64_t*>(&node->mDateAdded)); |
|
1896 NS_ENSURE_SUCCESS(rv, rv); |
|
1897 rv = aRow->GetInt64(nsNavHistory::kGetInfoIndex_ItemLastModified, |
|
1898 reinterpret_cast<int64_t*>(&node->mLastModified)); |
|
1899 NS_ENSURE_SUCCESS(rv, rv); |
|
1900 } |
|
1901 |
|
1902 // Store the index of the node within this container. Note that this is not |
|
1903 // moz_bookmarks.position. |
|
1904 node->mBookmarkIndex = aCurrentIndex; |
|
1905 |
|
1906 rv = aRow->GetUTF8String(kGetChildrenIndex_Guid, node->mBookmarkGuid); |
|
1907 NS_ENSURE_SUCCESS(rv, rv); |
|
1908 |
|
1909 NS_ENSURE_TRUE(aChildren->AppendObject(node), NS_ERROR_OUT_OF_MEMORY); |
|
1910 |
|
1911 return NS_OK; |
|
1912 } |
|
1913 |
|
1914 |
|
1915 nsresult |
|
1916 nsNavBookmarks::QueryFolderChildrenAsync( |
|
1917 nsNavHistoryFolderResultNode* aNode, |
|
1918 int64_t aFolderId, |
|
1919 mozIStoragePendingStatement** _pendingStmt) |
|
1920 { |
|
1921 NS_ENSURE_ARG_POINTER(aNode); |
|
1922 NS_ENSURE_ARG_POINTER(_pendingStmt); |
|
1923 |
|
1924 // Select all children of a given folder, sorted by position. |
|
1925 // This is a LEFT JOIN because not all bookmarks types have a place. |
|
1926 // We construct a result where the first columns exactly match those returned |
|
1927 // by mDBGetURLPageInfo, and additionally contains columns for position, |
|
1928 // item_child, and folder_child from moz_bookmarks. |
|
1929 nsCOMPtr<mozIStorageAsyncStatement> stmt = mDB->GetAsyncStatement( |
|
1930 "SELECT h.id, h.url, IFNULL(b.title, h.title), h.rev_host, h.visit_count, " |
|
1931 "h.last_visit_date, f.url, b.id, b.dateAdded, b.lastModified, " |
|
1932 "b.parent, null, h.frecency, h.hidden, h.guid, b.guid, " |
|
1933 "b.position, b.type, b.fk " |
|
1934 "FROM moz_bookmarks b " |
|
1935 "LEFT JOIN moz_places h ON b.fk = h.id " |
|
1936 "LEFT JOIN moz_favicons f ON h.favicon_id = f.id " |
|
1937 "WHERE b.parent = :parent " |
|
1938 "ORDER BY b.position ASC" |
|
1939 ); |
|
1940 NS_ENSURE_STATE(stmt); |
|
1941 |
|
1942 nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aFolderId); |
|
1943 NS_ENSURE_SUCCESS(rv, rv); |
|
1944 |
|
1945 nsCOMPtr<mozIStoragePendingStatement> pendingStmt; |
|
1946 rv = stmt->ExecuteAsync(aNode, getter_AddRefs(pendingStmt)); |
|
1947 NS_ENSURE_SUCCESS(rv, rv); |
|
1948 |
|
1949 NS_IF_ADDREF(*_pendingStmt = pendingStmt); |
|
1950 return NS_OK; |
|
1951 } |
|
1952 |
|
1953 |
|
1954 nsresult |
|
1955 nsNavBookmarks::FetchFolderInfo(int64_t aFolderId, |
|
1956 int32_t* _folderCount, |
|
1957 nsACString& _guid, |
|
1958 int64_t* _parentId) |
|
1959 { |
|
1960 *_folderCount = 0; |
|
1961 *_parentId = -1; |
|
1962 |
|
1963 // This query has to always return results, so it can't be written as a join, |
|
1964 // though a left join of 2 subqueries would have the same cost. |
|
1965 nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement( |
|
1966 "SELECT count(*), " |
|
1967 "(SELECT guid FROM moz_bookmarks WHERE id = :parent), " |
|
1968 "(SELECT parent FROM moz_bookmarks WHERE id = :parent) " |
|
1969 "FROM moz_bookmarks " |
|
1970 "WHERE parent = :parent" |
|
1971 ); |
|
1972 NS_ENSURE_STATE(stmt); |
|
1973 mozStorageStatementScoper scoper(stmt); |
|
1974 |
|
1975 nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aFolderId); |
|
1976 NS_ENSURE_SUCCESS(rv, rv); |
|
1977 |
|
1978 bool hasResult; |
|
1979 rv = stmt->ExecuteStep(&hasResult); |
|
1980 NS_ENSURE_SUCCESS(rv, rv); |
|
1981 NS_ENSURE_TRUE(hasResult, NS_ERROR_UNEXPECTED); |
|
1982 |
|
1983 // Ensure that the folder we are looking for exists. |
|
1984 // Can't rely only on parent, since the root has parent 0, that doesn't exist. |
|
1985 bool isNull; |
|
1986 rv = stmt->GetIsNull(2, &isNull); |
|
1987 NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && (!isNull || aFolderId == 0), |
|
1988 NS_ERROR_INVALID_ARG); |
|
1989 |
|
1990 rv = stmt->GetInt32(0, _folderCount); |
|
1991 NS_ENSURE_SUCCESS(rv, rv); |
|
1992 if (!isNull) { |
|
1993 rv = stmt->GetUTF8String(1, _guid); |
|
1994 NS_ENSURE_SUCCESS(rv, rv); |
|
1995 rv = stmt->GetInt64(2, _parentId); |
|
1996 NS_ENSURE_SUCCESS(rv, rv); |
|
1997 } |
|
1998 |
|
1999 return NS_OK; |
|
2000 } |
|
2001 |
|
2002 |
|
2003 NS_IMETHODIMP |
|
2004 nsNavBookmarks::IsBookmarked(nsIURI* aURI, bool* aBookmarked) |
|
2005 { |
|
2006 NS_ENSURE_ARG(aURI); |
|
2007 NS_ENSURE_ARG_POINTER(aBookmarked); |
|
2008 |
|
2009 nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement( |
|
2010 "SELECT 1 FROM moz_bookmarks b " |
|
2011 "JOIN moz_places h ON b.fk = h.id " |
|
2012 "WHERE h.url = :page_url" |
|
2013 ); |
|
2014 NS_ENSURE_STATE(stmt); |
|
2015 mozStorageStatementScoper scoper(stmt); |
|
2016 |
|
2017 nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), aURI); |
|
2018 NS_ENSURE_SUCCESS(rv, rv); |
|
2019 rv = stmt->ExecuteStep(aBookmarked); |
|
2020 NS_ENSURE_SUCCESS(rv, rv); |
|
2021 |
|
2022 return NS_OK; |
|
2023 } |
|
2024 |
|
2025 |
|
2026 NS_IMETHODIMP |
|
2027 nsNavBookmarks::GetBookmarkedURIFor(nsIURI* aURI, nsIURI** _retval) |
|
2028 { |
|
2029 NS_ENSURE_ARG(aURI); |
|
2030 NS_ENSURE_ARG_POINTER(_retval); |
|
2031 |
|
2032 *_retval = nullptr; |
|
2033 |
|
2034 nsNavHistory* history = nsNavHistory::GetHistoryService(); |
|
2035 NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY); |
|
2036 int64_t placeId; |
|
2037 nsAutoCString placeGuid; |
|
2038 nsresult rv = history->GetIdForPage(aURI, &placeId, placeGuid); |
|
2039 NS_ENSURE_SUCCESS(rv, rv); |
|
2040 if (!placeId) { |
|
2041 // This URI is unknown, just return null. |
|
2042 return NS_OK; |
|
2043 } |
|
2044 |
|
2045 // Check if a bookmark exists in the redirects chain for this URI. |
|
2046 // The query will also check if the page is directly bookmarked, and return |
|
2047 // the first found bookmark in case. The check is directly on moz_bookmarks |
|
2048 // without special filtering. |
|
2049 // The next query finds the bookmarked ancestors in a redirects chain. |
|
2050 // It won't go further than 3 levels of redirects (a->b->c->your_place_id). |
|
2051 // To make this path 100% correct (up to any level) we would need either: |
|
2052 // - A separate hash, build through recursive querying of the database. |
|
2053 // This solution was previously implemented, but it had a negative effect |
|
2054 // on startup since at each startup we have to recursively query the |
|
2055 // database to rebuild a hash that is always the same across sessions. |
|
2056 // It must be updated at each visit and bookmarks change too. The code to |
|
2057 // manage it is complex and prone to errors, sometimes causing incorrect |
|
2058 // data fetches (for example wrong favicon for a redirected bookmark). |
|
2059 // - A better way to track redirects for a visit. |
|
2060 // We would need a separate table to track redirects, in the table we would |
|
2061 // have visit_id, redirect_session. To get all sources for |
|
2062 // a visit then we could just join this table and get all visit_id that |
|
2063 // are in the same redirect_session as our visit. This has the drawback |
|
2064 // that we can't ensure data integrity in the downgrade -> upgrade path, |
|
2065 // since an old version would not update the table on new visits. |
|
2066 // |
|
2067 // For most cases these levels of redirects should be fine though, it's hard |
|
2068 // to hit a page that is 4 or 5 levels of redirects below a bookmarked page. |
|
2069 // |
|
2070 // As a bonus the query also checks first if place_id is already a bookmark, |
|
2071 // so you don't have to check that apart. |
|
2072 |
|
2073 nsCString query = nsPrintfCString( |
|
2074 "SELECT url FROM moz_places WHERE id = ( " |
|
2075 "SELECT :page_id FROM moz_bookmarks WHERE fk = :page_id " |
|
2076 "UNION ALL " |
|
2077 "SELECT COALESCE(grandparent.place_id, parent.place_id) AS r_place_id " |
|
2078 "FROM moz_historyvisits dest " |
|
2079 "LEFT JOIN moz_historyvisits parent ON parent.id = dest.from_visit " |
|
2080 "AND dest.visit_type IN (%d, %d) " |
|
2081 "LEFT JOIN moz_historyvisits grandparent ON parent.from_visit = grandparent.id " |
|
2082 "AND parent.visit_type IN (%d, %d) " |
|
2083 "WHERE dest.place_id = :page_id " |
|
2084 "AND EXISTS(SELECT 1 FROM moz_bookmarks WHERE fk = r_place_id) " |
|
2085 "LIMIT 1 " |
|
2086 ")", |
|
2087 nsINavHistoryService::TRANSITION_REDIRECT_PERMANENT, |
|
2088 nsINavHistoryService::TRANSITION_REDIRECT_TEMPORARY, |
|
2089 nsINavHistoryService::TRANSITION_REDIRECT_PERMANENT, |
|
2090 nsINavHistoryService::TRANSITION_REDIRECT_TEMPORARY |
|
2091 ); |
|
2092 |
|
2093 nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(query); |
|
2094 NS_ENSURE_STATE(stmt); |
|
2095 mozStorageStatementScoper scoper(stmt); |
|
2096 |
|
2097 rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), placeId); |
|
2098 NS_ENSURE_SUCCESS(rv, rv); |
|
2099 bool hasBookmarkedOrigin; |
|
2100 if (NS_SUCCEEDED(stmt->ExecuteStep(&hasBookmarkedOrigin)) && |
|
2101 hasBookmarkedOrigin) { |
|
2102 nsAutoCString spec; |
|
2103 rv = stmt->GetUTF8String(0, spec); |
|
2104 NS_ENSURE_SUCCESS(rv, rv); |
|
2105 rv = NS_NewURI(_retval, spec); |
|
2106 NS_ENSURE_SUCCESS(rv, rv); |
|
2107 } |
|
2108 |
|
2109 // If there is no bookmarked origin, we will just return null. |
|
2110 return NS_OK; |
|
2111 } |
|
2112 |
|
2113 |
|
2114 NS_IMETHODIMP |
|
2115 nsNavBookmarks::ChangeBookmarkURI(int64_t aBookmarkId, nsIURI* aNewURI) |
|
2116 { |
|
2117 NS_ENSURE_ARG_MIN(aBookmarkId, 1); |
|
2118 NS_ENSURE_ARG(aNewURI); |
|
2119 |
|
2120 BookmarkData bookmark; |
|
2121 nsresult rv = FetchItemInfo(aBookmarkId, bookmark); |
|
2122 NS_ENSURE_SUCCESS(rv, rv); |
|
2123 NS_ENSURE_ARG(bookmark.type == TYPE_BOOKMARK); |
|
2124 |
|
2125 mozStorageTransaction transaction(mDB->MainConn(), false); |
|
2126 |
|
2127 nsNavHistory* history = nsNavHistory::GetHistoryService(); |
|
2128 NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY); |
|
2129 int64_t newPlaceId; |
|
2130 nsAutoCString newPlaceGuid; |
|
2131 rv = history->GetOrCreateIdForPage(aNewURI, &newPlaceId, newPlaceGuid); |
|
2132 NS_ENSURE_SUCCESS(rv, rv); |
|
2133 if (!newPlaceId) |
|
2134 return NS_ERROR_INVALID_ARG; |
|
2135 |
|
2136 BEGIN_CRITICAL_BOOKMARK_CACHE_SECTION(bookmark.id); |
|
2137 |
|
2138 nsCOMPtr<mozIStorageStatement> statement = mDB->GetStatement( |
|
2139 "UPDATE moz_bookmarks SET fk = :page_id, lastModified = :date " |
|
2140 "WHERE id = :item_id " |
|
2141 ); |
|
2142 NS_ENSURE_STATE(statement); |
|
2143 mozStorageStatementScoper scoper(statement); |
|
2144 |
|
2145 rv = statement->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), newPlaceId); |
|
2146 NS_ENSURE_SUCCESS(rv, rv); |
|
2147 bookmark.lastModified = PR_Now(); |
|
2148 rv = statement->BindInt64ByName(NS_LITERAL_CSTRING("date"), |
|
2149 bookmark.lastModified); |
|
2150 NS_ENSURE_SUCCESS(rv, rv); |
|
2151 rv = statement->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), bookmark.id); |
|
2152 NS_ENSURE_SUCCESS(rv, rv); |
|
2153 rv = statement->Execute(); |
|
2154 NS_ENSURE_SUCCESS(rv, rv); |
|
2155 |
|
2156 rv = transaction.Commit(); |
|
2157 NS_ENSURE_SUCCESS(rv, rv); |
|
2158 |
|
2159 END_CRITICAL_BOOKMARK_CACHE_SECTION(bookmark.id); |
|
2160 |
|
2161 rv = history->UpdateFrecency(newPlaceId); |
|
2162 NS_ENSURE_SUCCESS(rv, rv); |
|
2163 |
|
2164 // Upon changing the URI for a bookmark, update the frecency for the old |
|
2165 // place as well. |
|
2166 rv = history->UpdateFrecency(bookmark.placeId); |
|
2167 NS_ENSURE_SUCCESS(rv, rv); |
|
2168 |
|
2169 nsAutoCString spec; |
|
2170 rv = aNewURI->GetSpec(spec); |
|
2171 NS_ENSURE_SUCCESS(rv, rv); |
|
2172 |
|
2173 NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers, |
|
2174 nsINavBookmarkObserver, |
|
2175 OnItemChanged(bookmark.id, |
|
2176 NS_LITERAL_CSTRING("uri"), |
|
2177 false, |
|
2178 spec, |
|
2179 bookmark.lastModified, |
|
2180 bookmark.type, |
|
2181 bookmark.parentId, |
|
2182 bookmark.guid, |
|
2183 bookmark.parentGuid)); |
|
2184 return NS_OK; |
|
2185 } |
|
2186 |
|
2187 |
|
2188 NS_IMETHODIMP |
|
2189 nsNavBookmarks::GetFolderIdForItem(int64_t aItemId, int64_t* _parentId) |
|
2190 { |
|
2191 NS_ENSURE_ARG_MIN(aItemId, 1); |
|
2192 NS_ENSURE_ARG_POINTER(_parentId); |
|
2193 |
|
2194 BookmarkData bookmark; |
|
2195 nsresult rv = FetchItemInfo(aItemId, bookmark); |
|
2196 NS_ENSURE_SUCCESS(rv, rv); |
|
2197 |
|
2198 // this should not happen, but see bug #400448 for details |
|
2199 NS_ENSURE_TRUE(bookmark.id != bookmark.parentId, NS_ERROR_UNEXPECTED); |
|
2200 |
|
2201 *_parentId = bookmark.parentId; |
|
2202 return NS_OK; |
|
2203 } |
|
2204 |
|
2205 |
|
2206 nsresult |
|
2207 nsNavBookmarks::GetBookmarkIdsForURITArray(nsIURI* aURI, |
|
2208 nsTArray<int64_t>& aResult, |
|
2209 bool aSkipTags) |
|
2210 { |
|
2211 NS_ENSURE_ARG(aURI); |
|
2212 |
|
2213 // Double ordering covers possible lastModified ties, that could happen when |
|
2214 // importing, syncing or due to extensions. |
|
2215 // Note: not using a JOIN is cheaper in this case. |
|
2216 nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement( |
|
2217 "SELECT b.id, b.guid, b.parent, b.lastModified, t.guid, t.parent " |
|
2218 "FROM moz_bookmarks b " |
|
2219 "JOIN moz_bookmarks t on t.id = b.parent " |
|
2220 "WHERE b.fk = (SELECT id FROM moz_places WHERE url = :page_url) " |
|
2221 "ORDER BY b.lastModified DESC, b.id DESC " |
|
2222 ); |
|
2223 NS_ENSURE_STATE(stmt); |
|
2224 mozStorageStatementScoper scoper(stmt); |
|
2225 |
|
2226 nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), aURI); |
|
2227 NS_ENSURE_SUCCESS(rv, rv); |
|
2228 |
|
2229 bool more; |
|
2230 while (NS_SUCCEEDED((rv = stmt->ExecuteStep(&more))) && more) { |
|
2231 if (aSkipTags) { |
|
2232 // Skip tags, for the use-cases of this async getter they are useless. |
|
2233 int64_t grandParentId; |
|
2234 nsresult rv = stmt->GetInt64(5, &grandParentId); |
|
2235 NS_ENSURE_SUCCESS(rv, rv); |
|
2236 if (grandParentId == mTagsRoot) { |
|
2237 continue; |
|
2238 } |
|
2239 } |
|
2240 int64_t bookmarkId; |
|
2241 rv = stmt->GetInt64(0, &bookmarkId); |
|
2242 NS_ENSURE_SUCCESS(rv, rv); |
|
2243 NS_ENSURE_TRUE(aResult.AppendElement(bookmarkId), NS_ERROR_OUT_OF_MEMORY); |
|
2244 } |
|
2245 NS_ENSURE_SUCCESS(rv, rv); |
|
2246 |
|
2247 return NS_OK; |
|
2248 } |
|
2249 |
|
2250 nsresult |
|
2251 nsNavBookmarks::GetBookmarksForURI(nsIURI* aURI, |
|
2252 nsTArray<BookmarkData>& aBookmarks) |
|
2253 { |
|
2254 NS_ENSURE_ARG(aURI); |
|
2255 |
|
2256 // Double ordering covers possible lastModified ties, that could happen when |
|
2257 // importing, syncing or due to extensions. |
|
2258 // Note: not using a JOIN is cheaper in this case. |
|
2259 nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement( |
|
2260 "SELECT b.id, b.guid, b.parent, b.lastModified, t.guid, t.parent " |
|
2261 "FROM moz_bookmarks b " |
|
2262 "JOIN moz_bookmarks t on t.id = b.parent " |
|
2263 "WHERE b.fk = (SELECT id FROM moz_places WHERE url = :page_url) " |
|
2264 "ORDER BY b.lastModified DESC, b.id DESC " |
|
2265 ); |
|
2266 NS_ENSURE_STATE(stmt); |
|
2267 mozStorageStatementScoper scoper(stmt); |
|
2268 |
|
2269 nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), aURI); |
|
2270 NS_ENSURE_SUCCESS(rv, rv); |
|
2271 |
|
2272 bool more; |
|
2273 nsAutoString tags; |
|
2274 while (NS_SUCCEEDED((rv = stmt->ExecuteStep(&more))) && more) { |
|
2275 // Skip tags. |
|
2276 int64_t grandParentId; |
|
2277 nsresult rv = stmt->GetInt64(5, &grandParentId); |
|
2278 NS_ENSURE_SUCCESS(rv, rv); |
|
2279 if (grandParentId == mTagsRoot) { |
|
2280 continue; |
|
2281 } |
|
2282 |
|
2283 BookmarkData bookmark; |
|
2284 bookmark.grandParentId = grandParentId; |
|
2285 rv = stmt->GetInt64(0, &bookmark.id); |
|
2286 NS_ENSURE_SUCCESS(rv, rv); |
|
2287 rv = stmt->GetUTF8String(1, bookmark.guid); |
|
2288 NS_ENSURE_SUCCESS(rv, rv); |
|
2289 rv = stmt->GetInt64(2, &bookmark.parentId); |
|
2290 NS_ENSURE_SUCCESS(rv, rv); |
|
2291 rv = stmt->GetInt64(3, reinterpret_cast<int64_t*>(&bookmark.lastModified)); |
|
2292 NS_ENSURE_SUCCESS(rv, rv); |
|
2293 rv = stmt->GetUTF8String(4, bookmark.parentGuid); |
|
2294 NS_ENSURE_SUCCESS(rv, rv); |
|
2295 |
|
2296 NS_ENSURE_TRUE(aBookmarks.AppendElement(bookmark), NS_ERROR_OUT_OF_MEMORY); |
|
2297 } |
|
2298 |
|
2299 return NS_OK; |
|
2300 } |
|
2301 |
|
2302 NS_IMETHODIMP |
|
2303 nsNavBookmarks::GetBookmarkIdsForURI(nsIURI* aURI, uint32_t* aCount, |
|
2304 int64_t** aBookmarks) |
|
2305 { |
|
2306 NS_ENSURE_ARG(aURI); |
|
2307 NS_ENSURE_ARG_POINTER(aCount); |
|
2308 NS_ENSURE_ARG_POINTER(aBookmarks); |
|
2309 |
|
2310 *aCount = 0; |
|
2311 *aBookmarks = nullptr; |
|
2312 nsTArray<int64_t> bookmarks; |
|
2313 |
|
2314 // Get the information from the DB as a TArray |
|
2315 // TODO (bug 653816): make this API skip tags by default. |
|
2316 nsresult rv = GetBookmarkIdsForURITArray(aURI, bookmarks, false); |
|
2317 NS_ENSURE_SUCCESS(rv, rv); |
|
2318 |
|
2319 // Copy the results into a new array for output |
|
2320 if (bookmarks.Length()) { |
|
2321 *aBookmarks = |
|
2322 static_cast<int64_t*>(nsMemory::Alloc(sizeof(int64_t) * bookmarks.Length())); |
|
2323 if (!*aBookmarks) |
|
2324 return NS_ERROR_OUT_OF_MEMORY; |
|
2325 for (uint32_t i = 0; i < bookmarks.Length(); i ++) |
|
2326 (*aBookmarks)[i] = bookmarks[i]; |
|
2327 } |
|
2328 |
|
2329 *aCount = bookmarks.Length(); |
|
2330 return NS_OK; |
|
2331 } |
|
2332 |
|
2333 |
|
2334 NS_IMETHODIMP |
|
2335 nsNavBookmarks::GetItemIndex(int64_t aItemId, int32_t* _index) |
|
2336 { |
|
2337 NS_ENSURE_ARG_MIN(aItemId, 1); |
|
2338 NS_ENSURE_ARG_POINTER(_index); |
|
2339 |
|
2340 BookmarkData bookmark; |
|
2341 nsresult rv = FetchItemInfo(aItemId, bookmark); |
|
2342 // With respect to the API. |
|
2343 if (NS_FAILED(rv)) { |
|
2344 *_index = -1; |
|
2345 return NS_OK; |
|
2346 } |
|
2347 |
|
2348 *_index = bookmark.position; |
|
2349 return NS_OK; |
|
2350 } |
|
2351 |
|
2352 NS_IMETHODIMP |
|
2353 nsNavBookmarks::SetItemIndex(int64_t aItemId, int32_t aNewIndex) |
|
2354 { |
|
2355 NS_ENSURE_ARG_MIN(aItemId, 1); |
|
2356 NS_ENSURE_ARG_MIN(aNewIndex, 0); |
|
2357 |
|
2358 BookmarkData bookmark; |
|
2359 nsresult rv = FetchItemInfo(aItemId, bookmark); |
|
2360 NS_ENSURE_SUCCESS(rv, rv); |
|
2361 |
|
2362 // Ensure we are not going out of range. |
|
2363 int32_t folderCount; |
|
2364 int64_t grandParentId; |
|
2365 nsAutoCString folderGuid; |
|
2366 rv = FetchFolderInfo(bookmark.parentId, &folderCount, folderGuid, &grandParentId); |
|
2367 NS_ENSURE_SUCCESS(rv, rv); |
|
2368 NS_ENSURE_TRUE(aNewIndex < folderCount, NS_ERROR_INVALID_ARG); |
|
2369 // Check the parent's guid is the expected one. |
|
2370 MOZ_ASSERT(bookmark.parentGuid == folderGuid); |
|
2371 |
|
2372 BEGIN_CRITICAL_BOOKMARK_CACHE_SECTION(bookmark.id); |
|
2373 |
|
2374 nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement( |
|
2375 "UPDATE moz_bookmarks SET position = :item_index WHERE id = :item_id" |
|
2376 ); |
|
2377 NS_ENSURE_STATE(stmt); |
|
2378 mozStorageStatementScoper scoper(stmt); |
|
2379 |
|
2380 rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), aItemId); |
|
2381 NS_ENSURE_SUCCESS(rv, rv); |
|
2382 rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("item_index"), aNewIndex); |
|
2383 NS_ENSURE_SUCCESS(rv, rv); |
|
2384 |
|
2385 rv = stmt->Execute(); |
|
2386 NS_ENSURE_SUCCESS(rv, rv); |
|
2387 |
|
2388 END_CRITICAL_BOOKMARK_CACHE_SECTION(bookmark.id); |
|
2389 |
|
2390 NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers, |
|
2391 nsINavBookmarkObserver, |
|
2392 OnItemMoved(bookmark.id, |
|
2393 bookmark.parentId, |
|
2394 bookmark.position, |
|
2395 bookmark.parentId, |
|
2396 aNewIndex, |
|
2397 bookmark.type, |
|
2398 bookmark.guid, |
|
2399 bookmark.parentGuid, |
|
2400 bookmark.parentGuid)); |
|
2401 |
|
2402 return NS_OK; |
|
2403 } |
|
2404 |
|
2405 |
|
2406 nsresult |
|
2407 nsNavBookmarks::UpdateKeywordsHashForRemovedBookmark(int64_t aItemId) |
|
2408 { |
|
2409 nsAutoString keyword; |
|
2410 if (NS_SUCCEEDED(GetKeywordForBookmark(aItemId, keyword)) && |
|
2411 !keyword.IsEmpty()) { |
|
2412 nsresult rv = EnsureKeywordsHash(); |
|
2413 NS_ENSURE_SUCCESS(rv, rv); |
|
2414 mBookmarkToKeywordHash.Remove(aItemId); |
|
2415 |
|
2416 // If the keyword is unused, remove it from the database. |
|
2417 keywordSearchData searchData; |
|
2418 searchData.keyword.Assign(keyword); |
|
2419 searchData.itemId = -1; |
|
2420 mBookmarkToKeywordHash.EnumerateRead(SearchBookmarkForKeyword, &searchData); |
|
2421 if (searchData.itemId == -1) { |
|
2422 nsCOMPtr<mozIStorageAsyncStatement> stmt = mDB->GetAsyncStatement( |
|
2423 "DELETE FROM moz_keywords " |
|
2424 "WHERE keyword = :keyword " |
|
2425 "AND NOT EXISTS ( " |
|
2426 "SELECT id " |
|
2427 "FROM moz_bookmarks " |
|
2428 "WHERE keyword_id = moz_keywords.id " |
|
2429 ")" |
|
2430 ); |
|
2431 NS_ENSURE_STATE(stmt); |
|
2432 |
|
2433 rv = stmt->BindStringByName(NS_LITERAL_CSTRING("keyword"), keyword); |
|
2434 NS_ENSURE_SUCCESS(rv, rv); |
|
2435 nsCOMPtr<mozIStoragePendingStatement> pendingStmt; |
|
2436 rv = stmt->ExecuteAsync(nullptr, getter_AddRefs(pendingStmt)); |
|
2437 NS_ENSURE_SUCCESS(rv, rv); |
|
2438 } |
|
2439 } |
|
2440 return NS_OK; |
|
2441 } |
|
2442 |
|
2443 |
|
2444 NS_IMETHODIMP |
|
2445 nsNavBookmarks::SetKeywordForBookmark(int64_t aBookmarkId, |
|
2446 const nsAString& aUserCasedKeyword) |
|
2447 { |
|
2448 NS_ENSURE_ARG_MIN(aBookmarkId, 1); |
|
2449 |
|
2450 // This also ensures the bookmark is valid. |
|
2451 BookmarkData bookmark; |
|
2452 nsresult rv = FetchItemInfo(aBookmarkId, bookmark); |
|
2453 NS_ENSURE_SUCCESS(rv, rv); |
|
2454 |
|
2455 rv = EnsureKeywordsHash(); |
|
2456 NS_ENSURE_SUCCESS(rv, rv); |
|
2457 |
|
2458 // Shortcuts are always lowercased internally. |
|
2459 nsAutoString keyword(aUserCasedKeyword); |
|
2460 ToLowerCase(keyword); |
|
2461 |
|
2462 // Check if bookmark was already associated to a keyword. |
|
2463 nsAutoString oldKeyword; |
|
2464 rv = GetKeywordForBookmark(bookmark.id, oldKeyword); |
|
2465 NS_ENSURE_SUCCESS(rv, rv); |
|
2466 |
|
2467 // Trying to set the same value or to remove a nonexistent keyword is a no-op. |
|
2468 if (keyword.Equals(oldKeyword) || (keyword.IsEmpty() && oldKeyword.IsEmpty())) |
|
2469 return NS_OK; |
|
2470 |
|
2471 mozStorageTransaction transaction(mDB->MainConn(), false); |
|
2472 |
|
2473 nsCOMPtr<mozIStorageStatement> updateBookmarkStmt = mDB->GetStatement( |
|
2474 "UPDATE moz_bookmarks " |
|
2475 "SET keyword_id = (SELECT id FROM moz_keywords WHERE keyword = :keyword), " |
|
2476 "lastModified = :date " |
|
2477 "WHERE id = :item_id " |
|
2478 ); |
|
2479 NS_ENSURE_STATE(updateBookmarkStmt); |
|
2480 mozStorageStatementScoper updateBookmarkScoper(updateBookmarkStmt); |
|
2481 |
|
2482 if (keyword.IsEmpty()) { |
|
2483 // Remove keyword association from the hash. |
|
2484 mBookmarkToKeywordHash.Remove(bookmark.id); |
|
2485 rv = updateBookmarkStmt->BindNullByName(NS_LITERAL_CSTRING("keyword")); |
|
2486 } |
|
2487 else { |
|
2488 // We are associating bookmark to a new keyword. Create a new keyword |
|
2489 // record if needed. |
|
2490 nsCOMPtr<mozIStorageStatement> newKeywordStmt = mDB->GetStatement( |
|
2491 "INSERT OR IGNORE INTO moz_keywords (keyword) VALUES (:keyword)" |
|
2492 ); |
|
2493 NS_ENSURE_STATE(newKeywordStmt); |
|
2494 mozStorageStatementScoper newKeywordScoper(newKeywordStmt); |
|
2495 |
|
2496 rv = newKeywordStmt->BindStringByName(NS_LITERAL_CSTRING("keyword"), |
|
2497 keyword); |
|
2498 NS_ENSURE_SUCCESS(rv, rv); |
|
2499 rv = newKeywordStmt->Execute(); |
|
2500 NS_ENSURE_SUCCESS(rv, rv); |
|
2501 |
|
2502 // Add new keyword association to the hash, removing the old one if needed. |
|
2503 if (!oldKeyword.IsEmpty()) |
|
2504 mBookmarkToKeywordHash.Remove(bookmark.id); |
|
2505 mBookmarkToKeywordHash.Put(bookmark.id, keyword); |
|
2506 rv = updateBookmarkStmt->BindStringByName(NS_LITERAL_CSTRING("keyword"), keyword); |
|
2507 } |
|
2508 NS_ENSURE_SUCCESS(rv, rv); |
|
2509 bookmark.lastModified = PR_Now(); |
|
2510 rv = updateBookmarkStmt->BindInt64ByName(NS_LITERAL_CSTRING("date"), |
|
2511 bookmark.lastModified); |
|
2512 NS_ENSURE_SUCCESS(rv, rv); |
|
2513 rv = updateBookmarkStmt->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), |
|
2514 bookmark.id); |
|
2515 NS_ENSURE_SUCCESS(rv, rv); |
|
2516 rv = updateBookmarkStmt->Execute(); |
|
2517 NS_ENSURE_SUCCESS(rv, rv); |
|
2518 |
|
2519 rv = transaction.Commit(); |
|
2520 NS_ENSURE_SUCCESS(rv, rv); |
|
2521 |
|
2522 // Update the cache entry, if needed. |
|
2523 BookmarkKeyClass* key = mRecentBookmarksCache.GetEntry(aBookmarkId); |
|
2524 if (key) { |
|
2525 key->bookmark.lastModified = bookmark.lastModified; |
|
2526 } |
|
2527 |
|
2528 NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers, |
|
2529 nsINavBookmarkObserver, |
|
2530 OnItemChanged(bookmark.id, |
|
2531 NS_LITERAL_CSTRING("keyword"), |
|
2532 false, |
|
2533 NS_ConvertUTF16toUTF8(keyword), |
|
2534 bookmark.lastModified, |
|
2535 bookmark.type, |
|
2536 bookmark.parentId, |
|
2537 bookmark.guid, |
|
2538 bookmark.parentGuid)); |
|
2539 |
|
2540 return NS_OK; |
|
2541 } |
|
2542 |
|
2543 |
|
2544 NS_IMETHODIMP |
|
2545 nsNavBookmarks::GetKeywordForURI(nsIURI* aURI, nsAString& aKeyword) |
|
2546 { |
|
2547 NS_ENSURE_ARG(aURI); |
|
2548 aKeyword.Truncate(0); |
|
2549 |
|
2550 nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement( |
|
2551 "SELECT k.keyword " |
|
2552 "FROM moz_places h " |
|
2553 "JOIN moz_bookmarks b ON b.fk = h.id " |
|
2554 "JOIN moz_keywords k ON k.id = b.keyword_id " |
|
2555 "WHERE h.url = :page_url " |
|
2556 ); |
|
2557 NS_ENSURE_STATE(stmt); |
|
2558 mozStorageStatementScoper scoper(stmt); |
|
2559 |
|
2560 nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), aURI); |
|
2561 NS_ENSURE_SUCCESS(rv, rv); |
|
2562 |
|
2563 bool hasMore = false; |
|
2564 rv = stmt->ExecuteStep(&hasMore); |
|
2565 if (NS_FAILED(rv) || !hasMore) { |
|
2566 aKeyword.SetIsVoid(true); |
|
2567 return NS_OK; // not found: return void keyword string |
|
2568 } |
|
2569 |
|
2570 // found, get the keyword |
|
2571 rv = stmt->GetString(0, aKeyword); |
|
2572 NS_ENSURE_SUCCESS(rv, rv); |
|
2573 return NS_OK; |
|
2574 } |
|
2575 |
|
2576 |
|
2577 NS_IMETHODIMP |
|
2578 nsNavBookmarks::GetKeywordForBookmark(int64_t aBookmarkId, nsAString& aKeyword) |
|
2579 { |
|
2580 NS_ENSURE_ARG_MIN(aBookmarkId, 1); |
|
2581 aKeyword.Truncate(0); |
|
2582 |
|
2583 nsresult rv = EnsureKeywordsHash(); |
|
2584 NS_ENSURE_SUCCESS(rv, rv); |
|
2585 |
|
2586 nsAutoString keyword; |
|
2587 if (!mBookmarkToKeywordHash.Get(aBookmarkId, &keyword)) { |
|
2588 aKeyword.SetIsVoid(true); |
|
2589 } |
|
2590 else { |
|
2591 aKeyword.Assign(keyword); |
|
2592 } |
|
2593 |
|
2594 return NS_OK; |
|
2595 } |
|
2596 |
|
2597 |
|
2598 NS_IMETHODIMP |
|
2599 nsNavBookmarks::GetURIForKeyword(const nsAString& aUserCasedKeyword, |
|
2600 nsIURI** aURI) |
|
2601 { |
|
2602 NS_ENSURE_ARG_POINTER(aURI); |
|
2603 NS_ENSURE_TRUE(!aUserCasedKeyword.IsEmpty(), NS_ERROR_INVALID_ARG); |
|
2604 *aURI = nullptr; |
|
2605 |
|
2606 // Shortcuts are always lowercased internally. |
|
2607 nsAutoString keyword(aUserCasedKeyword); |
|
2608 ToLowerCase(keyword); |
|
2609 |
|
2610 nsresult rv = EnsureKeywordsHash(); |
|
2611 NS_ENSURE_SUCCESS(rv, rv); |
|
2612 |
|
2613 keywordSearchData searchData; |
|
2614 searchData.keyword.Assign(keyword); |
|
2615 searchData.itemId = -1; |
|
2616 mBookmarkToKeywordHash.EnumerateRead(SearchBookmarkForKeyword, &searchData); |
|
2617 |
|
2618 if (searchData.itemId == -1) { |
|
2619 // Not found. |
|
2620 return NS_OK; |
|
2621 } |
|
2622 |
|
2623 rv = GetBookmarkURI(searchData.itemId, aURI); |
|
2624 NS_ENSURE_SUCCESS(rv, rv); |
|
2625 |
|
2626 return NS_OK; |
|
2627 } |
|
2628 |
|
2629 |
|
2630 nsresult |
|
2631 nsNavBookmarks::EnsureKeywordsHash() { |
|
2632 if (mBookmarkToKeywordHashInitialized) { |
|
2633 return NS_OK; |
|
2634 } |
|
2635 mBookmarkToKeywordHashInitialized = true; |
|
2636 |
|
2637 nsCOMPtr<mozIStorageStatement> stmt; |
|
2638 nsresult rv = mDB->MainConn()->CreateStatement(NS_LITERAL_CSTRING( |
|
2639 "SELECT b.id, k.keyword " |
|
2640 "FROM moz_bookmarks b " |
|
2641 "JOIN moz_keywords k ON k.id = b.keyword_id " |
|
2642 ), getter_AddRefs(stmt)); |
|
2643 NS_ENSURE_SUCCESS(rv, rv); |
|
2644 |
|
2645 bool hasMore; |
|
2646 while (NS_SUCCEEDED(stmt->ExecuteStep(&hasMore)) && hasMore) { |
|
2647 int64_t itemId; |
|
2648 rv = stmt->GetInt64(0, &itemId); |
|
2649 NS_ENSURE_SUCCESS(rv, rv); |
|
2650 nsAutoString keyword; |
|
2651 rv = stmt->GetString(1, keyword); |
|
2652 NS_ENSURE_SUCCESS(rv, rv); |
|
2653 |
|
2654 mBookmarkToKeywordHash.Put(itemId, keyword); |
|
2655 } |
|
2656 |
|
2657 return NS_OK; |
|
2658 } |
|
2659 |
|
2660 |
|
2661 NS_IMETHODIMP |
|
2662 nsNavBookmarks::RunInBatchMode(nsINavHistoryBatchCallback* aCallback, |
|
2663 nsISupports* aUserData) { |
|
2664 PROFILER_LABEL("bookmarks", "RunInBatchMode"); |
|
2665 NS_ENSURE_ARG(aCallback); |
|
2666 |
|
2667 mBatching = true; |
|
2668 |
|
2669 // Just forward the request to history. History service must exist for |
|
2670 // bookmarks to work and we are observing it, thus batch notifications will be |
|
2671 // forwarded to bookmarks observers. |
|
2672 nsNavHistory* history = nsNavHistory::GetHistoryService(); |
|
2673 NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY); |
|
2674 nsresult rv = history->RunInBatchMode(aCallback, aUserData); |
|
2675 NS_ENSURE_SUCCESS(rv, rv); |
|
2676 |
|
2677 return NS_OK; |
|
2678 } |
|
2679 |
|
2680 |
|
2681 NS_IMETHODIMP |
|
2682 nsNavBookmarks::AddObserver(nsINavBookmarkObserver* aObserver, |
|
2683 bool aOwnsWeak) |
|
2684 { |
|
2685 NS_ENSURE_ARG(aObserver); |
|
2686 return mObservers.AppendWeakElement(aObserver, aOwnsWeak); |
|
2687 } |
|
2688 |
|
2689 |
|
2690 NS_IMETHODIMP |
|
2691 nsNavBookmarks::RemoveObserver(nsINavBookmarkObserver* aObserver) |
|
2692 { |
|
2693 return mObservers.RemoveWeakElement(aObserver); |
|
2694 } |
|
2695 |
|
2696 void |
|
2697 nsNavBookmarks::NotifyItemVisited(const ItemVisitData& aData) |
|
2698 { |
|
2699 nsCOMPtr<nsIURI> uri; |
|
2700 (void)NS_NewURI(getter_AddRefs(uri), aData.bookmark.url); |
|
2701 NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers, |
|
2702 nsINavBookmarkObserver, |
|
2703 OnItemVisited(aData.bookmark.id, |
|
2704 aData.visitId, |
|
2705 aData.time, |
|
2706 aData.transitionType, |
|
2707 uri, |
|
2708 aData.bookmark.parentId, |
|
2709 aData.bookmark.guid, |
|
2710 aData.bookmark.parentGuid)); |
|
2711 } |
|
2712 |
|
2713 void |
|
2714 nsNavBookmarks::NotifyItemChanged(const ItemChangeData& aData) |
|
2715 { |
|
2716 // A guid must always be defined. |
|
2717 MOZ_ASSERT(!aData.bookmark.guid.IsEmpty()); |
|
2718 NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers, |
|
2719 nsINavBookmarkObserver, |
|
2720 OnItemChanged(aData.bookmark.id, |
|
2721 aData.property, |
|
2722 aData.isAnnotation, |
|
2723 aData.newValue, |
|
2724 aData.bookmark.lastModified, |
|
2725 aData.bookmark.type, |
|
2726 aData.bookmark.parentId, |
|
2727 aData.bookmark.guid, |
|
2728 aData.bookmark.parentGuid)); |
|
2729 } |
|
2730 |
|
2731 //////////////////////////////////////////////////////////////////////////////// |
|
2732 //// nsIObserver |
|
2733 |
|
2734 NS_IMETHODIMP |
|
2735 nsNavBookmarks::Observe(nsISupports *aSubject, const char *aTopic, |
|
2736 const char16_t *aData) |
|
2737 { |
|
2738 NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread"); |
|
2739 |
|
2740 if (strcmp(aTopic, TOPIC_PLACES_MAINTENANCE) == 0) { |
|
2741 // Maintenance can execute direct writes to the database, thus clear all |
|
2742 // the cached bookmarks. |
|
2743 mRecentBookmarksCache.Clear(); |
|
2744 } |
|
2745 else if (strcmp(aTopic, TOPIC_PLACES_SHUTDOWN) == 0) { |
|
2746 // Stop Observing annotations. |
|
2747 nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService(); |
|
2748 if (annosvc) { |
|
2749 annosvc->RemoveObserver(this); |
|
2750 } |
|
2751 } |
|
2752 else if (strcmp(aTopic, TOPIC_PLACES_CONNECTION_CLOSED) == 0) { |
|
2753 // Don't even try to notify observers from this point on, the category |
|
2754 // cache would init services that could try to use our APIs. |
|
2755 mCanNotify = false; |
|
2756 } |
|
2757 |
|
2758 return NS_OK; |
|
2759 } |
|
2760 |
|
2761 //////////////////////////////////////////////////////////////////////////////// |
|
2762 //// nsINavHistoryObserver |
|
2763 |
|
2764 NS_IMETHODIMP |
|
2765 nsNavBookmarks::OnBeginUpdateBatch() |
|
2766 { |
|
2767 NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers, |
|
2768 nsINavBookmarkObserver, OnBeginUpdateBatch()); |
|
2769 return NS_OK; |
|
2770 } |
|
2771 |
|
2772 |
|
2773 NS_IMETHODIMP |
|
2774 nsNavBookmarks::OnEndUpdateBatch() |
|
2775 { |
|
2776 if (mBatching) { |
|
2777 mBatching = false; |
|
2778 } |
|
2779 |
|
2780 NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers, |
|
2781 nsINavBookmarkObserver, OnEndUpdateBatch()); |
|
2782 return NS_OK; |
|
2783 } |
|
2784 |
|
2785 |
|
2786 NS_IMETHODIMP |
|
2787 nsNavBookmarks::OnVisit(nsIURI* aURI, int64_t aVisitId, PRTime aTime, |
|
2788 int64_t aSessionID, int64_t aReferringID, |
|
2789 uint32_t aTransitionType, const nsACString& aGUID, |
|
2790 bool aHidden) |
|
2791 { |
|
2792 // If the page is bookmarked, notify observers for each associated bookmark. |
|
2793 ItemVisitData visitData; |
|
2794 nsresult rv = aURI->GetSpec(visitData.bookmark.url); |
|
2795 NS_ENSURE_SUCCESS(rv, rv); |
|
2796 visitData.visitId = aVisitId; |
|
2797 visitData.time = aTime; |
|
2798 visitData.transitionType = aTransitionType; |
|
2799 |
|
2800 nsRefPtr< AsyncGetBookmarksForURI<ItemVisitMethod, ItemVisitData> > notifier = |
|
2801 new AsyncGetBookmarksForURI<ItemVisitMethod, ItemVisitData>(this, &nsNavBookmarks::NotifyItemVisited, visitData); |
|
2802 notifier->Init(); |
|
2803 return NS_OK; |
|
2804 } |
|
2805 |
|
2806 |
|
2807 NS_IMETHODIMP |
|
2808 nsNavBookmarks::OnDeleteURI(nsIURI* aURI, |
|
2809 const nsACString& aGUID, |
|
2810 uint16_t aReason) |
|
2811 { |
|
2812 #ifdef DEBUG |
|
2813 nsNavHistory* history = nsNavHistory::GetHistoryService(); |
|
2814 int64_t placeId; |
|
2815 nsAutoCString placeGuid; |
|
2816 NS_ABORT_IF_FALSE( |
|
2817 history && NS_SUCCEEDED(history->GetIdForPage(aURI, &placeId, placeGuid)) && !placeId, |
|
2818 "OnDeleteURI was notified for a page that still exists?" |
|
2819 ); |
|
2820 #endif |
|
2821 return NS_OK; |
|
2822 } |
|
2823 |
|
2824 |
|
2825 NS_IMETHODIMP |
|
2826 nsNavBookmarks::OnClearHistory() |
|
2827 { |
|
2828 // TODO(bryner): we should notify on visited-time change for all URIs |
|
2829 return NS_OK; |
|
2830 } |
|
2831 |
|
2832 |
|
2833 NS_IMETHODIMP |
|
2834 nsNavBookmarks::OnTitleChanged(nsIURI* aURI, |
|
2835 const nsAString& aPageTitle, |
|
2836 const nsACString& aGUID) |
|
2837 { |
|
2838 // NOOP. We don't consume page titles from moz_places anymore. |
|
2839 // Title-change notifications are sent from SetItemTitle. |
|
2840 return NS_OK; |
|
2841 } |
|
2842 |
|
2843 |
|
2844 NS_IMETHODIMP |
|
2845 nsNavBookmarks::OnFrecencyChanged(nsIURI* aURI, |
|
2846 int32_t aNewFrecency, |
|
2847 const nsACString& aGUID, |
|
2848 bool aHidden, |
|
2849 PRTime aLastVisitDate) |
|
2850 { |
|
2851 return NS_OK; |
|
2852 } |
|
2853 |
|
2854 |
|
2855 NS_IMETHODIMP |
|
2856 nsNavBookmarks::OnManyFrecenciesChanged() |
|
2857 { |
|
2858 return NS_OK; |
|
2859 } |
|
2860 |
|
2861 |
|
2862 NS_IMETHODIMP |
|
2863 nsNavBookmarks::OnPageChanged(nsIURI* aURI, |
|
2864 uint32_t aChangedAttribute, |
|
2865 const nsAString& aNewValue, |
|
2866 const nsACString& aGUID) |
|
2867 { |
|
2868 nsresult rv; |
|
2869 if (aChangedAttribute == nsINavHistoryObserver::ATTRIBUTE_FAVICON) { |
|
2870 ItemChangeData changeData; |
|
2871 rv = aURI->GetSpec(changeData.bookmark.url); |
|
2872 NS_ENSURE_SUCCESS(rv, rv); |
|
2873 changeData.property = NS_LITERAL_CSTRING("favicon"); |
|
2874 changeData.isAnnotation = false; |
|
2875 changeData.newValue = NS_ConvertUTF16toUTF8(aNewValue); |
|
2876 changeData.bookmark.lastModified = 0; |
|
2877 changeData.bookmark.type = TYPE_BOOKMARK; |
|
2878 |
|
2879 // Favicons may be set to either pure URIs or to folder URIs |
|
2880 bool isPlaceURI; |
|
2881 rv = aURI->SchemeIs("place", &isPlaceURI); |
|
2882 NS_ENSURE_SUCCESS(rv, rv); |
|
2883 if (isPlaceURI) { |
|
2884 nsNavHistory* history = nsNavHistory::GetHistoryService(); |
|
2885 NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY); |
|
2886 |
|
2887 nsCOMArray<nsNavHistoryQuery> queries; |
|
2888 nsCOMPtr<nsNavHistoryQueryOptions> options; |
|
2889 rv = history->QueryStringToQueryArray(changeData.bookmark.url, |
|
2890 &queries, getter_AddRefs(options)); |
|
2891 NS_ENSURE_SUCCESS(rv, rv); |
|
2892 |
|
2893 if (queries.Count() == 1 && queries[0]->Folders().Length() == 1) { |
|
2894 // Fetch missing data. |
|
2895 rv = FetchItemInfo(queries[0]->Folders()[0], changeData.bookmark); |
|
2896 NS_ENSURE_SUCCESS(rv, rv); |
|
2897 NotifyItemChanged(changeData); |
|
2898 } |
|
2899 } |
|
2900 else { |
|
2901 nsRefPtr< AsyncGetBookmarksForURI<ItemChangeMethod, ItemChangeData> > notifier = |
|
2902 new AsyncGetBookmarksForURI<ItemChangeMethod, ItemChangeData>(this, &nsNavBookmarks::NotifyItemChanged, changeData); |
|
2903 notifier->Init(); |
|
2904 } |
|
2905 } |
|
2906 return NS_OK; |
|
2907 } |
|
2908 |
|
2909 |
|
2910 NS_IMETHODIMP |
|
2911 nsNavBookmarks::OnDeleteVisits(nsIURI* aURI, PRTime aVisitTime, |
|
2912 const nsACString& aGUID, |
|
2913 uint16_t aReason, uint32_t aTransitionType) |
|
2914 { |
|
2915 // Notify "cleartime" only if all visits to the page have been removed. |
|
2916 if (!aVisitTime) { |
|
2917 // If the page is bookmarked, notify observers for each associated bookmark. |
|
2918 ItemChangeData changeData; |
|
2919 nsresult rv = aURI->GetSpec(changeData.bookmark.url); |
|
2920 NS_ENSURE_SUCCESS(rv, rv); |
|
2921 changeData.property = NS_LITERAL_CSTRING("cleartime"); |
|
2922 changeData.isAnnotation = false; |
|
2923 changeData.bookmark.lastModified = 0; |
|
2924 changeData.bookmark.type = TYPE_BOOKMARK; |
|
2925 |
|
2926 nsRefPtr< AsyncGetBookmarksForURI<ItemChangeMethod, ItemChangeData> > notifier = |
|
2927 new AsyncGetBookmarksForURI<ItemChangeMethod, ItemChangeData>(this, &nsNavBookmarks::NotifyItemChanged, changeData); |
|
2928 notifier->Init(); |
|
2929 } |
|
2930 return NS_OK; |
|
2931 } |
|
2932 |
|
2933 |
|
2934 // nsIAnnotationObserver |
|
2935 |
|
2936 NS_IMETHODIMP |
|
2937 nsNavBookmarks::OnPageAnnotationSet(nsIURI* aPage, const nsACString& aName) |
|
2938 { |
|
2939 return NS_OK; |
|
2940 } |
|
2941 |
|
2942 |
|
2943 NS_IMETHODIMP |
|
2944 nsNavBookmarks::OnItemAnnotationSet(int64_t aItemId, const nsACString& aName) |
|
2945 { |
|
2946 BookmarkData bookmark; |
|
2947 nsresult rv = FetchItemInfo(aItemId, bookmark); |
|
2948 NS_ENSURE_SUCCESS(rv, rv); |
|
2949 |
|
2950 bookmark.lastModified = PR_Now(); |
|
2951 rv = SetItemDateInternal(LAST_MODIFIED, bookmark.id, bookmark.lastModified); |
|
2952 NS_ENSURE_SUCCESS(rv, rv); |
|
2953 |
|
2954 NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers, |
|
2955 nsINavBookmarkObserver, |
|
2956 OnItemChanged(bookmark.id, |
|
2957 aName, |
|
2958 true, |
|
2959 EmptyCString(), |
|
2960 bookmark.lastModified, |
|
2961 bookmark.type, |
|
2962 bookmark.parentId, |
|
2963 bookmark.guid, |
|
2964 bookmark.parentGuid)); |
|
2965 return NS_OK; |
|
2966 } |
|
2967 |
|
2968 |
|
2969 NS_IMETHODIMP |
|
2970 nsNavBookmarks::OnPageAnnotationRemoved(nsIURI* aPage, const nsACString& aName) |
|
2971 { |
|
2972 return NS_OK; |
|
2973 } |
|
2974 |
|
2975 |
|
2976 NS_IMETHODIMP |
|
2977 nsNavBookmarks::OnItemAnnotationRemoved(int64_t aItemId, const nsACString& aName) |
|
2978 { |
|
2979 // As of now this is doing the same as OnItemAnnotationSet, so just forward |
|
2980 // the call. |
|
2981 nsresult rv = OnItemAnnotationSet(aItemId, aName); |
|
2982 NS_ENSURE_SUCCESS(rv, rv); |
|
2983 return NS_OK; |
|
2984 } |