/src/mozilla-central/toolkit/components/places/nsNavBookmarks.cpp
Line | Count | Source (jump to first uncovered line) |
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 "nsQueryObject.h" |
18 | | #include "mozilla/Preferences.h" |
19 | | #include "mozilla/storage.h" |
20 | | #include "mozilla/dom/PlacesObservers.h" |
21 | | #include "mozilla/dom/PlacesVisit.h" |
22 | | |
23 | | #include "GeckoProfiler.h" |
24 | | |
25 | | using namespace mozilla; |
26 | | |
27 | | // These columns sit to the right of the kGetInfoIndex_* columns. |
28 | | const int32_t nsNavBookmarks::kGetChildrenIndex_Guid = 18; |
29 | | const int32_t nsNavBookmarks::kGetChildrenIndex_Position = 19; |
30 | | const int32_t nsNavBookmarks::kGetChildrenIndex_Type = 20; |
31 | | const int32_t nsNavBookmarks::kGetChildrenIndex_PlaceID = 21; |
32 | | const int32_t nsNavBookmarks::kGetChildrenIndex_SyncStatus = 22; |
33 | | |
34 | | using namespace mozilla::places; |
35 | | |
36 | | PLACES_FACTORY_SINGLETON_IMPLEMENTATION(nsNavBookmarks, gBookmarksService) |
37 | | |
38 | | #define BOOKMARKS_ANNO_PREFIX "bookmarks/" |
39 | | #define BOOKMARKS_TOOLBAR_FOLDER_ANNO NS_LITERAL_CSTRING(BOOKMARKS_ANNO_PREFIX "toolbarFolder") |
40 | 0 | #define FEED_URI_ANNO NS_LITERAL_CSTRING("livemark/feedURI") |
41 | | #define SYNC_PARENT_ANNO "sync/parent" |
42 | 0 | #define SQLITE_MAX_VARIABLE_NUMBER 999 |
43 | | |
44 | | |
45 | | namespace { |
46 | | |
47 | | #define SKIP_TAGS(condition) ((condition) ? SkipTags : DontSkip) |
48 | | |
49 | 0 | bool DontSkip(nsCOMPtr<nsINavBookmarkObserver> obs) { return false; } |
50 | 0 | bool SkipTags(nsCOMPtr<nsINavBookmarkObserver> obs) { |
51 | 0 | bool skipTags = false; |
52 | 0 | (void) obs->GetSkipTags(&skipTags); |
53 | 0 | return skipTags; |
54 | 0 | } |
55 | 0 | bool SkipDescendants(nsCOMPtr<nsINavBookmarkObserver> obs) { |
56 | 0 | bool skipDescendantsOnItemRemoval = false; |
57 | 0 | (void) obs->GetSkipDescendantsOnItemRemoval(&skipDescendantsOnItemRemoval); |
58 | 0 | return skipDescendantsOnItemRemoval; |
59 | 0 | } |
60 | | |
61 | | template<typename Method, typename DataType> |
62 | | class AsyncGetBookmarksForURI : public AsyncStatementCallback |
63 | | { |
64 | | public: |
65 | | AsyncGetBookmarksForURI(nsNavBookmarks* aBookmarksSvc, |
66 | | Method aCallback, |
67 | | const DataType& aData) |
68 | | : mBookmarksSvc(aBookmarksSvc) |
69 | | , mCallback(aCallback) |
70 | | , mData(aData) |
71 | 0 | { |
72 | 0 | } Unexecuted instantiation: Unified_cpp_components_places0.cpp:(anonymous namespace)::AsyncGetBookmarksForURI<void (nsNavBookmarks::*)(mozilla::places::ItemVisitData const&), mozilla::places::ItemVisitData>::AsyncGetBookmarksForURI(nsNavBookmarks*, void (nsNavBookmarks::*)(mozilla::places::ItemVisitData const&), mozilla::places::ItemVisitData const&) Unexecuted instantiation: Unified_cpp_components_places0.cpp:(anonymous namespace)::AsyncGetBookmarksForURI<void (nsNavBookmarks::*)(mozilla::places::ItemChangeData const&), mozilla::places::ItemChangeData>::AsyncGetBookmarksForURI(nsNavBookmarks*, void (nsNavBookmarks::*)(mozilla::places::ItemChangeData const&), mozilla::places::ItemChangeData const&) |
73 | | |
74 | | void Init() |
75 | 0 | { |
76 | 0 | RefPtr<Database> DB = Database::GetDatabase(); |
77 | 0 | if (DB) { |
78 | 0 | nsCOMPtr<mozIStorageAsyncStatement> stmt = DB->GetAsyncStatement( |
79 | 0 | "/* do not warn (bug 1175249) */ " |
80 | 0 | "SELECT b.id, b.guid, b.parent, b.lastModified, t.guid, t.parent " |
81 | 0 | "FROM moz_bookmarks b " |
82 | 0 | "JOIN moz_bookmarks t on t.id = b.parent " |
83 | 0 | "WHERE b.fk = (SELECT id FROM moz_places WHERE url_hash = hash(:page_url) AND url = :page_url) " |
84 | 0 | "ORDER BY b.lastModified DESC, b.id DESC " |
85 | 0 | ); |
86 | 0 | if (stmt) { |
87 | 0 | (void)URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), |
88 | 0 | mData.bookmark.url); |
89 | 0 | nsCOMPtr<mozIStoragePendingStatement> pendingStmt; |
90 | 0 | (void)stmt->ExecuteAsync(this, getter_AddRefs(pendingStmt)); |
91 | 0 | } |
92 | 0 | } |
93 | 0 | } Unexecuted instantiation: Unified_cpp_components_places0.cpp:(anonymous namespace)::AsyncGetBookmarksForURI<void (nsNavBookmarks::*)(mozilla::places::ItemVisitData const&), mozilla::places::ItemVisitData>::Init() Unexecuted instantiation: Unified_cpp_components_places0.cpp:(anonymous namespace)::AsyncGetBookmarksForURI<void (nsNavBookmarks::*)(mozilla::places::ItemChangeData const&), mozilla::places::ItemChangeData>::Init() |
94 | | |
95 | | NS_IMETHOD HandleResult(mozIStorageResultSet* aResultSet) override |
96 | 0 | { |
97 | 0 | nsCOMPtr<mozIStorageRow> row; |
98 | 0 | while (NS_SUCCEEDED(aResultSet->GetNextRow(getter_AddRefs(row))) && row) { |
99 | 0 | // Skip tags, for the use-cases of this async getter they are useless. |
100 | 0 | int64_t grandParentId = -1, tagsFolderId = -1; |
101 | 0 | nsresult rv = row->GetInt64(5, &grandParentId); |
102 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
103 | 0 | rv = mBookmarksSvc->GetTagsFolder(&tagsFolderId); |
104 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
105 | 0 | if (grandParentId == tagsFolderId) { |
106 | 0 | continue; |
107 | 0 | } |
108 | 0 | |
109 | 0 | mData.bookmark.grandParentId = grandParentId; |
110 | 0 | rv = row->GetInt64(0, &mData.bookmark.id); |
111 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
112 | 0 | rv = row->GetUTF8String(1, mData.bookmark.guid); |
113 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
114 | 0 | rv = row->GetInt64(2, &mData.bookmark.parentId); |
115 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
116 | 0 | // lastModified (3) should not be set for the use-cases of this getter. |
117 | 0 | rv = row->GetUTF8String(4, mData.bookmark.parentGuid); |
118 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
119 | 0 |
|
120 | 0 | if (mCallback) { |
121 | 0 | ((*mBookmarksSvc).*mCallback)(mData); |
122 | 0 | } |
123 | 0 | } |
124 | 0 | return NS_OK; |
125 | 0 | } Unexecuted instantiation: Unified_cpp_components_places0.cpp:(anonymous namespace)::AsyncGetBookmarksForURI<void (nsNavBookmarks::*)(mozilla::places::ItemVisitData const&), mozilla::places::ItemVisitData>::HandleResult(mozIStorageResultSet*) Unexecuted instantiation: Unified_cpp_components_places0.cpp:(anonymous namespace)::AsyncGetBookmarksForURI<void (nsNavBookmarks::*)(mozilla::places::ItemChangeData const&), mozilla::places::ItemChangeData>::HandleResult(mozIStorageResultSet*) |
126 | | |
127 | | private: |
128 | | RefPtr<nsNavBookmarks> mBookmarksSvc; |
129 | | Method mCallback; |
130 | | DataType mData; |
131 | | }; |
132 | | |
133 | | // Returns the sync change counter increment for a change source constant. |
134 | | inline int64_t |
135 | 0 | DetermineSyncChangeDelta(uint16_t aSource) { |
136 | 0 | return aSource == nsINavBookmarksService::SOURCE_SYNC ? 0 : 1; |
137 | 0 | } |
138 | | |
139 | | // Returns the sync status for a new item inserted by a change source. |
140 | | inline int32_t |
141 | 0 | DetermineInitialSyncStatus(uint16_t aSource) { |
142 | 0 | if (aSource == nsINavBookmarksService::SOURCE_SYNC) { |
143 | 0 | return nsINavBookmarksService::SYNC_STATUS_NORMAL; |
144 | 0 | } |
145 | 0 | if (aSource == nsINavBookmarksService::SOURCE_RESTORE_ON_STARTUP) { |
146 | 0 | return nsINavBookmarksService::SYNC_STATUS_UNKNOWN; |
147 | 0 | } |
148 | 0 | return nsINavBookmarksService::SYNC_STATUS_NEW; |
149 | 0 | } |
150 | | |
151 | | // Indicates whether an item has been uploaded to the server and |
152 | | // needs a tombstone on deletion. |
153 | | inline bool |
154 | 0 | NeedsTombstone(const BookmarkData& aBookmark) { |
155 | 0 | return aBookmark.syncStatus == nsINavBookmarksService::SYNC_STATUS_NORMAL; |
156 | 0 | } |
157 | | |
158 | | } // namespace |
159 | | |
160 | | |
161 | | nsNavBookmarks::nsNavBookmarks() |
162 | | : mCanNotify(false) |
163 | 0 | { |
164 | 0 | NS_ASSERTION(!gBookmarksService, |
165 | 0 | "Attempting to create two instances of the service!"); |
166 | 0 | gBookmarksService = this; |
167 | 0 | } |
168 | | |
169 | | |
170 | | nsNavBookmarks::~nsNavBookmarks() |
171 | 0 | { |
172 | 0 | NS_ASSERTION(gBookmarksService == this, |
173 | 0 | "Deleting a non-singleton instance of the service"); |
174 | 0 | if (gBookmarksService == this) |
175 | 0 | gBookmarksService = nullptr; |
176 | 0 | } |
177 | | |
178 | | |
179 | | NS_IMPL_ISUPPORTS(nsNavBookmarks |
180 | | , nsINavBookmarksService |
181 | | , nsINavHistoryObserver |
182 | | , nsIObserver |
183 | | , nsISupportsWeakReference |
184 | | ) |
185 | | |
186 | | |
187 | | Atomic<int64_t> nsNavBookmarks::sLastInsertedItemId(0); |
188 | | |
189 | | |
190 | | void // static |
191 | | nsNavBookmarks::StoreLastInsertedId(const nsACString& aTable, |
192 | 0 | const int64_t aLastInsertedId) { |
193 | 0 | MOZ_ASSERT(aTable.EqualsLiteral("moz_bookmarks")); |
194 | 0 | sLastInsertedItemId = aLastInsertedId; |
195 | 0 | } |
196 | | |
197 | | |
198 | | Atomic<int64_t> nsNavBookmarks::sTotalSyncChanges(0); |
199 | | |
200 | | |
201 | | void // static |
202 | | nsNavBookmarks::NoteSyncChange() |
203 | 0 | { |
204 | 0 | sTotalSyncChanges++; |
205 | 0 | } |
206 | | |
207 | | |
208 | | nsresult |
209 | | nsNavBookmarks::Init() |
210 | 0 | { |
211 | 0 | mDB = Database::GetDatabase(); |
212 | 0 | NS_ENSURE_STATE(mDB); |
213 | 0 |
|
214 | 0 | nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); |
215 | 0 | if (os) { |
216 | 0 | (void)os->AddObserver(this, TOPIC_PLACES_CONNECTION_CLOSED, true); |
217 | 0 | } |
218 | 0 |
|
219 | 0 | mCanNotify = true; |
220 | 0 |
|
221 | 0 | // Allows us to notify on title changes. MUST BE LAST so it is impossible |
222 | 0 | // to fail after this call, or the history service will have a reference to |
223 | 0 | // us and we won't go away. |
224 | 0 | nsNavHistory* history = nsNavHistory::GetHistoryService(); |
225 | 0 | NS_ENSURE_STATE(history); |
226 | 0 | history->AddObserver(this, true); |
227 | 0 | AutoTArray<PlacesEventType, 1> events; |
228 | 0 | events.AppendElement(PlacesEventType::Page_visited); |
229 | 0 | PlacesObservers::AddListener(events, this); |
230 | 0 |
|
231 | 0 | // DO NOT PUT STUFF HERE that can fail. See observer comment above. |
232 | 0 |
|
233 | 0 | return NS_OK; |
234 | 0 | } |
235 | | |
236 | | nsresult |
237 | | nsNavBookmarks::AdjustIndices(int64_t aFolderId, |
238 | | int32_t aStartIndex, |
239 | | int32_t aEndIndex, |
240 | | int32_t aDelta) |
241 | 0 | { |
242 | 0 | NS_ASSERTION(aStartIndex >= 0 && aEndIndex <= INT32_MAX && |
243 | 0 | aStartIndex <= aEndIndex, "Bad indices"); |
244 | 0 |
|
245 | 0 | nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement( |
246 | 0 | "UPDATE moz_bookmarks SET position = position + :delta " |
247 | 0 | "WHERE parent = :parent " |
248 | 0 | "AND position BETWEEN :from_index AND :to_index" |
249 | 0 | ); |
250 | 0 | NS_ENSURE_STATE(stmt); |
251 | 0 | mozStorageStatementScoper scoper(stmt); |
252 | 0 |
|
253 | 0 | nsresult rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("delta"), aDelta); |
254 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
255 | 0 | rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aFolderId); |
256 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
257 | 0 | rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("from_index"), aStartIndex); |
258 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
259 | 0 | rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("to_index"), aEndIndex); |
260 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
261 | 0 |
|
262 | 0 | rv = stmt->Execute(); |
263 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
264 | 0 |
|
265 | 0 | return NS_OK; |
266 | 0 | } |
267 | | |
268 | | |
269 | | nsresult |
270 | | nsNavBookmarks::AdjustSeparatorsSyncCounter(int64_t aFolderId, |
271 | | int32_t aStartIndex, |
272 | | int64_t aSyncChangeDelta) |
273 | 0 | { |
274 | 0 | MOZ_ASSERT(aStartIndex >= 0, "Bad start position"); |
275 | 0 | if (!aSyncChangeDelta) { |
276 | 0 | return NS_OK; |
277 | 0 | } |
278 | 0 | |
279 | 0 | nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement( |
280 | 0 | "UPDATE moz_bookmarks SET syncChangeCounter = syncChangeCounter + :delta " |
281 | 0 | "WHERE parent = :parent AND position >= :start_index " |
282 | 0 | "AND type = :item_type " |
283 | 0 | ); |
284 | 0 | NS_ENSURE_STATE(stmt); |
285 | 0 | mozStorageStatementScoper scoper(stmt); |
286 | 0 |
|
287 | 0 | nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("delta"), aSyncChangeDelta); |
288 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
289 | 0 | rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aFolderId); |
290 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
291 | 0 | rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("start_index"), aStartIndex); |
292 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
293 | 0 | rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("item_type"), TYPE_SEPARATOR); |
294 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
295 | 0 |
|
296 | 0 | rv = stmt->Execute(); |
297 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
298 | 0 |
|
299 | 0 | return NS_OK; |
300 | 0 | } |
301 | | |
302 | | |
303 | | NS_IMETHODIMP |
304 | | nsNavBookmarks::GetPlacesRoot(int64_t* aRoot) |
305 | 0 | { |
306 | 0 | int64_t id = mDB->GetRootFolderId(); |
307 | 0 | NS_ENSURE_TRUE(id > 0, NS_ERROR_UNEXPECTED); |
308 | 0 | *aRoot = id; |
309 | 0 | return NS_OK; |
310 | 0 | } |
311 | | |
312 | | |
313 | | NS_IMETHODIMP |
314 | | nsNavBookmarks::GetBookmarksMenuFolder(int64_t* aRoot) |
315 | 0 | { |
316 | 0 | int64_t id = mDB->GetMenuFolderId(); |
317 | 0 | NS_ENSURE_TRUE(id > 0, NS_ERROR_UNEXPECTED); |
318 | 0 | *aRoot = id; |
319 | 0 | return NS_OK; |
320 | 0 | } |
321 | | |
322 | | |
323 | | NS_IMETHODIMP |
324 | | nsNavBookmarks::GetToolbarFolder(int64_t* aRoot) |
325 | 0 | { |
326 | 0 | int64_t id = mDB->GetToolbarFolderId(); |
327 | 0 | NS_ENSURE_TRUE(id > 0, NS_ERROR_UNEXPECTED); |
328 | 0 | *aRoot = id; |
329 | 0 | return NS_OK; |
330 | 0 | } |
331 | | |
332 | | |
333 | | NS_IMETHODIMP |
334 | | nsNavBookmarks::GetTagsFolder(int64_t* aRoot) |
335 | 0 | { |
336 | 0 | int64_t id = mDB->GetTagsFolderId(); |
337 | 0 | NS_ENSURE_TRUE(id > 0, NS_ERROR_UNEXPECTED); |
338 | 0 | *aRoot = id; |
339 | 0 | return NS_OK; |
340 | 0 | } |
341 | | |
342 | | |
343 | | NS_IMETHODIMP |
344 | | nsNavBookmarks::GetTotalSyncChanges(int64_t* aTotalSyncChanges) |
345 | 0 | { |
346 | 0 | *aTotalSyncChanges = sTotalSyncChanges; |
347 | 0 | return NS_OK; |
348 | 0 | } |
349 | | |
350 | | |
351 | | nsresult |
352 | | nsNavBookmarks::InsertBookmarkInDB(int64_t aPlaceId, |
353 | | enum ItemType aItemType, |
354 | | int64_t aParentId, |
355 | | int32_t aIndex, |
356 | | const nsACString& aTitle, |
357 | | PRTime aDateAdded, |
358 | | PRTime aLastModified, |
359 | | const nsACString& aParentGuid, |
360 | | int64_t aGrandParentId, |
361 | | nsIURI* aURI, |
362 | | uint16_t aSource, |
363 | | int64_t* _itemId, |
364 | | nsACString& _guid) |
365 | 0 | { |
366 | 0 | // Check for a valid itemId. |
367 | 0 | MOZ_ASSERT(_itemId && (*_itemId == -1 || *_itemId > 0)); |
368 | 0 | // Check for a valid placeId. |
369 | 0 | MOZ_ASSERT(aPlaceId && (aPlaceId == -1 || aPlaceId > 0)); |
370 | 0 |
|
371 | 0 | nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement( |
372 | 0 | "INSERT INTO moz_bookmarks " |
373 | 0 | "(id, fk, type, parent, position, title, " |
374 | 0 | "dateAdded, lastModified, guid, syncStatus, syncChangeCounter) " |
375 | 0 | "VALUES (:item_id, :page_id, :item_type, :parent, :item_index, " |
376 | 0 | ":item_title, :date_added, :last_modified, " |
377 | 0 | ":item_guid, :sync_status, :change_counter)" |
378 | 0 | ); |
379 | 0 | NS_ENSURE_STATE(stmt); |
380 | 0 | mozStorageStatementScoper scoper(stmt); |
381 | 0 |
|
382 | 0 | nsresult rv; |
383 | 0 | if (*_itemId != -1) |
384 | 0 | rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), *_itemId); |
385 | 0 | else |
386 | 0 | rv = stmt->BindNullByName(NS_LITERAL_CSTRING("item_id")); |
387 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
388 | 0 |
|
389 | 0 | if (aPlaceId != -1) |
390 | 0 | rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), aPlaceId); |
391 | 0 | else |
392 | 0 | rv = stmt->BindNullByName(NS_LITERAL_CSTRING("page_id")); |
393 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
394 | 0 |
|
395 | 0 | rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("item_type"), aItemType); |
396 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
397 | 0 | rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aParentId); |
398 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
399 | 0 | rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("item_index"), aIndex); |
400 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
401 | 0 |
|
402 | 0 | if (aTitle.IsEmpty()) |
403 | 0 | rv = stmt->BindNullByName(NS_LITERAL_CSTRING("item_title")); |
404 | 0 | else |
405 | 0 | rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("item_title"), aTitle); |
406 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
407 | 0 |
|
408 | 0 | rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("date_added"), aDateAdded); |
409 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
410 | 0 |
|
411 | 0 | if (aLastModified) { |
412 | 0 | rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("last_modified"), |
413 | 0 | aLastModified); |
414 | 0 | } |
415 | 0 | else { |
416 | 0 | rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("last_modified"), aDateAdded); |
417 | 0 | } |
418 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
419 | 0 |
|
420 | 0 | // Could use IsEmpty because our callers check for GUID validity, |
421 | 0 | // but it doesn't hurt. |
422 | 0 | bool hasExistingGuid = _guid.Length() == 12; |
423 | 0 | if (hasExistingGuid) { |
424 | 0 | MOZ_ASSERT(IsValidGUID(_guid)); |
425 | 0 | rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("item_guid"), _guid); |
426 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
427 | 0 | } |
428 | 0 | else { |
429 | 0 | nsAutoCString guid; |
430 | 0 | rv = GenerateGUID(guid); |
431 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
432 | 0 | rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("item_guid"), guid); |
433 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
434 | 0 | _guid.Assign(guid); |
435 | 0 | } |
436 | 0 |
|
437 | 0 | int64_t syncChangeDelta = DetermineSyncChangeDelta(aSource); |
438 | 0 | rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("change_counter"), |
439 | 0 | syncChangeDelta); |
440 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
441 | 0 |
|
442 | 0 | uint16_t syncStatus = DetermineInitialSyncStatus(aSource); |
443 | 0 | rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("sync_status"), |
444 | 0 | syncStatus); |
445 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
446 | 0 |
|
447 | 0 | rv = stmt->Execute(); |
448 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
449 | 0 |
|
450 | 0 | // Remove stale tombstones if we're reinserting an item. |
451 | 0 | if (hasExistingGuid) { |
452 | 0 | rv = RemoveTombstone(_guid); |
453 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
454 | 0 | } |
455 | 0 |
|
456 | 0 | if (*_itemId == -1) { |
457 | 0 | *_itemId = sLastInsertedItemId; |
458 | 0 | } |
459 | 0 |
|
460 | 0 | if (aParentId > 0) { |
461 | 0 | // Update last modified date of the ancestors. |
462 | 0 | // TODO (bug 408991): Doing this for all ancestors would be slow without a |
463 | 0 | // nested tree, so for now update only the parent. |
464 | 0 | rv = SetItemDateInternal(LAST_MODIFIED, syncChangeDelta, aParentId, |
465 | 0 | aDateAdded); |
466 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
467 | 0 | } |
468 | 0 |
|
469 | 0 | int64_t tagsRootId = TagsRootId(); |
470 | 0 | bool isTagging = aGrandParentId == tagsRootId; |
471 | 0 | if (isTagging) { |
472 | 0 | // If we're tagging a bookmark, increment the change counter for all |
473 | 0 | // bookmarks with the URI. |
474 | 0 | rv = AddSyncChangesForBookmarksWithURI(aURI, syncChangeDelta); |
475 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
476 | 0 | } |
477 | 0 |
|
478 | 0 | // Mark all affected separators as changed |
479 | 0 | rv = AdjustSeparatorsSyncCounter(aParentId, aIndex + 1, syncChangeDelta); |
480 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
481 | 0 |
|
482 | 0 | // Add a cache entry since we know everything about this bookmark. |
483 | 0 | BookmarkData bookmark; |
484 | 0 | bookmark.id = *_itemId; |
485 | 0 | bookmark.guid.Assign(_guid); |
486 | 0 | if (!aTitle.IsEmpty()) { |
487 | 0 | bookmark.title.Assign(aTitle); |
488 | 0 | } |
489 | 0 | bookmark.position = aIndex; |
490 | 0 | bookmark.placeId = aPlaceId; |
491 | 0 | bookmark.parentId = aParentId; |
492 | 0 | bookmark.type = aItemType; |
493 | 0 | bookmark.dateAdded = aDateAdded; |
494 | 0 | if (aLastModified) |
495 | 0 | bookmark.lastModified = aLastModified; |
496 | 0 | else |
497 | 0 | bookmark.lastModified = aDateAdded; |
498 | 0 | if (aURI) { |
499 | 0 | rv = aURI->GetSpec(bookmark.url); |
500 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
501 | 0 | } |
502 | 0 | bookmark.parentGuid = aParentGuid; |
503 | 0 | bookmark.grandParentId = aGrandParentId; |
504 | 0 | bookmark.syncStatus = syncStatus; |
505 | 0 |
|
506 | 0 | return NS_OK; |
507 | 0 | } |
508 | | |
509 | | NS_IMETHODIMP |
510 | | nsNavBookmarks::InsertBookmark(int64_t aFolder, |
511 | | nsIURI* aURI, |
512 | | int32_t aIndex, |
513 | | const nsACString& aTitle, |
514 | | const nsACString& aGUID, |
515 | | uint16_t aSource, |
516 | | int64_t* aNewBookmarkId) |
517 | 0 | { |
518 | 0 | NS_ENSURE_ARG(aURI); |
519 | 0 | NS_ENSURE_ARG_POINTER(aNewBookmarkId); |
520 | 0 | NS_ENSURE_ARG_MIN(aIndex, nsINavBookmarksService::DEFAULT_INDEX); |
521 | 0 |
|
522 | 0 | if (!aGUID.IsEmpty() && !IsValidGUID(aGUID)) |
523 | 0 | return NS_ERROR_INVALID_ARG; |
524 | 0 | |
525 | 0 | mozStorageTransaction transaction(mDB->MainConn(), false); |
526 | 0 |
|
527 | 0 | nsNavHistory* history = nsNavHistory::GetHistoryService(); |
528 | 0 | NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY); |
529 | 0 | int64_t placeId; |
530 | 0 | nsAutoCString placeGuid; |
531 | 0 | nsresult rv = history->GetOrCreateIdForPage(aURI, &placeId, placeGuid); |
532 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
533 | 0 |
|
534 | 0 | // Get the correct index for insertion. This also ensures the parent exists. |
535 | 0 | int32_t index, folderCount; |
536 | 0 | int64_t grandParentId; |
537 | 0 | nsAutoCString folderGuid; |
538 | 0 | rv = FetchFolderInfo(aFolder, &folderCount, folderGuid, &grandParentId); |
539 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
540 | 0 | if (aIndex == nsINavBookmarksService::DEFAULT_INDEX || |
541 | 0 | aIndex >= folderCount) { |
542 | 0 | index = folderCount; |
543 | 0 | } |
544 | 0 | else { |
545 | 0 | index = aIndex; |
546 | 0 | // Create space for the insertion. |
547 | 0 | rv = AdjustIndices(aFolder, index, INT32_MAX, 1); |
548 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
549 | 0 | } |
550 | 0 |
|
551 | 0 | *aNewBookmarkId = -1; |
552 | 0 | PRTime dateAdded = RoundedPRNow(); |
553 | 0 | nsAutoCString guid(aGUID); |
554 | 0 | nsCString title; |
555 | 0 | TruncateTitle(aTitle, title); |
556 | 0 |
|
557 | 0 | rv = InsertBookmarkInDB(placeId, BOOKMARK, aFolder, index, title, dateAdded, |
558 | 0 | 0, folderGuid, grandParentId, aURI, aSource, |
559 | 0 | aNewBookmarkId, guid); |
560 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
561 | 0 |
|
562 | 0 | // If not a tag, recalculate frecency for this entry, since it changed. |
563 | 0 | int64_t tagsRootId = TagsRootId(); |
564 | 0 | if (grandParentId != tagsRootId) { |
565 | 0 | rv = history->UpdateFrecency(placeId); |
566 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
567 | 0 | } |
568 | 0 |
|
569 | 0 | rv = transaction.Commit(); |
570 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
571 | 0 |
|
572 | 0 | NOTIFY_BOOKMARKS_OBSERVERS(mCanNotify, mObservers, |
573 | 0 | SKIP_TAGS(grandParentId == mDB->GetTagsFolderId()), |
574 | 0 | OnItemAdded(*aNewBookmarkId, aFolder, index, |
575 | 0 | TYPE_BOOKMARK, aURI, title, dateAdded, |
576 | 0 | guid, folderGuid, aSource)); |
577 | 0 |
|
578 | 0 | // If the bookmark has been added to a tag container, notify all |
579 | 0 | // bookmark-folder result nodes which contain a bookmark for the new |
580 | 0 | // bookmark's url. |
581 | 0 | if (grandParentId == tagsRootId) { |
582 | 0 | // Notify a tags change to all bookmarks for this URI. |
583 | 0 | nsTArray<BookmarkData> bookmarks; |
584 | 0 | rv = GetBookmarksForURI(aURI, bookmarks); |
585 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
586 | 0 |
|
587 | 0 | for (uint32_t i = 0; i < bookmarks.Length(); ++i) { |
588 | 0 | // Check that bookmarks doesn't include the current tag itemId. |
589 | 0 | MOZ_ASSERT(bookmarks[i].id != *aNewBookmarkId); |
590 | 0 |
|
591 | 0 | NOTIFY_BOOKMARKS_OBSERVERS(mCanNotify, mObservers, DontSkip, |
592 | 0 | OnItemChanged(bookmarks[i].id, |
593 | 0 | NS_LITERAL_CSTRING("tags"), |
594 | 0 | false, |
595 | 0 | EmptyCString(), |
596 | 0 | bookmarks[i].lastModified, |
597 | 0 | TYPE_BOOKMARK, |
598 | 0 | bookmarks[i].parentId, |
599 | 0 | bookmarks[i].guid, |
600 | 0 | bookmarks[i].parentGuid, |
601 | 0 | EmptyCString(), |
602 | 0 | aSource)); |
603 | 0 | } |
604 | 0 | } |
605 | 0 |
|
606 | 0 | return NS_OK; |
607 | 0 | } |
608 | | |
609 | | |
610 | | NS_IMETHODIMP |
611 | | nsNavBookmarks::RemoveItem(int64_t aItemId, uint16_t aSource) |
612 | 0 | { |
613 | 0 | AUTO_PROFILER_LABEL("nsNavBookmarks::RemoveItem", OTHER); |
614 | 0 |
|
615 | 0 | NS_ENSURE_ARG(!IsRoot(aItemId)); |
616 | 0 |
|
617 | 0 | BookmarkData bookmark; |
618 | 0 | nsresult rv = FetchItemInfo(aItemId, bookmark); |
619 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
620 | 0 |
|
621 | 0 | mozStorageTransaction transaction(mDB->MainConn(), false); |
622 | 0 |
|
623 | 0 | // First, if not a tag, remove item annotations. |
624 | 0 | int64_t tagsRootId = TagsRootId(); |
625 | 0 | bool isUntagging = bookmark.grandParentId == tagsRootId; |
626 | 0 | if (bookmark.parentId != tagsRootId && !isUntagging) { |
627 | 0 | nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService(); |
628 | 0 | NS_ENSURE_TRUE(annosvc, NS_ERROR_OUT_OF_MEMORY); |
629 | 0 | rv = annosvc->RemoveItemAnnotations(bookmark.id); |
630 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
631 | 0 | } |
632 | 0 |
|
633 | 0 | if (bookmark.type == TYPE_FOLDER) { |
634 | 0 | // Remove all of the folder's children. |
635 | 0 | rv = RemoveFolderChildren(bookmark.id, aSource); |
636 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
637 | 0 | } |
638 | 0 |
|
639 | 0 | nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement( |
640 | 0 | "DELETE FROM moz_bookmarks WHERE id = :item_id" |
641 | 0 | ); |
642 | 0 | NS_ENSURE_STATE(stmt); |
643 | 0 | mozStorageStatementScoper scoper(stmt); |
644 | 0 |
|
645 | 0 | rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), bookmark.id); |
646 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
647 | 0 | rv = stmt->Execute(); |
648 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
649 | 0 |
|
650 | 0 | // Fix indices in the parent. |
651 | 0 | if (bookmark.position != DEFAULT_INDEX) { |
652 | 0 | rv = AdjustIndices(bookmark.parentId, |
653 | 0 | bookmark.position + 1, INT32_MAX, -1); |
654 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
655 | 0 | } |
656 | 0 |
|
657 | 0 | int64_t syncChangeDelta = DetermineSyncChangeDelta(aSource); |
658 | 0 |
|
659 | 0 | // Add a tombstone for synced items. |
660 | 0 | if (syncChangeDelta) { |
661 | 0 | rv = InsertTombstone(bookmark); |
662 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
663 | 0 | } |
664 | 0 |
|
665 | 0 | bookmark.lastModified = RoundedPRNow(); |
666 | 0 | rv = SetItemDateInternal(LAST_MODIFIED, syncChangeDelta, |
667 | 0 | bookmark.parentId, bookmark.lastModified); |
668 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
669 | 0 |
|
670 | 0 | // Mark all affected separators as changed |
671 | 0 | rv = AdjustSeparatorsSyncCounter(bookmark.parentId, bookmark.position, syncChangeDelta); |
672 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
673 | 0 |
|
674 | 0 | if (isUntagging) { |
675 | 0 | // If we're removing a tag, increment the change counter for all bookmarks |
676 | 0 | // with the URI. |
677 | 0 | rv = AddSyncChangesForBookmarksWithURL(bookmark.url, syncChangeDelta); |
678 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
679 | 0 | } |
680 | 0 |
|
681 | 0 | rv = transaction.Commit(); |
682 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
683 | 0 |
|
684 | 0 | nsCOMPtr<nsIURI> uri; |
685 | 0 | if (bookmark.type == TYPE_BOOKMARK) { |
686 | 0 | // If not a tag, recalculate frecency for this entry, since it changed. |
687 | 0 | if (bookmark.grandParentId != tagsRootId) { |
688 | 0 | nsNavHistory* history = nsNavHistory::GetHistoryService(); |
689 | 0 | NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY); |
690 | 0 | rv = history->UpdateFrecency(bookmark.placeId); |
691 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
692 | 0 | } |
693 | 0 | // A broken url should not interrupt the removal process. |
694 | 0 | (void)NS_NewURI(getter_AddRefs(uri), bookmark.url); |
695 | 0 | // We cannot assert since some automated tests are checking this path. |
696 | 0 | NS_WARNING_ASSERTION(uri, "Invalid URI in RemoveItem"); |
697 | 0 | } |
698 | 0 |
|
699 | 0 | NOTIFY_BOOKMARKS_OBSERVERS(mCanNotify, mObservers, |
700 | 0 | SKIP_TAGS(bookmark.parentId == tagsRootId || |
701 | 0 | bookmark.grandParentId == tagsRootId), |
702 | 0 | OnItemRemoved(bookmark.id, |
703 | 0 | bookmark.parentId, |
704 | 0 | bookmark.position, |
705 | 0 | bookmark.type, |
706 | 0 | uri, |
707 | 0 | bookmark.guid, |
708 | 0 | bookmark.parentGuid, |
709 | 0 | aSource)); |
710 | 0 |
|
711 | 0 | if (bookmark.type == TYPE_BOOKMARK && bookmark.grandParentId == tagsRootId && |
712 | 0 | uri) { |
713 | 0 | // If the removed bookmark was child of a tag container, notify a tags |
714 | 0 | // change to all bookmarks for this URI. |
715 | 0 | nsTArray<BookmarkData> bookmarks; |
716 | 0 | rv = GetBookmarksForURI(uri, bookmarks); |
717 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
718 | 0 |
|
719 | 0 | for (uint32_t i = 0; i < bookmarks.Length(); ++i) { |
720 | 0 | NOTIFY_BOOKMARKS_OBSERVERS(mCanNotify, mObservers, DontSkip, |
721 | 0 | OnItemChanged(bookmarks[i].id, |
722 | 0 | NS_LITERAL_CSTRING("tags"), |
723 | 0 | false, |
724 | 0 | EmptyCString(), |
725 | 0 | bookmarks[i].lastModified, |
726 | 0 | TYPE_BOOKMARK, |
727 | 0 | bookmarks[i].parentId, |
728 | 0 | bookmarks[i].guid, |
729 | 0 | bookmarks[i].parentGuid, |
730 | 0 | EmptyCString(), |
731 | 0 | aSource)); |
732 | 0 | } |
733 | 0 |
|
734 | 0 | } |
735 | 0 |
|
736 | 0 | return NS_OK; |
737 | 0 | } |
738 | | |
739 | | |
740 | | NS_IMETHODIMP |
741 | | nsNavBookmarks::CreateFolder(int64_t aParent, const nsACString& aTitle, |
742 | | int32_t aIndex, const nsACString& aGUID, |
743 | | uint16_t aSource, int64_t* aNewFolderId) |
744 | 0 | { |
745 | 0 | // NOTE: aParent can be null for root creation, so not checked |
746 | 0 | NS_ENSURE_ARG_POINTER(aNewFolderId); |
747 | 0 | NS_ENSURE_ARG_MIN(aIndex, nsINavBookmarksService::DEFAULT_INDEX); |
748 | 0 | if (!aGUID.IsEmpty() && !IsValidGUID(aGUID)) |
749 | 0 | return NS_ERROR_INVALID_ARG; |
750 | 0 | |
751 | 0 | // Get the correct index for insertion. This also ensures the parent exists. |
752 | 0 | int32_t index = aIndex, folderCount; |
753 | 0 | int64_t grandParentId; |
754 | 0 | nsAutoCString folderGuid; |
755 | 0 | nsresult rv = FetchFolderInfo(aParent, &folderCount, folderGuid, &grandParentId); |
756 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
757 | 0 |
|
758 | 0 | mozStorageTransaction transaction(mDB->MainConn(), false); |
759 | 0 |
|
760 | 0 | if (aIndex == nsINavBookmarksService::DEFAULT_INDEX || aIndex >= folderCount) { |
761 | 0 | index = folderCount; |
762 | 0 | } else { |
763 | 0 | // Create space for the insertion. |
764 | 0 | rv = AdjustIndices(aParent, index, INT32_MAX, 1); |
765 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
766 | 0 | } |
767 | 0 |
|
768 | 0 | *aNewFolderId = -1; |
769 | 0 | PRTime dateAdded = RoundedPRNow(); |
770 | 0 | nsAutoCString guid(aGUID); |
771 | 0 | nsCString title; |
772 | 0 | TruncateTitle(aTitle, title); |
773 | 0 |
|
774 | 0 | rv = InsertBookmarkInDB(-1, FOLDER, aParent, index, |
775 | 0 | title, dateAdded, 0, folderGuid, grandParentId, |
776 | 0 | nullptr, aSource, aNewFolderId, guid); |
777 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
778 | 0 |
|
779 | 0 | rv = transaction.Commit(); |
780 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
781 | 0 |
|
782 | 0 | int64_t tagsRootId = TagsRootId(); |
783 | 0 |
|
784 | 0 | NOTIFY_BOOKMARKS_OBSERVERS(mCanNotify, mObservers, |
785 | 0 | SKIP_TAGS(aParent == tagsRootId), |
786 | 0 | OnItemAdded(*aNewFolderId, aParent, index, FOLDER, |
787 | 0 | nullptr, title, dateAdded, guid, |
788 | 0 | folderGuid, aSource)); |
789 | 0 |
|
790 | 0 | return NS_OK; |
791 | 0 | } |
792 | | |
793 | | bool nsNavBookmarks::IsLivemark(int64_t aFolderId) |
794 | 0 | { |
795 | 0 | nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService(); |
796 | 0 | NS_ENSURE_TRUE(annosvc, false); |
797 | 0 | bool isLivemark; |
798 | 0 | nsresult rv = annosvc->ItemHasAnnotation(aFolderId, |
799 | 0 | FEED_URI_ANNO, |
800 | 0 | &isLivemark); |
801 | 0 | NS_ENSURE_SUCCESS(rv, false); |
802 | 0 | return isLivemark; |
803 | 0 | } |
804 | | |
805 | | nsresult |
806 | | nsNavBookmarks::GetDescendantChildren(int64_t aFolderId, |
807 | | const nsACString& aFolderGuid, |
808 | | int64_t aGrandParentId, |
809 | 0 | nsTArray<BookmarkData>& aFolderChildrenArray) { |
810 | 0 | // New children will be added from this index on. |
811 | 0 | uint32_t startIndex = aFolderChildrenArray.Length(); |
812 | 0 | nsresult rv; |
813 | 0 | { |
814 | 0 | // Collect children informations. |
815 | 0 | // Select all children of a given folder, sorted by position. |
816 | 0 | // This is a LEFT JOIN because not all bookmarks types have a place. |
817 | 0 | // We construct a result where the first columns exactly match |
818 | 0 | // kGetInfoIndex_* order, and additionally contains columns for position, |
819 | 0 | // item_child, and folder_child from moz_bookmarks. |
820 | 0 | nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement( |
821 | 0 | "SELECT h.id, h.url, b.title, h.rev_host, h.visit_count, " |
822 | 0 | "h.last_visit_date, null, b.id, b.dateAdded, b.lastModified, " |
823 | 0 | "b.parent, null, h.frecency, h.hidden, h.guid, null, null, null, " |
824 | 0 | "b.guid, b.position, b.type, b.fk, b.syncStatus " |
825 | 0 | "FROM moz_bookmarks b " |
826 | 0 | "LEFT JOIN moz_places h ON b.fk = h.id " |
827 | 0 | "WHERE b.parent = :parent " |
828 | 0 | "ORDER BY b.position ASC" |
829 | 0 | ); |
830 | 0 | NS_ENSURE_STATE(stmt); |
831 | 0 | mozStorageStatementScoper scoper(stmt); |
832 | 0 |
|
833 | 0 | rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aFolderId); |
834 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
835 | 0 |
|
836 | 0 | bool hasMore; |
837 | 0 | while (NS_SUCCEEDED(stmt->ExecuteStep(&hasMore)) && hasMore) { |
838 | 0 | BookmarkData child; |
839 | 0 | rv = stmt->GetInt64(nsNavHistory::kGetInfoIndex_ItemId, &child.id); |
840 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
841 | 0 | child.parentId = aFolderId; |
842 | 0 | child.grandParentId = aGrandParentId; |
843 | 0 | child.parentGuid = aFolderGuid; |
844 | 0 | rv = stmt->GetInt32(kGetChildrenIndex_Type, &child.type); |
845 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
846 | 0 | rv = stmt->GetInt64(kGetChildrenIndex_PlaceID, &child.placeId); |
847 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
848 | 0 | rv = stmt->GetInt32(kGetChildrenIndex_Position, &child.position); |
849 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
850 | 0 | rv = stmt->GetUTF8String(kGetChildrenIndex_Guid, child.guid); |
851 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
852 | 0 | rv = stmt->GetInt32(kGetChildrenIndex_SyncStatus, &child.syncStatus); |
853 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
854 | 0 |
|
855 | 0 | if (child.type == TYPE_BOOKMARK) { |
856 | 0 | rv = stmt->GetUTF8String(nsNavHistory::kGetInfoIndex_URL, child.url); |
857 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
858 | 0 | } |
859 | 0 |
|
860 | 0 | // Append item to children's array. |
861 | 0 | aFolderChildrenArray.AppendElement(child); |
862 | 0 | } |
863 | 0 | } |
864 | 0 |
|
865 | 0 | // Recursively call GetDescendantChildren for added folders. |
866 | 0 | // We start at startIndex since previous folders are checked |
867 | 0 | // by previous calls to this method. |
868 | 0 | uint32_t childCount = aFolderChildrenArray.Length(); |
869 | 0 | for (uint32_t i = startIndex; i < childCount; ++i) { |
870 | 0 | if (aFolderChildrenArray[i].type == TYPE_FOLDER) { |
871 | 0 | // nsTarray assumes that all children can be memmove()d, thus we can't |
872 | 0 | // just pass aFolderChildrenArray[i].guid to a method that will change |
873 | 0 | // the array itself. Otherwise, since it's passed by reference, after a |
874 | 0 | // memmove() it could point to garbage and cause intermittent crashes. |
875 | 0 | nsCString guid = aFolderChildrenArray[i].guid; |
876 | 0 | GetDescendantChildren(aFolderChildrenArray[i].id, |
877 | 0 | guid, |
878 | 0 | aFolderId, |
879 | 0 | aFolderChildrenArray); |
880 | 0 | } |
881 | 0 | } |
882 | 0 |
|
883 | 0 | return NS_OK; |
884 | 0 | } |
885 | | |
886 | | |
887 | | nsresult |
888 | | nsNavBookmarks::RemoveFolderChildren(int64_t aFolderId, uint16_t aSource) |
889 | 0 | { |
890 | 0 | AUTO_PROFILER_LABEL("nsNavBookmarks::RemoveFolderChilder", OTHER); |
891 | 0 |
|
892 | 0 | NS_ENSURE_ARG_MIN(aFolderId, 1); |
893 | 0 | int64_t rootId = -1; |
894 | 0 | nsresult rv = GetPlacesRoot(&rootId); |
895 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
896 | 0 | NS_ENSURE_ARG(aFolderId != rootId); |
897 | 0 |
|
898 | 0 | BookmarkData folder; |
899 | 0 | rv = FetchItemInfo(aFolderId, folder); |
900 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
901 | 0 | NS_ENSURE_ARG(folder.type == TYPE_FOLDER); |
902 | 0 |
|
903 | 0 | // Fill folder children array recursively. |
904 | 0 | nsTArray<BookmarkData> folderChildrenArray; |
905 | 0 | rv = GetDescendantChildren(folder.id, folder.guid, folder.parentId, |
906 | 0 | folderChildrenArray); |
907 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
908 | 0 |
|
909 | 0 | // Build a string of folders whose children will be removed. |
910 | 0 | nsCString foldersToRemove; |
911 | 0 | for (uint32_t i = 0; i < folderChildrenArray.Length(); ++i) { |
912 | 0 | BookmarkData& child = folderChildrenArray[i]; |
913 | 0 |
|
914 | 0 | if (child.type == TYPE_FOLDER) { |
915 | 0 | foldersToRemove.Append(','); |
916 | 0 | foldersToRemove.AppendInt(child.id); |
917 | 0 | } |
918 | 0 | } |
919 | 0 |
|
920 | 0 | int64_t syncChangeDelta = DetermineSyncChangeDelta(aSource); |
921 | 0 |
|
922 | 0 | // Delete items from the database now. |
923 | 0 | mozStorageTransaction transaction(mDB->MainConn(), false); |
924 | 0 |
|
925 | 0 | nsCOMPtr<mozIStorageStatement> deleteStatement = mDB->GetStatement( |
926 | 0 | NS_LITERAL_CSTRING( |
927 | 0 | "DELETE FROM moz_bookmarks " |
928 | 0 | "WHERE parent IN (:parent") + foldersToRemove + NS_LITERAL_CSTRING(")") |
929 | 0 | ); |
930 | 0 | NS_ENSURE_STATE(deleteStatement); |
931 | 0 | mozStorageStatementScoper deleteStatementScoper(deleteStatement); |
932 | 0 |
|
933 | 0 | rv = deleteStatement->BindInt64ByName(NS_LITERAL_CSTRING("parent"), folder.id); |
934 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
935 | 0 | rv = deleteStatement->Execute(); |
936 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
937 | 0 |
|
938 | 0 | // Clean up orphan items annotations. |
939 | 0 | nsCOMPtr<mozIStorageConnection> conn = mDB->MainConn(); |
940 | 0 | if (!conn) { |
941 | 0 | return NS_ERROR_UNEXPECTED; |
942 | 0 | } |
943 | 0 | rv = conn->ExecuteSimpleSQL( |
944 | 0 | NS_LITERAL_CSTRING( |
945 | 0 | "DELETE FROM moz_items_annos " |
946 | 0 | "WHERE id IN (" |
947 | 0 | "SELECT a.id from moz_items_annos a " |
948 | 0 | "LEFT JOIN moz_bookmarks b ON a.item_id = b.id " |
949 | 0 | "WHERE b.id ISNULL)")); |
950 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
951 | 0 |
|
952 | 0 | // Set the lastModified date. |
953 | 0 | rv = SetItemDateInternal(LAST_MODIFIED, syncChangeDelta, folder.id, |
954 | 0 | RoundedPRNow()); |
955 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
956 | 0 |
|
957 | 0 | int64_t tagsRootId = TagsRootId(); |
958 | 0 |
|
959 | 0 | if (syncChangeDelta) { |
960 | 0 | nsTArray<TombstoneData> tombstones(folderChildrenArray.Length()); |
961 | 0 | PRTime dateRemoved = RoundedPRNow(); |
962 | 0 |
|
963 | 0 | for (uint32_t i = 0; i < folderChildrenArray.Length(); ++i) { |
964 | 0 | BookmarkData& child = folderChildrenArray[i]; |
965 | 0 | if (NeedsTombstone(child)) { |
966 | 0 | // Write tombstones for synced children. |
967 | 0 | TombstoneData childTombstone = {child.guid, dateRemoved}; |
968 | 0 | tombstones.AppendElement(childTombstone); |
969 | 0 | } |
970 | 0 | bool isUntagging = child.grandParentId == tagsRootId; |
971 | 0 | if (isUntagging) { |
972 | 0 | // Bump the change counter for all tagged bookmarks when removing a tag |
973 | 0 | // folder. |
974 | 0 | rv = AddSyncChangesForBookmarksWithURL(child.url, syncChangeDelta); |
975 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
976 | 0 | } |
977 | 0 | } |
978 | 0 |
|
979 | 0 | rv = InsertTombstones(tombstones); |
980 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
981 | 0 | } |
982 | 0 |
|
983 | 0 | rv = transaction.Commit(); |
984 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
985 | 0 |
|
986 | 0 | // Call observers in reverse order to serve children before their parent. |
987 | 0 | for (int32_t i = folderChildrenArray.Length() - 1; i >= 0; --i) { |
988 | 0 | BookmarkData& child = folderChildrenArray[i]; |
989 | 0 |
|
990 | 0 | nsCOMPtr<nsIURI> uri; |
991 | 0 | if (child.type == TYPE_BOOKMARK) { |
992 | 0 | // If not a tag, recalculate frecency for this entry, since it changed. |
993 | 0 | if (child.grandParentId != tagsRootId) { |
994 | 0 | nsNavHistory* history = nsNavHistory::GetHistoryService(); |
995 | 0 | NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY); |
996 | 0 | rv = history->UpdateFrecency(child.placeId); |
997 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
998 | 0 | } |
999 | 0 | // A broken url should not interrupt the removal process. |
1000 | 0 | (void)NS_NewURI(getter_AddRefs(uri), child.url); |
1001 | 0 | // We cannot assert since some automated tests are checking this path. |
1002 | 0 | NS_WARNING_ASSERTION(uri, "Invalid URI in RemoveFolderChildren"); |
1003 | 0 | } |
1004 | 0 |
|
1005 | 0 | NOTIFY_BOOKMARKS_OBSERVERS(mCanNotify, mObservers, |
1006 | 0 | ((child.grandParentId == tagsRootId) ? SkipTags : SkipDescendants), |
1007 | 0 | OnItemRemoved(child.id, |
1008 | 0 | child.parentId, |
1009 | 0 | child.position, |
1010 | 0 | child.type, |
1011 | 0 | uri, |
1012 | 0 | child.guid, |
1013 | 0 | child.parentGuid, |
1014 | 0 | aSource)); |
1015 | 0 |
|
1016 | 0 | if (child.type == TYPE_BOOKMARK && child.grandParentId == tagsRootId && |
1017 | 0 | uri) { |
1018 | 0 | // If the removed bookmark was a child of a tag container, notify all |
1019 | 0 | // bookmark-folder result nodes which contain a bookmark for the removed |
1020 | 0 | // bookmark's url. |
1021 | 0 | nsTArray<BookmarkData> bookmarks; |
1022 | 0 | rv = GetBookmarksForURI(uri, bookmarks); |
1023 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1024 | 0 |
|
1025 | 0 | for (uint32_t i = 0; i < bookmarks.Length(); ++i) { |
1026 | 0 | NOTIFY_BOOKMARKS_OBSERVERS(mCanNotify, mObservers, DontSkip, |
1027 | 0 | OnItemChanged(bookmarks[i].id, |
1028 | 0 | NS_LITERAL_CSTRING("tags"), |
1029 | 0 | false, |
1030 | 0 | EmptyCString(), |
1031 | 0 | bookmarks[i].lastModified, |
1032 | 0 | TYPE_BOOKMARK, |
1033 | 0 | bookmarks[i].parentId, |
1034 | 0 | bookmarks[i].guid, |
1035 | 0 | bookmarks[i].parentGuid, |
1036 | 0 | EmptyCString(), |
1037 | 0 | aSource)); |
1038 | 0 | } |
1039 | 0 | } |
1040 | 0 | } |
1041 | 0 |
|
1042 | 0 | return NS_OK; |
1043 | 0 | } |
1044 | | |
1045 | | |
1046 | | nsresult |
1047 | | nsNavBookmarks::FetchItemInfo(int64_t aItemId, |
1048 | | BookmarkData& _bookmark) |
1049 | 0 | { |
1050 | 0 | // LEFT JOIN since not all bookmarks have an associated place. |
1051 | 0 | nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement( |
1052 | 0 | "SELECT b.id, h.url, b.title, b.position, b.fk, b.parent, b.type, " |
1053 | 0 | "b.dateAdded, b.lastModified, b.guid, t.guid, t.parent, " |
1054 | 0 | "b.syncStatus " |
1055 | 0 | "FROM moz_bookmarks b " |
1056 | 0 | "LEFT JOIN moz_bookmarks t ON t.id = b.parent " |
1057 | 0 | "LEFT JOIN moz_places h ON h.id = b.fk " |
1058 | 0 | "WHERE b.id = :item_id" |
1059 | 0 | ); |
1060 | 0 | NS_ENSURE_STATE(stmt); |
1061 | 0 | mozStorageStatementScoper scoper(stmt); |
1062 | 0 |
|
1063 | 0 | nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), aItemId); |
1064 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1065 | 0 |
|
1066 | 0 | bool hasResult; |
1067 | 0 | rv = stmt->ExecuteStep(&hasResult); |
1068 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1069 | 0 | if (!hasResult) { |
1070 | 0 | return NS_ERROR_INVALID_ARG; |
1071 | 0 | } |
1072 | 0 | |
1073 | 0 | _bookmark.id = aItemId; |
1074 | 0 | rv = stmt->GetUTF8String(1, _bookmark.url); |
1075 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1076 | 0 |
|
1077 | 0 | bool isNull; |
1078 | 0 | rv = stmt->GetIsNull(2, &isNull); |
1079 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1080 | 0 | if (!isNull) { |
1081 | 0 | rv = stmt->GetUTF8String(2, _bookmark.title); |
1082 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1083 | 0 | } |
1084 | 0 | rv = stmt->GetInt32(3, &_bookmark.position); |
1085 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1086 | 0 | rv = stmt->GetInt64(4, &_bookmark.placeId); |
1087 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1088 | 0 | rv = stmt->GetInt64(5, &_bookmark.parentId); |
1089 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1090 | 0 | rv = stmt->GetInt32(6, &_bookmark.type); |
1091 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1092 | 0 | rv = stmt->GetInt64(7, reinterpret_cast<int64_t*>(&_bookmark.dateAdded)); |
1093 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1094 | 0 | rv = stmt->GetInt64(8, reinterpret_cast<int64_t*>(&_bookmark.lastModified)); |
1095 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1096 | 0 | rv = stmt->GetUTF8String(9, _bookmark.guid); |
1097 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1098 | 0 | // Getting properties of the root would show no parent. |
1099 | 0 | rv = stmt->GetIsNull(10, &isNull); |
1100 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1101 | 0 | if (!isNull) { |
1102 | 0 | rv = stmt->GetUTF8String(10, _bookmark.parentGuid); |
1103 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1104 | 0 | rv = stmt->GetInt64(11, &_bookmark.grandParentId); |
1105 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1106 | 0 | } |
1107 | 0 | else { |
1108 | 0 | _bookmark.grandParentId = -1; |
1109 | 0 | } |
1110 | 0 | rv = stmt->GetInt32(12, &_bookmark.syncStatus); |
1111 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1112 | 0 |
|
1113 | 0 | return NS_OK; |
1114 | 0 | } |
1115 | | |
1116 | | |
1117 | | nsresult |
1118 | | nsNavBookmarks::FetchItemInfo(const nsCString& aGUID, |
1119 | | BookmarkData& _bookmark) |
1120 | 0 | { |
1121 | 0 | // LEFT JOIN since not all bookmarks have an associated place. |
1122 | 0 | nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement( |
1123 | 0 | "SELECT b.id, h.url, b.title, b.position, b.fk, b.parent, b.type, " |
1124 | 0 | "b.dateAdded, b.lastModified, t.guid, t.parent, " |
1125 | 0 | "b.syncStatus " |
1126 | 0 | "FROM moz_bookmarks b " |
1127 | 0 | "LEFT JOIN moz_bookmarks t ON t.id = b.parent " |
1128 | 0 | "LEFT JOIN moz_places h ON h.id = b.fk " |
1129 | 0 | "WHERE b.guid = :item_guid" |
1130 | 0 | ); |
1131 | 0 | NS_ENSURE_STATE(stmt); |
1132 | 0 | mozStorageStatementScoper scoper(stmt); |
1133 | 0 |
|
1134 | 0 | nsresult rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("item_guid"), aGUID); |
1135 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1136 | 0 |
|
1137 | 0 | _bookmark.guid = aGUID; |
1138 | 0 |
|
1139 | 0 | bool hasResult; |
1140 | 0 | rv = stmt->ExecuteStep(&hasResult); |
1141 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1142 | 0 | if (!hasResult) { |
1143 | 0 | return NS_ERROR_INVALID_ARG; |
1144 | 0 | } |
1145 | 0 | |
1146 | 0 | rv = stmt->GetInt64(0, &_bookmark.id); |
1147 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1148 | 0 |
|
1149 | 0 | rv = stmt->GetUTF8String(1, _bookmark.url); |
1150 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1151 | 0 |
|
1152 | 0 | bool isNull; |
1153 | 0 | rv = stmt->GetIsNull(2, &isNull); |
1154 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1155 | 0 | if (!isNull) { |
1156 | 0 | rv = stmt->GetUTF8String(2, _bookmark.title); |
1157 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1158 | 0 | } |
1159 | 0 | rv = stmt->GetInt32(3, &_bookmark.position); |
1160 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1161 | 0 | rv = stmt->GetInt64(4, &_bookmark.placeId); |
1162 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1163 | 0 | rv = stmt->GetInt64(5, &_bookmark.parentId); |
1164 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1165 | 0 | rv = stmt->GetInt32(6, &_bookmark.type); |
1166 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1167 | 0 | rv = stmt->GetInt64(7, reinterpret_cast<int64_t*>(&_bookmark.dateAdded)); |
1168 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1169 | 0 | rv = stmt->GetInt64(8, reinterpret_cast<int64_t*>(&_bookmark.lastModified)); |
1170 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1171 | 0 | // Getting properties of the root would show no parent. |
1172 | 0 | rv = stmt->GetIsNull(9, &isNull); |
1173 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1174 | 0 | if (!isNull) { |
1175 | 0 | rv = stmt->GetUTF8String(9, _bookmark.parentGuid); |
1176 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1177 | 0 | rv = stmt->GetInt64(10, &_bookmark.grandParentId); |
1178 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1179 | 0 | } |
1180 | 0 | else { |
1181 | 0 | _bookmark.grandParentId = -1; |
1182 | 0 | } |
1183 | 0 | rv = stmt->GetInt32(11, &_bookmark.syncStatus); |
1184 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1185 | 0 |
|
1186 | 0 | return NS_OK; |
1187 | 0 | } |
1188 | | |
1189 | | |
1190 | | nsresult |
1191 | | nsNavBookmarks::SetItemDateInternal(enum BookmarkDate aDateType, |
1192 | | int64_t aSyncChangeDelta, |
1193 | | int64_t aItemId, |
1194 | | PRTime aValue) |
1195 | 0 | { |
1196 | 0 | aValue = RoundToMilliseconds(aValue); |
1197 | 0 |
|
1198 | 0 | nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement( |
1199 | 0 | "UPDATE moz_bookmarks SET lastModified = :date, " |
1200 | 0 | "syncChangeCounter = syncChangeCounter + :delta " |
1201 | 0 | "WHERE id = :item_id" |
1202 | 0 | ); |
1203 | 0 | NS_ENSURE_STATE(stmt); |
1204 | 0 | mozStorageStatementScoper scoper(stmt); |
1205 | 0 |
|
1206 | 0 | nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("date"), aValue); |
1207 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1208 | 0 | rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), aItemId); |
1209 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1210 | 0 | rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("delta"), aSyncChangeDelta); |
1211 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1212 | 0 |
|
1213 | 0 | rv = stmt->Execute(); |
1214 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1215 | 0 |
|
1216 | 0 | // note, we are not notifying the observers |
1217 | 0 | // that the item has changed. |
1218 | 0 |
|
1219 | 0 | return NS_OK; |
1220 | 0 | } |
1221 | | |
1222 | | NS_IMETHODIMP |
1223 | | nsNavBookmarks::SetItemLastModified(int64_t aItemId, PRTime aLastModified, |
1224 | | uint16_t aSource) |
1225 | 0 | { |
1226 | 0 | NS_ENSURE_ARG_MIN(aItemId, 1); |
1227 | 0 |
|
1228 | 0 | BookmarkData bookmark; |
1229 | 0 | nsresult rv = FetchItemInfo(aItemId, bookmark); |
1230 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1231 | 0 |
|
1232 | 0 | int64_t tagsRootId = TagsRootId(); |
1233 | 0 | bool isTagging = bookmark.grandParentId == tagsRootId; |
1234 | 0 | int64_t syncChangeDelta = DetermineSyncChangeDelta(aSource); |
1235 | 0 |
|
1236 | 0 | // Round here so that we notify with the right value. |
1237 | 0 | bookmark.lastModified = RoundToMilliseconds(aLastModified); |
1238 | 0 |
|
1239 | 0 | if (isTagging) { |
1240 | 0 | // If we're changing a tag, bump the change counter for all tagged |
1241 | 0 | // bookmarks. We use a separate code path to avoid a transaction for |
1242 | 0 | // non-tags. |
1243 | 0 | mozStorageTransaction transaction(mDB->MainConn(), false); |
1244 | 0 |
|
1245 | 0 | rv = SetItemDateInternal(LAST_MODIFIED, syncChangeDelta, bookmark.id, |
1246 | 0 | bookmark.lastModified); |
1247 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1248 | 0 |
|
1249 | 0 | rv = AddSyncChangesForBookmarksWithURL(bookmark.url, syncChangeDelta); |
1250 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1251 | 0 |
|
1252 | 0 | rv = transaction.Commit(); |
1253 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1254 | 0 | } else { |
1255 | 0 | rv = SetItemDateInternal(LAST_MODIFIED, syncChangeDelta, bookmark.id, |
1256 | 0 | bookmark.lastModified); |
1257 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1258 | 0 | } |
1259 | 0 |
|
1260 | 0 | // Note: mDBSetItemDateAdded also sets lastModified to aDateAdded. |
1261 | 0 | NOTIFY_BOOKMARKS_OBSERVERS(mCanNotify, mObservers, |
1262 | 0 | SKIP_TAGS(isTagging || bookmark.parentId == tagsRootId), |
1263 | 0 | OnItemChanged(bookmark.id, |
1264 | 0 | NS_LITERAL_CSTRING("lastModified"), |
1265 | 0 | false, |
1266 | 0 | nsPrintfCString("%" PRId64, bookmark.lastModified), |
1267 | 0 | bookmark.lastModified, |
1268 | 0 | bookmark.type, |
1269 | 0 | bookmark.parentId, |
1270 | 0 | bookmark.guid, |
1271 | 0 | bookmark.parentGuid, |
1272 | 0 | EmptyCString(), |
1273 | 0 | aSource)); |
1274 | 0 | return NS_OK; |
1275 | 0 | } |
1276 | | |
1277 | | |
1278 | | nsresult |
1279 | | nsNavBookmarks::AddSyncChangesForBookmarksWithURL(const nsACString& aURL, |
1280 | | int64_t aSyncChangeDelta) |
1281 | 0 | { |
1282 | 0 | if (!aSyncChangeDelta) { |
1283 | 0 | return NS_OK; |
1284 | 0 | } |
1285 | 0 | nsCOMPtr<nsIURI> uri; |
1286 | 0 | nsresult rv = NS_NewURI(getter_AddRefs(uri), aURL); |
1287 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
1288 | 0 | // Ignore sync changes for invalid URLs. |
1289 | 0 | return NS_OK; |
1290 | 0 | } |
1291 | 0 | return AddSyncChangesForBookmarksWithURI(uri, aSyncChangeDelta); |
1292 | 0 | } |
1293 | | |
1294 | | |
1295 | | nsresult |
1296 | | nsNavBookmarks::AddSyncChangesForBookmarksWithURI(nsIURI* aURI, |
1297 | | int64_t aSyncChangeDelta) |
1298 | 0 | { |
1299 | 0 | if (NS_WARN_IF(!aURI) || !aSyncChangeDelta) { |
1300 | 0 | // Ignore sync changes for invalid URIs. |
1301 | 0 | return NS_OK; |
1302 | 0 | } |
1303 | 0 | |
1304 | 0 | nsCOMPtr<mozIStorageStatement> statement = mDB->GetStatement( |
1305 | 0 | "UPDATE moz_bookmarks SET " |
1306 | 0 | "syncChangeCounter = syncChangeCounter + :delta " |
1307 | 0 | "WHERE type = :type AND " |
1308 | 0 | "fk = (SELECT id FROM moz_places WHERE url_hash = hash(:url) AND " |
1309 | 0 | "url = :url)" |
1310 | 0 | ); |
1311 | 0 | NS_ENSURE_STATE(statement); |
1312 | 0 | mozStorageStatementScoper scoper(statement); |
1313 | 0 |
|
1314 | 0 | nsresult rv = statement->BindInt64ByName(NS_LITERAL_CSTRING("delta"), |
1315 | 0 | aSyncChangeDelta); |
1316 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1317 | 0 | rv = statement->BindInt64ByName(NS_LITERAL_CSTRING("type"), |
1318 | 0 | nsINavBookmarksService::TYPE_BOOKMARK); |
1319 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1320 | 0 | rv = URIBinder::Bind(statement, NS_LITERAL_CSTRING("url"), aURI); |
1321 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1322 | 0 |
|
1323 | 0 | return statement->Execute(); |
1324 | 0 | } |
1325 | | |
1326 | | |
1327 | | nsresult |
1328 | | nsNavBookmarks::AddSyncChangesForBookmarksInFolder(int64_t aFolderId, |
1329 | | int64_t aSyncChangeDelta) |
1330 | 0 | { |
1331 | 0 | if (!aSyncChangeDelta) { |
1332 | 0 | return NS_OK; |
1333 | 0 | } |
1334 | 0 | |
1335 | 0 | nsCOMPtr<mozIStorageStatement> statement = mDB->GetStatement( |
1336 | 0 | "UPDATE moz_bookmarks SET " |
1337 | 0 | "syncChangeCounter = syncChangeCounter + :delta " |
1338 | 0 | "WHERE type = :type AND " |
1339 | 0 | "fk = (SELECT fk FROM moz_bookmarks WHERE parent = :parent)" |
1340 | 0 | ); |
1341 | 0 | NS_ENSURE_STATE(statement); |
1342 | 0 | mozStorageStatementScoper scoper(statement); |
1343 | 0 |
|
1344 | 0 | nsresult rv = statement->BindInt64ByName(NS_LITERAL_CSTRING("delta"), |
1345 | 0 | aSyncChangeDelta); |
1346 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1347 | 0 | rv = statement->BindInt64ByName(NS_LITERAL_CSTRING("type"), |
1348 | 0 | nsINavBookmarksService::TYPE_BOOKMARK); |
1349 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1350 | 0 | rv = statement->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aFolderId); |
1351 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1352 | 0 |
|
1353 | 0 | rv = statement->Execute(); |
1354 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1355 | 0 |
|
1356 | 0 | return NS_OK; |
1357 | 0 | } |
1358 | | |
1359 | | |
1360 | | nsresult |
1361 | | nsNavBookmarks::InsertTombstone(const BookmarkData& aBookmark) |
1362 | 0 | { |
1363 | 0 | if (!NeedsTombstone(aBookmark)) { |
1364 | 0 | return NS_OK; |
1365 | 0 | } |
1366 | 0 | nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement( |
1367 | 0 | "INSERT INTO moz_bookmarks_deleted (guid, dateRemoved) " |
1368 | 0 | "VALUES (:guid, :date_removed)" |
1369 | 0 | ); |
1370 | 0 | NS_ENSURE_STATE(stmt); |
1371 | 0 | mozStorageStatementScoper scoper(stmt); |
1372 | 0 |
|
1373 | 0 | nsresult rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"), |
1374 | 0 | aBookmark.guid); |
1375 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1376 | 0 | rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("date_removed"), |
1377 | 0 | RoundedPRNow()); |
1378 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1379 | 0 |
|
1380 | 0 | rv = stmt->Execute(); |
1381 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1382 | 0 |
|
1383 | 0 | return NS_OK; |
1384 | 0 | } |
1385 | | |
1386 | | |
1387 | | nsresult |
1388 | | nsNavBookmarks::InsertTombstones(const nsTArray<TombstoneData>& aTombstones) |
1389 | 0 | { |
1390 | 0 | if (aTombstones.IsEmpty()) { |
1391 | 0 | return NS_OK; |
1392 | 0 | } |
1393 | 0 | |
1394 | 0 | size_t maxRowsPerChunk = SQLITE_MAX_VARIABLE_NUMBER / 2; |
1395 | 0 | for (uint32_t startIndex = 0; startIndex < aTombstones.Length(); startIndex += maxRowsPerChunk) { |
1396 | 0 | size_t rowsPerChunk = std::min(maxRowsPerChunk, aTombstones.Length() - startIndex); |
1397 | 0 |
|
1398 | 0 | // Build a query to insert all tombstones in a single statement, chunking to |
1399 | 0 | // avoid the SQLite bound parameter limit. |
1400 | 0 | nsAutoCString tombstonesToInsert; |
1401 | 0 | tombstonesToInsert.AppendLiteral("VALUES (?, ?)"); |
1402 | 0 | for (uint32_t i = 1; i < rowsPerChunk; ++i) { |
1403 | 0 | tombstonesToInsert.AppendLiteral(", (?, ?)"); |
1404 | 0 | } |
1405 | | #ifdef DEBUG |
1406 | | MOZ_ASSERT(tombstonesToInsert.CountChar('?') == rowsPerChunk * 2, |
1407 | | "Expected one binding param per column for each tombstone"); |
1408 | | #endif |
1409 | |
|
1410 | 0 | nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement( |
1411 | 0 | NS_LITERAL_CSTRING("INSERT INTO moz_bookmarks_deleted " |
1412 | 0 | "(guid, dateRemoved) ") + |
1413 | 0 | tombstonesToInsert |
1414 | 0 | ); |
1415 | 0 | NS_ENSURE_STATE(stmt); |
1416 | 0 | mozStorageStatementScoper scoper(stmt); |
1417 | 0 |
|
1418 | 0 | uint32_t paramIndex = 0; |
1419 | 0 | nsresult rv; |
1420 | 0 | for (uint32_t i = 0; i < rowsPerChunk; ++i) { |
1421 | 0 | const TombstoneData& tombstone = aTombstones[startIndex + i]; |
1422 | 0 | rv = stmt->BindUTF8StringByIndex(paramIndex++, tombstone.guid); |
1423 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1424 | 0 | rv = stmt->BindInt64ByIndex(paramIndex++, tombstone.dateRemoved); |
1425 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1426 | 0 | } |
1427 | 0 |
|
1428 | 0 | rv = stmt->Execute(); |
1429 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1430 | 0 | } |
1431 | 0 |
|
1432 | 0 | return NS_OK; |
1433 | 0 | } |
1434 | | |
1435 | | |
1436 | | nsresult |
1437 | | nsNavBookmarks::RemoveTombstone(const nsACString& aGUID) |
1438 | 0 | { |
1439 | 0 | nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement( |
1440 | 0 | "DELETE FROM moz_bookmarks_deleted WHERE guid = :guid" |
1441 | 0 | ); |
1442 | 0 | NS_ENSURE_STATE(stmt); |
1443 | 0 | mozStorageStatementScoper scoper(stmt); |
1444 | 0 |
|
1445 | 0 | nsresult rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"), aGUID); |
1446 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1447 | 0 |
|
1448 | 0 | return stmt->Execute(); |
1449 | 0 | } |
1450 | | |
1451 | | |
1452 | | NS_IMETHODIMP |
1453 | | nsNavBookmarks::SetItemTitle(int64_t aItemId, const nsACString& aTitle, |
1454 | | uint16_t aSource) |
1455 | 0 | { |
1456 | 0 | NS_ENSURE_ARG_MIN(aItemId, 1); |
1457 | 0 |
|
1458 | 0 | BookmarkData bookmark; |
1459 | 0 | nsresult rv = FetchItemInfo(aItemId, bookmark); |
1460 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1461 | 0 |
|
1462 | 0 | int64_t tagsRootId = TagsRootId(); |
1463 | 0 | bool isChangingTagFolder = bookmark.parentId == tagsRootId; |
1464 | 0 | int64_t syncChangeDelta = DetermineSyncChangeDelta(aSource); |
1465 | 0 |
|
1466 | 0 | nsAutoCString title; |
1467 | 0 | TruncateTitle(aTitle, title); |
1468 | 0 |
|
1469 | 0 | if (isChangingTagFolder) { |
1470 | 0 | // If we're changing the title of a tag folder, bump the change counter |
1471 | 0 | // for all tagged bookmarks. We use a separate code path to avoid a |
1472 | 0 | // transaction for non-tags. |
1473 | 0 | mozStorageTransaction transaction(mDB->MainConn(), false); |
1474 | 0 |
|
1475 | 0 | rv = SetItemTitleInternal(bookmark, title, syncChangeDelta); |
1476 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1477 | 0 |
|
1478 | 0 | rv = AddSyncChangesForBookmarksInFolder(bookmark.id, syncChangeDelta); |
1479 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1480 | 0 |
|
1481 | 0 | rv = transaction.Commit(); |
1482 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1483 | 0 | } else { |
1484 | 0 | rv = SetItemTitleInternal(bookmark, title, syncChangeDelta); |
1485 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1486 | 0 | } |
1487 | 0 |
|
1488 | 0 | NOTIFY_BOOKMARKS_OBSERVERS(mCanNotify, mObservers, SKIP_TAGS(isChangingTagFolder), |
1489 | 0 | OnItemChanged(bookmark.id, |
1490 | 0 | NS_LITERAL_CSTRING("title"), |
1491 | 0 | false, |
1492 | 0 | title, |
1493 | 0 | bookmark.lastModified, |
1494 | 0 | bookmark.type, |
1495 | 0 | bookmark.parentId, |
1496 | 0 | bookmark.guid, |
1497 | 0 | bookmark.parentGuid, |
1498 | 0 | EmptyCString(), |
1499 | 0 | aSource)); |
1500 | 0 | return NS_OK; |
1501 | 0 | } |
1502 | | |
1503 | | |
1504 | | nsresult |
1505 | | nsNavBookmarks::SetItemTitleInternal(BookmarkData& aBookmark, |
1506 | | const nsACString& aTitle, |
1507 | | int64_t aSyncChangeDelta) |
1508 | 0 | { |
1509 | 0 | nsCOMPtr<mozIStorageStatement> statement = mDB->GetStatement( |
1510 | 0 | "UPDATE moz_bookmarks SET " |
1511 | 0 | "title = :item_title, lastModified = :date, " |
1512 | 0 | "syncChangeCounter = syncChangeCounter + :delta " |
1513 | 0 | "WHERE id = :item_id" |
1514 | 0 | ); |
1515 | 0 | NS_ENSURE_STATE(statement); |
1516 | 0 | mozStorageStatementScoper scoper(statement); |
1517 | 0 |
|
1518 | 0 | nsresult rv; |
1519 | 0 | if (aTitle.IsEmpty()) { |
1520 | 0 | rv = statement->BindNullByName(NS_LITERAL_CSTRING("item_title")); |
1521 | 0 | } |
1522 | 0 | else { |
1523 | 0 | rv = statement->BindUTF8StringByName(NS_LITERAL_CSTRING("item_title"), |
1524 | 0 | aTitle); |
1525 | 0 | } |
1526 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1527 | 0 | aBookmark.lastModified = RoundToMilliseconds(RoundedPRNow()); |
1528 | 0 | rv = statement->BindInt64ByName(NS_LITERAL_CSTRING("date"), |
1529 | 0 | aBookmark.lastModified); |
1530 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1531 | 0 | rv = statement->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), aBookmark.id); |
1532 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1533 | 0 | rv = statement->BindInt64ByName(NS_LITERAL_CSTRING("delta"), |
1534 | 0 | aSyncChangeDelta); |
1535 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1536 | 0 |
|
1537 | 0 | rv = statement->Execute(); |
1538 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1539 | 0 |
|
1540 | 0 | return NS_OK; |
1541 | 0 | } |
1542 | | |
1543 | | |
1544 | | NS_IMETHODIMP |
1545 | | nsNavBookmarks::GetItemTitle(int64_t aItemId, |
1546 | | nsACString& _title) |
1547 | 0 | { |
1548 | 0 | NS_ENSURE_ARG_MIN(aItemId, 1); |
1549 | 0 |
|
1550 | 0 | BookmarkData bookmark; |
1551 | 0 | nsresult rv = FetchItemInfo(aItemId, bookmark); |
1552 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1553 | 0 |
|
1554 | 0 | _title = bookmark.title; |
1555 | 0 | return NS_OK; |
1556 | 0 | } |
1557 | | |
1558 | | |
1559 | | nsresult |
1560 | | nsNavBookmarks::GetBookmarkURI(int64_t aItemId, |
1561 | | nsIURI** _URI) |
1562 | 0 | { |
1563 | 0 | NS_ENSURE_ARG_MIN(aItemId, 1); |
1564 | 0 | NS_ENSURE_ARG_POINTER(_URI); |
1565 | 0 |
|
1566 | 0 | BookmarkData bookmark; |
1567 | 0 | nsresult rv = FetchItemInfo(aItemId, bookmark); |
1568 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1569 | 0 |
|
1570 | 0 | rv = NS_NewURI(_URI, bookmark.url); |
1571 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1572 | 0 |
|
1573 | 0 | return NS_OK; |
1574 | 0 | } |
1575 | | |
1576 | | |
1577 | | nsresult |
1578 | | nsNavBookmarks::ResultNodeForContainer(const nsCString& aGUID, |
1579 | | nsNavHistoryQueryOptions* aOptions, |
1580 | | nsNavHistoryResultNode** aNode) |
1581 | 0 | { |
1582 | 0 | BookmarkData bookmark; |
1583 | 0 | nsresult rv = FetchItemInfo(aGUID, bookmark); |
1584 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1585 | 0 |
|
1586 | 0 | if (bookmark.type == TYPE_FOLDER) { // TYPE_FOLDER |
1587 | 0 | *aNode = new nsNavHistoryFolderResultNode(bookmark.title, |
1588 | 0 | aOptions, |
1589 | 0 | bookmark.id); |
1590 | 0 | } |
1591 | 0 | else { |
1592 | 0 | return NS_ERROR_INVALID_ARG; |
1593 | 0 | } |
1594 | 0 | |
1595 | 0 | (*aNode)->mDateAdded = bookmark.dateAdded; |
1596 | 0 | (*aNode)->mLastModified = bookmark.lastModified; |
1597 | 0 | (*aNode)->mBookmarkGuid = bookmark.guid; |
1598 | 0 | (*aNode)->GetAsFolder()->mTargetFolderGuid = bookmark.guid; |
1599 | 0 |
|
1600 | 0 | NS_ADDREF(*aNode); |
1601 | 0 | return NS_OK; |
1602 | 0 | } |
1603 | | |
1604 | | |
1605 | | nsresult |
1606 | | nsNavBookmarks::QueryFolderChildren( |
1607 | | int64_t aFolderId, |
1608 | | nsNavHistoryQueryOptions* aOptions, |
1609 | | nsCOMArray<nsNavHistoryResultNode>* aChildren) |
1610 | 0 | { |
1611 | 0 | NS_ENSURE_ARG_POINTER(aOptions); |
1612 | 0 | NS_ENSURE_ARG_POINTER(aChildren); |
1613 | 0 |
|
1614 | 0 | // Select all children of a given folder, sorted by position. |
1615 | 0 | // This is a LEFT JOIN because not all bookmarks types have a place. |
1616 | 0 | // We construct a result where the first columns exactly match those returned |
1617 | 0 | // by mDBGetURLPageInfo, and additionally contains columns for position, |
1618 | 0 | // item_child, and folder_child from moz_bookmarks. |
1619 | 0 | nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement( |
1620 | 0 | "SELECT h.id, h.url, b.title, h.rev_host, h.visit_count, " |
1621 | 0 | "h.last_visit_date, null, b.id, b.dateAdded, b.lastModified, " |
1622 | 0 | "b.parent, null, h.frecency, h.hidden, h.guid, null, null, null, " |
1623 | 0 | "b.guid, b.position, b.type, b.fk " |
1624 | 0 | "FROM moz_bookmarks b " |
1625 | 0 | "LEFT JOIN moz_places h ON b.fk = h.id " |
1626 | 0 | "WHERE b.parent = :parent " |
1627 | 0 | "AND (NOT :excludeItems OR " |
1628 | 0 | "b.type = :folder OR " |
1629 | 0 | "h.url_hash BETWEEN hash('place', 'prefix_lo') AND hash('place', 'prefix_hi')) " |
1630 | 0 | "ORDER BY b.position ASC" |
1631 | 0 | ); |
1632 | 0 | NS_ENSURE_STATE(stmt); |
1633 | 0 | mozStorageStatementScoper scoper(stmt); |
1634 | 0 |
|
1635 | 0 | nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aFolderId); |
1636 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1637 | 0 | rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("folder"), TYPE_FOLDER); |
1638 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1639 | 0 | rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("excludeItems"), aOptions->ExcludeItems()); |
1640 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1641 | 0 |
|
1642 | 0 | nsCOMPtr<mozIStorageValueArray> row = do_QueryInterface(stmt, &rv); |
1643 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1644 | 0 |
|
1645 | 0 | int32_t index = -1; |
1646 | 0 | bool hasResult; |
1647 | 0 | while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) { |
1648 | 0 | rv = ProcessFolderNodeRow(row, aOptions, aChildren, index); |
1649 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1650 | 0 | } |
1651 | 0 |
|
1652 | 0 | return NS_OK; |
1653 | 0 | } |
1654 | | |
1655 | | |
1656 | | nsresult |
1657 | | nsNavBookmarks::ProcessFolderNodeRow( |
1658 | | mozIStorageValueArray* aRow, |
1659 | | nsNavHistoryQueryOptions* aOptions, |
1660 | | nsCOMArray<nsNavHistoryResultNode>* aChildren, |
1661 | | int32_t& aCurrentIndex) |
1662 | 0 | { |
1663 | 0 | NS_ENSURE_ARG_POINTER(aRow); |
1664 | 0 | NS_ENSURE_ARG_POINTER(aOptions); |
1665 | 0 | NS_ENSURE_ARG_POINTER(aChildren); |
1666 | 0 |
|
1667 | 0 | // The results will be in order of aCurrentIndex. Even if we don't add a node |
1668 | 0 | // because it was excluded, we need to count its index, so do that before |
1669 | 0 | // doing anything else. |
1670 | 0 | aCurrentIndex++; |
1671 | 0 |
|
1672 | 0 | int32_t itemType; |
1673 | 0 | nsresult rv = aRow->GetInt32(kGetChildrenIndex_Type, &itemType); |
1674 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1675 | 0 | int64_t id; |
1676 | 0 | rv = aRow->GetInt64(nsNavHistory::kGetInfoIndex_ItemId, &id); |
1677 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1678 | 0 |
|
1679 | 0 | RefPtr<nsNavHistoryResultNode> node; |
1680 | 0 |
|
1681 | 0 | if (itemType == TYPE_BOOKMARK) { |
1682 | 0 | nsNavHistory* history = nsNavHistory::GetHistoryService(); |
1683 | 0 | NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY); |
1684 | 0 | rv = history->RowToResult(aRow, aOptions, getter_AddRefs(node)); |
1685 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1686 | 0 | uint32_t nodeType; |
1687 | 0 | node->GetType(&nodeType); |
1688 | 0 | if (nodeType == nsINavHistoryResultNode::RESULT_TYPE_QUERY && |
1689 | 0 | aOptions->ExcludeQueries()) { |
1690 | 0 | return NS_OK; |
1691 | 0 | } |
1692 | 0 | } |
1693 | 0 | else if (itemType == TYPE_FOLDER) { |
1694 | 0 | // ExcludeReadOnlyFolders currently means "ExcludeLivemarks" (to be fixed in |
1695 | 0 | // bug 1072833) |
1696 | 0 | if (aOptions->ExcludeReadOnlyFolders()) { |
1697 | 0 | if (IsLivemark(id)) |
1698 | 0 | return NS_OK; |
1699 | 0 | } |
1700 | 0 | |
1701 | 0 | nsAutoCString title; |
1702 | 0 | bool isNull; |
1703 | 0 | rv = aRow->GetIsNull(nsNavHistory::kGetInfoIndex_Title, &isNull); |
1704 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1705 | 0 | if (!isNull) { |
1706 | 0 | rv = aRow->GetUTF8String(nsNavHistory::kGetInfoIndex_Title, title); |
1707 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1708 | 0 | } |
1709 | 0 |
|
1710 | 0 | // Don't use options from the parent to build the new folder node, it will |
1711 | 0 | // inherit those later when it's inserted in the result. |
1712 | 0 | node = new nsNavHistoryFolderResultNode(title, new nsNavHistoryQueryOptions(), id); |
1713 | 0 |
|
1714 | 0 | rv = aRow->GetUTF8String(kGetChildrenIndex_Guid, node->mBookmarkGuid); |
1715 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1716 | 0 | node->GetAsFolder()->mTargetFolderGuid = node->mBookmarkGuid; |
1717 | 0 |
|
1718 | 0 | rv = aRow->GetInt64(nsNavHistory::kGetInfoIndex_ItemDateAdded, |
1719 | 0 | reinterpret_cast<int64_t*>(&node->mDateAdded)); |
1720 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1721 | 0 | rv = aRow->GetInt64(nsNavHistory::kGetInfoIndex_ItemLastModified, |
1722 | 0 | reinterpret_cast<int64_t*>(&node->mLastModified)); |
1723 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1724 | 0 | } |
1725 | 0 | else { |
1726 | 0 | // This is a separator. |
1727 | 0 | node = new nsNavHistorySeparatorResultNode(); |
1728 | 0 |
|
1729 | 0 | node->mItemId = id; |
1730 | 0 | rv = aRow->GetUTF8String(kGetChildrenIndex_Guid, node->mBookmarkGuid); |
1731 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1732 | 0 | rv = aRow->GetInt64(nsNavHistory::kGetInfoIndex_ItemDateAdded, |
1733 | 0 | reinterpret_cast<int64_t*>(&node->mDateAdded)); |
1734 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1735 | 0 | rv = aRow->GetInt64(nsNavHistory::kGetInfoIndex_ItemLastModified, |
1736 | 0 | reinterpret_cast<int64_t*>(&node->mLastModified)); |
1737 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1738 | 0 | } |
1739 | 0 |
|
1740 | 0 | // Store the index of the node within this container. Note that this is not |
1741 | 0 | // moz_bookmarks.position. |
1742 | 0 | node->mBookmarkIndex = aCurrentIndex; |
1743 | 0 |
|
1744 | 0 | NS_ENSURE_TRUE(aChildren->AppendObject(node), NS_ERROR_OUT_OF_MEMORY); |
1745 | 0 | return NS_OK; |
1746 | 0 | } |
1747 | | |
1748 | | |
1749 | | nsresult |
1750 | | nsNavBookmarks::QueryFolderChildrenAsync( |
1751 | | nsNavHistoryFolderResultNode* aNode, |
1752 | | mozIStoragePendingStatement** _pendingStmt) |
1753 | 0 | { |
1754 | 0 | NS_ENSURE_ARG_POINTER(aNode); |
1755 | 0 | NS_ENSURE_ARG_POINTER(_pendingStmt); |
1756 | 0 |
|
1757 | 0 | // Select all children of a given folder, sorted by position. |
1758 | 0 | // This is a LEFT JOIN because not all bookmarks types have a place. |
1759 | 0 | // We construct a result where the first columns exactly match those returned |
1760 | 0 | // by mDBGetURLPageInfo, and additionally contains columns for position, |
1761 | 0 | // item_child, and folder_child from moz_bookmarks. |
1762 | 0 | nsCOMPtr<mozIStorageAsyncStatement> stmt = mDB->GetAsyncStatement( |
1763 | 0 | "SELECT h.id, h.url, b.title, h.rev_host, h.visit_count, " |
1764 | 0 | "h.last_visit_date, null, b.id, b.dateAdded, b.lastModified, " |
1765 | 0 | "b.parent, null, h.frecency, h.hidden, h.guid, null, null, null, " |
1766 | 0 | "b.guid, b.position, b.type, b.fk " |
1767 | 0 | "FROM moz_bookmarks b " |
1768 | 0 | "LEFT JOIN moz_places h ON b.fk = h.id " |
1769 | 0 | "WHERE b.parent = :parent " |
1770 | 0 | "AND (NOT :excludeItems OR " |
1771 | 0 | "b.type = :folder OR " |
1772 | 0 | "h.url_hash BETWEEN hash('place', 'prefix_lo') AND hash('place', 'prefix_hi')) " |
1773 | 0 | "ORDER BY b.position ASC" |
1774 | 0 | ); |
1775 | 0 | NS_ENSURE_STATE(stmt); |
1776 | 0 |
|
1777 | 0 | nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aNode->mTargetFolderItemId); |
1778 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1779 | 0 | rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("folder"), TYPE_FOLDER); |
1780 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1781 | 0 | rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("excludeItems"), aNode->mOptions->ExcludeItems()); |
1782 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1783 | 0 |
|
1784 | 0 | nsCOMPtr<mozIStoragePendingStatement> pendingStmt; |
1785 | 0 | rv = stmt->ExecuteAsync(aNode, getter_AddRefs(pendingStmt)); |
1786 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1787 | 0 |
|
1788 | 0 | NS_IF_ADDREF(*_pendingStmt = pendingStmt); |
1789 | 0 | return NS_OK; |
1790 | 0 | } |
1791 | | |
1792 | | |
1793 | | nsresult |
1794 | | nsNavBookmarks::FetchFolderInfo(int64_t aFolderId, |
1795 | | int32_t* _folderCount, |
1796 | | nsACString& _guid, |
1797 | | int64_t* _parentId) |
1798 | 0 | { |
1799 | 0 | *_folderCount = 0; |
1800 | 0 | *_parentId = -1; |
1801 | 0 |
|
1802 | 0 | // This query has to always return results, so it can't be written as a join, |
1803 | 0 | // though a left join of 2 subqueries would have the same cost. |
1804 | 0 | nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement( |
1805 | 0 | "SELECT count(*), " |
1806 | 0 | "(SELECT guid FROM moz_bookmarks WHERE id = :parent), " |
1807 | 0 | "(SELECT parent FROM moz_bookmarks WHERE id = :parent) " |
1808 | 0 | "FROM moz_bookmarks " |
1809 | 0 | "WHERE parent = :parent" |
1810 | 0 | ); |
1811 | 0 | NS_ENSURE_STATE(stmt); |
1812 | 0 | mozStorageStatementScoper scoper(stmt); |
1813 | 0 |
|
1814 | 0 | nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aFolderId); |
1815 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1816 | 0 |
|
1817 | 0 | bool hasResult; |
1818 | 0 | rv = stmt->ExecuteStep(&hasResult); |
1819 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1820 | 0 | NS_ENSURE_TRUE(hasResult, NS_ERROR_UNEXPECTED); |
1821 | 0 |
|
1822 | 0 | // Ensure that the folder we are looking for exists. |
1823 | 0 | // Can't rely only on parent, since the root has parent 0, that doesn't exist. |
1824 | 0 | bool isNull; |
1825 | 0 | rv = stmt->GetIsNull(2, &isNull); |
1826 | 0 | NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && (!isNull || aFolderId == 0), |
1827 | 0 | NS_ERROR_INVALID_ARG); |
1828 | 0 |
|
1829 | 0 | rv = stmt->GetInt32(0, _folderCount); |
1830 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1831 | 0 | if (!isNull) { |
1832 | 0 | rv = stmt->GetUTF8String(1, _guid); |
1833 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1834 | 0 | rv = stmt->GetInt64(2, _parentId); |
1835 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1836 | 0 | } |
1837 | 0 |
|
1838 | 0 | return NS_OK; |
1839 | 0 | } |
1840 | | |
1841 | | |
1842 | | NS_IMETHODIMP |
1843 | | nsNavBookmarks::GetFolderIdForItem(int64_t aItemId, int64_t* _parentId) |
1844 | 0 | { |
1845 | 0 | NS_ENSURE_ARG_MIN(aItemId, 1); |
1846 | 0 | NS_ENSURE_ARG_POINTER(_parentId); |
1847 | 0 |
|
1848 | 0 | BookmarkData bookmark; |
1849 | 0 | nsresult rv = FetchItemInfo(aItemId, bookmark); |
1850 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1851 | 0 |
|
1852 | 0 | // this should not happen, but see bug #400448 for details |
1853 | 0 | NS_ENSURE_TRUE(bookmark.id != bookmark.parentId, NS_ERROR_UNEXPECTED); |
1854 | 0 |
|
1855 | 0 | *_parentId = bookmark.parentId; |
1856 | 0 | return NS_OK; |
1857 | 0 | } |
1858 | | |
1859 | | nsresult |
1860 | | nsNavBookmarks::GetBookmarksForURI(nsIURI* aURI, |
1861 | | nsTArray<BookmarkData>& aBookmarks) |
1862 | 0 | { |
1863 | 0 | NS_ENSURE_ARG(aURI); |
1864 | 0 |
|
1865 | 0 | // Double ordering covers possible lastModified ties, that could happen when |
1866 | 0 | // importing, syncing or due to extensions. |
1867 | 0 | // Note: not using a JOIN is cheaper in this case. |
1868 | 0 | nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement( |
1869 | 0 | "/* do not warn (bug 1175249) */ " |
1870 | 0 | "SELECT b.id, b.guid, b.parent, b.lastModified, t.guid, t.parent, b.syncStatus " |
1871 | 0 | "FROM moz_bookmarks b " |
1872 | 0 | "JOIN moz_bookmarks t on t.id = b.parent " |
1873 | 0 | "WHERE b.fk = (SELECT id FROM moz_places WHERE url_hash = hash(:page_url) AND url = :page_url) " |
1874 | 0 | "ORDER BY b.lastModified DESC, b.id DESC " |
1875 | 0 | ); |
1876 | 0 | NS_ENSURE_STATE(stmt); |
1877 | 0 | mozStorageStatementScoper scoper(stmt); |
1878 | 0 |
|
1879 | 0 | nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), aURI); |
1880 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1881 | 0 |
|
1882 | 0 | int64_t tagsRootId = TagsRootId(); |
1883 | 0 |
|
1884 | 0 | bool more; |
1885 | 0 | nsAutoString tags; |
1886 | 0 | while (NS_SUCCEEDED((rv = stmt->ExecuteStep(&more))) && more) { |
1887 | 0 | // Skip tags. |
1888 | 0 | int64_t grandParentId; |
1889 | 0 | nsresult rv = stmt->GetInt64(5, &grandParentId); |
1890 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1891 | 0 | if (grandParentId == tagsRootId) { |
1892 | 0 | continue; |
1893 | 0 | } |
1894 | 0 | |
1895 | 0 | BookmarkData bookmark; |
1896 | 0 | bookmark.grandParentId = grandParentId; |
1897 | 0 | rv = stmt->GetInt64(0, &bookmark.id); |
1898 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1899 | 0 | rv = stmt->GetUTF8String(1, bookmark.guid); |
1900 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1901 | 0 | rv = stmt->GetInt64(2, &bookmark.parentId); |
1902 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1903 | 0 | rv = stmt->GetInt64(3, reinterpret_cast<int64_t*>(&bookmark.lastModified)); |
1904 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1905 | 0 | rv = stmt->GetUTF8String(4, bookmark.parentGuid); |
1906 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1907 | 0 | rv = stmt->GetInt32(6, &bookmark.syncStatus); |
1908 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1909 | 0 |
|
1910 | 0 | NS_ENSURE_TRUE(aBookmarks.AppendElement(bookmark), NS_ERROR_OUT_OF_MEMORY); |
1911 | 0 | } |
1912 | 0 |
|
1913 | 0 | return NS_OK; |
1914 | 0 | } |
1915 | | |
1916 | | |
1917 | | |
1918 | | NS_IMETHODIMP |
1919 | | nsNavBookmarks::AddObserver(nsINavBookmarkObserver* aObserver, |
1920 | | bool aOwnsWeak) |
1921 | 0 | { |
1922 | 0 | NS_ENSURE_ARG(aObserver); |
1923 | 0 |
|
1924 | 0 | if (NS_WARN_IF(!mCanNotify)) |
1925 | 0 | return NS_ERROR_UNEXPECTED; |
1926 | 0 | |
1927 | 0 | return mObservers.AppendWeakElement(aObserver, aOwnsWeak); |
1928 | 0 | } |
1929 | | |
1930 | | |
1931 | | NS_IMETHODIMP |
1932 | | nsNavBookmarks::RemoveObserver(nsINavBookmarkObserver* aObserver) |
1933 | 0 | { |
1934 | 0 | return mObservers.RemoveWeakElement(aObserver); |
1935 | 0 | } |
1936 | | |
1937 | | NS_IMETHODIMP |
1938 | | nsNavBookmarks::GetObservers(uint32_t* _count, |
1939 | | nsINavBookmarkObserver*** _observers) |
1940 | 0 | { |
1941 | 0 | NS_ENSURE_ARG_POINTER(_count); |
1942 | 0 | NS_ENSURE_ARG_POINTER(_observers); |
1943 | 0 |
|
1944 | 0 | *_count = 0; |
1945 | 0 | *_observers = nullptr; |
1946 | 0 |
|
1947 | 0 | if (!mCanNotify) |
1948 | 0 | return NS_OK; |
1949 | 0 | |
1950 | 0 | nsCOMArray<nsINavBookmarkObserver> observers; |
1951 | 0 |
|
1952 | 0 | for (uint32_t i = 0; i < mObservers.Length(); ++i) { |
1953 | 0 | const nsCOMPtr<nsINavBookmarkObserver> &observer = mObservers.ElementAt(i).GetValue(); |
1954 | 0 | // Skip nullified weak observers. |
1955 | 0 | if (observer) |
1956 | 0 | observers.AppendElement(observer); |
1957 | 0 | } |
1958 | 0 |
|
1959 | 0 | if (observers.Count() == 0) |
1960 | 0 | return NS_OK; |
1961 | 0 | |
1962 | 0 | *_count = observers.Count(); |
1963 | 0 | observers.Forget(_observers); |
1964 | 0 |
|
1965 | 0 | return NS_OK; |
1966 | 0 | } |
1967 | | |
1968 | | void |
1969 | | nsNavBookmarks::NotifyItemVisited(const ItemVisitData& aData) |
1970 | 0 | { |
1971 | 0 | nsCOMPtr<nsIURI> uri; |
1972 | 0 | MOZ_ALWAYS_SUCCEEDS(NS_NewURI(getter_AddRefs(uri), aData.bookmark.url)); |
1973 | 0 | // Notify the visit only if we have a valid uri, otherwise the observer |
1974 | 0 | // couldn't gather enough data from the notification. |
1975 | 0 | // This should be false only if there's a bug in the code preceding us. |
1976 | 0 | if (uri) { |
1977 | 0 | NOTIFY_OBSERVERS(mCanNotify, mObservers, nsINavBookmarkObserver, |
1978 | 0 | OnItemVisited(aData.bookmark.id, |
1979 | 0 | aData.visitId, |
1980 | 0 | aData.time, |
1981 | 0 | aData.transitionType, |
1982 | 0 | uri, |
1983 | 0 | aData.bookmark.parentId, |
1984 | 0 | aData.bookmark.guid, |
1985 | 0 | aData.bookmark.parentGuid)); |
1986 | 0 | } |
1987 | 0 | } |
1988 | | |
1989 | | void |
1990 | | nsNavBookmarks::NotifyItemChanged(const ItemChangeData& aData) |
1991 | 0 | { |
1992 | 0 | // A guid must always be defined. |
1993 | 0 | MOZ_ASSERT(!aData.bookmark.guid.IsEmpty()); |
1994 | 0 |
|
1995 | 0 | PRTime lastModified = aData.bookmark.lastModified; |
1996 | 0 | if (aData.updateLastModified) { |
1997 | 0 | lastModified = RoundedPRNow(); |
1998 | 0 | MOZ_ALWAYS_SUCCEEDS(SetItemDateInternal( |
1999 | 0 | LAST_MODIFIED, DetermineSyncChangeDelta(aData.source), |
2000 | 0 | aData.bookmark.id, lastModified)); |
2001 | 0 | } |
2002 | 0 |
|
2003 | 0 | NOTIFY_OBSERVERS(mCanNotify, mObservers, nsINavBookmarkObserver, |
2004 | 0 | OnItemChanged(aData.bookmark.id, |
2005 | 0 | aData.property, |
2006 | 0 | aData.isAnnotation, |
2007 | 0 | aData.newValue, |
2008 | 0 | lastModified, |
2009 | 0 | aData.bookmark.type, |
2010 | 0 | aData.bookmark.parentId, |
2011 | 0 | aData.bookmark.guid, |
2012 | 0 | aData.bookmark.parentGuid, |
2013 | 0 | aData.oldValue, |
2014 | 0 | aData.source)); |
2015 | 0 | } |
2016 | | |
2017 | | //////////////////////////////////////////////////////////////////////////////// |
2018 | | //// nsIObserver |
2019 | | |
2020 | | NS_IMETHODIMP |
2021 | | nsNavBookmarks::Observe(nsISupports *aSubject, const char *aTopic, |
2022 | | const char16_t *aData) |
2023 | 0 | { |
2024 | 0 | NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread"); |
2025 | 0 |
|
2026 | 0 | if (strcmp(aTopic, TOPIC_PLACES_CONNECTION_CLOSED) == 0) { |
2027 | 0 | // Don't even try to notify observers from this point on, the category |
2028 | 0 | // cache would init services that could try to use our APIs. |
2029 | 0 | mCanNotify = false; |
2030 | 0 | mObservers.Clear(); |
2031 | 0 | } |
2032 | 0 |
|
2033 | 0 | return NS_OK; |
2034 | 0 | } |
2035 | | |
2036 | | //////////////////////////////////////////////////////////////////////////////// |
2037 | | //// nsINavHistoryObserver |
2038 | | |
2039 | | NS_IMETHODIMP |
2040 | | nsNavBookmarks::OnBeginUpdateBatch() |
2041 | 0 | { |
2042 | 0 | NOTIFY_OBSERVERS(mCanNotify, mObservers, |
2043 | 0 | nsINavBookmarkObserver, OnBeginUpdateBatch()); |
2044 | 0 | return NS_OK; |
2045 | 0 | } |
2046 | | |
2047 | | |
2048 | | NS_IMETHODIMP |
2049 | | nsNavBookmarks::OnEndUpdateBatch() |
2050 | 0 | { |
2051 | 0 | NOTIFY_OBSERVERS(mCanNotify, mObservers, |
2052 | 0 | nsINavBookmarkObserver, OnEndUpdateBatch()); |
2053 | 0 | return NS_OK; |
2054 | 0 | } |
2055 | | |
2056 | | |
2057 | | void |
2058 | | nsNavBookmarks::HandlePlacesEvent(const PlacesEventSequence& aEvents) |
2059 | 0 | { |
2060 | 0 | for (const auto& event : aEvents) { |
2061 | 0 | if (NS_WARN_IF(event->Type() != PlacesEventType::Page_visited)) { |
2062 | 0 | continue; |
2063 | 0 | } |
2064 | 0 | |
2065 | 0 | const dom::PlacesVisit* visit = event->AsPlacesVisit(); |
2066 | 0 | if (NS_WARN_IF(!visit)) { |
2067 | 0 | continue; |
2068 | 0 | } |
2069 | 0 | |
2070 | 0 | ItemVisitData visitData; |
2071 | 0 | visitData.visitId = visit->mVisitId; |
2072 | 0 | visitData.bookmark.url = NS_ConvertUTF16toUTF8(visit->mUrl); |
2073 | 0 | visitData.time = visit->mVisitTime * 1000; |
2074 | 0 | visitData.transitionType = visit->mTransitionType; |
2075 | 0 | RefPtr< AsyncGetBookmarksForURI<ItemVisitMethod, ItemVisitData> > notifier = |
2076 | 0 | new AsyncGetBookmarksForURI<ItemVisitMethod, ItemVisitData>(this, &nsNavBookmarks::NotifyItemVisited, visitData); |
2077 | 0 | notifier->Init(); |
2078 | 0 | } |
2079 | 0 | } |
2080 | | |
2081 | | |
2082 | | NS_IMETHODIMP |
2083 | | nsNavBookmarks::OnDeleteURI(nsIURI* aURI, |
2084 | | const nsACString& aGUID, |
2085 | | uint16_t aReason) |
2086 | 0 | { |
2087 | 0 | return NS_OK; |
2088 | 0 | } |
2089 | | |
2090 | | |
2091 | | NS_IMETHODIMP |
2092 | | nsNavBookmarks::OnClearHistory() |
2093 | 0 | { |
2094 | 0 | // TODO(bryner): we should notify on visited-time change for all URIs |
2095 | 0 | return NS_OK; |
2096 | 0 | } |
2097 | | |
2098 | | |
2099 | | NS_IMETHODIMP |
2100 | | nsNavBookmarks::OnTitleChanged(nsIURI* aURI, |
2101 | | const nsAString& aPageTitle, |
2102 | | const nsACString& aGUID) |
2103 | 0 | { |
2104 | 0 | // NOOP. We don't consume page titles from moz_places anymore. |
2105 | 0 | // Title-change notifications are sent from SetItemTitle. |
2106 | 0 | return NS_OK; |
2107 | 0 | } |
2108 | | |
2109 | | |
2110 | | NS_IMETHODIMP |
2111 | | nsNavBookmarks::OnFrecencyChanged(nsIURI* aURI, |
2112 | | int32_t aNewFrecency, |
2113 | | const nsACString& aGUID, |
2114 | | bool aHidden, |
2115 | | PRTime aLastVisitDate) |
2116 | 0 | { |
2117 | 0 | return NS_OK; |
2118 | 0 | } |
2119 | | |
2120 | | |
2121 | | NS_IMETHODIMP |
2122 | | nsNavBookmarks::OnManyFrecenciesChanged() |
2123 | 0 | { |
2124 | 0 | return NS_OK; |
2125 | 0 | } |
2126 | | |
2127 | | |
2128 | | NS_IMETHODIMP |
2129 | | nsNavBookmarks::OnPageChanged(nsIURI* aURI, |
2130 | | uint32_t aChangedAttribute, |
2131 | | const nsAString& aNewValue, |
2132 | | const nsACString& aGUID) |
2133 | 0 | { |
2134 | 0 | NS_ENSURE_ARG(aURI); |
2135 | 0 |
|
2136 | 0 | nsresult rv; |
2137 | 0 | if (aChangedAttribute == nsINavHistoryObserver::ATTRIBUTE_FAVICON) { |
2138 | 0 | ItemChangeData changeData; |
2139 | 0 | rv = aURI->GetSpec(changeData.bookmark.url); |
2140 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
2141 | 0 | changeData.property = NS_LITERAL_CSTRING("favicon"); |
2142 | 0 | changeData.isAnnotation = false; |
2143 | 0 | changeData.newValue = NS_ConvertUTF16toUTF8(aNewValue); |
2144 | 0 | changeData.bookmark.lastModified = 0; |
2145 | 0 | changeData.bookmark.type = TYPE_BOOKMARK; |
2146 | 0 |
|
2147 | 0 | // Favicons may be set to either pure URIs or to folder URIs |
2148 | 0 | bool isPlaceURI; |
2149 | 0 | rv = aURI->SchemeIs("place", &isPlaceURI); |
2150 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
2151 | 0 | if (isPlaceURI) { |
2152 | 0 | nsNavHistory* history = nsNavHistory::GetHistoryService(); |
2153 | 0 | NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY); |
2154 | 0 |
|
2155 | 0 | nsCOMPtr<nsINavHistoryQuery> query; |
2156 | 0 | nsCOMPtr<nsINavHistoryQueryOptions> options; |
2157 | 0 | rv = history->QueryStringToQuery(changeData.bookmark.url, |
2158 | 0 | getter_AddRefs(query), |
2159 | 0 | getter_AddRefs(options)); |
2160 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
2161 | 0 |
|
2162 | 0 | RefPtr<nsNavHistoryQuery> queryObj = do_QueryObject(query); |
2163 | 0 | if (queryObj->Parents().Length() == 1) { |
2164 | 0 | // Fetch missing data. |
2165 | 0 | rv = FetchItemInfo(queryObj->Parents()[0], changeData.bookmark); |
2166 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
2167 | 0 | NotifyItemChanged(changeData); |
2168 | 0 | } |
2169 | 0 | } |
2170 | 0 | else { |
2171 | 0 | RefPtr< AsyncGetBookmarksForURI<ItemChangeMethod, ItemChangeData> > notifier = |
2172 | 0 | new AsyncGetBookmarksForURI<ItemChangeMethod, ItemChangeData>(this, &nsNavBookmarks::NotifyItemChanged, changeData); |
2173 | 0 | notifier->Init(); |
2174 | 0 | } |
2175 | 0 | } |
2176 | 0 | return NS_OK; |
2177 | 0 | } |
2178 | | |
2179 | | |
2180 | | NS_IMETHODIMP |
2181 | | nsNavBookmarks::OnDeleteVisits(nsIURI* aURI, bool aPartialRemoval, |
2182 | | const nsACString& aGUID, |
2183 | | uint16_t aReason, uint32_t aTransitionType) |
2184 | 0 | { |
2185 | 0 | NS_ENSURE_ARG(aURI); |
2186 | 0 |
|
2187 | 0 | // Notify "cleartime" only if all visits to the page have been removed. |
2188 | 0 | if (!aPartialRemoval) { |
2189 | 0 | // If the page is bookmarked, notify observers for each associated bookmark. |
2190 | 0 | ItemChangeData changeData; |
2191 | 0 | nsresult rv = aURI->GetSpec(changeData.bookmark.url); |
2192 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
2193 | 0 | changeData.property = NS_LITERAL_CSTRING("cleartime"); |
2194 | 0 | changeData.isAnnotation = false; |
2195 | 0 | changeData.bookmark.lastModified = 0; |
2196 | 0 | changeData.bookmark.type = TYPE_BOOKMARK; |
2197 | 0 |
|
2198 | 0 | RefPtr< AsyncGetBookmarksForURI<ItemChangeMethod, ItemChangeData> > notifier = |
2199 | 0 | new AsyncGetBookmarksForURI<ItemChangeMethod, ItemChangeData>(this, &nsNavBookmarks::NotifyItemChanged, changeData); |
2200 | 0 | notifier->Init(); |
2201 | 0 | } |
2202 | 0 | return NS_OK; |
2203 | 0 | } |