1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is Places.
17 * The Initial Developer of the Original Code is
19 * Portions created by the Initial Developer are Copyright (C) 2005
20 * the Initial Developer. All Rights Reserved.
23 * Brian Ryner <bryner@brianryner.com> (original author)
24 * Dietrich Ayala <dietrich@mozilla.com>
25 * Drew Willcoxon <adw@mozilla.com>
26 * Marco Bonardo <mak77@bonardo.net>
28 * Alternatively, the contents of this file may be used under the terms of
29 * either the GNU General Public License Version 2 or later (the "GPL"), or
30 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 * in which case the provisions of the GPL or the LGPL are applicable instead
32 * of those above. If you wish to allow use of your version of this file only
33 * under the terms of either the GPL or the LGPL, and not to allow others to
34 * use your version of this file under the terms of the MPL, indicate your
35 * decision by deleting the provisions above and replace them with the notice
36 * and other provisions required by the GPL or the LGPL. If you do not delete
37 * the provisions above, a recipient may use your version of this file under
38 * the terms of any one of the MPL, the GPL or the LGPL.
40 * ***** END LICENSE BLOCK ***** */
42 #include "nsAppDirectoryServiceDefs.h"
43 #include "nsNavBookmarks.h"
44 #include "nsNavHistory.h"
45 #include "mozStorageHelper.h"
46 #include "nsIServiceManager.h"
47 #include "nsNetUtil.h"
48 #include "nsIDynamicContainer.h"
49 #include "nsUnicharUtils.h"
50 #include "nsFaviconService.h"
51 #include "nsAnnotationService.h"
52 #include "nsPrintfCString.h"
53 #include "nsIUUIDGenerator.h"
55 #include "nsILivemarkService.h"
56 #include "nsPlacesTriggers.h"
57 #include "nsPlacesTables.h"
58 #include "nsPlacesIndexes.h"
59 #include "nsPlacesMacros.h"
62 #include "mozilla/FunctionTimer.h"
64 #define BOOKMARKS_TO_KEYWORDS_INITIAL_CACHE_SIZE 64
66 const PRInt32
nsNavBookmarks::kFindBookmarksIndex_ID
= 0;
67 const PRInt32
nsNavBookmarks::kFindBookmarksIndex_Type
= 1;
68 const PRInt32
nsNavBookmarks::kFindBookmarksIndex_PlaceID
= 2;
69 const PRInt32
nsNavBookmarks::kFindBookmarksIndex_Parent
= 3;
70 const PRInt32
nsNavBookmarks::kFindBookmarksIndex_Position
= 4;
71 const PRInt32
nsNavBookmarks::kFindBookmarksIndex_Title
= 5;
73 // These columns sit to the right of the kGetInfoIndex_* columns.
74 const PRInt32
nsNavBookmarks::kGetChildrenIndex_Position
= 13;
75 const PRInt32
nsNavBookmarks::kGetChildrenIndex_Type
= 14;
76 const PRInt32
nsNavBookmarks::kGetChildrenIndex_PlaceID
= 15;
77 const PRInt32
nsNavBookmarks::kGetChildrenIndex_ServiceContractId
= 16;
79 const PRInt32
nsNavBookmarks::kGetItemPropertiesIndex_ID
= 0;
80 const PRInt32
nsNavBookmarks::kGetItemPropertiesIndex_URI
= 1;
81 const PRInt32
nsNavBookmarks::kGetItemPropertiesIndex_Title
= 2;
82 const PRInt32
nsNavBookmarks::kGetItemPropertiesIndex_Position
= 3;
83 const PRInt32
nsNavBookmarks::kGetItemPropertiesIndex_PlaceID
= 4;
84 const PRInt32
nsNavBookmarks::kGetItemPropertiesIndex_Parent
= 5;
85 const PRInt32
nsNavBookmarks::kGetItemPropertiesIndex_Type
= 6;
86 const PRInt32
nsNavBookmarks::kGetItemPropertiesIndex_ServiceContractId
= 7;
87 const PRInt32
nsNavBookmarks::kGetItemPropertiesIndex_DateAdded
= 8;
88 const PRInt32
nsNavBookmarks::kGetItemPropertiesIndex_LastModified
= 9;
90 using namespace mozilla::places
;
92 PLACES_FACTORY_SINGLETON_IMPLEMENTATION(nsNavBookmarks
, gBookmarksService
)
94 #define BOOKMARKS_ANNO_PREFIX "bookmarks/"
95 #define BOOKMARKS_TOOLBAR_FOLDER_ANNO NS_LITERAL_CSTRING(BOOKMARKS_ANNO_PREFIX "toolbarFolder")
96 #define GUID_ANNO NS_LITERAL_CSTRING("placesInternal/GUID")
97 #define READ_ONLY_ANNO NS_LITERAL_CSTRING("placesInternal/READ_ONLY")
102 struct keywordSearchData
109 SearchBookmarkForKeyword(nsTrimInt64HashKey::KeyType aKey
,
110 const nsString aValue
,
113 keywordSearchData
* data
= reinterpret_cast<keywordSearchData
*>(aUserArg
);
114 if (data
->keyword
.Equals(aValue
)) {
116 return PL_DHASH_STOP
;
118 return PL_DHASH_NEXT
;
121 template<typename Method
, typename DataType
>
122 class AsyncGetBookmarksForURI
: public AsyncStatementCallback
125 AsyncGetBookmarksForURI(nsNavBookmarks
* aBookmarksSvc
,
127 const DataType
& aData
)
128 : mBookmarksSvc(aBookmarksSvc
)
129 , mCallback(aCallback
)
136 nsCOMPtr
<mozIStorageStatement
> stmt
=
137 mBookmarksSvc
->GetStatementById(DB_GET_BOOKMARKS_FOR_URI
);
139 (void)URIBinder::Bind(stmt
, NS_LITERAL_CSTRING("page_url"), mData
.uri
);
140 nsCOMPtr
<mozIStoragePendingStatement
> pendingStmt
;
141 (void)stmt
->ExecuteAsync(this, getter_AddRefs(pendingStmt
));
145 NS_IMETHOD
HandleResult(mozIStorageResultSet
* aResultSet
)
147 nsCOMPtr
<mozIStorageRow
> row
;
148 while (NS_SUCCEEDED(aResultSet
->GetNextRow(getter_AddRefs(row
))) && row
) {
149 nsresult rv
= row
->GetInt64(0, &mData
.itemId
);
150 NS_ENSURE_SUCCESS(rv
, rv
);
152 ((*mBookmarksSvc
).*mCallback
)(mData
);
159 nsRefPtr
<nsNavBookmarks
> mBookmarksSvc
;
164 } // Anonymous namespace.
167 nsNavBookmarks::nsNavBookmarks() : mItemCount(0)
174 , mCacheObservers("bookmark-observers")
175 , mShuttingDown(false)
178 NS_ASSERTION(!gBookmarksService
,
179 "Attempting to create two instances of the service!");
180 gBookmarksService
= this;
184 nsNavBookmarks::~nsNavBookmarks()
186 NS_ASSERTION(gBookmarksService
== this,
187 "Deleting a non-singleton instance of the service");
188 if (gBookmarksService
== this)
189 gBookmarksService
= nsnull
;
193 NS_IMPL_ISUPPORTS3(nsNavBookmarks
,
194 nsINavBookmarksService
,
195 nsINavHistoryObserver
,
196 nsIAnnotationObserver
)
200 nsNavBookmarks::Init()
204 nsNavHistory
* history
= nsNavHistory::GetHistoryService();
205 NS_ENSURE_TRUE(history
, NS_ERROR_OUT_OF_MEMORY
);
206 mDBConn
= history
->GetStorageConnection();
207 NS_ENSURE_STATE(mDBConn
);
209 // Get our read-only cloned connection.
210 nsresult rv
= mDBConn
->Clone(PR_TRUE
, getter_AddRefs(mDBReadOnlyConn
));
211 NS_ENSURE_SUCCESS(rv
, rv
);
214 rv
= history
->GetDatabaseStatus(&dbStatus
);
215 NS_ENSURE_SUCCESS(rv
, rv
);
216 rv
= InitRoots(dbStatus
!= nsINavHistoryService::DATABASE_STATUS_OK
);
217 NS_ENSURE_SUCCESS(rv
, rv
);
221 // Observe annotations.
222 nsAnnotationService
* annosvc
= nsAnnotationService::GetAnnotationService();
223 NS_ENSURE_TRUE(annosvc
, NS_ERROR_OUT_OF_MEMORY
);
224 annosvc
->AddObserver(this);
226 // Allows us to notify on title changes. MUST BE LAST so it is impossible
227 // to fail after this call, or the history service will have a reference to
228 // us and we won't go away.
229 history
->AddObserver(this, PR_FALSE
);
231 // DO NOT PUT STUFF HERE that can fail. See observer comment above.
238 * All commands that initialize the database schema should be here.
239 * This is called from history init after database connection has been
243 nsNavBookmarks::InitTables(mozIStorageConnection
* aDBConn
)
246 nsresult rv
= aDBConn
->TableExists(NS_LITERAL_CSTRING("moz_bookmarks"), &exists
);
247 NS_ENSURE_SUCCESS(rv
, rv
);
249 rv
= aDBConn
->ExecuteSimpleSQL(CREATE_MOZ_BOOKMARKS
);
250 NS_ENSURE_SUCCESS(rv
, rv
);
252 // This index will make it faster to determine if a given item is
253 // bookmarked (used by history queries and vacuuming, for example).
254 // Making it compound with "type" speeds up type-differentiation
255 // queries, such as expiration and search.
256 rv
= aDBConn
->ExecuteSimpleSQL(CREATE_IDX_MOZ_BOOKMARKS_PLACETYPE
);
257 NS_ENSURE_SUCCESS(rv
, rv
);
259 // The most common operation is to find the children given a parent and position.
260 rv
= aDBConn
->ExecuteSimpleSQL(CREATE_IDX_MOZ_BOOKMARKS_PARENTPOSITION
);
261 NS_ENSURE_SUCCESS(rv
, rv
);
263 // fast access to lastModified is useful during sync and to get
264 // last modified bookmark title for tags container's children.
265 rv
= aDBConn
->ExecuteSimpleSQL(CREATE_IDX_MOZ_BOOKMARKS_PLACELASTMODIFIED
);
266 NS_ENSURE_SUCCESS(rv
, rv
);
268 // Selecting by guid needs to be fast.
269 rv
= aDBConn
->ExecuteSimpleSQL(CREATE_IDX_MOZ_BOOKMARKS_GUID
);
270 NS_ENSURE_SUCCESS(rv
, rv
);
273 rv
= aDBConn
->TableExists(NS_LITERAL_CSTRING("moz_bookmarks_roots"), &exists
);
274 NS_ENSURE_SUCCESS(rv
, rv
);
276 rv
= aDBConn
->ExecuteSimpleSQL(CREATE_MOZ_BOOKMARKS_ROOTS
);
277 NS_ENSURE_SUCCESS(rv
, rv
);
280 rv
= aDBConn
->TableExists(NS_LITERAL_CSTRING("moz_keywords"), &exists
);
281 NS_ENSURE_SUCCESS(rv
, rv
);
283 rv
= aDBConn
->ExecuteSimpleSQL(CREATE_MOZ_KEYWORDS
);
284 NS_ENSURE_SUCCESS(rv
, rv
);
286 // Create trigger to update as well
287 rv
= aDBConn
->ExecuteSimpleSQL(CREATE_KEYWORD_VALIDITY_TRIGGER
);
288 NS_ENSURE_SUCCESS(rv
, rv
);
295 mozIStorageStatement
*
296 nsNavBookmarks::GetStatement(const nsCOMPtr
<mozIStorageStatement
>& aStmt
)
301 // Double ordering covers possible lastModified ties, that could happen when
302 // importing, syncing or due to extensions.
303 // Note: not using a JOIN is cheaper in this case.
304 RETURN_IF_STMT(mDBFindURIBookmarks
, NS_LITERAL_CSTRING(
306 "FROM moz_bookmarks b "
307 "WHERE b.fk = (SELECT id FROM moz_places WHERE url = :page_url) "
308 "ORDER BY b.lastModified DESC, b.id DESC "));
310 // Select all children of a given folder, sorted by position.
311 // This is a LEFT JOIN because not all bookmarks types have a place.
312 // We construct a result where the first columns exactly match those returned
313 // by mDBGetURLPageInfo, and additionally contains columns for position,
314 // item_child, and folder_child from moz_bookmarks.
315 RETURN_IF_STMT(mDBGetChildren
, NS_LITERAL_CSTRING(
316 "SELECT h.id, h.url, IFNULL(b.title, h.title), h.rev_host, h.visit_count, "
317 "h.last_visit_date, f.url, null, b.id, b.dateAdded, b.lastModified, "
318 "b.parent, null, b.position, b.type, b.fk, b.folder_type "
319 "FROM moz_bookmarks b "
320 "LEFT JOIN moz_places h ON b.fk = h.id "
321 "LEFT JOIN moz_favicons f ON h.favicon_id = f.id "
322 "WHERE b.parent = :parent "
323 "ORDER BY b.position ASC"));
325 // Count all of the children of a given folder and checks that it exists.
326 RETURN_IF_STMT(mDBFolderCount
, NS_LITERAL_CSTRING(
328 "(SELECT id FROM moz_bookmarks WHERE id = :parent) "
329 "FROM moz_bookmarks WHERE parent = :parent"));
331 RETURN_IF_STMT(mDBGetChildAt
, NS_LITERAL_CSTRING(
332 "SELECT id, fk, type FROM moz_bookmarks "
333 "WHERE parent = :parent AND position = :item_index"));
335 // Get bookmark/folder/separator properties.
336 // This is a LEFT JOIN because not all bookmarks types have a place.
337 RETURN_IF_STMT(mDBGetItemProperties
, NS_LITERAL_CSTRING(
338 "SELECT b.id, h.url, b.title, b.position, b.fk, b.parent, b.type, "
339 "b.folder_type, b.dateAdded, b.lastModified "
340 "FROM moz_bookmarks b "
341 "LEFT JOIN moz_places h ON h.id = b.fk "
342 "WHERE b.id = :item_id"));
344 RETURN_IF_STMT(mDBGetItemIdForGUID
, NS_LITERAL_CSTRING(
345 "SELECT item_id FROM moz_items_annos "
346 "WHERE content = :guid "
349 RETURN_IF_STMT(mDBInsertBookmark
, NS_LITERAL_CSTRING(
350 "INSERT INTO moz_bookmarks "
351 "(id, fk, type, parent, position, title, folder_type, "
352 "dateAdded, lastModified, guid) "
353 "VALUES (:item_id, :page_id, :item_type, :parent, :item_index, "
354 ":item_title, :folder_type, :date_added, :last_modified, "
355 "GENERATE_GUID())"));
357 // Just select position since it's just an int32 and may be faster.
358 // We don't actually care about the data, just whether there is any.
359 RETURN_IF_STMT(mDBIsBookmarkedInDatabase
, NS_LITERAL_CSTRING(
360 "SELECT 1 FROM moz_bookmarks WHERE fk = :page_id"));
362 RETURN_IF_STMT(mDBIsURIBookmarkedInDatabase
, NS_LITERAL_CSTRING(
363 "SELECT 1 FROM moz_bookmarks b "
364 "JOIN moz_places h ON b.fk = h.id "
365 "WHERE h.url = :page_url"));
367 // Checks to make sure a place id is a bookmark, and isn't a livemark.
368 RETURN_IF_STMT(mDBIsRealBookmark
, NS_LITERAL_CSTRING(
370 "FROM moz_bookmarks "
371 "WHERE fk = :page_id "
372 "AND type = :item_type "
373 "AND parent NOT IN ("
375 "FROM moz_items_annos a "
376 "JOIN moz_anno_attributes n ON a.anno_attribute_id = n.id "
377 "WHERE n.name = :anno_name"
380 RETURN_IF_STMT(mDBGetLastBookmarkID
, NS_LITERAL_CSTRING(
382 "FROM moz_bookmarks "
383 "ORDER BY ROWID DESC "
386 // lastModified is set to the same value as dateAdded. We do this for
387 // performance reasons, since it will allow us to use an index to sort items
389 RETURN_IF_STMT(mDBSetItemDateAdded
, NS_LITERAL_CSTRING(
390 "UPDATE moz_bookmarks SET dateAdded = :date, lastModified = :date "
391 "WHERE id = :item_id"));
393 RETURN_IF_STMT(mDBSetItemLastModified
, NS_LITERAL_CSTRING(
394 "UPDATE moz_bookmarks SET lastModified = :date WHERE id = :item_id"));
396 RETURN_IF_STMT(mDBSetItemIndex
, NS_LITERAL_CSTRING(
397 "UPDATE moz_bookmarks SET position = :item_index WHERE id = :item_id"));
399 RETURN_IF_STMT(mDBGetKeywordForURI
, NS_LITERAL_CSTRING(
402 "JOIN moz_bookmarks b ON b.fk = h.id "
403 "JOIN moz_keywords k ON k.id = b.keyword_id "
404 "WHERE h.url = :page_url "));
406 RETURN_IF_STMT(mDBAdjustPosition
, NS_LITERAL_CSTRING(
407 "UPDATE moz_bookmarks SET position = position + :delta "
408 "WHERE parent = :parent "
409 "AND position BETWEEN :from_index AND :to_index"));
411 RETURN_IF_STMT(mDBRemoveItem
, NS_LITERAL_CSTRING(
412 "DELETE FROM moz_bookmarks WHERE id = :item_id"));
414 RETURN_IF_STMT(mDBGetLastChildId
, NS_LITERAL_CSTRING(
415 "SELECT id FROM moz_bookmarks WHERE parent = :parent "
416 "ORDER BY position DESC LIMIT 1"));
418 RETURN_IF_STMT(mDBMoveItem
, NS_LITERAL_CSTRING(
419 "UPDATE moz_bookmarks SET parent = :parent, position = :item_index "
420 "WHERE id = :item_id "));
422 RETURN_IF_STMT(mDBSetItemTitle
, NS_LITERAL_CSTRING(
423 "UPDATE moz_bookmarks SET title = :item_title, lastModified = :date "
424 "WHERE id = :item_id "));
426 RETURN_IF_STMT(mDBChangeBookmarkURI
, NS_LITERAL_CSTRING(
427 "UPDATE moz_bookmarks SET fk = :page_id, lastModified = :date "
428 "WHERE id = :item_id "));
430 // The next query finds the bookmarked ancestors in a redirects chain.
431 // It won't go further than 3 levels of redirects (a->b->c->your_place_id).
432 // To make this path 100% correct (up to any level) we would need either:
433 // - A separate hash, build through recursive querying of the database.
434 // This solution was previously implemented, but it had a negative effect
435 // on startup since at each startup we have to recursively query the
436 // database to rebuild a hash that is always the same across sessions.
437 // It must be updated at each visit and bookmarks change too. The code to
438 // manage it is complex and prone to errors, sometimes causing incorrect
439 // data fetches (for example wrong favicon for a redirected bookmark).
440 // - A better way to track redirects for a visit.
441 // We would need a separate table to track redirects, in the table we would
442 // have visit_id, redirect_session. To get all sources for
443 // a visit then we could just join this table and get all visit_id that
444 // are in the same redirect_session as our visit. This has the drawback
445 // that we can't ensure data integrity in the downgrade -> upgrade path,
446 // since an old version would not update the table on new visits.
448 // For most cases these levels of redirects should be fine though, it's hard
449 // to hit a page that is 4 or 5 levels of redirects below a bookmarked page.
451 // As a bonus the query also checks first if place_id is already a bookmark,
452 // so you don't have to check that apart.
454 #define COALESCE_PLACEID \
455 "COALESCE(greatgrandparent.place_id, grandparent.place_id, parent.place_id) "
457 nsCString redirectsFragment
=
458 nsPrintfCString(3, "%d,%d",
459 nsINavHistoryService::TRANSITION_REDIRECT_PERMANENT
,
460 nsINavHistoryService::TRANSITION_REDIRECT_TEMPORARY
);
462 RETURN_IF_STMT(mDBFindRedirectedBookmark
, NS_LITERAL_CSTRING(
464 "(SELECT url FROM moz_places WHERE id = :page_id) "
465 "FROM moz_bookmarks b "
466 "WHERE b.fk = :page_id "
467 "UNION ALL " // Not directly bookmarked.
469 "(SELECT url FROM moz_places WHERE id = " COALESCE_PLACEID
") "
470 "FROM moz_historyvisits self "
471 "JOIN moz_bookmarks b ON b.fk = " COALESCE_PLACEID
472 "LEFT JOIN moz_historyvisits parent ON parent.id = self.from_visit "
473 "LEFT JOIN moz_historyvisits grandparent ON parent.from_visit = grandparent.id "
474 "AND parent.visit_type IN (") + redirectsFragment
+ NS_LITERAL_CSTRING(") "
475 "LEFT JOIN moz_historyvisits greatgrandparent ON grandparent.from_visit = greatgrandparent.id "
476 "AND grandparent.visit_type IN (") + redirectsFragment
+ NS_LITERAL_CSTRING(") "
477 "WHERE self.visit_type IN (") + redirectsFragment
+ NS_LITERAL_CSTRING(") "
478 "AND self.place_id = :page_id "
479 "LIMIT 1 " // Stop at the first result.
481 #undef COALESCE_PLACEID
488 nsNavBookmarks::FinalizeStatements() {
489 mShuttingDown
= true;
491 mozIStorageStatement
* stmts
[] = {
496 mDBGetItemProperties
,
499 mDBIsBookmarkedInDatabase
,
501 mDBGetLastBookmarkID
,
503 mDBSetItemLastModified
,
511 mDBChangeBookmarkURI
,
512 mDBIsURIBookmarkedInDatabase
,
513 mDBFindRedirectedBookmark
,
516 for (PRUint32 i
= 0; i
< NS_ARRAY_LENGTH(stmts
); i
++) {
517 nsresult rv
= nsNavHistory::FinalizeStatement(stmts
[i
]);
518 NS_ENSURE_SUCCESS(rv
, rv
);
521 // Since we are shutting down, close the read-only connection.
522 (void)mDBReadOnlyConn
->AsyncClose(nsnull
);
525 // Sanity check that all bookmarks have guids.
526 nsCOMPtr
<mozIStorageStatement
> stmt
;
527 nsresult rv
= mDBConn
->CreateStatement(NS_LITERAL_CSTRING(
529 "FROM moz_bookmarks "
530 "WHERE guid IS NULL "
531 ), getter_AddRefs(stmt
));
532 NS_ENSURE_SUCCESS(rv
, rv
);
534 PRBool haveNullGuids
;
535 rv
= stmt
->ExecuteStep(&haveNullGuids
);
536 NS_ENSURE_SUCCESS(rv
, rv
);
537 NS_ASSERTION(!haveNullGuids
,
538 "Someone added a bookmark without adding a GUID!");
546 nsNavBookmarks::InitRoots(bool aForceCreate
)
548 nsCOMPtr
<mozIStorageStatement
> stmt
;
549 nsresult rv
= mDBReadOnlyConn
->CreateStatement(NS_LITERAL_CSTRING(
550 "SELECT root_name, folder_id FROM moz_bookmarks_roots"
551 ), getter_AddRefs(stmt
));
552 NS_ENSURE_SUCCESS(rv
, rv
);
555 while (NS_SUCCEEDED(stmt
->ExecuteStep(&hasResult
)) && hasResult
) {
556 nsCAutoString rootName
;
557 rv
= stmt
->GetUTF8String(0, rootName
);
558 NS_ENSURE_SUCCESS(rv
, rv
);
560 rv
= stmt
->GetInt64(1, &rootId
);
561 NS_ENSURE_SUCCESS(rv
, rv
);
562 NS_ABORT_IF_FALSE(rootId
!= 0, "Root id is 0, that is an invalid value.");
564 if (rootName
.EqualsLiteral("places")) {
567 else if (rootName
.EqualsLiteral("menu")) {
570 else if (rootName
.EqualsLiteral("toolbar")) {
571 mToolbarRoot
= rootId
;
573 else if (rootName
.EqualsLiteral("tags")) {
576 else if (rootName
.EqualsLiteral("unfiled")) {
577 mUnfiledRoot
= rootId
;
582 nsNavHistory
* history
= nsNavHistory::GetHistoryService();
583 NS_ENSURE_TRUE(history
, NS_ERROR_OUT_OF_MEMORY
);
584 nsIStringBundle
* bundle
= history
->GetBundle();
585 NS_ENSURE_TRUE(bundle
, NS_ERROR_OUT_OF_MEMORY
);
587 mozStorageTransaction
transaction(mDBConn
, PR_FALSE
);
589 rv
= CreateRoot(NS_LITERAL_CSTRING("places"), &mRoot
, 0,
591 NS_ENSURE_SUCCESS(rv
, rv
);
593 rv
= CreateRoot(NS_LITERAL_CSTRING("menu"), &mMenuRoot
, mRoot
, bundle
,
594 NS_LITERAL_STRING("BookmarksMenuFolderTitle").get());
595 NS_ENSURE_SUCCESS(rv
, rv
);
597 rv
= CreateRoot(NS_LITERAL_CSTRING("toolbar"), &mToolbarRoot
, mRoot
, bundle
,
598 NS_LITERAL_STRING("BookmarksToolbarFolderTitle").get());
599 NS_ENSURE_SUCCESS(rv
, rv
);
601 rv
= CreateRoot(NS_LITERAL_CSTRING("tags"), &mTagsRoot
, mRoot
, bundle
,
602 NS_LITERAL_STRING("TagsFolderTitle").get());
603 NS_ENSURE_SUCCESS(rv
, rv
);
605 rv
= CreateRoot(NS_LITERAL_CSTRING("unfiled"), &mUnfiledRoot
, mRoot
, bundle
,
606 NS_LITERAL_STRING("UnsortedBookmarksFolderTitle").get());
607 NS_ENSURE_SUCCESS(rv
, rv
);
609 rv
= transaction
.Commit();
610 NS_ENSURE_SUCCESS(rv
, rv
);
613 ForceWALCheckpoint(mDBConn
);
622 nsNavBookmarks::CreateRoot(const nsCString
& name
,
625 nsIStringBundle
* aBundle
,
626 const PRUnichar
* aTitleStringId
)
631 // The root does not exist. Create a new untitled folder for it.
632 rv
= CreateFolder(aParentId
, EmptyCString(), DEFAULT_INDEX
, _itemId
);
633 NS_ENSURE_SUCCESS(rv
, rv
);
635 // Create a entry in moz_bookmarks_roots to link the folder to the root.
636 nsCOMPtr
<mozIStorageStatement
> stmt
;
637 rv
= mDBConn
->CreateStatement(NS_LITERAL_CSTRING(
638 "INSERT INTO moz_bookmarks_roots (root_name, folder_id) "
639 "VALUES (:root_name, :item_id)"
640 ), getter_AddRefs(stmt
));
641 NS_ENSURE_SUCCESS(rv
, rv
);
642 rv
= stmt
->BindUTF8StringByName(NS_LITERAL_CSTRING("root_name"), name
);
643 NS_ENSURE_SUCCESS(rv
, rv
);
644 rv
= stmt
->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), *_itemId
);
645 NS_ENSURE_SUCCESS(rv
, rv
);
646 rv
= stmt
->Execute();
647 NS_ENSURE_SUCCESS(rv
, rv
);
650 // Now set the title on the root. Notice we do this regardless, to take in
651 // could title changes when schema changes.
652 if (aTitleStringId
) {
654 rv
= aBundle
->GetStringFromName(aTitleStringId
, getter_Copies(title
));
655 NS_ENSURE_SUCCESS(rv
, rv
);
656 rv
= SetItemTitle(*_itemId
, NS_ConvertUTF16toUTF8(title
));
657 NS_ENSURE_SUCCESS(rv
, rv
);
665 nsNavBookmarks::IsRealBookmark(PRInt64 aPlaceId
)
667 DECLARE_AND_ASSIGN_SCOPED_LAZY_STMT(stmt
, mDBIsRealBookmark
);
668 nsresult rv
= stmt
->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), aPlaceId
);
669 NS_WARN_IF_FALSE(NS_SUCCEEDED(rv
), "Binding failed");
670 rv
= stmt
->BindInt32ByName(NS_LITERAL_CSTRING("item_type"), TYPE_BOOKMARK
);
671 NS_WARN_IF_FALSE(NS_SUCCEEDED(rv
), "Binding failed");
672 rv
= stmt
->BindUTF8StringByName(NS_LITERAL_CSTRING("anno_name"),
673 NS_LITERAL_CSTRING(LMANNO_FEEDURI
));
674 NS_WARN_IF_FALSE(NS_SUCCEEDED(rv
), "Binding failed");
676 // If we get any rows, then there exists at least one bookmark corresponding
677 // to aPlaceId that is not a livemark item.
679 rv
= stmt
->ExecuteStep(&isBookmark
);
680 NS_WARN_IF_FALSE(NS_SUCCEEDED(rv
), "ExecuteStep failed");
681 if (NS_SUCCEEDED(rv
))
688 // nsNavBookmarks::IsBookmarkedInDatabase
690 // This checks to see if the specified place_id is actually bookmarked.
693 nsNavBookmarks::IsBookmarkedInDatabase(PRInt64 aPlaceId
,
694 PRBool
* aIsBookmarked
)
696 DECLARE_AND_ASSIGN_SCOPED_LAZY_STMT(stmt
, mDBIsBookmarkedInDatabase
);
697 nsresult rv
= stmt
->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), aPlaceId
);
698 NS_ENSURE_SUCCESS(rv
, rv
);
699 rv
= stmt
->ExecuteStep(aIsBookmarked
);
700 NS_ENSURE_SUCCESS(rv
, rv
);
706 nsNavBookmarks::AdjustIndices(PRInt64 aFolderId
,
711 NS_ASSERTION(aStartIndex
>= 0 && aEndIndex
<= PR_INT32_MAX
&&
712 aStartIndex
<= aEndIndex
, "Bad indices");
714 DECLARE_AND_ASSIGN_SCOPED_LAZY_STMT(stmt
, mDBAdjustPosition
);
715 nsresult rv
= stmt
->BindInt32ByName(NS_LITERAL_CSTRING("delta"), aDelta
);
716 NS_ENSURE_SUCCESS(rv
, rv
);
717 rv
= stmt
->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aFolderId
);
718 NS_ENSURE_SUCCESS(rv
, rv
);
719 rv
= stmt
->BindInt32ByName(NS_LITERAL_CSTRING("from_index"), aStartIndex
);
720 NS_ENSURE_SUCCESS(rv
, rv
);
721 rv
= stmt
->BindInt32ByName(NS_LITERAL_CSTRING("to_index"), aEndIndex
);
722 NS_ENSURE_SUCCESS(rv
, rv
);
724 rv
= stmt
->Execute();
725 NS_ENSURE_SUCCESS(rv
, rv
);
732 nsNavBookmarks::GetPlacesRoot(PRInt64
* aRoot
)
740 nsNavBookmarks::GetBookmarksMenuFolder(PRInt64
* aRoot
)
748 nsNavBookmarks::GetToolbarFolder(PRInt64
* aFolderId
)
750 *aFolderId
= mToolbarRoot
;
756 nsNavBookmarks::GetTagsFolder(PRInt64
* aRoot
)
764 nsNavBookmarks::GetUnfiledBookmarksFolder(PRInt64
* aRoot
)
766 *aRoot
= mUnfiledRoot
;
772 nsNavBookmarks::InsertBookmarkInDB(PRInt64 aItemId
,
774 enum ItemType aItemType
,
777 const nsACString
& aTitle
,
779 PRTime aLastModified
,
780 const nsAString
& aServiceContractId
,
783 NS_ASSERTION(_newItemId
, "Null pointer passed to InsertBookmarkInDB!");
785 DECLARE_AND_ASSIGN_SCOPED_LAZY_STMT(stmt
, mDBInsertBookmark
);
787 if (aItemId
&& aItemId
!= -1)
788 rv
= stmt
->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), aItemId
);
790 rv
= stmt
->BindNullByName(NS_LITERAL_CSTRING("item_id"));
791 NS_ENSURE_SUCCESS(rv
, rv
);
793 if (aPlaceId
&& aPlaceId
!= -1)
794 rv
= stmt
->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), aPlaceId
);
796 rv
= stmt
->BindNullByName(NS_LITERAL_CSTRING("page_id"));
797 NS_ENSURE_SUCCESS(rv
, rv
);
799 rv
= stmt
->BindInt32ByName(NS_LITERAL_CSTRING("item_type"), aItemType
);
800 NS_ENSURE_SUCCESS(rv
, rv
);
801 rv
= stmt
->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aParentId
);
802 NS_ENSURE_SUCCESS(rv
, rv
);
803 rv
= stmt
->BindInt32ByName(NS_LITERAL_CSTRING("item_index"), aIndex
);
804 NS_ENSURE_SUCCESS(rv
, rv
);
806 // Support NULL titles.
808 rv
= stmt
->BindNullByName(NS_LITERAL_CSTRING("item_title"));
810 rv
= stmt
->BindUTF8StringByName(NS_LITERAL_CSTRING("item_title"), aTitle
);
811 NS_ENSURE_SUCCESS(rv
, rv
);
813 if (aServiceContractId
.IsEmpty()) {
814 rv
= stmt
->BindNullByName(NS_LITERAL_CSTRING("folder_type"));
817 rv
= stmt
->BindStringByName(NS_LITERAL_CSTRING("folder_type"),
820 NS_ENSURE_SUCCESS(rv
, rv
);
822 rv
= stmt
->BindInt64ByName(NS_LITERAL_CSTRING("date_added"), aDateAdded
);
823 NS_ENSURE_SUCCESS(rv
, rv
);
826 rv
= stmt
->BindInt64ByName(NS_LITERAL_CSTRING("last_modified"),
830 rv
= stmt
->BindInt64ByName(NS_LITERAL_CSTRING("last_modified"), aDateAdded
);
832 NS_ENSURE_SUCCESS(rv
, rv
);
834 rv
= stmt
->Execute();
835 NS_ENSURE_SUCCESS(rv
, rv
);
837 if (!aItemId
|| aItemId
== -1) {
838 // Get the new inserted item id.
839 DECLARE_AND_ASSIGN_SCOPED_LAZY_STMT(lastInsertIdStmt
, mDBGetLastBookmarkID
);
841 rv
= lastInsertIdStmt
->ExecuteStep(&hasResult
);
842 NS_ENSURE_SUCCESS(rv
, rv
);
843 NS_ENSURE_TRUE(hasResult
, NS_ERROR_UNEXPECTED
);
844 rv
= lastInsertIdStmt
->GetInt64(0, _newItemId
);
845 NS_ENSURE_SUCCESS(rv
, rv
);
848 *_newItemId
= aItemId
;
851 // Update last modified date of the parent folder.
852 // XXX TODO: This should be done recursively for all ancestors, that would
853 // be slow without a nested tree though. See bug 408991.
854 rv
= SetItemDateInternal(GetStatement(mDBSetItemLastModified
),
855 aParentId
, aDateAdded
);
856 NS_ENSURE_SUCCESS(rv
, rv
);
863 nsNavBookmarks::InsertBookmark(PRInt64 aFolder
,
866 const nsACString
& aTitle
,
867 PRInt64
* aNewBookmarkId
)
870 NS_ENSURE_ARG_POINTER(aNewBookmarkId
);
872 // You can pass -1 to indicate append, but no other negative number is allowed
873 if (aIndex
< nsINavBookmarksService::DEFAULT_INDEX
)
874 return NS_ERROR_INVALID_ARG
;
876 mozStorageTransaction
transaction(mDBConn
, PR_FALSE
);
878 nsNavHistory
* history
= nsNavHistory::GetHistoryService();
879 NS_ENSURE_TRUE(history
, NS_ERROR_OUT_OF_MEMORY
);
881 // This is really a place ID
883 nsresult rv
= history
->GetUrlIdFor(aURI
, &childID
, PR_TRUE
);
884 NS_ENSURE_SUCCESS(rv
, rv
);
888 rv
= FolderCount(aFolder
, &folderCount
);
889 NS_ENSURE_SUCCESS(rv
, rv
);
890 if (aIndex
== nsINavBookmarksService::DEFAULT_INDEX
||
891 aIndex
>= folderCount
) {
896 rv
= AdjustIndices(aFolder
, index
, PR_INT32_MAX
, 1);
897 NS_ENSURE_SUCCESS(rv
, rv
);
900 rv
= InsertBookmarkInDB(-1, childID
, BOOKMARK
, aFolder
, index
,
901 aTitle
, PR_Now(), nsnull
, EmptyString(),
903 NS_ENSURE_SUCCESS(rv
, rv
);
906 // 0n import / fx 2 migration, is the frecency work going to slow us down?
907 // We might want to skip this stuff, as well as the frecency work
908 // caused by GetUrlIdFor() which calls InternalAddNewPage().
909 // If we do skip this, after import, we will
910 // need to call FixInvalidFrecenciesForExcludedPlaces().
911 // We might need to call it anyways, if items aren't properly annotated
912 // as livemarks feeds yet.
915 rv
= aURI
->GetSpec(url
);
916 NS_ENSURE_SUCCESS(rv
, rv
);
918 // Re-calculate the frecency for this moz_place entry since it was set to -1.
919 rv
= history
->UpdateFrecency(childID
);
920 NS_ENSURE_SUCCESS(rv
, rv
);
922 rv
= transaction
.Commit();
923 NS_ENSURE_SUCCESS(rv
, rv
);
926 ForceWALCheckpoint(mDBConn
);
929 NOTIFY_OBSERVERS(mCanNotify
, mCacheObservers
, mObservers
,
930 nsINavBookmarkObserver
,
931 OnItemAdded(*aNewBookmarkId
, aFolder
, index
, TYPE_BOOKMARK
,
934 // If the bookmark has been added to a tag container, notify all
935 // bookmark-folder result nodes which contain a bookmark for the new
937 PRInt64 grandParentId
;
938 rv
= GetFolderIdForItem(aFolder
, &grandParentId
);
939 NS_ENSURE_SUCCESS(rv
, rv
);
940 if (grandParentId
== mTagsRoot
) {
941 // query for all bookmarks for that URI, notify for each
942 nsTArray
<PRInt64
> bookmarks
;
943 rv
= GetBookmarkIdsForURITArray(aURI
, bookmarks
);
944 NS_ENSURE_SUCCESS(rv
, rv
);
946 if (bookmarks
.Length()) {
947 for (PRUint32 i
= 0; i
< bookmarks
.Length(); i
++) {
948 // Don't notify to the same tag entry we just added.
949 if (bookmarks
[i
] == *aNewBookmarkId
) {
953 NOTIFY_OBSERVERS(mCanNotify
, mCacheObservers
, mObservers
,
954 nsINavBookmarkObserver
,
955 OnItemChanged(bookmarks
[i
], NS_LITERAL_CSTRING("tags"),
956 PR_FALSE
, EmptyCString(), 0,
966 nsNavBookmarks::RemoveItem(PRInt64 aItemId
)
968 NS_ENSURE_TRUE(aItemId
!= mRoot
, NS_ERROR_INVALID_ARG
);
972 PRInt64 placeId
, folderId
;
974 nsCAutoString buffer
;
977 { // scoping to ensure the statement gets reset
978 DECLARE_AND_ASSIGN_SCOPED_LAZY_STMT(getInfoStmt
, mDBGetItemProperties
);
979 rv
= getInfoStmt
->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), aItemId
);
980 NS_ENSURE_SUCCESS(rv
, rv
);
983 rv
= getInfoStmt
->ExecuteStep(&hasResult
);
984 NS_ENSURE_SUCCESS(rv
, rv
);
986 return NS_ERROR_INVALID_ARG
; // invalid bookmark id
988 rv
= getInfoStmt
->GetInt32(kGetItemPropertiesIndex_Position
, &childIndex
);
989 NS_ENSURE_SUCCESS(rv
, rv
);
990 rv
= getInfoStmt
->GetInt64(kGetItemPropertiesIndex_PlaceID
, &placeId
);
991 NS_ENSURE_SUCCESS(rv
, rv
);
992 rv
= getInfoStmt
->GetInt64(kGetItemPropertiesIndex_Parent
, &folderId
);
993 NS_ENSURE_SUCCESS(rv
, rv
);
994 rv
= getInfoStmt
->GetInt32(kGetItemPropertiesIndex_Type
, &itemType
);
995 NS_ENSURE_SUCCESS(rv
, rv
);
996 if (itemType
== TYPE_BOOKMARK
) {
997 rv
= getInfoStmt
->GetUTF8String(kGetItemPropertiesIndex_URI
, spec
);
998 NS_ENSURE_SUCCESS(rv
, rv
);
1002 if (itemType
== TYPE_FOLDER
) {
1003 rv
= RemoveFolder(aItemId
);
1004 NS_ENSURE_SUCCESS(rv
, rv
);
1008 NOTIFY_OBSERVERS(mCanNotify
, mCacheObservers
, mObservers
,
1009 nsINavBookmarkObserver
,
1010 OnBeforeItemRemoved(aItemId
, itemType
));
1012 mozStorageTransaction
transaction(mDBConn
, PR_FALSE
);
1014 // First, remove item annotations
1015 nsAnnotationService
* annosvc
= nsAnnotationService::GetAnnotationService();
1016 NS_ENSURE_TRUE(annosvc
, NS_ERROR_OUT_OF_MEMORY
);
1017 rv
= annosvc
->RemoveItemAnnotations(aItemId
);
1018 NS_ENSURE_SUCCESS(rv
, rv
);
1021 DECLARE_AND_ASSIGN_SCOPED_LAZY_STMT(stmt
, mDBRemoveItem
);
1022 rv
= stmt
->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), aItemId
);
1023 NS_ENSURE_SUCCESS(rv
, rv
);
1024 rv
= stmt
->Execute();
1025 NS_ENSURE_SUCCESS(rv
, rv
);
1028 if (childIndex
!= -1) {
1029 rv
= AdjustIndices(folderId
, childIndex
+ 1, PR_INT32_MAX
, -1);
1030 NS_ENSURE_SUCCESS(rv
, rv
);
1033 rv
= SetItemDateInternal(GetStatement(mDBSetItemLastModified
),
1034 folderId
, PR_Now());
1035 NS_ENSURE_SUCCESS(rv
, rv
);
1037 rv
= transaction
.Commit();
1038 NS_ENSURE_SUCCESS(rv
, rv
);
1041 ForceWALCheckpoint(mDBConn
);
1044 if (itemType
== TYPE_BOOKMARK
) {
1045 nsNavHistory
* history
= nsNavHistory::GetHistoryService();
1046 NS_ENSURE_TRUE(history
, NS_ERROR_OUT_OF_MEMORY
);
1047 rv
= history
->UpdateFrecency(placeId
);
1048 NS_ENSURE_SUCCESS(rv
, rv
);
1050 rv
= UpdateKeywordsHashForRemovedBookmark(aItemId
);
1051 NS_ENSURE_SUCCESS(rv
, rv
);
1054 bool isTagEntry
= false;
1055 if (itemType
== TYPE_BOOKMARK
) {
1056 // Check if the removed bookmark was child of a tag container.
1057 // This is done before notifying since during the notification the parent
1058 // could be removed as well.
1059 PRInt64 grandParentId
;
1060 rv
= GetFolderIdForItem(folderId
, &grandParentId
);
1061 NS_ENSURE_SUCCESS(rv
, rv
);
1062 isTagEntry
= grandParentId
== mTagsRoot
;
1065 NOTIFY_OBSERVERS(mCanNotify
, mCacheObservers
, mObservers
,
1066 nsINavBookmarkObserver
,
1067 OnItemRemoved(aItemId
, folderId
, childIndex
, itemType
));
1070 // Get all bookmarks pointing to the same uri as this tag entry and
1071 // notify them that tags changed.
1072 nsCOMPtr
<nsIURI
> uri
;
1073 rv
= NS_NewURI(getter_AddRefs(uri
), spec
);
1074 NS_ENSURE_SUCCESS(rv
, rv
);
1075 nsTArray
<PRInt64
> bookmarks
;
1076 rv
= GetBookmarkIdsForURITArray(uri
, bookmarks
);
1077 NS_ENSURE_SUCCESS(rv
, rv
);
1079 for (PRUint32 i
= 0; i
< bookmarks
.Length(); i
++) {
1080 NOTIFY_OBSERVERS(mCanNotify
, mCacheObservers
, mObservers
,
1081 nsINavBookmarkObserver
,
1082 OnItemChanged(bookmarks
[i
],
1083 NS_LITERAL_CSTRING("tags"), PR_FALSE
,
1084 EmptyCString(), 0, TYPE_BOOKMARK
));
1093 nsNavBookmarks::CreateFolder(PRInt64 aParent
, const nsACString
& aName
,
1094 PRInt32 aIndex
, PRInt64
* aNewFolder
)
1096 // NOTE: aParent can be null for root creation, so not checked
1097 NS_ENSURE_ARG_POINTER(aNewFolder
);
1099 // CreateContainerWithID returns the index of the new folder, but that's not
1100 // used here. To avoid any risk of corrupting data should this function
1101 // be changed, we'll use a local variable to hold it. The PR_TRUE argument
1102 // will cause notifications to be sent to bookmark observers.
1103 PRInt32 localIndex
= aIndex
;
1104 nsresult rv
= CreateContainerWithID(-1, aParent
, aName
, EmptyString(),
1105 PR_TRUE
, &localIndex
, aNewFolder
);
1106 NS_ENSURE_SUCCESS(rv
, rv
);
1112 nsNavBookmarks::CreateDynamicContainer(PRInt64 aParent
,
1113 const nsACString
& aName
,
1114 const nsAString
& aContractId
,
1116 PRInt64
* aNewFolder
)
1118 NS_ENSURE_FALSE(aContractId
.IsEmpty(), NS_ERROR_INVALID_ARG
);
1120 nsresult rv
= CreateContainerWithID(-1, aParent
, aName
, aContractId
,
1121 PR_FALSE
, &aIndex
, aNewFolder
);
1122 NS_ENSURE_SUCCESS(rv
, rv
);
1128 nsNavBookmarks::GetFolderReadonly(PRInt64 aFolder
, PRBool
* aResult
)
1130 NS_ENSURE_ARG_MIN(aFolder
, 1);
1131 NS_ENSURE_ARG_POINTER(aResult
);
1133 nsAnnotationService
* annosvc
= nsAnnotationService::GetAnnotationService();
1134 NS_ENSURE_TRUE(annosvc
, NS_ERROR_OUT_OF_MEMORY
);
1135 nsresult rv
= annosvc
->ItemHasAnnotation(aFolder
, READ_ONLY_ANNO
, aResult
);
1136 NS_ENSURE_SUCCESS(rv
, rv
);
1142 nsNavBookmarks::SetFolderReadonly(PRInt64 aFolder
, PRBool aReadOnly
)
1144 NS_ENSURE_ARG_MIN(aFolder
, 1);
1146 nsAnnotationService
* annosvc
= nsAnnotationService::GetAnnotationService();
1147 NS_ENSURE_TRUE(annosvc
, NS_ERROR_OUT_OF_MEMORY
);
1150 rv
= annosvc
->SetItemAnnotationInt32(aFolder
, READ_ONLY_ANNO
, 1, 0,
1151 nsAnnotationService::EXPIRE_NEVER
);
1152 NS_ENSURE_SUCCESS(rv
, rv
);
1156 rv
= annosvc
->ItemHasAnnotation(aFolder
, READ_ONLY_ANNO
, &hasAnno
);
1157 NS_ENSURE_SUCCESS(rv
, rv
);
1159 rv
= annosvc
->RemoveItemAnnotation(aFolder
, READ_ONLY_ANNO
);
1160 NS_ENSURE_SUCCESS(rv
, rv
);
1168 nsNavBookmarks::CreateContainerWithID(PRInt64 aItemId
,
1170 const nsACString
& aName
,
1171 const nsAString
& aContractId
,
1172 PRBool aIsBookmarkFolder
,
1174 PRInt64
* aNewFolder
)
1176 // You can pass -1 to indicate append, but no other negative number is allowed
1178 return NS_ERROR_INVALID_ARG
;
1180 mozStorageTransaction
transaction(mDBConn
, PR_FALSE
);
1183 PRInt32 folderCount
;
1184 nsresult rv
= FolderCount(aParent
, &folderCount
);
1185 NS_ENSURE_SUCCESS(rv
, rv
);
1186 if (*aIndex
== nsINavBookmarksService::DEFAULT_INDEX
||
1187 *aIndex
>= folderCount
) {
1188 index
= folderCount
;
1191 rv
= AdjustIndices(aParent
, index
, PR_INT32_MAX
, 1);
1192 NS_ENSURE_SUCCESS(rv
, rv
);
1195 ItemType containerType
= aIsBookmarkFolder
? FOLDER
1196 : DYNAMIC_CONTAINER
;
1197 rv
= InsertBookmarkInDB(aItemId
, nsnull
, containerType
, aParent
, index
,
1198 aName
, PR_Now(), nsnull
, aContractId
, aNewFolder
);
1199 NS_ENSURE_SUCCESS(rv
, rv
);
1201 rv
= transaction
.Commit();
1202 NS_ENSURE_SUCCESS(rv
, rv
);
1205 ForceWALCheckpoint(mDBConn
);
1208 NOTIFY_OBSERVERS(mCanNotify
, mCacheObservers
, mObservers
,
1209 nsINavBookmarkObserver
,
1210 OnItemAdded(*aNewFolder
, aParent
, index
, containerType
,
1219 nsNavBookmarks::InsertSeparator(PRInt64 aParent
,
1221 PRInt64
* aNewItemId
)
1223 NS_ENSURE_ARG_MIN(aParent
, 1);
1224 // -1 means "append", but no other negative value is allowed.
1225 NS_ENSURE_ARG_MIN(aIndex
, -1);
1226 NS_ENSURE_ARG_POINTER(aNewItemId
);
1228 mozStorageTransaction
transaction(mDBConn
, PR_FALSE
);
1231 PRInt32 folderCount
;
1232 nsresult rv
= FolderCount(aParent
, &folderCount
);
1233 NS_ENSURE_SUCCESS(rv
, rv
);
1234 if (aIndex
== nsINavBookmarksService::DEFAULT_INDEX
||
1235 aIndex
>= folderCount
) {
1236 index
= folderCount
;
1240 rv
= AdjustIndices(aParent
, index
, PR_INT32_MAX
, 1);
1241 NS_ENSURE_SUCCESS(rv
, rv
);
1244 // Set a NULL title, not an empty title.
1245 nsCString voidString
;
1246 voidString
.SetIsVoid(PR_TRUE
);
1247 rv
= InsertBookmarkInDB(-1, nsnull
, SEPARATOR
, aParent
, index
,
1248 voidString
, PR_Now(), nsnull
, EmptyString(),
1250 NS_ENSURE_SUCCESS(rv
, rv
);
1252 rv
= transaction
.Commit();
1253 NS_ENSURE_SUCCESS(rv
, rv
);
1255 NOTIFY_OBSERVERS(mCanNotify
, mCacheObservers
, mObservers
,
1256 nsINavBookmarkObserver
,
1257 OnItemAdded(*aNewItemId
, aParent
, index
, TYPE_SEPARATOR
,
1265 nsNavBookmarks::GetLastChildId(PRInt64 aFolderId
, PRInt64
* aItemId
)
1267 NS_ASSERTION(aFolderId
> 0, "Invalid folder id");
1270 DECLARE_AND_ASSIGN_SCOPED_LAZY_STMT(stmt
, mDBGetLastChildId
);
1271 nsresult rv
= stmt
->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aFolderId
);
1272 NS_ENSURE_SUCCESS(rv
, rv
);
1274 rv
= stmt
->ExecuteStep(&found
);
1275 NS_ENSURE_SUCCESS(rv
, rv
);
1277 rv
= stmt
->GetInt64(0, aItemId
);
1278 NS_ENSURE_SUCCESS(rv
, rv
);
1286 nsNavBookmarks::GetIdForItemAt(PRInt64 aFolder
,
1290 NS_ENSURE_ARG_MIN(aFolder
, 1);
1291 NS_ENSURE_ARG_POINTER(aItemId
);
1296 if (aIndex
== nsINavBookmarksService::DEFAULT_INDEX
) {
1297 // Get last item within aFolder.
1298 rv
= GetLastChildId(aFolder
, aItemId
);
1299 NS_ENSURE_SUCCESS(rv
, rv
);
1302 // Get the item in aFolder with position aIndex.
1303 DECLARE_AND_ASSIGN_SCOPED_LAZY_STMT(stmt
, mDBGetChildAt
);
1304 rv
= stmt
->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aFolder
);
1305 NS_ENSURE_SUCCESS(rv
, rv
);
1306 rv
= stmt
->BindInt32ByName(NS_LITERAL_CSTRING("item_index"), aIndex
);
1307 NS_ENSURE_SUCCESS(rv
, rv
);
1310 rv
= stmt
->ExecuteStep(&found
);
1311 NS_ENSURE_SUCCESS(rv
, rv
);
1313 rv
= stmt
->GetInt64(0, aItemId
);
1314 NS_ENSURE_SUCCESS(rv
, rv
);
1322 nsNavBookmarks::GetParentAndIndexOfFolder(PRInt64 aFolderId
,
1326 DECLARE_AND_ASSIGN_SCOPED_LAZY_STMT(stmt
, mDBGetItemProperties
);
1327 nsresult rv
= stmt
->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), aFolderId
);
1328 NS_ENSURE_SUCCESS(rv
, rv
);
1331 rv
= stmt
->ExecuteStep(&hasResult
);
1332 NS_ENSURE_SUCCESS(rv
, rv
);
1333 NS_ENSURE_TRUE(hasResult
, NS_ERROR_INVALID_ARG
);
1335 rv
= stmt
->GetInt64(kGetItemPropertiesIndex_Parent
, _aParent
);
1336 NS_ENSURE_SUCCESS(rv
, rv
);
1337 rv
= stmt
->GetInt32(kGetItemPropertiesIndex_Position
, _aIndex
);
1338 NS_ENSURE_SUCCESS(rv
, rv
);
1345 nsNavBookmarks::RemoveFolder(PRInt64 aFolderId
)
1347 NS_ENSURE_TRUE(aFolderId
!= mRoot
, NS_ERROR_INVALID_ARG
);
1349 NOTIFY_OBSERVERS(mCanNotify
, mCacheObservers
, mObservers
,
1350 nsINavBookmarkObserver
,
1351 OnBeforeItemRemoved(aFolderId
, TYPE_FOLDER
));
1353 mozStorageTransaction
transaction(mDBConn
, PR_FALSE
);
1357 PRInt32 index
, type
;
1358 nsCAutoString folderType
;
1360 DECLARE_AND_ASSIGN_SCOPED_LAZY_STMT(getInfoStmt
, mDBGetItemProperties
);
1361 rv
= getInfoStmt
->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), aFolderId
);
1362 NS_ENSURE_SUCCESS(rv
, rv
);
1365 rv
= getInfoStmt
->ExecuteStep(&hasResult
);
1366 NS_ENSURE_SUCCESS(rv
, rv
);
1368 return NS_ERROR_INVALID_ARG
; // folder is not in the hierarchy
1371 rv
= getInfoStmt
->GetInt32(kGetItemPropertiesIndex_Type
, &type
);
1372 NS_ENSURE_SUCCESS(rv
, rv
);
1373 rv
= getInfoStmt
->GetInt64(kGetItemPropertiesIndex_Parent
, &parent
);
1374 NS_ENSURE_SUCCESS(rv
, rv
);
1375 rv
= getInfoStmt
->GetInt32(kGetItemPropertiesIndex_Position
, &index
);
1376 NS_ENSURE_SUCCESS(rv
, rv
);
1377 rv
= getInfoStmt
->GetUTF8String(kGetItemPropertiesIndex_ServiceContractId
,
1379 NS_ENSURE_SUCCESS(rv
, rv
);
1382 // Ensure this is really a folder.
1383 NS_ENSURE_TRUE(type
== TYPE_FOLDER
, NS_ERROR_INVALID_ARG
);
1385 // First, remove item annotations
1386 nsAnnotationService
* annosvc
= nsAnnotationService::GetAnnotationService();
1387 NS_ENSURE_TRUE(annosvc
, NS_ERROR_OUT_OF_MEMORY
);
1388 rv
= annosvc
->RemoveItemAnnotations(aFolderId
);
1389 NS_ENSURE_SUCCESS(rv
, rv
);
1391 // If this is a container bookmark, try to notify its service.
1392 if (folderType
.Length() > 0) {
1393 // There is a type associated with this folder.
1394 nsCOMPtr
<nsIDynamicContainer
> bmcServ
= do_GetService(folderType
.get());
1396 rv
= bmcServ
->OnContainerRemoving(aFolderId
);
1397 NS_WARN_IF_FALSE(NS_SUCCEEDED(rv
),
1398 "Remove folder container notification failed.");
1402 // Remove all of the folder's children
1403 rv
= RemoveFolderChildren(aFolderId
);
1404 NS_ENSURE_SUCCESS(rv
, rv
);
1407 // Remove the folder from its parent.
1408 DECLARE_AND_ASSIGN_SCOPED_LAZY_STMT(stmt
, mDBRemoveItem
);
1409 rv
= stmt
->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), aFolderId
);
1410 NS_ENSURE_SUCCESS(rv
, rv
);
1411 rv
= stmt
->Execute();
1412 NS_ENSURE_SUCCESS(rv
, rv
);
1415 rv
= AdjustIndices(parent
, index
+ 1, PR_INT32_MAX
, -1);
1416 NS_ENSURE_SUCCESS(rv
, rv
);
1418 rv
= SetItemDateInternal(GetStatement(mDBSetItemLastModified
),
1420 NS_ENSURE_SUCCESS(rv
, rv
);
1422 rv
= transaction
.Commit();
1423 NS_ENSURE_SUCCESS(rv
, rv
);
1426 ForceWALCheckpoint(mDBConn
);
1429 if (aFolderId
== mToolbarRoot
) {
1433 NOTIFY_OBSERVERS(mCanNotify
, mCacheObservers
, mObservers
,
1434 nsINavBookmarkObserver
,
1435 OnItemRemoved(aFolderId
, parent
, index
, TYPE_FOLDER
));
1441 NS_IMPL_ISUPPORTS1(nsNavBookmarks::RemoveFolderTransaction
, nsITransaction
)
1444 nsNavBookmarks::GetRemoveFolderTransaction(PRInt64 aFolderId
, nsITransaction
** aResult
)
1446 NS_ENSURE_ARG_MIN(aFolderId
, 1);
1447 NS_ENSURE_ARG_POINTER(aResult
);
1449 // Create and initialize a RemoveFolderTransaction object that can be used to
1450 // recreate the folder safely later.
1452 RemoveFolderTransaction
* rft
=
1453 new RemoveFolderTransaction(aFolderId
);
1455 return NS_ERROR_OUT_OF_MEMORY
;
1457 NS_ADDREF(*aResult
= rft
);
1463 nsNavBookmarks::GetDescendantChildren(PRInt64 aFolderId
,
1464 PRInt64 aGrandParentId
,
1465 nsTArray
<folderChildrenInfo
>& aFolderChildrenArray
) {
1466 // New children will be added from this index on.
1467 PRUint32 startIndex
= aFolderChildrenArray
.Length();
1470 // Collect children informations.
1471 DECLARE_AND_ASSIGN_SCOPED_LAZY_STMT(stmt
, mDBGetChildren
);
1472 rv
= stmt
->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aFolderId
);
1473 NS_ENSURE_SUCCESS(rv
, rv
);
1476 while (NS_SUCCEEDED(stmt
->ExecuteStep(&hasMore
)) && hasMore
) {
1477 folderChildrenInfo child
;
1478 rv
= stmt
->GetInt64(nsNavHistory::kGetInfoIndex_ItemId
, &child
.itemId
);
1479 NS_ENSURE_SUCCESS(rv
, rv
);
1480 child
.parentId
= aFolderId
;
1481 child
.grandParentId
= aGrandParentId
;
1483 rv
= stmt
->GetInt32(kGetChildrenIndex_Type
, &itemType
);
1484 child
.itemType
= (PRUint16
)itemType
;
1485 NS_ENSURE_SUCCESS(rv
, rv
);
1486 rv
= stmt
->GetInt64(kGetChildrenIndex_PlaceID
, &child
.placeId
);
1487 NS_ENSURE_SUCCESS(rv
, rv
);
1488 rv
= stmt
->GetInt32(kGetChildrenIndex_Position
, &child
.index
);
1489 NS_ENSURE_SUCCESS(rv
, rv
);
1491 if (child
.itemType
== TYPE_BOOKMARK
) {
1492 nsCAutoString URIString
;
1493 rv
= stmt
->GetUTF8String(nsNavHistory::kGetInfoIndex_URL
, URIString
);
1494 NS_ENSURE_SUCCESS(rv
, rv
);
1495 child
.url
= URIString
;
1497 else if (child
.itemType
== TYPE_FOLDER
) {
1498 nsCAutoString folderType
;
1499 rv
= stmt
->GetUTF8String(kGetChildrenIndex_ServiceContractId
,
1501 NS_ENSURE_SUCCESS(rv
, rv
);
1502 child
.folderType
= folderType
;
1504 // Append item to children's array.
1505 aFolderChildrenArray
.AppendElement(child
);
1509 // Recursively call GetDescendantChildren for added folders.
1510 // We start at startIndex since previous folders are checked
1511 // by previous calls to this method.
1512 PRUint32 childCount
= aFolderChildrenArray
.Length();
1513 for (PRUint32 i
= startIndex
; i
< childCount
; i
++) {
1514 if (aFolderChildrenArray
[i
].itemType
== TYPE_FOLDER
) {
1515 GetDescendantChildren(aFolderChildrenArray
[i
].itemId
,
1517 aFolderChildrenArray
);
1526 nsNavBookmarks::RemoveFolderChildren(PRInt64 aFolderId
)
1528 NS_ENSURE_ARG_MIN(aFolderId
, 1);
1532 PRInt64 grandParentId
;
1534 DECLARE_AND_ASSIGN_SCOPED_LAZY_STMT(getInfoStmt
, mDBGetItemProperties
);
1535 rv
= getInfoStmt
->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), aFolderId
);
1536 NS_ENSURE_SUCCESS(rv
, rv
);
1538 // Sanity check: ensure that item exists.
1539 PRBool folderExists
;
1540 if (NS_FAILED(getInfoStmt
->ExecuteStep(&folderExists
)) || !folderExists
)
1541 return NS_ERROR_INVALID_ARG
;
1543 // Sanity check: ensure that this is a folder.
1544 rv
= getInfoStmt
->GetInt32(kGetItemPropertiesIndex_Type
, &itemType
);
1545 NS_ENSURE_SUCCESS(rv
, rv
);
1546 if (itemType
!= TYPE_FOLDER
)
1547 return NS_ERROR_INVALID_ARG
;
1549 // Get the grandParent.
1550 // We have to do this only once since recursion will give us other
1551 // grandParents without the need of additional queries.
1552 rv
= getInfoStmt
->GetInt64(kGetItemPropertiesIndex_Parent
, &grandParentId
);
1553 NS_ENSURE_SUCCESS(rv
, rv
);
1556 // Fill folder children array recursively.
1557 nsTArray
<folderChildrenInfo
> folderChildrenArray
;
1558 rv
= GetDescendantChildren(aFolderId
, grandParentId
, folderChildrenArray
);
1559 NS_ENSURE_SUCCESS(rv
, rv
);
1561 // Build a string of folders whose children will be removed.
1562 nsCString foldersToRemove
;
1563 for (PRUint32 i
= 0; i
< folderChildrenArray
.Length(); i
++) {
1564 folderChildrenInfo child
= folderChildrenArray
[i
];
1566 // Notify observers that we are about to remove this child.
1567 NOTIFY_OBSERVERS(mCanNotify
, mCacheObservers
, mObservers
,
1568 nsINavBookmarkObserver
,
1569 OnBeforeItemRemoved(child
.itemId
, child
.itemType
));
1571 if (child
.itemType
== TYPE_FOLDER
) {
1572 foldersToRemove
.AppendLiteral(",");
1573 foldersToRemove
.AppendInt(child
.itemId
);
1575 // If this is a dynamic container, try to notify its service that we
1576 // are going to remove it.
1577 // XXX (bug 484094) this should use a bookmark observer!
1578 if (child
.folderType
.Length() > 0) {
1579 nsCOMPtr
<nsIDynamicContainer
> bmcServ
=
1580 do_GetService(child
.folderType
.get());
1582 rv
= bmcServ
->OnContainerRemoving(child
.itemId
);
1584 NS_WARNING("Remove folder container notification failed.");
1590 // Delete items from the database now.
1591 mozStorageTransaction
transaction(mDBConn
, PR_FALSE
);
1593 nsCOMPtr
<mozIStorageStatement
> deleteStatement
;
1594 rv
= mDBConn
->CreateStatement(NS_LITERAL_CSTRING(
1595 "DELETE FROM moz_bookmarks "
1596 "WHERE parent IN (:parent") +
1598 NS_LITERAL_CSTRING(")"),
1599 getter_AddRefs(deleteStatement
));
1600 NS_ENSURE_SUCCESS(rv
, rv
);
1601 rv
= deleteStatement
->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aFolderId
);
1602 NS_ENSURE_SUCCESS(rv
, rv
);
1603 rv
= deleteStatement
->Execute();
1604 NS_ENSURE_SUCCESS(rv
, rv
);
1606 // Clean up orphan items annotations.
1607 rv
= mDBConn
->ExecuteSimpleSQL(
1609 "DELETE FROM moz_items_annos "
1611 "SELECT a.id from moz_items_annos a "
1612 "LEFT JOIN moz_bookmarks b ON a.item_id = b.id "
1613 "WHERE b.id ISNULL)"));
1614 NS_ENSURE_SUCCESS(rv
, rv
);
1616 // Set the lastModified date.
1617 rv
= SetItemDateInternal(GetStatement(mDBSetItemLastModified
),
1618 aFolderId
, PR_Now());
1619 NS_ENSURE_SUCCESS(rv
, rv
);
1621 for (PRUint32 i
= 0; i
< folderChildrenArray
.Length(); i
++) {
1622 folderChildrenInfo child
= folderChildrenArray
[i
];
1623 if (child
.itemType
== TYPE_BOOKMARK
) {
1624 PRInt64 placeId
= child
.placeId
;
1625 nsNavHistory
* history
= nsNavHistory::GetHistoryService();
1626 NS_ENSURE_TRUE(history
, NS_ERROR_OUT_OF_MEMORY
);
1627 rv
= history
->UpdateFrecency(placeId
);
1628 NS_ENSURE_SUCCESS(rv
, rv
);
1630 rv
= UpdateKeywordsHashForRemovedBookmark(child
.itemId
);
1631 NS_ENSURE_SUCCESS(rv
, rv
);
1635 rv
= transaction
.Commit();
1636 NS_ENSURE_SUCCESS(rv
, rv
);
1639 ForceWALCheckpoint(mDBConn
);
1642 // Call observers in reverse order to serve children before their parent.
1643 for (PRInt32 i
= folderChildrenArray
.Length() - 1; i
>= 0 ; i
--) {
1644 folderChildrenInfo child
= folderChildrenArray
[i
];
1646 NOTIFY_OBSERVERS(mCanNotify
, mCacheObservers
, mObservers
,
1647 nsINavBookmarkObserver
,
1648 OnItemRemoved(child
.itemId
, child
.parentId
, child
.index
,
1651 if (child
.itemType
== TYPE_BOOKMARK
) {
1652 // If the removed bookmark was a child of a tag container, notify all
1653 // bookmark-folder result nodes which contain a bookmark for the removed
1656 if (child
.grandParentId
== mTagsRoot
) {
1657 nsCOMPtr
<nsIURI
> uri
;
1658 rv
= NS_NewURI(getter_AddRefs(uri
), child
.url
);
1659 NS_ENSURE_SUCCESS(rv
, rv
);
1661 nsTArray
<PRInt64
> bookmarks
;
1662 rv
= GetBookmarkIdsForURITArray(uri
, bookmarks
);
1663 NS_ENSURE_SUCCESS(rv
, rv
);
1665 if (bookmarks
.Length()) {
1666 for (PRUint32 i
= 0; i
< bookmarks
.Length(); i
++) {
1667 NOTIFY_OBSERVERS(mCanNotify
, mCacheObservers
, mObservers
,
1668 nsINavBookmarkObserver
,
1669 OnItemChanged(bookmarks
[i
],
1670 NS_LITERAL_CSTRING("tags"), PR_FALSE
,
1671 EmptyCString(), 0, TYPE_BOOKMARK
));
1683 nsNavBookmarks::MoveItem(PRInt64 aItemId
, PRInt64 aNewParent
, PRInt32 aIndex
)
1685 NS_ENSURE_TRUE(aItemId
!= mRoot
, NS_ERROR_INVALID_ARG
);
1686 NS_ENSURE_ARG_MIN(aItemId
, 1);
1687 NS_ENSURE_ARG_MIN(aNewParent
, 1);
1688 // -1 is append, but no other negative number is allowed.
1689 NS_ENSURE_ARG_MIN(aIndex
, -1);
1690 // Disallow making an item its own parent.
1691 NS_ENSURE_TRUE(aItemId
!= aNewParent
, NS_ERROR_INVALID_ARG
);
1693 mozStorageTransaction
transaction(mDBConn
, PR_FALSE
);
1695 // get item properties
1700 nsCAutoString folderType
;
1702 DECLARE_AND_ASSIGN_SCOPED_LAZY_STMT(getInfoStmt
, mDBGetItemProperties
);
1703 rv
= getInfoStmt
->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), aItemId
);
1704 NS_ENSURE_SUCCESS(rv
, rv
);
1707 rv
= getInfoStmt
->ExecuteStep(&hasResult
);
1708 NS_ENSURE_SUCCESS(rv
, rv
);
1710 return NS_ERROR_INVALID_ARG
; // folder is not in the hierarchy
1713 rv
= getInfoStmt
->GetInt64(kGetItemPropertiesIndex_Parent
, &oldParent
);
1714 NS_ENSURE_SUCCESS(rv
, rv
);
1715 rv
= getInfoStmt
->GetInt32(kGetItemPropertiesIndex_Position
, &oldIndex
);
1716 NS_ENSURE_SUCCESS(rv
, rv
);
1717 rv
= getInfoStmt
->GetInt32(kGetItemPropertiesIndex_Type
, &itemType
);
1718 NS_ENSURE_SUCCESS(rv
, rv
);
1719 if (itemType
== TYPE_FOLDER
) {
1720 rv
= getInfoStmt
->GetUTF8String(kGetItemPropertiesIndex_ServiceContractId
,
1722 NS_ENSURE_SUCCESS(rv
, rv
);
1726 // if parent and index are the same, nothing to do
1727 if (oldParent
== aNewParent
&& oldIndex
== aIndex
)
1730 // Make sure aNewParent is not aFolder or a subfolder of aFolder
1731 if (itemType
== TYPE_FOLDER
) {
1732 PRInt64 ancestorId
= aNewParent
;
1734 while (ancestorId
) {
1735 if (ancestorId
== aItemId
) {
1736 return NS_ERROR_INVALID_ARG
;
1739 DECLARE_AND_ASSIGN_SCOPED_LAZY_STMT(getInfoStmt
, mDBGetItemProperties
);
1740 rv
= getInfoStmt
->BindInt64ByName(NS_LITERAL_CSTRING("item_id"),
1742 NS_ENSURE_SUCCESS(rv
, rv
);
1745 rv
= getInfoStmt
->ExecuteStep(&hasResult
);
1746 NS_ENSURE_SUCCESS(rv
, rv
);
1748 rv
= getInfoStmt
->GetInt64(kGetItemPropertiesIndex_Parent
, &ancestorId
);
1749 NS_ENSURE_SUCCESS(rv
, rv
);
1757 // calculate new index
1759 PRInt32 folderCount
;
1760 rv
= FolderCount(aNewParent
, &folderCount
);
1761 NS_ENSURE_SUCCESS(rv
, rv
);
1762 if (aIndex
== nsINavBookmarksService::DEFAULT_INDEX
||
1763 aIndex
>= folderCount
) {
1764 newIndex
= folderCount
;
1765 // If the parent remains the same, then the folder is really being moved
1766 // to count - 1 (since it's being removed from the old position)
1767 if (oldParent
== aNewParent
) {
1773 if (oldParent
== aNewParent
&& newIndex
> oldIndex
) {
1774 // when an item is being moved lower in the same folder, the new index
1775 // refers to the index before it was removed. Removal causes everything
1781 // this is like the previous check, except this covers if
1782 // the specified index was -1 (append), and the calculated
1783 // new index is the same as the existing index
1784 if (aNewParent
== oldParent
&& newIndex
== oldIndex
) {
1789 // adjust indices to account for the move
1790 // do this before we update the parent/index fields
1791 // or we'll re-adjust the index for the item we are moving
1792 if (oldParent
== aNewParent
) {
1793 // We can optimize the updates if moving within the same container.
1794 // We only shift the items between the old and new positions, since the
1795 // insertion will offset the deletion.
1796 if (oldIndex
> newIndex
) {
1797 rv
= AdjustIndices(oldParent
, newIndex
, oldIndex
- 1, 1);
1800 rv
= AdjustIndices(oldParent
, oldIndex
+ 1, newIndex
, -1);
1802 NS_ENSURE_SUCCESS(rv
, rv
);
1805 // We're moving between containers, so this happens in two steps.
1806 // First, fill the hole from the removal from the old parent.
1807 rv
= AdjustIndices(oldParent
, oldIndex
+ 1, PR_INT32_MAX
, -1);
1808 NS_ENSURE_SUCCESS(rv
, rv
);
1809 // Now, make room in the new parent for the insertion.
1810 rv
= AdjustIndices(aNewParent
, newIndex
, PR_INT32_MAX
, 1);
1811 NS_ENSURE_SUCCESS(rv
, rv
);
1815 // Update parent and position.
1816 DECLARE_AND_ASSIGN_SCOPED_LAZY_STMT(stmt
, mDBMoveItem
);
1817 rv
= stmt
->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aNewParent
);
1818 NS_ENSURE_SUCCESS(rv
, rv
);
1819 rv
= stmt
->BindInt32ByName(NS_LITERAL_CSTRING("item_index"), newIndex
);
1820 NS_ENSURE_SUCCESS(rv
, rv
);
1821 rv
= stmt
->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), aItemId
);
1822 NS_ENSURE_SUCCESS(rv
, rv
);
1823 rv
= stmt
->Execute();
1824 NS_ENSURE_SUCCESS(rv
, rv
);
1827 PRTime now
= PR_Now();
1828 rv
= SetItemDateInternal(GetStatement(mDBSetItemLastModified
),
1830 NS_ENSURE_SUCCESS(rv
, rv
);
1831 rv
= SetItemDateInternal(GetStatement(mDBSetItemLastModified
),
1833 NS_ENSURE_SUCCESS(rv
, rv
);
1835 rv
= transaction
.Commit();
1836 NS_ENSURE_SUCCESS(rv
, rv
);
1838 NOTIFY_OBSERVERS(mCanNotify
, mCacheObservers
, mObservers
,
1839 nsINavBookmarkObserver
,
1840 OnItemMoved(aItemId
, oldParent
, oldIndex
, aNewParent
,
1841 newIndex
, itemType
));
1843 // notify dynamic container provider if there is one
1844 if (!folderType
.IsEmpty()) {
1845 nsCOMPtr
<nsIDynamicContainer
> container
=
1846 do_GetService(folderType
.get(), &rv
);
1847 if (NS_SUCCEEDED(rv
)) {
1848 rv
= container
->OnContainerMoved(aItemId
, aNewParent
, newIndex
);
1849 NS_ENSURE_SUCCESS(rv
, rv
);
1857 nsNavBookmarks::SetItemDateInternal(mozIStorageStatement
* aStatement
,
1861 NS_ENSURE_STATE(aStatement
);
1862 mozStorageStatementScoper
scoper(aStatement
);
1864 nsresult rv
= aStatement
->BindInt64ByName(NS_LITERAL_CSTRING("date"), aValue
);
1865 NS_ENSURE_SUCCESS(rv
, rv
);
1866 rv
= aStatement
->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), aItemId
);
1867 NS_ENSURE_SUCCESS(rv
, rv
);
1869 rv
= aStatement
->Execute();
1870 NS_ENSURE_SUCCESS(rv
, rv
);
1872 // note, we are not notifying the observers
1873 // that the item has changed.
1880 nsNavBookmarks::SetItemDateAdded(PRInt64 aItemId
, PRTime aDateAdded
)
1882 // GetItemType also ensures that aItemId points to a valid item.
1884 nsresult rv
= GetItemType(aItemId
, &itemType
);
1885 NS_ENSURE_SUCCESS(rv
, rv
);
1887 rv
= SetItemDateInternal(GetStatement(mDBSetItemDateAdded
),
1888 aItemId
, aDateAdded
);
1889 NS_ENSURE_SUCCESS(rv
, rv
);
1891 // Note: mDBSetItemDateAdded also sets lastModified to aDateAdded.
1892 NOTIFY_OBSERVERS(mCanNotify
, mCacheObservers
, mObservers
,
1893 nsINavBookmarkObserver
,
1894 OnItemChanged(aItemId
, NS_LITERAL_CSTRING("dateAdded"),
1896 nsPrintfCString(16, "%lld", aDateAdded
),
1897 aDateAdded
, itemType
));
1903 nsNavBookmarks::GetItemDateAdded(PRInt64 aItemId
, PRTime
* _dateAdded
)
1905 NS_ENSURE_ARG_MIN(aItemId
, 1);
1906 NS_ENSURE_ARG_POINTER(_dateAdded
);
1908 DECLARE_AND_ASSIGN_SCOPED_LAZY_STMT(stmt
, mDBGetItemProperties
);
1909 nsresult rv
= stmt
->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), aItemId
);
1910 NS_ENSURE_SUCCESS(rv
, rv
);
1913 rv
= stmt
->ExecuteStep(&hasResult
);
1914 NS_ENSURE_SUCCESS(rv
, rv
);
1915 NS_ENSURE_TRUE(hasResult
, NS_ERROR_INVALID_ARG
); // Invalid itemId.
1917 rv
= stmt
->GetInt64(kGetItemPropertiesIndex_DateAdded
, _dateAdded
);
1918 NS_ENSURE_SUCCESS(rv
, rv
);
1925 nsNavBookmarks::SetItemLastModified(PRInt64 aItemId
, PRTime aLastModified
)
1927 // GetItemType also ensures that aItemId points to a valid item.
1929 nsresult rv
= GetItemType(aItemId
, &itemType
);
1930 NS_ENSURE_SUCCESS(rv
, rv
);
1932 rv
= SetItemDateInternal(GetStatement(mDBSetItemLastModified
),
1933 aItemId
, aLastModified
);
1934 NS_ENSURE_SUCCESS(rv
, rv
);
1936 NOTIFY_OBSERVERS(mCanNotify
, mCacheObservers
, mObservers
,
1937 nsINavBookmarkObserver
,
1938 OnItemChanged(aItemId
, NS_LITERAL_CSTRING("lastModified"),
1940 nsPrintfCString(16, "%lld", aLastModified
),
1941 aLastModified
, itemType
));
1947 nsNavBookmarks::GetItemLastModified(PRInt64 aItemId
, PRTime
* aLastModified
)
1949 NS_ENSURE_ARG_MIN(aItemId
, 1);
1950 NS_ENSURE_ARG_POINTER(aLastModified
);
1952 DECLARE_AND_ASSIGN_SCOPED_LAZY_STMT(stmt
, mDBGetItemProperties
);
1953 nsresult rv
= stmt
->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), aItemId
);
1954 NS_ENSURE_SUCCESS(rv
, rv
);
1957 rv
= stmt
->ExecuteStep(&hasResult
);
1958 NS_ENSURE_SUCCESS(rv
, rv
);
1961 return NS_ERROR_INVALID_ARG
; // invalid item id
1963 rv
= stmt
->GetInt64(kGetItemPropertiesIndex_LastModified
, aLastModified
);
1964 NS_ENSURE_SUCCESS(rv
, rv
);
1970 nsNavBookmarks::GetGUIDBase(nsAString
&aGUIDBase
)
1972 if (!mGUIDBase
.IsEmpty()) {
1973 aGUIDBase
= mGUIDBase
;
1977 // generate a new GUID base for this session
1978 nsCOMPtr
<nsIUUIDGenerator
> uuidgen
=
1979 do_GetService("@mozilla.org/uuid-generator;1");
1980 NS_ENSURE_TRUE(uuidgen
, NS_ERROR_OUT_OF_MEMORY
);
1982 nsresult rv
= uuidgen
->GenerateUUIDInPlace(&GUID
);
1983 NS_ENSURE_SUCCESS(rv
, rv
);
1984 char GUIDChars
[NSID_LENGTH
];
1985 GUID
.ToProvidedString(GUIDChars
);
1986 CopyASCIItoUTF16(GUIDChars
, mGUIDBase
);
1987 aGUIDBase
= mGUIDBase
;
1993 nsNavBookmarks::GetItemGUID(PRInt64 aItemId
, nsAString
& aGUID
)
1995 NS_ENSURE_ARG_MIN(aItemId
, 1);
1997 nsAnnotationService
* annosvc
= nsAnnotationService::GetAnnotationService();
1998 NS_ENSURE_TRUE(annosvc
, NS_ERROR_OUT_OF_MEMORY
);
1999 nsresult rv
= annosvc
->GetItemAnnotationString(aItemId
, GUID_ANNO
, aGUID
);
2000 if (NS_SUCCEEDED(rv
) || rv
!= NS_ERROR_NOT_AVAILABLE
)
2004 tmp
.AppendInt(mItemCount
++);
2005 aGUID
.SetCapacity(NSID_LENGTH
- 1 + tmp
.Length());
2007 rv
= GetGUIDBase(GUIDBase
);
2008 NS_ENSURE_SUCCESS(rv
, rv
);
2009 aGUID
.Assign(GUIDBase
);
2012 rv
= SetItemGUID(aItemId
, aGUID
);
2013 NS_ENSURE_SUCCESS(rv
, rv
);
2019 nsNavBookmarks::SetItemGUID(PRInt64 aItemId
, const nsAString
& aGUID
)
2021 NS_ENSURE_ARG_MIN(aItemId
, 1);
2024 GetItemIdForGUID(aGUID
, &checkId
);
2026 return NS_ERROR_INVALID_ARG
; // invalid GUID, already exists
2028 nsAnnotationService
* annosvc
= nsAnnotationService::GetAnnotationService();
2029 NS_ENSURE_TRUE(annosvc
, NS_ERROR_OUT_OF_MEMORY
);
2030 nsresult rv
= annosvc
->SetItemAnnotationString(aItemId
, GUID_ANNO
, aGUID
, 0,
2031 nsIAnnotationService::EXPIRE_NEVER
);
2032 NS_ENSURE_SUCCESS(rv
, rv
);
2038 nsNavBookmarks::GetItemIdForGUID(const nsAString
& aGUID
, PRInt64
* aItemId
)
2040 NS_ENSURE_ARG_POINTER(aItemId
);
2042 DECLARE_AND_ASSIGN_SCOPED_LAZY_STMT(stmt
, mDBGetItemIdForGUID
);
2043 nsresult rv
= stmt
->BindStringByName(NS_LITERAL_CSTRING("guid"), aGUID
);
2044 NS_ENSURE_SUCCESS(rv
, rv
);
2046 PRBool hasMore
= PR_FALSE
;
2047 rv
= stmt
->ExecuteStep(&hasMore
);
2048 if (NS_FAILED(rv
) || ! hasMore
) {
2050 return NS_OK
; // not found: return -1
2053 // found, get the itemId
2054 rv
= stmt
->GetInt64(0, aItemId
);
2055 NS_ENSURE_SUCCESS(rv
, rv
);
2061 nsNavBookmarks::SetItemTitle(PRInt64 aItemId
, const nsACString
& aTitle
)
2063 // GetItemType also ensures that aItemId points to a valid item.
2065 nsresult rv
= GetItemType(aItemId
, &itemType
);
2066 NS_ENSURE_SUCCESS(rv
, rv
);
2068 DECLARE_AND_ASSIGN_SCOPED_LAZY_STMT(statement
, mDBSetItemTitle
);
2069 // Support setting a null title, we support this in insertBookmark.
2070 if (aTitle
.IsVoid())
2071 rv
= statement
->BindNullByName(NS_LITERAL_CSTRING("item_title"));
2073 rv
= statement
->BindUTF8StringByName(NS_LITERAL_CSTRING("item_title"), aTitle
);
2074 NS_ENSURE_SUCCESS(rv
, rv
);
2075 PRTime lastModified
= PR_Now();
2076 rv
= statement
->BindInt64ByName(NS_LITERAL_CSTRING("date"), lastModified
);
2077 NS_ENSURE_SUCCESS(rv
, rv
);
2078 rv
= statement
->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), aItemId
);
2079 NS_ENSURE_SUCCESS(rv
, rv
);
2081 rv
= statement
->Execute();
2082 NS_ENSURE_SUCCESS(rv
, rv
);
2084 NOTIFY_OBSERVERS(mCanNotify
, mCacheObservers
, mObservers
,
2085 nsINavBookmarkObserver
,
2086 OnItemChanged(aItemId
, NS_LITERAL_CSTRING("title"), PR_FALSE
,
2087 aTitle
, lastModified
, itemType
));
2093 nsNavBookmarks::GetItemTitle(PRInt64 aItemId
, nsACString
& aTitle
)
2095 NS_ENSURE_ARG_MIN(aItemId
, 1);
2097 DECLARE_AND_ASSIGN_SCOPED_LAZY_STMT(stmt
, mDBGetItemProperties
);
2098 nsresult rv
= stmt
->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), aItemId
);
2099 NS_ENSURE_SUCCESS(rv
, rv
);
2102 rv
= stmt
->ExecuteStep(&hasResult
);
2103 NS_ENSURE_SUCCESS(rv
, rv
);
2106 return NS_ERROR_INVALID_ARG
; // invalid bookmark id
2108 rv
= stmt
->GetUTF8String(kGetItemPropertiesIndex_Title
, aTitle
);
2109 NS_ENSURE_SUCCESS(rv
, rv
);
2115 nsNavBookmarks::GetBookmarkURI(PRInt64 aItemId
, nsIURI
** aURI
)
2117 NS_ENSURE_ARG_MIN(aItemId
, 1);
2118 NS_ENSURE_ARG_POINTER(aURI
);
2120 DECLARE_AND_ASSIGN_SCOPED_LAZY_STMT(stmt
, mDBGetItemProperties
);
2121 nsresult rv
= stmt
->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), aItemId
);
2122 NS_ENSURE_SUCCESS(rv
, rv
);
2125 rv
= stmt
->ExecuteStep(&hasResult
);
2126 NS_ENSURE_SUCCESS(rv
, rv
);
2129 return NS_ERROR_INVALID_ARG
; // invalid bookmark id
2132 rv
= stmt
->GetInt32(kGetItemPropertiesIndex_Type
, &type
);
2133 NS_ENSURE_SUCCESS(rv
, rv
);
2134 // Ensure this is a bookmark.
2135 NS_ENSURE_TRUE(type
== TYPE_BOOKMARK
, NS_ERROR_INVALID_ARG
);
2138 rv
= stmt
->GetUTF8String(kGetItemPropertiesIndex_URI
, spec
);
2139 NS_ENSURE_SUCCESS(rv
, rv
);
2141 rv
= NS_NewURI(aURI
, spec
);
2142 NS_ENSURE_SUCCESS(rv
, rv
);
2149 nsNavBookmarks::GetItemType(PRInt64 aItemId
, PRUint16
* _type
)
2151 NS_ENSURE_ARG_MIN(aItemId
, 1);
2152 NS_ENSURE_ARG_POINTER(_type
);
2154 DECLARE_AND_ASSIGN_SCOPED_LAZY_STMT(stmt
, mDBGetItemProperties
);
2155 nsresult rv
= stmt
->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), aItemId
);
2156 NS_ENSURE_SUCCESS(rv
, rv
);
2159 rv
= stmt
->ExecuteStep(&hasResult
);
2160 NS_ENSURE_SUCCESS(rv
, rv
);
2163 return NS_ERROR_INVALID_ARG
; // invalid bookmark id
2167 rv
= stmt
->GetInt32(kGetItemPropertiesIndex_Type
, &itemType
);
2168 NS_ENSURE_SUCCESS(rv
, rv
);
2176 nsNavBookmarks::GetFolderType(PRInt64 aFolder
, nsACString
& aType
)
2178 DECLARE_AND_ASSIGN_SCOPED_LAZY_STMT(stmt
, mDBGetItemProperties
);
2179 nsresult rv
= stmt
->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), aFolder
);
2180 NS_ENSURE_SUCCESS(rv
, rv
);
2183 rv
= stmt
->ExecuteStep(&hasResult
);
2184 NS_ENSURE_SUCCESS(rv
, rv
);
2187 return NS_ERROR_INVALID_ARG
;
2190 return stmt
->GetUTF8String(kGetItemPropertiesIndex_ServiceContractId
, aType
);
2195 nsNavBookmarks::ResultNodeForContainer(PRInt64 aID
,
2196 nsNavHistoryQueryOptions
* aOptions
,
2197 nsNavHistoryResultNode
** aNode
)
2199 DECLARE_AND_ASSIGN_SCOPED_LAZY_STMT(stmt
, mDBGetItemProperties
);
2200 nsresult rv
= stmt
->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), aID
);
2201 NS_ENSURE_SUCCESS(rv
, rv
);
2204 rv
= stmt
->ExecuteStep(&hasResult
);
2205 NS_ENSURE_SUCCESS(rv
, rv
);
2206 NS_ENSURE_TRUE(hasResult
, NS_ERROR_INVALID_ARG
);
2208 nsCAutoString title
;
2209 rv
= stmt
->GetUTF8String(kGetItemPropertiesIndex_Title
, title
);
2210 NS_ENSURE_SUCCESS(rv
, rv
);
2213 rv
= stmt
->GetInt32(kGetItemPropertiesIndex_Type
, &itemType
);
2214 NS_ENSURE_SUCCESS(rv
, rv
);
2215 if (itemType
== TYPE_DYNAMIC_CONTAINER
) {
2217 nsCAutoString contractId
;
2218 rv
= stmt
->GetUTF8String(kGetItemPropertiesIndex_ServiceContractId
,
2220 NS_ENSURE_SUCCESS(rv
, rv
);
2221 *aNode
= new nsNavHistoryContainerResultNode(EmptyCString(), title
,
2223 nsINavHistoryResultNode::RESULT_TYPE_DYNAMIC_CONTAINER
,
2224 PR_TRUE
, contractId
, aOptions
);
2225 (*aNode
)->mItemId
= aID
;
2227 else { // TYPE_FOLDER
2228 *aNode
= new nsNavHistoryFolderResultNode(title
, aOptions
, aID
, EmptyCString());
2231 return NS_ERROR_OUT_OF_MEMORY
;
2233 rv
= stmt
->GetInt64(kGetItemPropertiesIndex_DateAdded
,
2234 &(*aNode
)->mDateAdded
);
2235 NS_ENSURE_SUCCESS(rv
, rv
);
2236 rv
= stmt
->GetInt64(kGetItemPropertiesIndex_LastModified
,
2237 &(*aNode
)->mLastModified
);
2238 NS_ENSURE_SUCCESS(rv
, rv
);
2246 nsNavBookmarks::QueryFolderChildren(
2248 nsNavHistoryQueryOptions
* aOptions
,
2249 nsCOMArray
<nsNavHistoryResultNode
>* aChildren
)
2251 NS_ENSURE_ARG_POINTER(aOptions
);
2252 NS_ENSURE_ARG_POINTER(aChildren
);
2254 DECLARE_AND_ASSIGN_SCOPED_LAZY_STMT(stmt
, mDBGetChildren
);
2255 nsresult rv
= stmt
->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aFolderId
);
2256 NS_ENSURE_SUCCESS(rv
, rv
);
2258 nsCOMPtr
<mozIStorageValueArray
> row
= do_QueryInterface(stmt
, &rv
);
2259 NS_ENSURE_SUCCESS(rv
, rv
);
2263 while (NS_SUCCEEDED(stmt
->ExecuteStep(&hasResult
)) && hasResult
) {
2264 rv
= ProcessFolderNodeRow(row
, aOptions
, aChildren
, index
);
2265 NS_ENSURE_SUCCESS(rv
, rv
);
2273 nsNavBookmarks::ProcessFolderNodeRow(
2274 mozIStorageValueArray
* aRow
,
2275 nsNavHistoryQueryOptions
* aOptions
,
2276 nsCOMArray
<nsNavHistoryResultNode
>* aChildren
,
2277 PRInt32
& aCurrentIndex
)
2279 NS_ENSURE_ARG_POINTER(aRow
);
2280 NS_ENSURE_ARG_POINTER(aOptions
);
2281 NS_ENSURE_ARG_POINTER(aChildren
);
2283 // The results will be in order of aCurrentIndex. Even if we don't add a node
2284 // because it was excluded, we need to count its index, so do that before
2285 // doing anything else.
2289 nsresult rv
= aRow
->GetInt32(kGetChildrenIndex_Type
, &itemType
);
2290 NS_ENSURE_SUCCESS(rv
, rv
);
2292 rv
= aRow
->GetInt64(nsNavHistory::kGetInfoIndex_ItemId
, &id
);
2293 NS_ENSURE_SUCCESS(rv
, rv
);
2294 nsRefPtr
<nsNavHistoryResultNode
> node
;
2295 if (itemType
== TYPE_BOOKMARK
) {
2296 nsNavHistory
* history
= nsNavHistory::GetHistoryService();
2297 NS_ENSURE_TRUE(history
, NS_ERROR_OUT_OF_MEMORY
);
2298 rv
= history
->RowToResult(aRow
, aOptions
, getter_AddRefs(node
));
2299 NS_ENSURE_SUCCESS(rv
, rv
);
2302 node
->GetType(&nodeType
);
2303 if ((nodeType
== nsINavHistoryResultNode::RESULT_TYPE_QUERY
&&
2304 aOptions
->ExcludeQueries()) ||
2305 (nodeType
!= nsINavHistoryResultNode::RESULT_TYPE_QUERY
&&
2306 nodeType
!= nsINavHistoryResultNode::RESULT_TYPE_FOLDER_SHORTCUT
&&
2307 aOptions
->ExcludeItems())) {
2311 else if (itemType
== TYPE_FOLDER
|| itemType
== TYPE_DYNAMIC_CONTAINER
) {
2312 if (aOptions
->ExcludeReadOnlyFolders()) {
2313 // If the folder is read-only, skip it.
2315 if (itemType
== TYPE_DYNAMIC_CONTAINER
) {
2319 readOnly
= PR_FALSE
;
2320 GetFolderReadonly(id
, &readOnly
);
2325 rv
= ResultNodeForContainer(id
, aOptions
, getter_AddRefs(node
));
2326 NS_ENSURE_SUCCESS(rv
, rv
);
2329 // This is a separator.
2330 if (aOptions
->ExcludeItems()) {
2333 node
= new nsNavHistorySeparatorResultNode();
2334 NS_ENSURE_TRUE(node
, NS_ERROR_OUT_OF_MEMORY
);
2336 rv
= aRow
->GetInt64(nsNavHistory::kGetInfoIndex_ItemId
, &node
->mItemId
);
2337 NS_ENSURE_SUCCESS(rv
, rv
);
2338 rv
= aRow
->GetInt64(nsNavHistory::kGetInfoIndex_ItemDateAdded
,
2340 NS_ENSURE_SUCCESS(rv
, rv
);
2341 rv
= aRow
->GetInt64(nsNavHistory::kGetInfoIndex_ItemLastModified
,
2342 &node
->mLastModified
);
2343 NS_ENSURE_SUCCESS(rv
, rv
);
2346 // Store the index of the node within this container. Note that this is not
2347 // moz_bookmarks.position.
2348 node
->mBookmarkIndex
= aCurrentIndex
;
2350 NS_ENSURE_TRUE(aChildren
->AppendObject(node
), NS_ERROR_OUT_OF_MEMORY
);
2357 nsNavBookmarks::QueryFolderChildrenAsync(
2358 nsNavHistoryFolderResultNode
* aNode
,
2360 mozIStoragePendingStatement
** _pendingStmt
)
2362 NS_ENSURE_ARG_POINTER(aNode
);
2363 NS_ENSURE_ARG_POINTER(_pendingStmt
);
2365 mozStorageStatementScoper
scope(mDBGetChildren
);
2367 nsresult rv
= mDBGetChildren
->BindInt64ByName(NS_LITERAL_CSTRING("parent"),
2369 NS_ENSURE_SUCCESS(rv
, rv
);
2371 nsCOMPtr
<mozIStoragePendingStatement
> pendingStmt
;
2372 rv
= mDBGetChildren
->ExecuteAsync(aNode
, getter_AddRefs(pendingStmt
));
2373 NS_ENSURE_SUCCESS(rv
, rv
);
2375 NS_IF_ADDREF(*_pendingStmt
= pendingStmt
);
2381 nsNavBookmarks::FolderCount(PRInt64 aFolderId
, PRInt32
* _folderCount
)
2384 DECLARE_AND_ASSIGN_SCOPED_LAZY_STMT(stmt
, mDBFolderCount
);
2385 nsresult rv
= stmt
->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aFolderId
);
2386 NS_ENSURE_SUCCESS(rv
, rv
);
2389 rv
= stmt
->ExecuteStep(&hasResult
);
2390 NS_ENSURE_SUCCESS(rv
, rv
);
2391 NS_ENSURE_TRUE(hasResult
, NS_ERROR_UNEXPECTED
);
2393 // Ensure that the folder we are looking for exists.
2394 PRInt64 confirmFolderId
;
2395 rv
= stmt
->GetInt64(1, &confirmFolderId
);
2396 NS_ENSURE_SUCCESS(rv
, rv
);
2397 NS_ENSURE_TRUE(confirmFolderId
== aFolderId
, NS_ERROR_INVALID_ARG
);
2399 rv
= stmt
->GetInt32(0, _folderCount
);
2400 NS_ENSURE_SUCCESS(rv
, rv
);
2407 nsNavBookmarks::IsBookmarked(nsIURI
* aURI
, PRBool
* aBookmarked
)
2409 NS_ENSURE_ARG(aURI
);
2410 NS_ENSURE_ARG_POINTER(aBookmarked
);
2412 DECLARE_AND_ASSIGN_SCOPED_LAZY_STMT(stmt
, mDBIsURIBookmarkedInDatabase
);
2413 nsresult rv
= URIBinder::Bind(stmt
, NS_LITERAL_CSTRING("page_url"), aURI
);
2414 NS_ENSURE_SUCCESS(rv
, rv
);
2415 rv
= stmt
->ExecuteStep(aBookmarked
);
2416 NS_ENSURE_SUCCESS(rv
, rv
);
2423 nsNavBookmarks::GetBookmarkedURIFor(nsIURI
* aURI
, nsIURI
** _retval
)
2425 NS_ENSURE_ARG(aURI
);
2426 NS_ENSURE_ARG_POINTER(_retval
);
2430 nsNavHistory
* history
= nsNavHistory::GetHistoryService();
2431 NS_ENSURE_TRUE(history
, NS_ERROR_OUT_OF_MEMORY
);
2433 nsresult rv
= history
->GetUrlIdFor(aURI
, &placeId
, PR_FALSE
);
2434 NS_ENSURE_SUCCESS(rv
, rv
);
2436 // This URI is unknown, just return null.
2440 // Check if a bookmark exists in the redirects chain for this URI.
2441 // The query will also check if the page is directly bookmarked, and return
2442 // the first found bookmark in case. The check is directly on moz_bookmarks
2443 // without special filtering.
2444 DECLARE_AND_ASSIGN_SCOPED_LAZY_STMT(stmt
, mDBFindRedirectedBookmark
);
2445 rv
= stmt
->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), placeId
);
2446 NS_ENSURE_SUCCESS(rv
, rv
);
2447 PRBool hasBookmarkedOrigin
;
2448 if (NS_SUCCEEDED(stmt
->ExecuteStep(&hasBookmarkedOrigin
)) &&
2449 hasBookmarkedOrigin
) {
2451 rv
= stmt
->GetUTF8String(0, spec
);
2452 NS_ENSURE_SUCCESS(rv
, rv
);
2453 rv
= NS_NewURI(_retval
, spec
);
2454 NS_ENSURE_SUCCESS(rv
, rv
);
2457 // If there is no bookmarked origin, we will just return null.
2463 nsNavBookmarks::ChangeBookmarkURI(PRInt64 aBookmarkId
, nsIURI
* aNewURI
)
2465 NS_ENSURE_ARG_MIN(aBookmarkId
, 1);
2466 NS_ENSURE_ARG(aNewURI
);
2468 mozStorageTransaction
transaction(mDBConn
, PR_FALSE
);
2470 nsNavHistory
* history
= nsNavHistory::GetHistoryService();
2471 NS_ENSURE_TRUE(history
, NS_ERROR_OUT_OF_MEMORY
);
2474 nsresult rv
= history
->GetUrlIdFor(aNewURI
, &placeId
, PR_TRUE
);
2475 NS_ENSURE_SUCCESS(rv
, rv
);
2477 return NS_ERROR_INVALID_ARG
;
2479 // We need the bookmark's current corresponding places ID below, so get it now
2480 // before we change it. GetBookmarkURI will fail if aBookmarkId is bad.
2481 nsCOMPtr
<nsIURI
> oldURI
;
2483 rv
= GetBookmarkURI(aBookmarkId
, getter_AddRefs(oldURI
));
2484 NS_ENSURE_SUCCESS(rv
, rv
);
2485 rv
= history
->GetUrlIdFor(oldURI
, &oldPlaceId
, PR_FALSE
);
2486 NS_ENSURE_SUCCESS(rv
, rv
);
2488 DECLARE_AND_ASSIGN_SCOPED_LAZY_STMT(statement
, mDBChangeBookmarkURI
);
2489 rv
= statement
->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), placeId
);
2490 NS_ENSURE_SUCCESS(rv
, rv
);
2491 PRTime lastModified
= PR_Now();
2492 rv
= statement
->BindInt64ByName(NS_LITERAL_CSTRING("date"), lastModified
);
2493 NS_ENSURE_SUCCESS(rv
, rv
);
2494 rv
= statement
->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), aBookmarkId
);
2495 NS_ENSURE_SUCCESS(rv
, rv
);
2496 rv
= statement
->Execute();
2497 NS_ENSURE_SUCCESS(rv
, rv
);
2499 rv
= transaction
.Commit();
2500 NS_ENSURE_SUCCESS(rv
, rv
);
2502 rv
= history
->UpdateFrecency(placeId
);
2503 NS_ENSURE_SUCCESS(rv
, rv
);
2505 // Upon changing the URI for a bookmark, update the frecency for the old
2507 rv
= history
->UpdateFrecency(oldPlaceId
);
2508 NS_ENSURE_SUCCESS(rv
, rv
);
2511 rv
= aNewURI
->GetSpec(spec
);
2512 NS_ENSURE_SUCCESS(rv
, rv
);
2514 // Pass the new URI to OnItemChanged.
2515 NOTIFY_OBSERVERS(mCanNotify
, mCacheObservers
, mObservers
,
2516 nsINavBookmarkObserver
,
2517 OnItemChanged(aBookmarkId
, NS_LITERAL_CSTRING("uri"),
2518 PR_FALSE
, spec
, lastModified
, TYPE_BOOKMARK
));
2525 nsNavBookmarks::GetFolderIdForItem(PRInt64 aItemId
, PRInt64
* aFolderId
)
2527 NS_ENSURE_ARG_MIN(aItemId
, 1);
2528 NS_ENSURE_ARG_POINTER(aFolderId
);
2530 DECLARE_AND_ASSIGN_SCOPED_LAZY_STMT(stmt
, mDBGetItemProperties
);
2531 nsresult rv
= stmt
->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), aItemId
);
2532 NS_ENSURE_SUCCESS(rv
, rv
);
2535 rv
= stmt
->ExecuteStep(&hasResult
);
2536 NS_ENSURE_SUCCESS(rv
, rv
);
2539 return NS_ERROR_INVALID_ARG
; // invalid item id
2541 rv
= stmt
->GetInt64(kGetItemPropertiesIndex_Parent
, aFolderId
);
2542 NS_ENSURE_SUCCESS(rv
, rv
);
2544 // this should not happen, but see bug #400448 for details
2545 NS_ENSURE_TRUE(aItemId
!= *aFolderId
, NS_ERROR_UNEXPECTED
);
2551 nsNavBookmarks::GetBookmarkIdsForURITArray(nsIURI
* aURI
,
2552 nsTArray
<PRInt64
>& aResult
)
2554 NS_ENSURE_ARG(aURI
);
2556 DECLARE_AND_ASSIGN_SCOPED_LAZY_STMT(stmt
, mDBFindURIBookmarks
);
2557 nsresult rv
= URIBinder::Bind(stmt
, NS_LITERAL_CSTRING("page_url"), aURI
);
2558 NS_ENSURE_SUCCESS(rv
, rv
);
2561 while (NS_SUCCEEDED((rv
= stmt
->ExecuteStep(&more
))) && more
) {
2563 rv
= stmt
->GetInt64(kFindBookmarksIndex_ID
, &bookmarkId
);
2564 NS_ENSURE_SUCCESS(rv
, rv
);
2565 NS_ENSURE_TRUE(aResult
.AppendElement(bookmarkId
), NS_ERROR_OUT_OF_MEMORY
);
2567 NS_ENSURE_SUCCESS(rv
, rv
);
2574 nsNavBookmarks::GetBookmarkIdsForURI(nsIURI
* aURI
, PRUint32
* aCount
,
2575 PRInt64
** aBookmarks
)
2577 NS_ENSURE_ARG(aURI
);
2578 NS_ENSURE_ARG_POINTER(aCount
);
2579 NS_ENSURE_ARG_POINTER(aBookmarks
);
2582 *aBookmarks
= nsnull
;
2583 nsTArray
<PRInt64
> bookmarks
;
2585 // Get the information from the DB as a TArray
2586 nsresult rv
= GetBookmarkIdsForURITArray(aURI
, bookmarks
);
2587 NS_ENSURE_SUCCESS(rv
, rv
);
2589 // Copy the results into a new array for output
2590 if (bookmarks
.Length()) {
2592 static_cast<PRInt64
*>(nsMemory::Alloc(sizeof(PRInt64
) * bookmarks
.Length()));
2594 return NS_ERROR_OUT_OF_MEMORY
;
2595 for (PRUint32 i
= 0; i
< bookmarks
.Length(); i
++)
2596 (*aBookmarks
)[i
] = bookmarks
[i
];
2598 *aCount
= bookmarks
.Length();
2605 nsNavBookmarks::GetItemIndex(PRInt64 aItemId
, PRInt32
* _index
)
2607 NS_ENSURE_ARG_MIN(aItemId
, 1);
2608 NS_ENSURE_ARG_POINTER(_index
);
2612 DECLARE_AND_ASSIGN_SCOPED_LAZY_STMT(stmt
, mDBGetItemProperties
);
2613 nsresult rv
= stmt
->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), aItemId
);
2614 NS_ENSURE_SUCCESS(rv
, rv
);
2616 rv
= stmt
->ExecuteStep(&hasResult
);
2617 NS_ENSURE_SUCCESS(rv
, rv
);
2621 rv
= stmt
->GetInt32(kGetItemPropertiesIndex_Position
, _index
);
2622 NS_ENSURE_SUCCESS(rv
, rv
);
2629 nsNavBookmarks::SetItemIndex(PRInt64 aItemId
, PRInt32 aNewIndex
)
2631 NS_ENSURE_ARG_MIN(aItemId
, 1);
2632 NS_ENSURE_ARG_MIN(aNewIndex
, 0);
2635 PRInt32 oldIndex
= 0;
2640 mozIStorageStatement
* getInfoStmt(mDBGetItemProperties
);
2641 NS_ENSURE_STATE(getInfoStmt
);
2642 mozStorageStatementScoper
scoper(getInfoStmt
);
2643 rv
= getInfoStmt
->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), aItemId
);
2644 NS_ENSURE_SUCCESS(rv
, rv
);
2647 rv
= getInfoStmt
->ExecuteStep(&hasResult
);
2648 NS_ENSURE_SUCCESS(rv
, rv
);
2652 rv
= getInfoStmt
->GetInt32(kGetItemPropertiesIndex_Position
, &oldIndex
);
2653 NS_ENSURE_SUCCESS(rv
, rv
);
2654 rv
= getInfoStmt
->GetInt32(kGetItemPropertiesIndex_Type
, &itemType
);
2655 NS_ENSURE_SUCCESS(rv
, rv
);
2656 rv
= getInfoStmt
->GetInt64(kGetItemPropertiesIndex_Parent
, &parent
);
2657 NS_ENSURE_SUCCESS(rv
, rv
);
2660 // Ensure we are not going out of range.
2661 PRInt32 folderCount
;
2662 rv
= FolderCount(parent
, &folderCount
);
2663 NS_ENSURE_SUCCESS(rv
, rv
);
2664 NS_ENSURE_TRUE(aNewIndex
< folderCount
, NS_ERROR_INVALID_ARG
);
2666 DECLARE_AND_ASSIGN_SCOPED_LAZY_STMT(stmt
, mDBSetItemIndex
);
2667 rv
= stmt
->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), aItemId
);
2668 NS_ENSURE_SUCCESS(rv
, rv
);
2669 rv
= stmt
->BindInt32ByName(NS_LITERAL_CSTRING("item_index"), aNewIndex
);
2670 NS_ENSURE_SUCCESS(rv
, rv
);
2672 rv
= stmt
->Execute();
2673 NS_ENSURE_SUCCESS(rv
, rv
);
2675 NOTIFY_OBSERVERS(mCanNotify
, mCacheObservers
, mObservers
,
2676 nsINavBookmarkObserver
,
2677 OnItemMoved(aItemId
, parent
, oldIndex
, parent
, aNewIndex
,
2685 nsNavBookmarks::UpdateKeywordsHashForRemovedBookmark(PRInt64 aItemId
)
2688 if (NS_SUCCEEDED(GetKeywordForBookmark(aItemId
, kw
)) && !kw
.IsEmpty()) {
2689 nsresult rv
= EnsureKeywordsHash();
2690 NS_ENSURE_SUCCESS(rv
, rv
);
2691 mBookmarkToKeywordHash
.Remove(aItemId
);
2698 nsNavBookmarks::SetKeywordForBookmark(PRInt64 aBookmarkId
,
2699 const nsAString
& aUserCasedKeyword
)
2701 NS_ENSURE_ARG_MIN(aBookmarkId
, 1);
2703 nsresult rv
= EnsureKeywordsHash();
2704 NS_ENSURE_SUCCESS(rv
, rv
);
2706 // Shortcuts are always lowercased internally.
2707 nsAutoString
keyword(aUserCasedKeyword
);
2708 ToLowerCase(keyword
);
2710 // Check if bookmark was already associated to a keyword.
2711 nsAutoString oldKeyword
;
2712 rv
= GetKeywordForBookmark(aBookmarkId
, oldKeyword
);
2713 NS_ENSURE_SUCCESS(rv
, rv
);
2715 // Trying to set the same value or to remove a nonexistent keyword is a no-op.
2716 if (keyword
.Equals(oldKeyword
) || (keyword
.IsEmpty() && oldKeyword
.IsEmpty()))
2719 mozStorageTransaction
transaction(mDBConn
, PR_FALSE
);
2721 nsCOMPtr
<mozIStorageStatement
> updateBookmarkStmt
;
2722 rv
= mDBConn
->CreateStatement(NS_LITERAL_CSTRING(
2723 "UPDATE moz_bookmarks "
2724 "SET keyword_id = (SELECT id FROM moz_keywords WHERE keyword = :keyword), "
2725 "lastModified = :date "
2726 "WHERE id = :item_id "
2727 ), getter_AddRefs(updateBookmarkStmt
));
2728 NS_ENSURE_SUCCESS(rv
, rv
);
2730 if (keyword
.IsEmpty()) {
2731 // Remove keyword association from the hash.
2732 mBookmarkToKeywordHash
.Remove(aBookmarkId
);
2733 rv
= updateBookmarkStmt
->BindNullByName(NS_LITERAL_CSTRING("keyword"));
2736 // We are associating bookmark to a new keyword. Create a new keyword
2737 // record if needed.
2738 nsCOMPtr
<mozIStorageStatement
> newKeywordStmt
;
2739 rv
= mDBConn
->CreateStatement(NS_LITERAL_CSTRING(
2740 "INSERT OR IGNORE INTO moz_keywords (keyword) VALUES (:keyword)"
2741 ), getter_AddRefs(newKeywordStmt
));
2742 NS_ENSURE_SUCCESS(rv
, rv
);
2743 rv
= newKeywordStmt
->BindStringByName(NS_LITERAL_CSTRING("keyword"),
2745 NS_ENSURE_SUCCESS(rv
, rv
);
2746 rv
= newKeywordStmt
->Execute();
2747 NS_ENSURE_SUCCESS(rv
, rv
);
2749 // Add new keyword association to the hash, removing the old one if needed.
2750 if (!oldKeyword
.IsEmpty())
2751 mBookmarkToKeywordHash
.Remove(aBookmarkId
);
2752 mBookmarkToKeywordHash
.Put(aBookmarkId
, keyword
);
2753 rv
= updateBookmarkStmt
->BindStringByName(NS_LITERAL_CSTRING("keyword"), keyword
);
2755 NS_ENSURE_SUCCESS(rv
, rv
);
2756 PRTime lastModified
= PR_Now();
2757 rv
= updateBookmarkStmt
->BindInt64ByName(NS_LITERAL_CSTRING("date"),
2759 NS_ENSURE_SUCCESS(rv
, rv
);
2760 rv
= updateBookmarkStmt
->BindInt64ByName(NS_LITERAL_CSTRING("item_id"),
2762 NS_ENSURE_SUCCESS(rv
, rv
);
2763 rv
= updateBookmarkStmt
->Execute();
2764 NS_ENSURE_SUCCESS(rv
, rv
);
2766 rv
= transaction
.Commit();
2767 NS_ENSURE_SUCCESS(rv
, rv
);
2769 NOTIFY_OBSERVERS(mCanNotify
, mCacheObservers
, mObservers
,
2770 nsINavBookmarkObserver
,
2771 OnItemChanged(aBookmarkId
, NS_LITERAL_CSTRING("keyword"),
2772 PR_FALSE
, NS_ConvertUTF16toUTF8(keyword
),
2773 lastModified
, TYPE_BOOKMARK
));
2780 nsNavBookmarks::GetKeywordForURI(nsIURI
* aURI
, nsAString
& aKeyword
)
2782 NS_ENSURE_ARG(aURI
);
2783 aKeyword
.Truncate(0);
2785 DECLARE_AND_ASSIGN_SCOPED_LAZY_STMT(stmt
, mDBGetKeywordForURI
);
2786 nsresult rv
= URIBinder::Bind(stmt
, NS_LITERAL_CSTRING("page_url"), aURI
);
2787 NS_ENSURE_SUCCESS(rv
, rv
);
2789 PRBool hasMore
= PR_FALSE
;
2790 rv
= stmt
->ExecuteStep(&hasMore
);
2791 if (NS_FAILED(rv
) || !hasMore
) {
2792 aKeyword
.SetIsVoid(PR_TRUE
);
2793 return NS_OK
; // not found: return void keyword string
2796 // found, get the keyword
2797 rv
= stmt
->GetString(0, aKeyword
);
2798 NS_ENSURE_SUCCESS(rv
, rv
);
2804 nsNavBookmarks::GetKeywordForBookmark(PRInt64 aBookmarkId
, nsAString
& aKeyword
)
2806 NS_ENSURE_ARG_MIN(aBookmarkId
, 1);
2807 aKeyword
.Truncate(0);
2809 nsresult rv
= EnsureKeywordsHash();
2810 NS_ENSURE_SUCCESS(rv
, rv
);
2812 nsAutoString keyword
;
2813 if (!mBookmarkToKeywordHash
.Get(aBookmarkId
, &keyword
)) {
2814 aKeyword
.SetIsVoid(PR_TRUE
);
2817 aKeyword
.Assign(keyword
);
2825 nsNavBookmarks::GetURIForKeyword(const nsAString
& aUserCasedKeyword
,
2828 NS_ENSURE_ARG_POINTER(aURI
);
2829 NS_ENSURE_TRUE(!aUserCasedKeyword
.IsEmpty(), NS_ERROR_INVALID_ARG
);
2832 // Shortcuts are always lowercased internally.
2833 nsAutoString
keyword(aUserCasedKeyword
);
2834 ToLowerCase(keyword
);
2836 nsresult rv
= EnsureKeywordsHash();
2837 NS_ENSURE_SUCCESS(rv
, rv
);
2839 keywordSearchData searchData
;
2840 searchData
.keyword
.Assign(keyword
);
2841 searchData
.itemId
= -1;
2842 mBookmarkToKeywordHash
.EnumerateRead(SearchBookmarkForKeyword
, &searchData
);
2844 if (searchData
.itemId
== -1) {
2849 rv
= GetBookmarkURI(searchData
.itemId
, aURI
);
2850 NS_ENSURE_SUCCESS(rv
, rv
);
2857 nsNavBookmarks::EnsureKeywordsHash() {
2858 if (mBookmarkToKeywordHash
.IsInitialized())
2861 mBookmarkToKeywordHash
.Init(BOOKMARKS_TO_KEYWORDS_INITIAL_CACHE_SIZE
);
2863 nsCOMPtr
<mozIStorageStatement
> stmt
;
2864 nsresult rv
= mDBConn
->CreateStatement(NS_LITERAL_CSTRING(
2865 "SELECT b.id, k.keyword "
2866 "FROM moz_bookmarks b "
2867 "JOIN moz_keywords k ON k.id = b.keyword_id "
2868 ), getter_AddRefs(stmt
));
2869 NS_ENSURE_SUCCESS(rv
, rv
);
2872 while (NS_SUCCEEDED(stmt
->ExecuteStep(&hasMore
)) && hasMore
) {
2874 rv
= stmt
->GetInt64(0, &itemId
);
2875 NS_ENSURE_SUCCESS(rv
, rv
);
2876 nsAutoString keyword
;
2877 rv
= stmt
->GetString(1, keyword
);
2878 NS_ENSURE_SUCCESS(rv
, rv
);
2880 rv
= mBookmarkToKeywordHash
.Put(itemId
, keyword
);
2881 NS_ENSURE_SUCCESS(rv
, rv
);
2889 nsNavBookmarks::RunInBatchMode(nsINavHistoryBatchCallback
* aCallback
,
2890 nsISupports
* aUserData
) {
2891 NS_ENSURE_ARG(aCallback
);
2895 // Just forward the request to history. History service must exist for
2896 // bookmarks to work and we are observing it, thus batch notifications will be
2897 // forwarded to bookmarks observers.
2898 nsNavHistory
* history
= nsNavHistory::GetHistoryService();
2899 NS_ENSURE_TRUE(history
, NS_ERROR_OUT_OF_MEMORY
);
2900 nsresult rv
= history
->RunInBatchMode(aCallback
, aUserData
);
2901 NS_ENSURE_SUCCESS(rv
, rv
);
2908 nsNavBookmarks::AddObserver(nsINavBookmarkObserver
* aObserver
,
2911 NS_ENSURE_ARG(aObserver
);
2912 return mObservers
.AppendWeakElement(aObserver
, aOwnsWeak
);
2917 nsNavBookmarks::RemoveObserver(nsINavBookmarkObserver
* aObserver
)
2919 return mObservers
.RemoveWeakElement(aObserver
);
2923 nsNavBookmarks::NotifyItemVisited(const ItemVisitData
& aData
)
2925 NOTIFY_OBSERVERS(mCanNotify
, mCacheObservers
, mObservers
, nsINavBookmarkObserver
,
2926 OnItemVisited(aData
.itemId
, aData
.visitId
, aData
.time
));
2930 nsNavBookmarks::NotifyItemChanged(const ItemChangeData
& aData
)
2932 NOTIFY_OBSERVERS(mCanNotify
, mCacheObservers
, mObservers
, nsINavBookmarkObserver
,
2933 OnItemChanged(aData
.itemId
, aData
.property
,
2934 aData
.isAnnotation
, aData
.newValue
,
2935 aData
.lastModified
, aData
.itemType
));
2938 ////////////////////////////////////////////////////////////////////////////////
2939 //// nsNavBookmarks::nsINavHistoryObserver
2942 nsNavBookmarks::OnBeginUpdateBatch()
2944 NOTIFY_OBSERVERS(mCanNotify
, mCacheObservers
, mObservers
,
2945 nsINavBookmarkObserver
, OnBeginUpdateBatch());
2951 nsNavBookmarks::OnEndUpdateBatch()
2955 ForceWALCheckpoint(mDBConn
);
2958 NOTIFY_OBSERVERS(mCanNotify
, mCacheObservers
, mObservers
,
2959 nsINavBookmarkObserver
, OnEndUpdateBatch());
2965 nsNavBookmarks::OnVisit(nsIURI
* aURI
, PRInt64 aVisitId
, PRTime aTime
,
2966 PRInt64 aSessionID
, PRInt64 aReferringID
,
2967 PRUint32 aTransitionType
, PRUint32
* aAdded
)
2969 // If the page is bookmarked, notify observers for each associated bookmark.
2970 ItemVisitData visitData
;
2971 visitData
.uri
= aURI
;
2972 visitData
.visitId
= aVisitId
;
2973 visitData
.time
= aTime
;
2975 nsRefPtr
< AsyncGetBookmarksForURI
<ItemVisitMethod
, ItemVisitData
> > notifier
=
2976 new AsyncGetBookmarksForURI
<ItemVisitMethod
, ItemVisitData
>(this, &nsNavBookmarks::NotifyItemVisited
, visitData
);
2983 nsNavBookmarks::OnBeforeDeleteURI(nsIURI
* aURI
)
2990 nsNavBookmarks::OnDeleteURI(nsIURI
* aURI
)
2992 // If the page is bookmarked, notify observers for each associated bookmark.
2993 ItemChangeData changeData
;
2994 changeData
.uri
= aURI
;
2995 changeData
.property
= NS_LITERAL_CSTRING("cleartime");
2996 changeData
.isAnnotation
= PR_FALSE
;
2997 changeData
.lastModified
= 0;
2998 changeData
.itemType
= TYPE_BOOKMARK
;
3000 nsRefPtr
< AsyncGetBookmarksForURI
<ItemChangeMethod
, ItemChangeData
> > notifier
=
3001 new AsyncGetBookmarksForURI
<ItemChangeMethod
, ItemChangeData
>(this, &nsNavBookmarks::NotifyItemChanged
, changeData
);
3008 nsNavBookmarks::OnClearHistory()
3010 // TODO(bryner): we should notify on visited-time change for all URIs
3016 nsNavBookmarks::OnTitleChanged(nsIURI
* aURI
, const nsAString
& aPageTitle
)
3018 // NOOP. We don't consume page titles from moz_places anymore.
3019 // Title-change notifications are sent from SetItemTitle.
3025 nsNavBookmarks::OnPageChanged(nsIURI
* aURI
, PRUint32 aWhat
,
3026 const nsAString
& aValue
)
3029 if (aWhat
== nsINavHistoryObserver::ATTRIBUTE_FAVICON
) {
3030 ItemChangeData changeData
;
3031 changeData
.uri
= aURI
;
3032 changeData
.property
= NS_LITERAL_CSTRING("favicon");
3033 changeData
.isAnnotation
= PR_FALSE
;
3034 changeData
.newValue
= NS_ConvertUTF16toUTF8(aValue
);
3035 changeData
.lastModified
= 0;
3036 changeData
.itemType
= TYPE_BOOKMARK
;
3038 // Favicons may be set to either pure URIs or to folder URIs
3040 rv
= aURI
->SchemeIs("place", &isPlaceURI
);
3041 NS_ENSURE_SUCCESS(rv
, rv
);
3044 rv
= aURI
->GetSpec(spec
);
3045 NS_ENSURE_SUCCESS(rv
, rv
);
3047 nsNavHistory
* history
= nsNavHistory::GetHistoryService();
3048 NS_ENSURE_TRUE(history
, NS_ERROR_OUT_OF_MEMORY
);
3050 nsCOMArray
<nsNavHistoryQuery
> queries
;
3051 nsCOMPtr
<nsNavHistoryQueryOptions
> options
;
3052 rv
= history
->QueryStringToQueryArray(spec
, &queries
, getter_AddRefs(options
));
3053 NS_ENSURE_SUCCESS(rv
, rv
);
3055 if (queries
.Count() == 1 && queries
[0]->Folders().Length() == 1) {
3056 changeData
.itemId
= queries
[0]->Folders()[0];
3057 NotifyItemChanged(changeData
);
3061 nsRefPtr
< AsyncGetBookmarksForURI
<ItemChangeMethod
, ItemChangeData
> > notifier
=
3062 new AsyncGetBookmarksForURI
<ItemChangeMethod
, ItemChangeData
>(this, &nsNavBookmarks::NotifyItemChanged
, changeData
);
3071 nsNavBookmarks::OnDeleteVisits(nsIURI
* aURI
, PRTime aVisitTime
)
3073 // pages that are bookmarks shouldn't expire, so we don't need to handle it
3078 // nsIAnnotationObserver
3081 nsNavBookmarks::OnPageAnnotationSet(nsIURI
* aPage
, const nsACString
& aName
)
3088 nsNavBookmarks::OnItemAnnotationSet(PRInt64 aItemId
, const nsACString
& aName
)
3090 // GetItemType also ensures that aItemId points to a valid item.
3092 nsresult rv
= GetItemType(aItemId
, &itemType
);
3093 NS_ENSURE_SUCCESS(rv
, rv
);
3095 PRTime lastModified
= PR_Now();
3096 rv
= SetItemDateInternal(GetStatement(mDBSetItemLastModified
),
3097 aItemId
, lastModified
);
3098 NS_ENSURE_SUCCESS(rv
, rv
);
3100 NOTIFY_OBSERVERS(mCanNotify
, mCacheObservers
, mObservers
,
3101 nsINavBookmarkObserver
,
3102 OnItemChanged(aItemId
, aName
, PR_TRUE
, EmptyCString(),
3103 lastModified
, itemType
));
3110 nsNavBookmarks::OnPageAnnotationRemoved(nsIURI
* aPage
, const nsACString
& aName
)
3117 nsNavBookmarks::OnItemAnnotationRemoved(PRInt64 aItemId
, const nsACString
& aName
)
3119 // GetItemType also ensures that aItemId points to a valid item.
3121 nsresult rv
= GetItemType(aItemId
, &itemType
);
3122 NS_ENSURE_SUCCESS(rv
, rv
);
3124 PRTime lastModified
= PR_Now();
3125 rv
= SetItemDateInternal(GetStatement(mDBSetItemLastModified
),
3126 aItemId
, lastModified
);
3127 NS_ENSURE_SUCCESS(rv
, rv
);
3129 NOTIFY_OBSERVERS(mCanNotify
, mCacheObservers
, mObservers
,
3130 nsINavBookmarkObserver
,
3131 OnItemChanged(aItemId
, aName
, PR_TRUE
, EmptyCString(),
3132 lastModified
, itemType
));
3139 nsNavBookmarks::ItemExists(PRInt64 aItemId
) {
3140 DECLARE_AND_ASSIGN_SCOPED_LAZY_STMT(stmt
, mDBGetItemProperties
);
3141 nsresult rv
= stmt
->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), aItemId
);
3142 NS_ENSURE_SUCCESS(rv
, PR_FALSE
);
3145 rv
= stmt
->ExecuteStep(&hasResult
);
3146 NS_ENSURE_SUCCESS(rv
, PR_FALSE
);