Bug 452317 - FeedConverter.js: QueryInterface should throw NS_ERROR_NO_INTERFACE...
[wine-gecko.git] / toolkit / components / places / src / nsNavBookmarks.cpp
blob8d03685140c0c061ec60fd5e55f6f69796b87fbd
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
13 * License.
15 * The Original Code is Places.
17 * The Initial Developer of the Original Code is
18 * Google Inc.
19 * Portions created by the Initial Developer are Copyright (C) 2005
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
23 * Brian Ryner <bryner@brianryner.com> (original author)
25 * Alternatively, the contents of this file may be used under the terms of
26 * either the GNU General Public License Version 2 or later (the "GPL"), or
27 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
39 #include "nsAppDirectoryServiceDefs.h"
40 #include "nsNavBookmarks.h"
41 #include "nsNavHistory.h"
42 #include "mozStorageHelper.h"
43 #include "nsIServiceManager.h"
44 #include "nsNetUtil.h"
45 #include "nsIDynamicContainer.h"
46 #include "nsUnicharUtils.h"
47 #include "nsFaviconService.h"
48 #include "nsAnnotationService.h"
49 #include "nsPrintfCString.h"
50 #include "nsIUUIDGenerator.h"
51 #include "prprf.h"
52 #include "nsILivemarkService.h"
53 #include "nsPlacesTriggers.h"
54 #include "nsPlacesTables.h"
56 const PRInt32 nsNavBookmarks::kFindBookmarksIndex_ID = 0;
57 const PRInt32 nsNavBookmarks::kFindBookmarksIndex_Type = 1;
58 const PRInt32 nsNavBookmarks::kFindBookmarksIndex_ForeignKey = 2;
59 const PRInt32 nsNavBookmarks::kFindBookmarksIndex_Parent = 3;
60 const PRInt32 nsNavBookmarks::kFindBookmarksIndex_Position = 4;
61 const PRInt32 nsNavBookmarks::kFindBookmarksIndex_Title = 5;
63 // These columns sit to the right of the kGetInfoIndex_* columns.
64 const PRInt32 nsNavBookmarks::kGetChildrenIndex_Position = 11;
65 const PRInt32 nsNavBookmarks::kGetChildrenIndex_Type = 12;
66 const PRInt32 nsNavBookmarks::kGetChildrenIndex_ForeignKey = 13;
68 const PRInt32 nsNavBookmarks::kGetItemPropertiesIndex_ID = 0;
69 const PRInt32 nsNavBookmarks::kGetItemPropertiesIndex_URI = 1;
70 const PRInt32 nsNavBookmarks::kGetItemPropertiesIndex_Title = 2;
71 const PRInt32 nsNavBookmarks::kGetItemPropertiesIndex_Position = 3;
72 const PRInt32 nsNavBookmarks::kGetItemPropertiesIndex_PlaceID = 4;
73 const PRInt32 nsNavBookmarks::kGetItemPropertiesIndex_Parent = 5;
74 const PRInt32 nsNavBookmarks::kGetItemPropertiesIndex_Type = 6;
75 const PRInt32 nsNavBookmarks::kGetItemPropertiesIndex_ServiceContractId = 7;
76 const PRInt32 nsNavBookmarks::kGetItemPropertiesIndex_DateAdded = 8;
77 const PRInt32 nsNavBookmarks::kGetItemPropertiesIndex_LastModified = 9;
79 nsNavBookmarks* nsNavBookmarks::sInstance = nsnull;
81 #define BOOKMARKS_ANNO_PREFIX "bookmarks/"
82 #define BOOKMARKS_TOOLBAR_FOLDER_ANNO NS_LITERAL_CSTRING(BOOKMARKS_ANNO_PREFIX "toolbarFolder")
83 #define GUID_ANNO NS_LITERAL_CSTRING("placesInternal/GUID")
84 #define READ_ONLY_ANNO NS_LITERAL_CSTRING("placesInternal/READ_ONLY")
86 nsNavBookmarks::nsNavBookmarks()
87 : mItemCount(0), mRoot(0), mBookmarksRoot(0), mTagRoot(0), mToolbarFolder(0), mBatchLevel(0),
88 mBatchHasTransaction(PR_FALSE)
90 NS_ASSERTION(!sInstance, "Multiple nsNavBookmarks instances!");
91 sInstance = this;
94 nsNavBookmarks::~nsNavBookmarks()
96 NS_ASSERTION(sInstance == this, "Expected sInstance == this");
97 sInstance = nsnull;
100 NS_IMPL_ISUPPORTS3(nsNavBookmarks,
101 nsINavBookmarksService,
102 nsINavHistoryObserver,
103 nsIAnnotationObserver)
105 nsresult
106 nsNavBookmarks::Init()
108 nsNavHistory *history = History();
109 NS_ENSURE_TRUE(history, NS_ERROR_UNEXPECTED);
110 mozIStorageConnection *dbConn = DBConn();
111 mozStorageTransaction transaction(dbConn, PR_FALSE);
112 nsresult rv;
115 nsCOMPtr<mozIStorageStatement> statement;
116 rv = dbConn->CreateStatement(NS_LITERAL_CSTRING("SELECT id FROM moz_bookmarks WHERE type = ?1 AND parent IS NULL"),
117 getter_AddRefs(statement));
118 NS_ENSURE_SUCCESS(rv, rv);
119 rv = statement->BindInt32Parameter(0, TYPE_FOLDER);
120 NS_ENSURE_SUCCESS(rv, rv);
122 PRBool results;
123 rv = statement->ExecuteStep(&results);
124 NS_ENSURE_SUCCESS(rv, rv);
125 if (results) {
126 mRoot = statement->AsInt64(0);
130 nsCAutoString buffer;
132 nsCOMPtr<nsIStringBundleService> bundleService =
133 do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
134 NS_ENSURE_SUCCESS(rv, rv);
135 rv = bundleService->CreateBundle(
136 "chrome://places/locale/places.properties",
137 getter_AddRefs(mBundle));
138 NS_ENSURE_SUCCESS(rv, rv);
140 // mDBFindURIBookmarks
141 // NOTE: Do not modify the ORDER BY segment of the query, as certain
142 // features depend on it. See bug 398914 for an example.
143 rv = dbConn->CreateStatement(NS_LITERAL_CSTRING(
144 "SELECT a.id "
145 "FROM moz_bookmarks a, moz_places h "
146 "WHERE h.url = ?1 AND a.fk = h.id and a.type = ?2 "
147 "ORDER BY MAX(COALESCE(a.lastModified, 0), a.dateAdded) DESC, a.id DESC"),
148 getter_AddRefs(mDBFindURIBookmarks));
149 NS_ENSURE_SUCCESS(rv, rv);
151 // Construct a result where the first columns exactly match those returned by
152 // mDBGetURLPageInfo, and additionally contains columns for position,
153 // item_child, and folder_child from moz_bookmarks.
154 // Results are kGetInfoIndex_*
156 // mDBGetChildren: select all children of a given folder, sorted by position
157 // This is a LEFT OUTER JOIN with moz_places since folders does not have
158 // a reference into that table.
159 rv = dbConn->CreateStatement(NS_LITERAL_CSTRING(
160 "SELECT h.id, h.url, COALESCE(b.title, h.title), "
161 "h.rev_host, h.visit_count, "
162 SQL_STR_FRAGMENT_MAX_VISIT_DATE( "h.id" )
163 ", f.url, null, b.id, "
164 "b.dateAdded, b.lastModified, "
165 "b.position, b.type, b.fk "
166 "FROM moz_bookmarks b "
167 "LEFT OUTER JOIN moz_places h ON b.fk = h.id "
168 "LEFT OUTER JOIN moz_favicons f ON h.favicon_id = f.id "
169 "WHERE b.parent = ?1 "
170 "ORDER BY b.position"),
171 getter_AddRefs(mDBGetChildren));
172 NS_ENSURE_SUCCESS(rv, rv);
174 // mDBFolderCount: count all of the children of a given folder
175 rv = dbConn->CreateStatement(NS_LITERAL_CSTRING("SELECT COUNT(*) FROM moz_bookmarks WHERE parent = ?1"),
176 getter_AddRefs(mDBFolderCount));
177 NS_ENSURE_SUCCESS(rv, rv);
179 rv = dbConn->CreateStatement(NS_LITERAL_CSTRING("SELECT position FROM moz_bookmarks WHERE id = ?1"),
180 getter_AddRefs(mDBGetItemIndex));
181 NS_ENSURE_SUCCESS(rv, rv);
183 rv = dbConn->CreateStatement(NS_LITERAL_CSTRING("SELECT id, fk, type FROM moz_bookmarks WHERE parent = ?1 AND position = ?2"),
184 getter_AddRefs(mDBGetChildAt));
185 NS_ENSURE_SUCCESS(rv, rv);
187 // get bookmark/folder/separator properties
188 rv = dbConn->CreateStatement(NS_LITERAL_CSTRING(
189 "SELECT b.id, (SELECT url from moz_places WHERE id = b.fk), b.title, b.position, b.fk, b.parent, b.type, b.folder_type, b.dateAdded, b.lastModified "
190 "FROM moz_bookmarks b "
191 "WHERE b.id = ?1"),
192 getter_AddRefs(mDBGetItemProperties));
193 NS_ENSURE_SUCCESS(rv, rv);
195 rv = dbConn->CreateStatement(NS_LITERAL_CSTRING(
196 "SELECT item_id FROM moz_items_annos "
197 "WHERE content = ?1 "
198 "LIMIT 1"),
199 getter_AddRefs(mDBGetItemIdForGUID));
200 NS_ENSURE_SUCCESS(rv, rv);
202 // mDBGetRedirectDestinations
203 // input = page ID, time threshold; output = unique ID input has redirected to
204 rv = dbConn->CreateStatement(NS_LITERAL_CSTRING(
205 "SELECT dest_v.place_id "
206 "FROM moz_historyvisits source_v "
207 "LEFT JOIN moz_historyvisits dest_v ON dest_v.from_visit = source_v.id "
208 "WHERE source_v.place_id = ?1 "
209 "AND source_v.visit_date >= ?2 "
210 "AND (dest_v.visit_type = 5 OR dest_v.visit_type = 6) "
211 "GROUP BY dest_v.place_id"),
212 getter_AddRefs(mDBGetRedirectDestinations));
213 NS_ENSURE_SUCCESS(rv, rv);
215 // mDBInsertBookmark
216 rv = dbConn->CreateStatement(NS_LITERAL_CSTRING("INSERT INTO moz_bookmarks "
217 "(fk, type, parent, position, title, dateAdded) "
218 "VALUES (?1, ?2, ?3, ?4, ?5, ?6)"),
219 getter_AddRefs(mDBInsertBookmark));
220 NS_ENSURE_SUCCESS(rv, rv);
222 // mDBIsBookmarkedInDatabase
223 // Just select position since it's just an int32 and may be faster.
224 // We don't actually care about the data, just whether there is any.
225 rv = DBConn()->CreateStatement(NS_LITERAL_CSTRING(
226 "SELECT position FROM moz_bookmarks WHERE fk = ?1 AND type = ?2"),
227 getter_AddRefs(mDBIsBookmarkedInDatabase));
228 NS_ENSURE_SUCCESS(rv, rv);
230 // mDBGetLastBookmarkID
231 rv = DBConn()->CreateStatement(NS_LITERAL_CSTRING(
232 "SELECT id "
233 "FROM moz_bookmarks "
234 "ORDER BY ROWID DESC "
235 "LIMIT 1"),
236 getter_AddRefs(mDBGetLastBookmarkID));
238 // mDBSetItemDateAdded
239 rv = dbConn->CreateStatement(NS_LITERAL_CSTRING("UPDATE moz_bookmarks SET dateAdded = ?1 WHERE id = ?2"),
240 getter_AddRefs(mDBSetItemDateAdded));
241 NS_ENSURE_SUCCESS(rv, rv);
243 // mDBSetItemLastModified
244 rv = dbConn->CreateStatement(NS_LITERAL_CSTRING("UPDATE moz_bookmarks SET lastModified = ?1 WHERE id = ?2"),
245 getter_AddRefs(mDBSetItemLastModified));
246 NS_ENSURE_SUCCESS(rv, rv);
248 // mDBSetItemIndex
249 rv = DBConn()->CreateStatement(NS_LITERAL_CSTRING("UPDATE moz_bookmarks SET position = ?2 WHERE id = ?1"),
250 getter_AddRefs(mDBSetItemIndex));
251 NS_ENSURE_SUCCESS(rv, rv);
253 FillBookmarksHash();
255 // must be last: This may cause bookmarks to be imported, which will exercise
256 // most of the bookmark system
258 // get keyword text for bookmark id
259 rv = dbConn->CreateStatement(NS_LITERAL_CSTRING(
260 "SELECT k.keyword FROM moz_bookmarks b "
261 "JOIN moz_keywords k ON k.id = b.keyword_id "
262 "WHERE b.id = ?1"),
263 getter_AddRefs(mDBGetKeywordForBookmark));
264 NS_ENSURE_SUCCESS(rv, rv);
265 // get keyword text for URI (must be a bookmarked URI)
266 rv = dbConn->CreateStatement(NS_LITERAL_CSTRING(
267 "SELECT k.keyword "
268 "FROM moz_places p "
269 "JOIN moz_bookmarks b ON b.fk = p.id "
270 "JOIN moz_keywords k ON k.id = b.keyword_id "
271 "WHERE p.url = ?1"),
272 getter_AddRefs(mDBGetKeywordForURI));
273 NS_ENSURE_SUCCESS(rv, rv);
274 // get URI for keyword
275 rv = dbConn->CreateStatement(NS_LITERAL_CSTRING(
276 "SELECT p.url FROM moz_keywords k "
277 "JOIN moz_bookmarks b ON b.keyword_id = k.id "
278 "JOIN moz_places p ON b.fk = p.id "
279 "WHERE k.keyword = ?1"),
280 getter_AddRefs(mDBGetURIForKeyword));
281 NS_ENSURE_SUCCESS(rv, rv);
283 // generate a new GUID base for this session
284 nsCOMPtr<nsIUUIDGenerator> uuidgen = do_GetService("@mozilla.org/uuid-generator;1", &rv);
285 NS_ENSURE_SUCCESS(rv, rv);
286 nsID GUID;
287 rv = uuidgen->GenerateUUIDInPlace(&GUID);
288 NS_ENSURE_SUCCESS(rv, rv);
289 char GUIDChars[NSID_LENGTH];
290 GUID.ToProvidedString(GUIDChars);
291 CopyASCIItoUTF16(GUIDChars, mGUIDBase);
293 rv = InitRoots();
294 NS_ENSURE_SUCCESS(rv, rv);
296 rv = transaction.Commit();
297 NS_ENSURE_SUCCESS(rv, rv);
299 // Temporary migration code for bug 396300
300 nsCOMPtr<mozIStorageStatement> moveUnfiledBookmarks;
301 rv = dbConn->CreateStatement(NS_LITERAL_CSTRING("UPDATE moz_bookmarks SET parent = ?1 WHERE type = ?2 AND parent=?3"),
302 getter_AddRefs(moveUnfiledBookmarks));
303 rv = moveUnfiledBookmarks->BindInt64Parameter(0, mUnfiledRoot);
304 NS_ENSURE_SUCCESS(rv, rv);
305 rv = moveUnfiledBookmarks->BindInt32Parameter(1, TYPE_BOOKMARK);
306 NS_ENSURE_SUCCESS(rv, rv);
307 rv = moveUnfiledBookmarks->BindInt64Parameter(2, mRoot);
308 NS_ENSURE_SUCCESS(rv, rv);
309 rv = moveUnfiledBookmarks->Execute();
310 NS_ENSURE_SUCCESS(rv, rv);
312 nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService();
313 NS_ENSURE_TRUE(annosvc, NS_ERROR_OUT_OF_MEMORY);
315 // allows us to notify on title changes. MUST BE LAST so it is impossible
316 // to fail after this call, or the history service will have a reference to
317 // us and we won't go away.
318 history->AddObserver(this, PR_FALSE);
319 annosvc->AddObserver(this);
321 // DO NOT PUT STUFF HERE that can fail. See observer comment above.
323 return NS_OK;
326 // nsNavBookmarks::InitTables
328 // All commands that initialize the schema of the DB go in here. This is
329 // called from history init before the dummy DB connection is started that
330 // will prevent us from modifying the schema.
332 nsresult // static
333 nsNavBookmarks::InitTables(mozIStorageConnection* aDBConn)
335 PRBool exists;
336 nsresult rv = aDBConn->TableExists(NS_LITERAL_CSTRING("moz_bookmarks"), &exists);
337 NS_ENSURE_SUCCESS(rv, rv);
338 if (! exists) {
339 rv = aDBConn->ExecuteSimpleSQL(CREATE_MOZ_BOOKMARKS);
340 NS_ENSURE_SUCCESS(rv, rv);
342 // This index will make it faster to determine if a given item is
343 // bookmarked (used by history queries and vacuuming, for example).
344 // Making it compound with "type" speeds up type-differentiation
345 // queries, such as expiration and search.
346 rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
347 "CREATE INDEX moz_bookmarks_itemindex ON moz_bookmarks (fk, type)"));
348 NS_ENSURE_SUCCESS(rv, rv);
350 // The most common operation is to find the children given a parent and position.
351 rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
352 "CREATE INDEX moz_bookmarks_parentindex "
353 "ON moz_bookmarks (parent, position)"));
354 NS_ENSURE_SUCCESS(rv, rv);
356 // fast access to lastModified is useful during sync and to get
357 // last modified bookmark title for tags container's children.
358 rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
359 "CREATE INDEX moz_bookmarks_itemlastmodifiedindex "
360 "ON moz_bookmarks (fk, lastModified)"));
361 NS_ENSURE_SUCCESS(rv, rv);
364 // moz_bookmarks_roots
365 rv = aDBConn->TableExists(NS_LITERAL_CSTRING("moz_bookmarks_roots"), &exists);
366 NS_ENSURE_SUCCESS(rv, rv);
367 if (!exists) {
368 rv = aDBConn->ExecuteSimpleSQL(CREATE_MOZ_BOOKMARKS_ROOTS);
369 NS_ENSURE_SUCCESS(rv, rv);
372 // moz_keywords
373 rv = aDBConn->TableExists(NS_LITERAL_CSTRING("moz_keywords"), &exists);
374 NS_ENSURE_SUCCESS(rv, rv);
375 if (! exists) {
376 rv = aDBConn->ExecuteSimpleSQL(CREATE_MOZ_KEYWORDS);
377 NS_ENSURE_SUCCESS(rv, rv);
379 // Create trigger to update as well
380 rv = aDBConn->ExecuteSimpleSQL(CREATE_KEYWORD_VALIDITY_TRIGGER);
381 NS_ENSURE_SUCCESS(rv, rv);
384 return NS_OK;
388 // nsNavBookmarks::InitRoots
390 // This locates and creates if necessary the root items in the bookmarks
391 // folder hierarchy. These items are stored in a special roots table that
392 // maps short predefined names to folder IDs.
394 // Normally, these folders will exist already and we will save their IDs
395 // which are exposed through the bookmark service interface.
397 // If the root does not exist, a folder is created for it and the ID is
398 // saved in the root table. No user-visible name is given to these folders
399 // and they have no parent or other attributes.
401 // These attributes are set when the default_places.html file is imported.
402 // It defines the hierarchy, and has special attributes that tell us when
403 // a folder is one of our well-known roots. We then insert the root in the
404 // defined point in the hierarchy and set its attributes from this.
406 // This should be called as the last part of the init process so that
407 // all of the statements are set up and the service is ready to use.
409 nsresult
410 nsNavBookmarks::InitRoots()
412 nsCOMPtr<mozIStorageStatement> getRootStatement;
413 nsresult rv = DBConn()->CreateStatement(NS_LITERAL_CSTRING("SELECT folder_id FROM moz_bookmarks_roots WHERE root_name = ?1"),
414 getter_AddRefs(getRootStatement));
415 NS_ENSURE_SUCCESS(rv, rv);
417 PRBool createdPlacesRoot = PR_FALSE;
418 rv = CreateRoot(getRootStatement, NS_LITERAL_CSTRING("places"), &mRoot, 0, &createdPlacesRoot);
419 NS_ENSURE_SUCCESS(rv, rv);
421 getRootStatement->Reset();
422 rv = CreateRoot(getRootStatement, NS_LITERAL_CSTRING("menu"), &mBookmarksRoot, mRoot, nsnull);
423 NS_ENSURE_SUCCESS(rv, rv);
425 PRBool createdToolbarFolder;
426 getRootStatement->Reset();
427 rv = CreateRoot(getRootStatement, NS_LITERAL_CSTRING("toolbar"), &mToolbarFolder, mRoot, &createdToolbarFolder);
428 NS_ENSURE_SUCCESS(rv, rv);
430 // Once toolbar was not a root, we may need to move over the items and
431 // delete the custom folder
432 if (!createdPlacesRoot && createdToolbarFolder) {
433 nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService();
434 NS_ENSURE_TRUE(annosvc, NS_ERROR_OUT_OF_MEMORY);
436 nsTArray<PRInt64> folders;
437 annosvc->GetItemsWithAnnotationTArray(BOOKMARKS_TOOLBAR_FOLDER_ANNO,
438 &folders);
439 if (folders.Length() > 0) {
440 nsCOMPtr<mozIStorageStatement> moveItems;
441 rv = DBConn()->CreateStatement(NS_LITERAL_CSTRING("UPDATE moz_bookmarks SET parent = ?1 WHERE parent=?2"),
442 getter_AddRefs(moveItems));
443 rv = moveItems->BindInt64Parameter(0, mToolbarFolder);
444 NS_ENSURE_SUCCESS(rv, rv);
445 rv = moveItems->BindInt64Parameter(1, folders[0]);
446 NS_ENSURE_SUCCESS(rv, rv);
447 rv = moveItems->Execute();
448 NS_ENSURE_SUCCESS(rv, rv);
449 rv = RemoveFolder(folders[0]);
450 NS_ENSURE_SUCCESS(rv, rv);
454 getRootStatement->Reset();
455 rv = CreateRoot(getRootStatement, NS_LITERAL_CSTRING("tags"), &mTagRoot, mRoot, nsnull);
456 NS_ENSURE_SUCCESS(rv, rv);
458 getRootStatement->Reset();
459 rv = CreateRoot(getRootStatement, NS_LITERAL_CSTRING("unfiled"), &mUnfiledRoot, mRoot, nsnull);
460 NS_ENSURE_SUCCESS(rv, rv);
462 // Set titles for special folders
463 // We cannot rely on createdPlacesRoot due to Fx3beta->final migration path
464 nsCOMPtr<nsIPrefService> prefService =
465 do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
466 NS_ENSURE_SUCCESS(rv, rv);
468 nsCOMPtr<nsIPrefBranch> prefBranch;
469 rv = prefService->GetBranch("", getter_AddRefs(prefBranch));
470 NS_ENSURE_SUCCESS(rv, rv);
472 PRBool importDefaults = PR_TRUE;
473 rv = prefBranch->GetBoolPref("browser.places.importDefaults", &importDefaults);
474 if (NS_FAILED(rv) || importDefaults) {
475 rv = InitDefaults();
476 NS_ENSURE_SUCCESS(rv, rv);
477 rv = prefBranch->SetBoolPref("browser.places.importDefaults", PR_FALSE);
478 NS_ENSURE_SUCCESS(rv, rv);
481 return NS_OK;
484 // nsNavBookmarks::InitDefaults
486 // Initializes default bookmarks and containers.
487 // Pulls from places.propertes for l10n.
488 // Replaces the old default_places.html file.
489 nsresult
490 nsNavBookmarks::InitDefaults()
492 // Bookmarks Menu
493 nsXPIDLString bookmarksTitle;
494 nsresult rv = mBundle->GetStringFromName(NS_LITERAL_STRING("BookmarksMenuFolderTitle").get(),
495 getter_Copies(bookmarksTitle));
496 NS_ENSURE_SUCCESS(rv, rv);
497 rv = SetItemTitle(mBookmarksRoot, NS_ConvertUTF16toUTF8(bookmarksTitle));
498 NS_ENSURE_SUCCESS(rv, rv);
500 // Bookmarks Toolbar
501 nsXPIDLString toolbarTitle;
502 rv = mBundle->GetStringFromName(NS_LITERAL_STRING("BookmarksToolbarFolderTitle").get(),
503 getter_Copies(toolbarTitle));
504 NS_ENSURE_SUCCESS(rv, rv);
505 rv = SetItemTitle(mToolbarFolder, NS_ConvertUTF16toUTF8(toolbarTitle));
506 NS_ENSURE_SUCCESS(rv, rv);
508 // Unsorted Bookmarks
509 nsXPIDLString unfiledTitle;
510 rv = mBundle->GetStringFromName(NS_LITERAL_STRING("UnsortedBookmarksFolderTitle").get(),
511 getter_Copies(unfiledTitle));
512 NS_ENSURE_SUCCESS(rv, rv);
513 rv = SetItemTitle(mUnfiledRoot, NS_ConvertUTF16toUTF8(unfiledTitle));
514 NS_ENSURE_SUCCESS(rv, rv);
516 // Tags
517 nsXPIDLString tagsTitle;
518 rv = mBundle->GetStringFromName(NS_LITERAL_STRING("TagsFolderTitle").get(),
519 getter_Copies(tagsTitle));
520 NS_ENSURE_SUCCESS(rv, rv);
521 rv = SetItemTitle(mTagRoot, NS_ConvertUTF16toUTF8(tagsTitle));
522 NS_ENSURE_SUCCESS(rv, rv);
524 return NS_OK;
527 // nsNavBookmarks::CreateRoot
529 // This gets or creates a root folder of the given type. aWasCreated
530 // (optional) is true if the folder had to be created, false if we just used
531 // an old one. The statement that gets a folder ID from a root name is
532 // passed in so the DB only needs to parse the statement once, and we don't
533 // have to have a global for this. Creation is less optimized because it
534 // happens rarely.
536 nsresult
537 nsNavBookmarks::CreateRoot(mozIStorageStatement* aGetRootStatement,
538 const nsCString& name, PRInt64* aID,
539 PRInt64 aParentID, PRBool* aWasCreated)
541 PRBool hasResult = PR_FALSE;
542 nsresult rv = aGetRootStatement->BindUTF8StringParameter(0, name);
543 NS_ENSURE_SUCCESS(rv, rv);
544 rv = aGetRootStatement->ExecuteStep(&hasResult);
545 NS_ENSURE_SUCCESS(rv, rv);
546 if (hasResult) {
547 if (aWasCreated)
548 *aWasCreated = PR_FALSE;
549 rv = aGetRootStatement->GetInt64(0, aID);
550 NS_ENSURE_SUCCESS(rv, rv);
551 NS_ASSERTION(*aID != 0, "Root is 0 for some reason, folders can't have 0 ID");
552 return NS_OK;
554 if (aWasCreated)
555 *aWasCreated = PR_TRUE;
557 // create folder with no name or attributes
558 nsCOMPtr<mozIStorageStatement> insertStatement;
559 rv = CreateFolder(aParentID, EmptyCString(), nsINavBookmarksService::DEFAULT_INDEX, aID);
560 NS_ENSURE_SUCCESS(rv, rv);
562 // save root ID
563 rv = DBConn()->CreateStatement(NS_LITERAL_CSTRING("INSERT INTO moz_bookmarks_roots (root_name, folder_id) VALUES (?1, ?2)"),
564 getter_AddRefs(insertStatement));
565 NS_ENSURE_SUCCESS(rv, rv);
566 rv = insertStatement->BindUTF8StringParameter(0, name);
567 NS_ENSURE_SUCCESS(rv, rv);
568 rv = insertStatement->BindInt64Parameter(1, *aID);
569 NS_ENSURE_SUCCESS(rv, rv);
570 rv = insertStatement->Execute();
571 NS_ENSURE_SUCCESS(rv, rv);
573 return NS_OK;
577 // nsNavBookmarks::FillBookmarksHash
579 // This initializes the bookmarks hashtable that tells us which bookmark
580 // a given URI redirects to. This hashtable includes all URIs that
581 // redirect to bookmarks.
583 // This is called from the bookmark init function and so is wrapped
584 // in that transaction (for better performance).
586 nsresult
587 nsNavBookmarks::FillBookmarksHash()
589 PRBool hasMore;
591 // first init the hashtable
592 NS_ENSURE_TRUE(mBookmarksHash.Init(1024), NS_ERROR_OUT_OF_MEMORY);
594 // first populate the table with all bookmarks
595 nsCOMPtr<mozIStorageStatement> statement;
596 nsresult rv = DBConn()->CreateStatement(NS_LITERAL_CSTRING(
597 "SELECT h.id "
598 "FROM moz_bookmarks b "
599 "LEFT JOIN moz_places h ON b.fk = h.id where b.type = ?1"),
600 getter_AddRefs(statement));
601 NS_ENSURE_SUCCESS(rv, rv);
602 rv = statement->BindInt32Parameter(0, TYPE_BOOKMARK);
603 NS_ENSURE_SUCCESS(rv, rv);
604 while (NS_SUCCEEDED(statement->ExecuteStep(&hasMore)) && hasMore) {
605 PRInt64 pageID;
606 rv = statement->GetInt64(0, &pageID);
607 NS_ENSURE_TRUE(mBookmarksHash.Put(pageID, pageID), NS_ERROR_OUT_OF_MEMORY);
610 // Find all pages h2 that have been redirected to from a bookmarked URI:
611 // bookmarked -> url (h1) url (h2)
612 // | ^
613 // . |
614 // visit (v1) -> destination visit (v2)
615 // This should catch most redirects, which are only one level. More levels of
616 // redirection will be handled separately.
617 rv = DBConn()->CreateStatement(NS_LITERAL_CSTRING(
618 "SELECT v1.place_id, v2.place_id "
619 "FROM moz_bookmarks b "
620 "LEFT JOIN moz_historyvisits v1 on b.fk = v1.place_id "
621 "LEFT JOIN moz_historyvisits v2 on v2.from_visit = v1.id "
622 "WHERE b.fk IS NOT NULL AND b.type = ?1 "
623 "AND v2.visit_type = 5 OR v2.visit_type = 6 " // perm. or temp. RDRs
624 "GROUP BY v2.place_id"),
625 getter_AddRefs(statement));
626 NS_ENSURE_SUCCESS(rv, rv);
627 rv = statement->BindInt64Parameter(0, TYPE_BOOKMARK);
628 NS_ENSURE_SUCCESS(rv, rv);
629 while (NS_SUCCEEDED(statement->ExecuteStep(&hasMore)) && hasMore) {
630 PRInt64 fromId, toId;
631 statement->GetInt64(0, &fromId);
632 statement->GetInt64(1, &toId);
634 NS_ENSURE_TRUE(mBookmarksHash.Put(toId, fromId), NS_ERROR_OUT_OF_MEMORY);
636 // handle redirects deeper than one level
637 rv = RecursiveAddBookmarkHash(fromId, toId, 0);
638 NS_ENSURE_SUCCESS(rv, rv);
641 return NS_OK;
645 // nsNavBookmarks::AddBookmarkToHash
647 // Given a bookmark that was potentially added, this goes through all
648 // redirects that this page may have resulted in and adds them to our hash.
649 // Note that this takes the ID of the URL in the history system, which we
650 // generally have when calling this function and which makes it faster.
652 // For better performance, this call should be in a DB transaction.
654 // @see RecursiveAddBookmarkHash
656 nsresult
657 nsNavBookmarks::AddBookmarkToHash(PRInt64 aPlaceId, PRTime aMinTime)
659 // this function might be called before our hashtable is initialized (for
660 // example, on history import), just ignore these, we'll pick up the add when
661 // the hashtable is initialized later
662 if (! mBookmarksHash.IsInitialized())
663 return NS_OK;
664 if (! mBookmarksHash.Put(aPlaceId, aPlaceId))
665 return NS_ERROR_OUT_OF_MEMORY;
666 return RecursiveAddBookmarkHash(aPlaceId, aPlaceId, aMinTime);
670 // nsNavBookmkars::RecursiveAddBookmarkHash
672 // Used to add a new level of redirect information to the bookmark hash.
673 // Given a source bookmark 'aBookmark' and 'aCurrentSource' that has already
674 // been added to the hashtable, this will add all redirect destinations of
675 // 'aCurrentSource'. Will call itself recursively to walk down the chain.
677 // 'aMinTime' is the minimum time to consider visits from. Visits previous
678 // to this will not be considered. This allows the search to be much more
679 // efficient if you know something happened recently. Use 0 for the min time
680 // to search all history for redirects.
682 nsresult
683 nsNavBookmarks::RecursiveAddBookmarkHash(PRInt64 aPlaceID,
684 PRInt64 aCurrentSource,
685 PRTime aMinTime)
687 nsresult rv;
688 nsTArray<PRInt64> found;
690 // scope for the DB statement. The statement must be reset by the time we
691 // recursively call ourselves again, because our recursive call will use the
692 // same statement.
694 mozStorageStatementScoper scoper(mDBGetRedirectDestinations);
695 rv = mDBGetRedirectDestinations->BindInt64Parameter(0, aCurrentSource);
696 NS_ENSURE_SUCCESS(rv, rv);
697 rv = mDBGetRedirectDestinations->BindInt64Parameter(1, aMinTime);
698 NS_ENSURE_SUCCESS(rv, rv);
700 PRBool hasMore;
701 while (NS_SUCCEEDED(mDBGetRedirectDestinations->ExecuteStep(&hasMore)) &&
702 hasMore) {
704 // add this newly found redirect destination to the hashtable
705 PRInt64 curID;
706 rv = mDBGetRedirectDestinations->GetInt64(0, &curID);
707 NS_ENSURE_SUCCESS(rv, rv);
709 // It is very important we ignore anything already in our hashtable. It
710 // is actually pretty common to get loops of redirects. For example,
711 // a restricted page will redirect you to a login page, which will
712 // redirect you to the restricted page again with the proper cookie.
713 PRInt64 alreadyExistingOne;
714 if (mBookmarksHash.Get(curID, &alreadyExistingOne))
715 continue;
717 if (! mBookmarksHash.Put(curID, aPlaceID))
718 return NS_ERROR_OUT_OF_MEMORY;
720 // save for recursion later
721 found.AppendElement(curID);
725 // recurse on each found item now that we're done with the statement
726 for (PRUint32 i = 0; i < found.Length(); i ++) {
727 rv = RecursiveAddBookmarkHash(aPlaceID, found[i], aMinTime);
728 NS_ENSURE_SUCCESS(rv, rv);
731 return NS_OK;
735 // nsNavBookmarks::UpdateBookmarkHashOnRemove
737 // Call this when a bookmark is removed. It will see if the bookmark still
738 // exists anywhere in the system, and, if not, remove all references to it
739 // in the bookmark hashtable.
741 // The callback takes a pointer to what bookmark is being removed (as
742 // an Int64 history page ID) as the userArg and removes all redirect
743 // destinations that reference it.
745 PR_STATIC_CALLBACK(PLDHashOperator)
746 RemoveBookmarkHashCallback(nsTrimInt64HashKey::KeyType aKey,
747 PRInt64& aPlaceId, void* aUserArg)
749 const PRInt64* removeThisOne = reinterpret_cast<const PRInt64*>(aUserArg);
750 if (aPlaceId == *removeThisOne)
751 return PL_DHASH_REMOVE;
752 return PL_DHASH_NEXT;
754 nsresult
755 nsNavBookmarks::UpdateBookmarkHashOnRemove(PRInt64 aPlaceId)
757 // note we have to use the DB version here since the hashtable may be
758 // out-of-date
759 PRBool inDB;
760 nsresult rv = IsBookmarkedInDatabase(aPlaceId, &inDB);
761 NS_ENSURE_SUCCESS(rv, rv);
762 if (inDB)
763 return NS_OK; // bookmark still exists, don't need to update hashtable
765 // remove it
766 mBookmarksHash.Enumerate(RemoveBookmarkHashCallback,
767 reinterpret_cast<void*>(&aPlaceId));
768 return NS_OK;
772 // nsNavBookmarks::IsBookmarkedInDatabase
774 // This checks to see if the specified URI is actually bookmarked, bypassing
775 // our hashtable. Normal IsBookmarked checks just use the hashtable.
777 nsresult
778 nsNavBookmarks::IsBookmarkedInDatabase(PRInt64 aPlaceId,
779 PRBool *aIsBookmarked)
781 mozStorageStatementScoper scope(mDBIsBookmarkedInDatabase);
782 nsresult rv = mDBIsBookmarkedInDatabase->BindInt64Parameter(0, aPlaceId);
783 NS_ENSURE_SUCCESS(rv, rv);
785 rv = mDBIsBookmarkedInDatabase->BindInt32Parameter(1, TYPE_BOOKMARK);
786 NS_ENSURE_SUCCESS(rv, rv);
788 return mDBIsBookmarkedInDatabase->ExecuteStep(aIsBookmarked);
792 nsresult
793 nsNavBookmarks::AdjustIndices(PRInt64 aFolder,
794 PRInt32 aStartIndex, PRInt32 aEndIndex,
795 PRInt32 aDelta)
797 NS_ASSERTION(aStartIndex <= aEndIndex, "start index must be <= end index");
799 nsCAutoString buffer;
800 buffer.AssignLiteral("UPDATE moz_bookmarks SET position = position + ");
801 buffer.AppendInt(aDelta);
802 buffer.AppendLiteral(" WHERE parent = ");
803 buffer.AppendInt(aFolder);
805 if (aStartIndex != 0) {
806 buffer.AppendLiteral(" AND position >= ");
807 buffer.AppendInt(aStartIndex);
809 if (aEndIndex != PR_INT32_MAX) {
810 buffer.AppendLiteral(" AND position <= ");
811 buffer.AppendInt(aEndIndex);
814 nsresult rv = DBConn()->ExecuteSimpleSQL(buffer);
815 NS_ENSURE_SUCCESS(rv, rv);
817 return NS_OK;
820 NS_IMETHODIMP
821 nsNavBookmarks::GetPlacesRoot(PRInt64 *aRoot)
823 *aRoot = mRoot;
824 return NS_OK;
827 NS_IMETHODIMP
828 nsNavBookmarks::GetBookmarksMenuFolder(PRInt64 *aRoot)
830 *aRoot = mBookmarksRoot;
831 return NS_OK;
834 NS_IMETHODIMP
835 nsNavBookmarks::GetToolbarFolder(PRInt64 *aFolderId)
837 *aFolderId = mToolbarFolder;
838 return NS_OK;
841 NS_IMETHODIMP
842 nsNavBookmarks::GetTagsFolder(PRInt64 *aRoot)
844 *aRoot = mTagRoot;
845 return NS_OK;
848 NS_IMETHODIMP
849 nsNavBookmarks::GetUnfiledBookmarksFolder(PRInt64 *aRoot)
851 *aRoot = mUnfiledRoot;
852 return NS_OK;
855 NS_IMETHODIMP
856 nsNavBookmarks::InsertBookmark(PRInt64 aFolder, nsIURI *aItem, PRInt32 aIndex,
857 const nsACString& aTitle,
858 PRInt64 *aNewBookmarkId)
860 // You can pass -1 to indicate append, but no other negative number is allowed
861 if (aIndex < nsINavBookmarksService::DEFAULT_INDEX)
862 return NS_ERROR_INVALID_ARG;
863 NS_ENSURE_ARG_POINTER(aNewBookmarkId);
865 mozIStorageConnection *dbConn = DBConn();
866 mozStorageTransaction transaction(dbConn, PR_FALSE);
868 // This is really a place ID
869 PRInt64 childID;
870 nsresult rv = History()->GetUrlIdFor(aItem, &childID, PR_TRUE);
871 NS_ENSURE_SUCCESS(rv, rv);
873 PRInt32 index;
874 if (aIndex == nsINavBookmarksService::DEFAULT_INDEX) {
875 index = FolderCount(aFolder);
876 } else {
877 index = aIndex;
878 rv = AdjustIndices(aFolder, index, PR_INT32_MAX, 1);
879 NS_ENSURE_SUCCESS(rv, rv);
883 mozStorageStatementScoper scope(mDBInsertBookmark);
884 rv = mDBInsertBookmark->BindInt64Parameter(0, childID);
885 NS_ENSURE_SUCCESS(rv, rv);
886 rv = mDBInsertBookmark->BindInt32Parameter(1, TYPE_BOOKMARK);
887 NS_ENSURE_SUCCESS(rv, rv);
888 rv = mDBInsertBookmark->BindInt64Parameter(2, aFolder);
889 NS_ENSURE_SUCCESS(rv, rv);
890 rv = mDBInsertBookmark->BindInt32Parameter(3, index);
891 NS_ENSURE_SUCCESS(rv, rv);
893 if (aTitle.IsVoid())
894 rv = mDBInsertBookmark->BindNullParameter(4);
895 else
896 rv = mDBInsertBookmark->BindUTF8StringParameter(4, aTitle);
897 NS_ENSURE_SUCCESS(rv, rv);
899 rv = mDBInsertBookmark->BindInt64Parameter(5, PR_Now());
900 NS_ENSURE_SUCCESS(rv, rv);
902 rv = mDBInsertBookmark->Execute();
903 NS_ENSURE_SUCCESS(rv, rv);
906 // get row id of the new bookmark
907 PRInt64 rowId;
909 mozStorageStatementScoper scoper(mDBGetLastBookmarkID);
911 PRBool hasResult;
912 rv = mDBGetLastBookmarkID->ExecuteStep(&hasResult);
913 NS_ENSURE_SUCCESS(rv, rv);
914 NS_ASSERTION(hasResult, "hasResult is false but the call succeeded?");
915 rowId = *aNewBookmarkId = mDBGetLastBookmarkID->AsInt64(0);
918 // XXX
919 // 0n import / fx 2 migration, is the frecency work going to slow us down?
920 // We might want to skip this stuff, as well as the frecency work
921 // caused by GetUrlIdFor() which calls InternalAddNewPage().
922 // If we do skip this, after import, we will
923 // need to call FixInvalidFrecenciesForExcludedPlaces().
924 // We might need to call it anyways, if items aren't properly annotated
925 // as livemarks feeds yet.
927 nsCAutoString url;
928 rv = aItem->GetSpec(url);
929 NS_ENSURE_SUCCESS(rv, rv);
931 // prevent place: queries from showing up in the URL bar autocomplete results
932 PRBool isBookmark = !IsQueryURI(url);
934 if (isBookmark) {
935 // if it is a livemark item (the parent is a livemark),
936 // we pass in false for isBookmark. otherwise, unvisited livemark
937 // items will appear in URL autocomplete before we visit them.
938 PRBool parentIsLivemark;
939 nsCOMPtr<nsILivemarkService> lms =
940 do_GetService(NS_LIVEMARKSERVICE_CONTRACTID, &rv);
941 NS_ENSURE_SUCCESS(rv, rv);
943 rv = lms->IsLivemark(aFolder, &parentIsLivemark);
944 NS_ENSURE_SUCCESS(rv, rv);
946 isBookmark = !parentIsLivemark;
949 // when we created the moz_place entry for the new bookmark
950 // (a side effect of calling GetUrlIdFor()) frecency -1;
951 // now we re-calculate the frecency for this moz_place entry.
952 rv = History()->UpdateFrecency(childID, isBookmark);
953 NS_ENSURE_SUCCESS(rv, rv);
955 rv = SetItemDateInternal(mDBSetItemLastModified, aFolder, PR_Now());
956 NS_ENSURE_SUCCESS(rv, rv);
958 rv = transaction.Commit();
959 NS_ENSURE_SUCCESS(rv, rv);
961 AddBookmarkToHash(childID, 0);
963 ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver,
964 OnItemAdded(rowId, aFolder, index))
966 // If the bookmark has been added to a tag container, notify all
967 // bookmark-folder result nodes which contain a bookmark for the new
968 // bookmark's url
969 PRInt64 grandParent;
970 rv = GetFolderIdForItem(aFolder, &grandParent);
971 NS_ENSURE_SUCCESS(rv, rv);
972 if (grandParent == mTagRoot) {
973 // query for all bookmarks for that URI, notify for each
974 nsTArray<PRInt64> bookmarks;
976 rv = GetBookmarkIdsForURITArray(aItem, &bookmarks);
977 NS_ENSURE_SUCCESS(rv, rv);
979 if (bookmarks.Length()) {
980 for (PRUint32 i = 0; i < bookmarks.Length(); i++) {
981 ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver,
982 OnItemChanged(bookmarks[i], NS_LITERAL_CSTRING("tags"),
983 PR_FALSE, EmptyCString()))
987 return NS_OK;
990 NS_IMETHODIMP
991 nsNavBookmarks::RemoveItem(PRInt64 aItemId)
993 nsresult rv;
994 PRInt32 childIndex;
995 PRInt64 placeId, folderId;
996 PRInt32 itemType;
997 nsCAutoString buffer;
998 nsCAutoString spec;
1000 { // scoping to ensure the statement gets reset
1001 mozStorageStatementScoper scope(mDBGetItemProperties);
1002 mDBGetItemProperties->BindInt64Parameter(0, aItemId);
1004 PRBool results;
1005 rv = mDBGetItemProperties->ExecuteStep(&results);
1006 NS_ENSURE_SUCCESS(rv, rv);
1008 if (!results)
1009 return NS_ERROR_INVALID_ARG; // invalid bookmark id
1011 childIndex = mDBGetItemProperties->AsInt32(kGetItemPropertiesIndex_Position);
1012 placeId = mDBGetItemProperties->AsInt64(kGetItemPropertiesIndex_PlaceID);
1013 folderId = mDBGetItemProperties->AsInt64(kGetItemPropertiesIndex_Parent);
1014 itemType = mDBGetItemProperties->AsInt32(kGetItemPropertiesIndex_Type);
1015 if (itemType == TYPE_BOOKMARK) {
1016 rv = mDBGetItemProperties->GetUTF8String(kGetItemPropertiesIndex_URI, spec);
1017 NS_ENSURE_SUCCESS(rv, rv);
1021 if (itemType == TYPE_FOLDER) {
1022 rv = RemoveFolder(aItemId);
1023 NS_ENSURE_SUCCESS(rv, rv);
1024 return NS_OK;
1027 mozIStorageConnection *dbConn = DBConn();
1028 mozStorageTransaction transaction(dbConn, PR_FALSE);
1030 // First, remove item annotations
1031 nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService();
1032 NS_ENSURE_TRUE(annosvc, NS_ERROR_OUT_OF_MEMORY);
1033 rv = annosvc->RemoveItemAnnotations(aItemId);
1034 NS_ENSURE_SUCCESS(rv, rv);
1036 buffer.AssignLiteral("DELETE FROM moz_bookmarks WHERE id = ");
1037 buffer.AppendInt(aItemId);
1039 rv = dbConn->ExecuteSimpleSQL(buffer);
1040 NS_ENSURE_SUCCESS(rv, rv);
1042 if (childIndex != -1) {
1043 rv = AdjustIndices(folderId, childIndex + 1, PR_INT32_MAX, -1);
1044 NS_ENSURE_SUCCESS(rv, rv);
1047 rv = SetItemDateInternal(mDBSetItemLastModified, folderId, PR_Now());
1048 NS_ENSURE_SUCCESS(rv, rv);
1050 rv = transaction.Commit();
1051 NS_ENSURE_SUCCESS(rv, rv);
1053 rv = UpdateBookmarkHashOnRemove(placeId);
1054 NS_ENSURE_SUCCESS(rv, rv);
1056 // XXX is this too expensive when updating livemarks?
1057 // UpdateBookmarkHashOnRemove() does a sanity check using
1058 // IsBookmarkedInDatabase(), so it might not have actually
1059 // removed the bookmark. should we have a boolean out param
1060 // for if we actually removed it, and use that to decide if we call
1061 // UpdateFrecency() and the rest of this code?
1062 if (itemType == TYPE_BOOKMARK) {
1063 rv = History()->UpdateFrecency(placeId, PR_FALSE /* isBookmark */);
1064 NS_ENSURE_SUCCESS(rv, rv);
1067 ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver,
1068 OnItemRemoved(aItemId, folderId, childIndex))
1070 if (itemType == TYPE_BOOKMARK) {
1071 // If the removed bookmark was a child of a tag container, notify all
1072 // bookmark-folder result nodes which contain a bookmark for the removed
1073 // bookmark's url.
1074 PRInt64 grandParent;
1075 rv = GetFolderIdForItem(folderId, &grandParent);
1076 NS_ENSURE_SUCCESS(rv, rv);
1077 if (grandParent == mTagRoot) {
1078 nsCOMPtr<nsIURI> uri;
1079 rv = NS_NewURI(getter_AddRefs(uri), spec);
1080 NS_ENSURE_SUCCESS(rv, rv);
1081 nsTArray<PRInt64> bookmarks;
1083 rv = GetBookmarkIdsForURITArray(uri, &bookmarks);
1084 NS_ENSURE_SUCCESS(rv, rv);
1086 if (bookmarks.Length()) {
1087 for (PRUint32 i = 0; i < bookmarks.Length(); i++) {
1088 ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver,
1089 OnItemChanged(bookmarks[i], NS_LITERAL_CSTRING("tags"),
1090 PR_FALSE, EmptyCString()))
1095 return NS_OK;
1099 NS_IMETHODIMP
1100 nsNavBookmarks::CreateFolder(PRInt64 aParent, const nsACString &aName,
1101 PRInt32 aIndex, PRInt64 *aNewFolder)
1103 // CreateContainerWithID returns the index of the new folder, but that's not
1104 // used here. To avoid any risk of corrupting data should this function
1105 // be changed, we'll use a local variable to hold it. The PR_TRUE argument
1106 // will cause notifications to be sent to bookmark observers.
1107 PRInt32 localIndex = aIndex;
1108 return CreateContainerWithID(-1, aParent, aName, EmptyString(), PR_TRUE,
1109 &localIndex, aNewFolder);
1112 NS_IMETHODIMP
1113 nsNavBookmarks::CreateDynamicContainer(PRInt64 aParent, const nsACString &aName,
1114 const nsAString &aContractId,
1115 PRInt32 aIndex,
1116 PRInt64 *aNewFolder)
1118 if (aContractId.IsEmpty())
1119 return NS_ERROR_INVALID_ARG;
1121 return CreateContainerWithID(-1, aParent, aName, aContractId, PR_FALSE,
1122 &aIndex, aNewFolder);
1125 NS_IMETHODIMP
1126 nsNavBookmarks::GetFolderReadonly(PRInt64 aFolder, PRBool *aResult)
1128 nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService();
1129 NS_ENSURE_TRUE(annosvc, NS_ERROR_OUT_OF_MEMORY);
1130 return annosvc->ItemHasAnnotation(aFolder, READ_ONLY_ANNO, aResult);
1133 NS_IMETHODIMP
1134 nsNavBookmarks::SetFolderReadonly(PRInt64 aFolder, PRBool aReadOnly)
1136 nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService();
1137 NS_ENSURE_TRUE(annosvc, NS_ERROR_OUT_OF_MEMORY);
1138 if (aReadOnly) {
1139 return annosvc->SetItemAnnotationInt32(aFolder, READ_ONLY_ANNO, 1,
1141 nsAnnotationService::EXPIRE_NEVER);
1143 else {
1144 PRBool hasAnno;
1145 nsresult rv = annosvc->ItemHasAnnotation(aFolder, READ_ONLY_ANNO, &hasAnno);
1146 NS_ENSURE_SUCCESS(rv, rv);
1147 if (hasAnno)
1148 return annosvc->RemoveItemAnnotation(aFolder, READ_ONLY_ANNO);
1150 return NS_OK;
1153 nsresult
1154 nsNavBookmarks::CreateContainerWithID(PRInt64 aItemId, PRInt64 aParent,
1155 const nsACString& aName,
1156 const nsAString& aContractId,
1157 PRBool aIsBookmarkFolder,
1158 PRInt32* aIndex, PRInt64* aNewFolder)
1160 // You can pass -1 to indicate append, but no other negative number is allowed
1161 if (*aIndex < -1)
1162 return NS_ERROR_INVALID_ARG;
1164 mozIStorageConnection *dbConn = DBConn();
1165 mozStorageTransaction transaction(dbConn, PR_FALSE);
1167 PRInt32 index;
1168 nsresult rv;
1169 if (*aIndex == nsINavBookmarksService::DEFAULT_INDEX) {
1170 index = FolderCount(aParent);
1171 } else {
1172 index = *aIndex;
1173 rv = AdjustIndices(aParent, index, PR_INT32_MAX, 1);
1174 NS_ENSURE_SUCCESS(rv, rv);
1177 nsCOMPtr<mozIStorageStatement> statement;
1178 if (aItemId == -1) {
1179 rv = dbConn->CreateStatement(NS_LITERAL_CSTRING("INSERT INTO moz_bookmarks (title, type, parent, position, folder_type, dateAdded) VALUES (?1, ?2, ?3, ?4, ?5, ?6)"),
1180 getter_AddRefs(statement));
1181 NS_ENSURE_SUCCESS(rv, rv);
1183 else {
1184 rv = dbConn->CreateStatement(NS_LITERAL_CSTRING("INSERT INTO moz_bookmarks (id, title, type, parent, position, folder_type, dateAdded) VALUES (?7, ?1, ?2, ?3, ?4, ?5, ?6)"),
1185 getter_AddRefs(statement));
1186 NS_ENSURE_SUCCESS(rv, rv);
1188 rv = statement->BindInt64Parameter(6, aItemId);
1189 NS_ENSURE_SUCCESS(rv, rv);
1192 rv = statement->BindUTF8StringParameter(0, aName);
1193 NS_ENSURE_SUCCESS(rv, rv);
1195 PRInt32 containerType =
1196 aIsBookmarkFolder ? TYPE_FOLDER : TYPE_DYNAMIC_CONTAINER;
1198 rv = statement->BindInt32Parameter(1, containerType);
1199 NS_ENSURE_SUCCESS(rv, rv);
1200 rv = statement->BindInt64Parameter(2, aParent);
1201 NS_ENSURE_SUCCESS(rv, rv);
1202 rv = statement->BindInt32Parameter(3, index);
1203 NS_ENSURE_SUCCESS(rv, rv);
1204 rv = statement->BindStringParameter(4, aContractId);
1205 NS_ENSURE_SUCCESS(rv, rv);
1206 rv = statement->BindInt64Parameter(5, PR_Now());
1207 NS_ENSURE_SUCCESS(rv, rv);
1209 rv = statement->Execute();
1210 NS_ENSURE_SUCCESS(rv, rv);
1212 PRInt64 id;
1213 if (aItemId == -1) {
1214 mozStorageStatementScoper scoper(mDBGetLastBookmarkID);
1216 PRBool hasResult;
1217 rv = mDBGetLastBookmarkID->ExecuteStep(&hasResult);
1218 NS_ENSURE_SUCCESS(rv, rv);
1219 NS_ASSERTION(hasResult, "hasResult is false but the call succeeded?");
1220 id = mDBGetLastBookmarkID->AsInt64(0);
1222 else {
1223 id = aItemId;
1226 rv = SetItemDateInternal(mDBSetItemLastModified, aParent, PR_Now());
1227 NS_ENSURE_SUCCESS(rv, rv);
1229 rv = transaction.Commit();
1230 NS_ENSURE_SUCCESS(rv, rv);
1232 ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver,
1233 OnItemAdded(id, aParent, index))
1235 *aIndex = index;
1236 *aNewFolder = id;
1237 return NS_OK;
1240 NS_IMETHODIMP
1241 nsNavBookmarks::InsertSeparator(PRInt64 aParent, PRInt32 aIndex,
1242 PRInt64* aNewItemId)
1244 // You can pass -1 to indicate append, but no other negative number is
1245 // allowed
1246 if (aIndex < -1)
1247 return NS_ERROR_INVALID_ARG;
1249 mozIStorageConnection *dbConn = DBConn();
1250 mozStorageTransaction transaction(dbConn, PR_FALSE);
1252 PRInt32 index;
1253 nsresult rv;
1254 if (aIndex == nsINavBookmarksService::DEFAULT_INDEX) {
1255 index = FolderCount(aParent);
1256 } else {
1257 index = aIndex;
1258 rv = AdjustIndices(aParent, index, PR_INT32_MAX, 1);
1259 NS_ENSURE_SUCCESS(rv, rv);
1262 nsCOMPtr<mozIStorageStatement> statement;
1263 rv = dbConn->CreateStatement(NS_LITERAL_CSTRING("INSERT INTO moz_bookmarks "
1264 "(type, parent, position, dateAdded) VALUES (?1, ?2, ?3, ?4)"),
1265 getter_AddRefs(statement));
1266 NS_ENSURE_SUCCESS(rv, rv);
1268 rv = statement->BindInt64Parameter(0, TYPE_SEPARATOR);
1269 NS_ENSURE_SUCCESS(rv, rv);
1270 rv = statement->BindInt64Parameter(1, aParent);
1271 NS_ENSURE_SUCCESS(rv, rv);
1272 rv = statement->BindInt32Parameter(2, index);
1273 NS_ENSURE_SUCCESS(rv, rv);
1274 rv = statement->BindInt64Parameter(3, PR_Now());
1275 NS_ENSURE_SUCCESS(rv, rv);
1277 rv = statement->Execute();
1278 NS_ENSURE_SUCCESS(rv, rv);
1280 PRInt64 rowId;
1282 mozStorageStatementScoper scoper(mDBGetLastBookmarkID);
1284 PRBool hasResult;
1285 rv = mDBGetLastBookmarkID->ExecuteStep(&hasResult);
1286 NS_ENSURE_SUCCESS(rv, rv);
1287 NS_ASSERTION(hasResult, "hasResult is false but the call succeeded?");
1288 rowId = *aNewItemId = mDBGetLastBookmarkID->AsInt64(0);
1291 rv = SetItemDateInternal(mDBSetItemLastModified, aParent, PR_Now());
1292 NS_ENSURE_SUCCESS(rv, rv);
1294 rv = transaction.Commit();
1295 NS_ENSURE_SUCCESS(rv, rv);
1297 ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver,
1298 OnItemAdded(rowId, aParent, index))
1300 return NS_OK;
1303 nsresult
1304 nsNavBookmarks::GetLastChildId(PRInt64 aFolder, PRInt64* aItemId)
1306 mozIStorageConnection *dbConn = DBConn();
1308 nsCOMPtr<mozIStorageStatement> statement;
1309 nsresult rv = dbConn->CreateStatement(NS_LITERAL_CSTRING(
1310 "SELECT id FROM moz_bookmarks WHERE parent = ?1 "
1311 "ORDER BY position DESC LIMIT 1"), getter_AddRefs(statement));
1312 NS_ENSURE_SUCCESS(rv, rv);
1314 rv = statement->BindInt64Parameter(0, aFolder);
1315 NS_ENSURE_SUCCESS(rv, rv);
1317 PRBool hasMore;
1318 rv = statement->ExecuteStep(&hasMore);
1319 NS_ENSURE_SUCCESS(rv, rv);
1320 if (!hasMore) {
1321 // Item doesn't exist
1322 return NS_ERROR_INVALID_ARG;
1325 *aItemId = statement->AsInt64(0);
1326 return NS_OK;
1329 NS_IMETHODIMP
1330 nsNavBookmarks::GetIdForItemAt(PRInt64 aFolder, PRInt32 aIndex, PRInt64* aItemId)
1332 nsresult rv;
1333 if (aIndex == nsINavBookmarksService::DEFAULT_INDEX) {
1334 // we want the last item within aFolder
1335 return GetLastChildId(aFolder, aItemId);
1336 } else {
1338 // get the item in aFolder with position aIndex
1339 mozStorageStatementScoper scope(mDBGetChildAt);
1341 rv = mDBGetChildAt->BindInt64Parameter(0, aFolder);
1342 NS_ENSURE_SUCCESS(rv, rv);
1343 rv = mDBGetChildAt->BindInt32Parameter(1, aIndex);
1344 NS_ENSURE_SUCCESS(rv, rv);
1346 PRBool hasMore;
1347 rv = mDBGetChildAt->ExecuteStep(&hasMore);
1348 NS_ENSURE_SUCCESS(rv, rv);
1349 if (!hasMore) {
1350 // Item doesn't exist
1351 return NS_ERROR_INVALID_ARG;
1353 // actually found an item
1354 *aItemId = mDBGetChildAt->AsInt64(0);
1357 return NS_OK;
1360 NS_IMETHODIMP
1361 nsNavBookmarks::RemoveChildAt(PRInt64 aParent, PRInt32 aIndex)
1363 mozIStorageConnection *dbConn = DBConn();
1364 mozStorageTransaction transaction(dbConn, PR_FALSE);
1365 nsresult rv;
1366 PRInt64 id;
1367 PRInt32 type;
1370 mozStorageStatementScoper scope(mDBGetChildAt);
1371 rv = mDBGetChildAt->BindInt64Parameter(0, aParent);
1372 NS_ENSURE_SUCCESS(rv, rv);
1373 rv = mDBGetChildAt->BindInt32Parameter(1, aIndex);
1374 NS_ENSURE_SUCCESS(rv, rv);
1376 PRBool hasMore;
1377 rv = mDBGetChildAt->ExecuteStep(&hasMore);
1378 NS_ENSURE_SUCCESS(rv, rv);
1379 if (!hasMore) {
1380 // Child doesn't exist
1381 return NS_ERROR_INVALID_ARG;
1384 id = mDBGetChildAt->AsInt64(0);
1385 type = mDBGetChildAt->AsInt32(2);
1388 if (type == TYPE_BOOKMARK || type == TYPE_SEPARATOR) {
1389 // Commit this transaction so that we don't notify observers mid-tranaction
1390 rv = transaction.Commit();
1391 NS_ENSURE_SUCCESS(rv, rv);
1393 return RemoveItem(id);
1395 else if (type == TYPE_FOLDER) {
1396 // Commit this transaction so that we don't notify observers mid-tranaction
1397 rv = transaction.Commit();
1398 NS_ENSURE_SUCCESS(rv, rv);
1400 return RemoveFolder(id);
1402 return NS_ERROR_INVALID_ARG;
1405 nsresult
1406 nsNavBookmarks::GetParentAndIndexOfFolder(PRInt64 aFolder, PRInt64* aParent,
1407 PRInt32* aIndex)
1409 nsCAutoString buffer;
1410 buffer.AssignLiteral("SELECT parent, position FROM moz_bookmarks WHERE id = ");
1411 buffer.AppendInt(aFolder);
1413 nsCOMPtr<mozIStorageStatement> statement;
1414 nsresult rv = DBConn()->CreateStatement(buffer, getter_AddRefs(statement));
1415 NS_ENSURE_SUCCESS(rv, rv);
1417 PRBool results;
1418 rv = statement->ExecuteStep(&results);
1419 NS_ENSURE_SUCCESS(rv, rv);
1420 if (!results) {
1421 return NS_ERROR_INVALID_ARG; // folder is not in the hierarchy
1424 *aParent = statement->AsInt64(0);
1425 *aIndex = statement->AsInt32(1);
1427 return NS_OK;
1430 NS_IMETHODIMP
1431 nsNavBookmarks::RemoveFolder(PRInt64 aFolderId)
1433 mozIStorageConnection *dbConn = DBConn();
1434 mozStorageTransaction transaction(dbConn, PR_FALSE);
1436 nsresult rv;
1438 PRInt64 parent;
1439 PRInt32 index, type;
1440 nsCAutoString folderType;
1442 mozStorageStatementScoper scope(mDBGetItemProperties);
1443 rv = mDBGetItemProperties->BindInt64Parameter(0, aFolderId);
1444 NS_ENSURE_SUCCESS(rv, rv);
1446 PRBool results;
1447 rv = mDBGetItemProperties->ExecuteStep(&results);
1448 NS_ENSURE_SUCCESS(rv, rv);
1449 if (!results) {
1450 return NS_ERROR_INVALID_ARG; // folder is not in the hierarchy
1453 type = mDBGetItemProperties->AsInt32(kGetItemPropertiesIndex_Type);
1454 parent = mDBGetItemProperties->AsInt64(kGetItemPropertiesIndex_Parent);
1455 index = mDBGetItemProperties->AsInt32(kGetItemPropertiesIndex_Position);
1456 rv = mDBGetItemProperties->GetUTF8String(kGetItemPropertiesIndex_ServiceContractId, folderType);
1457 NS_ENSURE_SUCCESS(rv, rv);
1460 if (type != TYPE_FOLDER) {
1461 NS_WARNING("RemoveFolder(): aFolderId is not a folder!");
1462 return NS_ERROR_INVALID_ARG; // aFolderId is not a folder!
1465 // First, remove item annotations
1466 nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService();
1467 NS_ENSURE_TRUE(annosvc, NS_ERROR_OUT_OF_MEMORY);
1468 rv = annosvc->RemoveItemAnnotations(aFolderId);
1469 NS_ENSURE_SUCCESS(rv, rv);
1471 // If this is a container bookmark, try to notify its service.
1472 if (folderType.Length() > 0) {
1473 // There is a type associated with this folder; it's a livemark.
1474 nsCOMPtr<nsIDynamicContainer> bmcServ = do_GetService(folderType.get());
1475 if (bmcServ) {
1476 rv = bmcServ->OnContainerRemoving(aFolderId);
1477 if (NS_FAILED(rv))
1478 NS_WARNING("Remove folder container notification failed.");
1482 // Remove all of the folder's children
1483 RemoveFolderChildren(aFolderId);
1485 // Remove the folder from its parent
1486 nsCAutoString buffer;
1487 buffer.AssignLiteral("DELETE FROM moz_bookmarks WHERE id = ");
1488 buffer.AppendInt(aFolderId);
1489 rv = dbConn->ExecuteSimpleSQL(buffer);
1490 NS_ENSURE_SUCCESS(rv, rv);
1492 rv = AdjustIndices(parent, index + 1, PR_INT32_MAX, -1);
1493 NS_ENSURE_SUCCESS(rv, rv);
1495 rv = SetItemDateInternal(mDBSetItemLastModified, parent, PR_Now());
1496 NS_ENSURE_SUCCESS(rv, rv);
1498 rv = transaction.Commit();
1499 NS_ENSURE_SUCCESS(rv, rv);
1501 if (aFolderId == mToolbarFolder) {
1502 mToolbarFolder = 0;
1505 ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver,
1506 OnItemRemoved(aFolderId, parent, index))
1508 return NS_OK;
1511 NS_IMPL_ISUPPORTS1(nsNavBookmarks::RemoveFolderTransaction, nsITransaction)
1513 NS_IMETHODIMP
1514 nsNavBookmarks::GetRemoveFolderTransaction(PRInt64 aFolder, nsITransaction** aResult)
1516 // Create and initialize a RemoveFolderTransaction object that can be used to
1517 // recreate the folder safely later.
1519 RemoveFolderTransaction* rft =
1520 new RemoveFolderTransaction(aFolder);
1521 if (!rft)
1522 return NS_ERROR_OUT_OF_MEMORY;
1524 NS_ADDREF(*aResult = rft);
1525 return NS_OK;
1528 NS_IMETHODIMP
1529 nsNavBookmarks::RemoveFolderChildren(PRInt64 aFolder)
1531 mozStorageTransaction transaction(DBConn(), PR_FALSE);
1533 nsTArray<PRInt64> folderChildren;
1534 nsTArray<PRInt64> itemChildren; // bookmarks / separators
1535 nsresult rv;
1537 mozStorageStatementScoper scope(mDBGetChildren);
1538 rv = mDBGetChildren->BindInt64Parameter(0, aFolder);
1539 NS_ENSURE_SUCCESS(rv, rv);
1541 PRBool hasMore;
1542 while (NS_SUCCEEDED(mDBGetChildren->ExecuteStep(&hasMore)) && hasMore) {
1543 PRInt32 type = mDBGetChildren->AsInt32(kGetChildrenIndex_Type);
1544 if (type == TYPE_FOLDER) {
1545 // folder
1546 folderChildren.AppendElement(
1547 mDBGetChildren->AsInt64(nsNavHistory::kGetInfoIndex_ItemId));
1548 } else {
1549 // bookmarks / separators
1550 itemChildren.AppendElement(mDBGetChildren->AsInt64(nsNavHistory::kGetInfoIndex_ItemId));
1555 PRUint32 i;
1557 // remove folders
1558 for (i = 0; i < folderChildren.Length(); ++i) {
1559 rv = RemoveFolder(folderChildren[i]);
1560 NS_ENSURE_SUCCESS(rv, rv);
1563 // remove items
1564 for (i = 0; i < itemChildren.Length(); ++i) {
1565 rv = RemoveItem(itemChildren[i]);
1566 NS_ENSURE_SUCCESS(rv, rv);
1569 transaction.Commit();
1570 return NS_OK;
1573 NS_IMETHODIMP
1574 nsNavBookmarks::MoveItem(PRInt64 aItemId, PRInt64 aNewParent, PRInt32 aIndex)
1576 // You can pass -1 to indicate append, but no other negative number is allowed
1577 if (aIndex < -1)
1578 return NS_ERROR_INVALID_ARG;
1580 // Disallow making an item its own parent.
1581 if (aItemId == aNewParent)
1582 return NS_ERROR_INVALID_ARG;
1584 mozIStorageConnection *dbConn = DBConn();
1585 mozStorageTransaction transaction(dbConn, PR_FALSE);
1587 // get item properties
1588 nsresult rv;
1589 PRInt64 oldParent;
1590 PRInt32 oldIndex, itemType;
1591 nsCAutoString folderType;
1593 mozStorageStatementScoper scope(mDBGetItemProperties);
1594 rv = mDBGetItemProperties->BindInt64Parameter(0, aItemId);
1595 NS_ENSURE_SUCCESS(rv, rv);
1597 PRBool results;
1598 rv = mDBGetItemProperties->ExecuteStep(&results);
1599 NS_ENSURE_SUCCESS(rv, rv);
1600 if (!results) {
1601 return NS_ERROR_INVALID_ARG; // folder is not in the hierarchy
1604 oldParent = mDBGetItemProperties->AsInt64(kGetItemPropertiesIndex_Parent);
1605 oldIndex = mDBGetItemProperties->AsInt32(kGetItemPropertiesIndex_Position);
1606 itemType = mDBGetItemProperties->AsInt32(kGetItemPropertiesIndex_Type);
1607 if (itemType == TYPE_FOLDER) {
1608 rv = mDBGetItemProperties->GetUTF8String(kGetItemPropertiesIndex_ServiceContractId,
1609 folderType);
1610 NS_ENSURE_SUCCESS(rv, rv);
1614 // if parent and index are the same, nothing to do
1615 if (oldParent == aNewParent && oldIndex == aIndex)
1616 return NS_OK;
1618 // Make sure aNewParent is not aFolder or a subfolder of aFolder
1619 if (itemType == TYPE_FOLDER) {
1620 PRInt64 p = aNewParent;
1622 while (p) {
1623 mozStorageStatementScoper scope(mDBGetItemProperties);
1624 if (p == aItemId) {
1625 return NS_ERROR_INVALID_ARG;
1628 rv = mDBGetItemProperties->BindInt64Parameter(0, p);
1629 NS_ENSURE_SUCCESS(rv, rv);
1631 PRBool results;
1632 rv = mDBGetItemProperties->ExecuteStep(&results);
1633 NS_ENSURE_SUCCESS(rv, rv);
1634 p = results ? mDBGetItemProperties->AsInt64(kGetItemPropertiesIndex_Parent) : 0;
1638 // calculate new index
1639 PRInt32 newIndex;
1640 if (aIndex == nsINavBookmarksService::DEFAULT_INDEX) {
1641 newIndex = FolderCount(aNewParent);
1642 // If the parent remains the same, then the folder is really being moved
1643 // to count - 1 (since it's being removed from the old position)
1644 if (oldParent == aNewParent) {
1645 --newIndex;
1647 } else {
1648 newIndex = aIndex;
1650 if (oldParent == aNewParent && newIndex > oldIndex) {
1651 // when an item is being moved lower in the same folder, the new index
1652 // refers to the index before it was removed. Removal causes everything
1653 // to shift up.
1654 --newIndex;
1658 // this is like the previous check, except this covers if
1659 // the specified index was -1 (append), and the calculated
1660 // new index is the same as the existing index
1661 if (aNewParent == oldParent && newIndex == oldIndex) {
1662 // Nothing to do!
1663 return NS_OK;
1666 // adjust indices to account for the move
1667 // do this before we update the parent/index fields
1668 // or we'll re-adjust the index for the item we are moving
1669 if (oldParent == aNewParent) {
1670 // We can optimize the updates if moving within the same container.
1671 // We only shift the items between the old and new positions, since the
1672 // insertion will offset the deletion.
1673 if (oldIndex > newIndex) {
1674 rv = AdjustIndices(oldParent, newIndex, oldIndex - 1, 1);
1675 } else {
1676 rv = AdjustIndices(oldParent, oldIndex + 1, newIndex, -1);
1678 } else {
1679 // We're moving between containers, so this happens in two steps.
1680 // First, fill the hole from the removal from the old parent.
1681 rv = AdjustIndices(oldParent, oldIndex + 1, PR_INT32_MAX, -1);
1682 NS_ENSURE_SUCCESS(rv, rv);
1683 // Now, make room in the new parent for the insertion.
1684 rv = AdjustIndices(aNewParent, newIndex, PR_INT32_MAX, 1);
1686 NS_ENSURE_SUCCESS(rv, rv);
1688 // update parent/index fields
1689 nsCAutoString buffer;
1690 buffer.AssignLiteral("UPDATE moz_bookmarks SET ");
1691 if (aNewParent != oldParent) {
1692 buffer.AppendLiteral(" parent = ");
1693 buffer.AppendInt(aNewParent);
1695 if (newIndex != oldIndex) {
1696 if (aNewParent != oldParent)
1697 buffer.AppendLiteral(", ");
1698 buffer.AppendLiteral(" position = ");
1699 buffer.AppendInt(newIndex);
1701 buffer.AppendLiteral(" WHERE id = ");
1702 buffer.AppendInt(aItemId);
1703 rv = dbConn->ExecuteSimpleSQL(buffer);
1704 NS_ENSURE_SUCCESS(rv, rv);
1706 PRTime now = PR_Now();
1707 rv = SetItemDateInternal(mDBSetItemLastModified, oldParent, now);
1708 NS_ENSURE_SUCCESS(rv, rv);
1709 rv = SetItemDateInternal(mDBSetItemLastModified, aNewParent, now);
1710 NS_ENSURE_SUCCESS(rv, rv);
1712 rv = transaction.Commit();
1713 NS_ENSURE_SUCCESS(rv, rv);
1715 // notify bookmark observers
1716 ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver,
1717 OnItemMoved(aItemId, oldParent, oldIndex, aNewParent,
1718 newIndex))
1720 // notify dynamic container provider if there is one
1721 if (!folderType.IsEmpty()) {
1722 nsCOMPtr<nsIDynamicContainer> container =
1723 do_GetService(folderType.get(), &rv);
1724 if (NS_SUCCEEDED(rv)) {
1725 rv = container->OnContainerMoved(aItemId, aNewParent, newIndex);
1726 NS_ENSURE_SUCCESS(rv, rv);
1729 return NS_OK;
1732 NS_IMETHODIMP
1733 nsNavBookmarks::GetChildFolder(PRInt64 aFolder, const nsAString& aSubFolder,
1734 PRInt64* _result)
1736 // note: we allow empty folder names
1737 nsresult rv;
1738 if (aFolder == 0)
1739 return NS_ERROR_INVALID_ARG;
1741 // If this gets used a lot, we'll want a precompiled statement
1742 nsCAutoString getChildFolderQuery =
1743 NS_LITERAL_CSTRING("SELECT id "
1744 "FROM moz_bookmarks "
1745 "WHERE parent = ?1 AND type = ") +
1746 nsPrintfCString("%d", TYPE_FOLDER) +
1747 NS_LITERAL_CSTRING(" AND title = ?2");
1748 nsCOMPtr<mozIStorageStatement> statement;
1749 rv = DBConn()->CreateStatement(getChildFolderQuery, getter_AddRefs(statement));
1750 NS_ENSURE_SUCCESS(rv, rv);
1751 statement->BindInt64Parameter(0, aFolder);
1752 statement->BindStringParameter(1, aSubFolder);
1754 PRBool hasResult = PR_FALSE;
1755 rv = statement->ExecuteStep(&hasResult);
1756 NS_ENSURE_SUCCESS(rv, rv);
1758 if (! hasResult) {
1759 // item not found
1760 *_result = 0;
1761 return NS_OK;
1764 return statement->GetInt64(0, _result);
1767 nsresult
1768 nsNavBookmarks::SetItemDateInternal(mozIStorageStatement* aStatement, PRInt64 aItemId, PRTime aValue)
1770 mozStorageStatementScoper scope(aStatement);
1771 nsresult rv = aStatement->BindInt64Parameter(0, aValue);
1772 NS_ENSURE_SUCCESS(rv, rv);
1773 rv = aStatement->BindInt64Parameter(1, aItemId);
1774 NS_ENSURE_SUCCESS(rv, rv);
1776 rv = aStatement->Execute();
1777 NS_ENSURE_SUCCESS(rv, rv);
1779 // note, we are not notifying the observers
1780 // that the item has changed.
1782 return NS_OK;
1785 NS_IMETHODIMP
1786 nsNavBookmarks::SetItemDateAdded(PRInt64 aItemId, PRTime aDateAdded)
1788 nsresult rv = SetItemDateInternal(mDBSetItemDateAdded, aItemId, aDateAdded);
1789 NS_ENSURE_SUCCESS(rv, rv);
1791 ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver,
1792 OnItemChanged(aItemId, NS_LITERAL_CSTRING("dateAdded"),
1793 PR_FALSE, nsPrintfCString(16, "%lld", aDateAdded)));
1794 return NS_OK;
1797 NS_IMETHODIMP
1798 nsNavBookmarks::GetItemDateAdded(PRInt64 aItemId, PRTime *aDateAdded)
1800 NS_ENSURE_ARG_POINTER(aDateAdded);
1802 mozStorageStatementScoper scope(mDBGetItemProperties);
1803 nsresult rv = mDBGetItemProperties->BindInt64Parameter(0, aItemId);
1804 NS_ENSURE_SUCCESS(rv, rv);
1806 PRBool results;
1807 rv = mDBGetItemProperties->ExecuteStep(&results);
1808 NS_ENSURE_SUCCESS(rv, rv);
1810 if (!results)
1811 return NS_ERROR_INVALID_ARG; // invalid item id
1813 return mDBGetItemProperties->GetInt64(kGetItemPropertiesIndex_DateAdded, aDateAdded);
1816 NS_IMETHODIMP
1817 nsNavBookmarks::SetItemLastModified(PRInt64 aItemId, PRTime aLastModified)
1819 nsresult rv = SetItemDateInternal(mDBSetItemLastModified, aItemId, aLastModified);
1820 NS_ENSURE_SUCCESS(rv, rv);
1822 ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver,
1823 OnItemChanged(aItemId, NS_LITERAL_CSTRING("lastModified"),
1824 PR_FALSE, nsPrintfCString(16, "%lld", aLastModified)));
1825 return NS_OK;
1828 NS_IMETHODIMP
1829 nsNavBookmarks::GetItemLastModified(PRInt64 aItemId, PRTime *aLastModified)
1831 NS_ENSURE_ARG_POINTER(aLastModified);
1833 mozStorageStatementScoper scope(mDBGetItemProperties);
1834 nsresult rv = mDBGetItemProperties->BindInt64Parameter(0, aItemId);
1835 NS_ENSURE_SUCCESS(rv, rv);
1837 PRBool results;
1838 rv = mDBGetItemProperties->ExecuteStep(&results);
1839 NS_ENSURE_SUCCESS(rv, rv);
1841 if (!results)
1842 return NS_ERROR_INVALID_ARG; // invalid item id
1844 return mDBGetItemProperties->GetInt64(kGetItemPropertiesIndex_LastModified, aLastModified);
1847 NS_IMETHODIMP
1848 nsNavBookmarks::GetItemGUID(PRInt64 aItemId, nsAString &aGUID)
1850 nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService();
1851 NS_ENSURE_TRUE(annosvc, NS_ERROR_OUT_OF_MEMORY);
1852 nsresult rv = annosvc->GetItemAnnotationString(aItemId, GUID_ANNO, aGUID);
1854 if (NS_SUCCEEDED(rv) || rv != NS_ERROR_NOT_AVAILABLE)
1855 return rv;
1857 nsAutoString tmp;
1858 tmp.AppendInt(mItemCount++);
1859 aGUID.SetCapacity(NSID_LENGTH - 1 + tmp.Length());
1860 aGUID.Assign(mGUIDBase);
1861 aGUID.Append(tmp);
1863 return SetItemGUID(aItemId, aGUID);
1866 NS_IMETHODIMP
1867 nsNavBookmarks::SetItemGUID(PRInt64 aItemId, const nsAString &aGUID)
1869 PRInt64 checkId;
1870 GetItemIdForGUID(aGUID, &checkId);
1871 if (checkId != -1)
1872 return NS_ERROR_INVALID_ARG; // invalid GUID, already exists
1874 nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService();
1875 NS_ENSURE_TRUE(annosvc, NS_ERROR_OUT_OF_MEMORY);
1876 return annosvc->SetItemAnnotationString(aItemId, GUID_ANNO, aGUID, 0,
1877 nsIAnnotationService::EXPIRE_NEVER);
1880 NS_IMETHODIMP
1881 nsNavBookmarks::GetItemIdForGUID(const nsAString &aGUID, PRInt64 *aItemId)
1883 mozStorageStatementScoper scoper(mDBGetItemIdForGUID);
1884 nsresult rv = mDBGetItemIdForGUID->BindStringParameter(0, aGUID);
1885 NS_ENSURE_SUCCESS(rv, rv);
1887 PRBool hasMore = PR_FALSE;
1888 rv = mDBGetItemIdForGUID->ExecuteStep(&hasMore);
1889 if (NS_FAILED(rv) || ! hasMore) {
1890 *aItemId = -1;
1891 return NS_OK; // not found: return -1
1894 // found, get the itemId
1895 return mDBGetItemIdForGUID->GetInt64(0, aItemId);
1898 NS_IMETHODIMP
1899 nsNavBookmarks::SetItemTitle(PRInt64 aItemId, const nsACString &aTitle)
1901 mozIStorageConnection *dbConn = DBConn();
1903 nsCOMPtr<mozIStorageStatement> statement;
1904 nsresult rv = dbConn->CreateStatement(NS_LITERAL_CSTRING(
1905 "UPDATE moz_bookmarks SET title = ?1, lastModified = ?2 WHERE id = ?3"),
1906 getter_AddRefs(statement));
1907 NS_ENSURE_SUCCESS(rv, rv);
1908 rv = statement->BindUTF8StringParameter(0, aTitle);
1909 NS_ENSURE_SUCCESS(rv, rv);
1910 rv = statement->BindInt64Parameter(1, PR_Now());
1911 NS_ENSURE_SUCCESS(rv, rv);
1912 rv = statement->BindInt64Parameter(2, aItemId);
1913 NS_ENSURE_SUCCESS(rv, rv);
1915 rv = statement->Execute();
1916 NS_ENSURE_SUCCESS(rv, rv);
1918 ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver,
1919 OnItemChanged(aItemId, NS_LITERAL_CSTRING("title"),
1920 PR_FALSE, aTitle));
1921 return NS_OK;
1924 NS_IMETHODIMP
1925 nsNavBookmarks::GetItemTitle(PRInt64 aItemId, nsACString &aTitle)
1927 mozStorageStatementScoper scope(mDBGetItemProperties);
1929 nsresult rv = mDBGetItemProperties->BindInt64Parameter(0, aItemId);
1930 NS_ENSURE_SUCCESS(rv, rv);
1932 PRBool results;
1933 rv = mDBGetItemProperties->ExecuteStep(&results);
1934 NS_ENSURE_SUCCESS(rv, rv);
1936 if (!results)
1937 return NS_ERROR_INVALID_ARG; // invalid bookmark id
1939 return mDBGetItemProperties->GetUTF8String(kGetItemPropertiesIndex_Title, aTitle);
1942 NS_IMETHODIMP
1943 nsNavBookmarks::GetBookmarkURI(PRInt64 aItemId, nsIURI **aURI)
1945 NS_ENSURE_ARG_POINTER(aURI);
1947 mozStorageStatementScoper scope(mDBGetItemProperties);
1948 nsresult rv = mDBGetItemProperties->BindInt64Parameter(0, aItemId);
1949 NS_ENSURE_SUCCESS(rv, rv);
1951 PRBool results;
1952 rv = mDBGetItemProperties->ExecuteStep(&results);
1953 NS_ENSURE_SUCCESS(rv, rv);
1955 if (!results)
1956 return NS_ERROR_INVALID_ARG; // invalid bookmark id
1958 PRInt32 type = mDBGetItemProperties->AsInt32(kGetItemPropertiesIndex_Type);
1959 if (type != TYPE_BOOKMARK)
1960 return NS_ERROR_INVALID_ARG; // invalid type (only for bookmarks)
1962 nsCAutoString spec;
1963 rv = mDBGetItemProperties->GetUTF8String(kGetItemPropertiesIndex_URI, spec);
1964 NS_ENSURE_SUCCESS(rv, rv);
1966 rv = NS_NewURI(aURI, spec);
1967 NS_ENSURE_SUCCESS(rv, rv);
1969 return NS_OK;
1972 NS_IMETHODIMP
1973 nsNavBookmarks::GetItemType(PRInt64 aItemId, PRUint16 *aType)
1975 mozStorageStatementScoper scope(mDBGetItemProperties);
1977 nsresult rv = mDBGetItemProperties->BindInt64Parameter(0, aItemId);
1978 NS_ENSURE_SUCCESS(rv, rv);
1980 PRBool results;
1981 rv = mDBGetItemProperties->ExecuteStep(&results);
1982 NS_ENSURE_SUCCESS(rv, rv);
1984 if (!results) {
1985 return NS_ERROR_INVALID_ARG; // invalid bookmark id
1988 *aType = (PRUint16)mDBGetItemProperties->AsInt32(kGetItemPropertiesIndex_Type);
1989 return NS_OK;
1992 nsresult
1993 nsNavBookmarks::GetFolderType(PRInt64 aFolder, nsACString &aType)
1995 mozStorageStatementScoper scope(mDBGetItemProperties);
1996 nsresult rv = mDBGetItemProperties->BindInt64Parameter(0, aFolder);
1997 NS_ENSURE_SUCCESS(rv, rv);
1999 PRBool results;
2000 rv = mDBGetItemProperties->ExecuteStep(&results);
2001 NS_ENSURE_SUCCESS(rv, rv);
2003 if (!results) {
2004 return NS_ERROR_INVALID_ARG;
2007 return mDBGetItemProperties->GetUTF8String(kGetItemPropertiesIndex_ServiceContractId, aType);
2010 nsresult
2011 nsNavBookmarks::ResultNodeForContainer(PRInt64 aID,
2012 nsNavHistoryQueryOptions *aOptions,
2013 nsNavHistoryResultNode **aNode)
2015 mozStorageStatementScoper scope(mDBGetItemProperties);
2016 mDBGetItemProperties->BindInt64Parameter(0, aID);
2018 PRBool results;
2019 nsresult rv = mDBGetItemProperties->ExecuteStep(&results);
2020 NS_ENSURE_SUCCESS(rv, rv);
2021 NS_ASSERTION(results, "ResultNodeForContainer expects a valid item id");
2023 // contract id
2024 nsCAutoString contractId;
2025 rv = mDBGetItemProperties->GetUTF8String(kGetItemPropertiesIndex_ServiceContractId,
2026 contractId);
2027 NS_ENSURE_SUCCESS(rv, rv);
2029 // title
2030 nsCAutoString title;
2031 rv = mDBGetItemProperties->GetUTF8String(kGetItemPropertiesIndex_Title, title);
2033 PRInt32 itemType = mDBGetItemProperties->AsInt32(kGetItemPropertiesIndex_Type);
2034 if (itemType == TYPE_DYNAMIC_CONTAINER) {
2035 *aNode = new nsNavHistoryContainerResultNode(EmptyCString(), title, EmptyCString(),
2036 nsINavHistoryResultNode::RESULT_TYPE_DYNAMIC_CONTAINER,
2037 PR_TRUE,
2038 contractId,
2039 aOptions);
2040 (*aNode)->mItemId = aID;
2041 } else { // TYPE_FOLDER
2042 *aNode = new nsNavHistoryFolderResultNode(title, aOptions, aID, contractId);
2044 if (!*aNode)
2045 return NS_ERROR_OUT_OF_MEMORY;
2047 (*aNode)->mDateAdded =
2048 mDBGetItemProperties->AsInt64(kGetItemPropertiesIndex_DateAdded);
2049 (*aNode)->mLastModified =
2050 mDBGetItemProperties->AsInt64(kGetItemPropertiesIndex_LastModified);
2052 NS_ADDREF(*aNode);
2053 return NS_OK;
2056 nsresult
2057 nsNavBookmarks::QueryFolderChildren(PRInt64 aFolderId,
2058 nsNavHistoryQueryOptions *aOptions,
2059 nsCOMArray<nsNavHistoryResultNode> *aChildren)
2061 mozStorageStatementScoper scope(mDBGetChildren);
2063 nsresult rv = mDBGetChildren->BindInt64Parameter(0, aFolderId);
2064 NS_ENSURE_SUCCESS(rv, rv);
2066 PRBool results;
2068 nsCOMPtr<nsNavHistoryQueryOptions> options = do_QueryInterface(aOptions, &rv);
2069 PRInt32 index = -1;
2070 while (NS_SUCCEEDED(mDBGetChildren->ExecuteStep(&results)) && results) {
2072 // The results will be in order of index. Even if we don't add a node
2073 // because it was excluded, we need to count it's index, so do that
2074 // before doing anything else. Index was initialized to -1 above, so
2075 // it will start counting at 0 the first time through the loop.
2076 index ++;
2078 PRInt32 itemType = mDBGetChildren->AsInt32(kGetChildrenIndex_Type);
2079 PRInt64 id = mDBGetChildren->AsInt64(nsNavHistory::kGetInfoIndex_ItemId);
2080 nsRefPtr<nsNavHistoryResultNode> node;
2081 if (itemType == TYPE_BOOKMARK) {
2082 rv = History()->RowToResult(mDBGetChildren, options,
2083 getter_AddRefs(node));
2084 NS_ENSURE_SUCCESS(rv, rv);
2086 PRUint32 nodeType;
2087 node->GetType(&nodeType);
2088 if ((nodeType == nsINavHistoryResultNode::RESULT_TYPE_QUERY &&
2089 aOptions->ExcludeQueries()) ||
2090 (nodeType != nsINavHistoryResultNode::RESULT_TYPE_QUERY &&
2091 nodeType != nsINavHistoryResultNode::RESULT_TYPE_FOLDER_SHORTCUT &&
2092 aOptions->ExcludeItems())) {
2093 continue;
2095 } else if (itemType == TYPE_FOLDER || itemType == TYPE_DYNAMIC_CONTAINER) {
2096 if (itemType == TYPE_DYNAMIC_CONTAINER ||
2097 (itemType == TYPE_FOLDER && options->ExcludeReadOnlyFolders())) {
2098 // see if it's read only and skip it
2099 PRBool readOnly = PR_FALSE;
2100 GetFolderReadonly(id, &readOnly);
2101 if (readOnly)
2102 continue; // skip
2105 rv = ResultNodeForContainer(id, aOptions, getter_AddRefs(node));
2106 if (NS_FAILED(rv))
2107 continue;
2108 } else {
2109 // separator
2110 if (aOptions->ExcludeItems()) {
2111 continue;
2113 node = new nsNavHistorySeparatorResultNode();
2114 NS_ENSURE_TRUE(node, NS_ERROR_OUT_OF_MEMORY);
2116 // add the item identifier (RowToResult does so for bookmark items in
2117 // the next else block);
2118 node->mItemId =
2119 mDBGetChildren->AsInt64(nsNavHistory::kGetInfoIndex_ItemId);
2121 // date-added and last-modified
2122 node->mDateAdded =
2123 mDBGetChildren->AsInt64(nsNavHistory::kGetInfoIndex_ItemDateAdded);
2124 node->mLastModified =
2125 mDBGetChildren->AsInt64(nsNavHistory::kGetInfoIndex_ItemLastModified);
2128 // this method fills all bookmark queries, so we store the index of the
2129 // item in its parent
2130 node->mBookmarkIndex = index;
2132 NS_ENSURE_TRUE(aChildren->AppendObject(node), NS_ERROR_OUT_OF_MEMORY);
2134 return NS_OK;
2137 PRInt32
2138 nsNavBookmarks::FolderCount(PRInt64 aFolder)
2140 mozStorageStatementScoper scope(mDBFolderCount);
2142 nsresult rv = mDBFolderCount->BindInt64Parameter(0, aFolder);
2143 NS_ENSURE_SUCCESS(rv, 0);
2145 PRBool results;
2146 rv = mDBFolderCount->ExecuteStep(&results);
2147 NS_ENSURE_SUCCESS(rv, rv);
2149 return mDBFolderCount->AsInt32(0);
2152 NS_IMETHODIMP
2153 nsNavBookmarks::IsBookmarked(nsIURI *aURI, PRBool *aBookmarked)
2155 NS_ENSURE_ARG(aURI);
2157 nsNavHistory* history = History();
2158 NS_ENSURE_TRUE(history, NS_ERROR_UNEXPECTED);
2160 // convert the URL to an ID
2161 PRInt64 urlID;
2162 nsresult rv = history->GetUrlIdFor(aURI, &urlID, PR_FALSE);
2163 NS_ENSURE_SUCCESS(rv, rv);
2164 if (! urlID) {
2165 // never seen this before, not even in history
2166 *aBookmarked = PR_FALSE;
2167 return NS_OK;
2170 PRInt64 bookmarkedID;
2171 PRBool foundOne = mBookmarksHash.Get(urlID, &bookmarkedID);
2173 // IsBookmarked only tests if this exact URI is bookmarked, so we need to
2174 // check that the destination matches
2175 if (foundOne)
2176 *aBookmarked = (urlID == bookmarkedID);
2177 else
2178 *aBookmarked = PR_FALSE;
2180 #ifdef DEBUG
2181 // sanity check for the bookmark hashtable
2182 PRBool realBookmarked;
2183 rv = IsBookmarkedInDatabase(urlID, &realBookmarked);
2184 NS_ASSERTION(realBookmarked == *aBookmarked,
2185 "Bookmark hash table out-of-sync with the database");
2186 #endif
2188 return NS_OK;
2191 NS_IMETHODIMP
2192 nsNavBookmarks::GetBookmarkedURIFor(nsIURI* aURI, nsIURI** _retval)
2194 *_retval = nsnull;
2196 nsNavHistory* history = History();
2197 NS_ENSURE_TRUE(history, NS_ERROR_UNEXPECTED);
2199 // convert the URL to an ID
2200 PRInt64 urlID;
2201 nsresult rv = history->GetUrlIdFor(aURI, &urlID, PR_FALSE);
2202 NS_ENSURE_SUCCESS(rv, rv);
2203 if (! urlID) {
2204 // never seen this before, not even in history, leave result NULL
2205 return NS_OK;
2208 PRInt64 bookmarkID;
2209 if (mBookmarksHash.Get(urlID, &bookmarkID)) {
2210 // found one, convert ID back to URL. This statement is NOT refcounted
2211 mozIStorageStatement* statement = history->DBGetIdPageInfo();
2212 NS_ENSURE_TRUE(statement, NS_ERROR_UNEXPECTED);
2213 mozStorageStatementScoper scoper(statement);
2215 rv = statement->BindInt64Parameter(0, bookmarkID);
2216 NS_ENSURE_SUCCESS(rv, rv);
2218 PRBool hasMore;
2219 if (NS_SUCCEEDED(statement->ExecuteStep(&hasMore)) && hasMore) {
2220 nsCAutoString spec;
2221 statement->GetUTF8String(nsNavHistory::kGetInfoIndex_URL, spec);
2222 return NS_NewURI(_retval, spec);
2225 return NS_OK;
2228 NS_IMETHODIMP
2229 nsNavBookmarks::ChangeBookmarkURI(PRInt64 aBookmarkId, nsIURI *aNewURI)
2231 NS_ENSURE_ARG(aNewURI);
2233 mozIStorageConnection *dbConn = DBConn();
2234 mozStorageTransaction transaction(dbConn, PR_FALSE);
2236 PRInt64 placeId;
2237 nsresult rv = History()->GetUrlIdFor(aNewURI, &placeId, PR_TRUE);
2238 NS_ENSURE_SUCCESS(rv, rv);
2239 if (!placeId)
2240 return NS_ERROR_INVALID_ARG;
2242 nsCOMPtr<mozIStorageStatement> statement;
2243 rv = dbConn->CreateStatement(NS_LITERAL_CSTRING("UPDATE moz_bookmarks SET fk = ?1 WHERE id = ?2"),
2244 getter_AddRefs(statement));
2245 statement->BindInt64Parameter(0, placeId);
2246 statement->BindInt64Parameter(1, aBookmarkId);
2248 rv = statement->Execute();
2249 NS_ENSURE_SUCCESS(rv, rv);
2251 rv = SetItemDateInternal(mDBSetItemLastModified, aBookmarkId, PR_Now());
2252 NS_ENSURE_SUCCESS(rv, rv);
2254 rv = transaction.Commit();
2255 NS_ENSURE_SUCCESS(rv, rv);
2257 // upon changing the uri for a bookmark, update the frecency for the new place
2258 // no need to check if this is a livemark, because...
2259 rv = History()->UpdateFrecency(placeId, PR_TRUE /* isBookmark */);
2260 NS_ENSURE_SUCCESS(rv, rv);
2262 #if 0
2263 // upon changing the uri for a bookmark, update the frecency for the old place
2264 // XXX todo, we need to get the oldPlaceId (fk) before changing it above
2265 // and then here, we need to determine if that oldPlaceId is still a bookmark (and not a livemark)
2266 rv = History()->UpdateFrecency(oldPlaceId, PR_FALSE /* isBookmark */);
2267 NS_ENSURE_SUCCESS(rv, rv);
2268 #endif
2270 nsCAutoString spec;
2271 rv = aNewURI->GetSpec(spec);
2272 NS_ENSURE_SUCCESS(rv, rv);
2274 // Pass the new URI to OnItemChanged.
2275 ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver,
2276 OnItemChanged(aBookmarkId, NS_LITERAL_CSTRING("uri"), PR_FALSE, spec))
2278 return NS_OK;
2281 NS_IMETHODIMP
2282 nsNavBookmarks::GetFolderIdForItem(PRInt64 aItemId, PRInt64 *aFolderId)
2284 NS_ENSURE_ARG_POINTER(aFolderId);
2286 mozStorageStatementScoper scope(mDBGetItemProperties);
2287 nsresult rv = mDBGetItemProperties->BindInt64Parameter(0, aItemId);
2288 NS_ENSURE_SUCCESS(rv, rv);
2290 PRBool results;
2291 rv = mDBGetItemProperties->ExecuteStep(&results);
2292 NS_ENSURE_SUCCESS(rv, rv);
2294 if (!results)
2295 return NS_ERROR_INVALID_ARG; // invalid item id
2297 rv = mDBGetItemProperties->GetInt64(kGetItemPropertiesIndex_Parent, aFolderId);
2298 NS_ENSURE_SUCCESS(rv, rv);
2300 // this should not happen, but see bug #400448 for details
2301 NS_ENSURE_TRUE(aItemId != *aFolderId, NS_ERROR_UNEXPECTED);
2302 return NS_OK;
2305 NS_IMETHODIMP
2306 nsNavBookmarks::GetBookmarkIdsForURITArray(nsIURI *aURI,
2307 nsTArray<PRInt64> *aResult)
2309 mozStorageStatementScoper scope(mDBFindURIBookmarks);
2311 nsresult rv = BindStatementURI(mDBFindURIBookmarks, 0, aURI);
2312 NS_ENSURE_SUCCESS(rv, rv);
2313 mDBFindURIBookmarks->BindInt32Parameter(1, TYPE_BOOKMARK);
2315 PRBool more;
2316 while (NS_SUCCEEDED((rv = mDBFindURIBookmarks->ExecuteStep(&more))) && more) {
2317 if (! aResult->AppendElement(
2318 mDBFindURIBookmarks->AsInt64(kFindBookmarksIndex_ID)))
2319 return NS_ERROR_OUT_OF_MEMORY;
2322 NS_ENSURE_SUCCESS(rv, rv);
2323 return NS_OK;
2326 NS_IMETHODIMP
2327 nsNavBookmarks::GetBookmarkIdsForURI(nsIURI *aURI, PRUint32 *aCount,
2328 PRInt64 **aBookmarks)
2330 *aCount = 0;
2331 *aBookmarks = nsnull;
2332 nsTArray<PRInt64> bookmarks;
2334 // Get the information from the DB as a TArray
2335 nsresult rv = GetBookmarkIdsForURITArray(aURI, &bookmarks);
2336 NS_ENSURE_SUCCESS(rv, rv);
2338 // Copy the results into a new array for output
2339 if (bookmarks.Length()) {
2340 *aBookmarks = static_cast<PRInt64*>
2341 (nsMemory::Alloc(sizeof(PRInt64) * bookmarks.Length()));
2342 if (! *aBookmarks)
2343 return NS_ERROR_OUT_OF_MEMORY;
2344 for (PRUint32 i = 0; i < bookmarks.Length(); i ++)
2345 (*aBookmarks)[i] = bookmarks[i];
2347 *aCount = bookmarks.Length();
2349 return NS_OK;
2352 NS_IMETHODIMP
2353 nsNavBookmarks::GetItemIndex(PRInt64 aItemId, PRInt32 *aIndex)
2355 mozStorageStatementScoper scope(mDBGetItemIndex);
2356 mDBGetItemIndex->BindInt64Parameter(0, aItemId);
2357 PRBool results;
2358 nsresult rv = mDBGetItemIndex->ExecuteStep(&results);
2359 NS_ENSURE_SUCCESS(rv, rv);
2360 if (!results) {
2361 *aIndex = -1;
2362 return NS_OK;
2365 *aIndex = mDBGetItemIndex->AsInt32(0);
2366 return NS_OK;
2369 NS_IMETHODIMP
2370 nsNavBookmarks::SetItemIndex(PRInt64 aItemId, PRInt32 aNewIndex)
2372 nsresult rv;
2373 PRInt32 oldIndex = 0;
2374 PRInt64 parent = 0;
2377 mozStorageStatementScoper scopeGet(mDBGetItemProperties);
2378 rv = mDBGetItemProperties->BindInt64Parameter(0, aItemId);
2379 NS_ENSURE_SUCCESS(rv, rv);
2381 PRBool results;
2382 rv = mDBGetItemProperties->ExecuteStep(&results);
2383 NS_ENSURE_SUCCESS(rv, rv);
2384 if (!results)
2385 return NS_OK;
2387 oldIndex = mDBGetItemProperties->AsInt32(kGetItemPropertiesIndex_Position);
2388 parent = mDBGetItemProperties->AsInt64(kGetItemPropertiesIndex_Parent);
2391 mozStorageStatementScoper scoper(mDBSetItemIndex);
2392 rv = mDBSetItemIndex->BindInt64Parameter(0, aItemId);
2393 NS_ENSURE_SUCCESS(rv, rv);
2394 rv = mDBSetItemIndex->BindInt32Parameter(1, aNewIndex);
2395 NS_ENSURE_SUCCESS(rv, rv);
2397 rv = mDBSetItemIndex->Execute();
2398 NS_ENSURE_SUCCESS(rv, rv);
2400 ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver,
2401 OnItemRemoved(aItemId, parent, oldIndex))
2402 ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver,
2403 OnItemAdded(aItemId, parent, aNewIndex))
2405 return NS_OK;
2408 NS_IMETHODIMP
2409 nsNavBookmarks::SetKeywordForBookmark(PRInt64 aBookmarkId, const nsAString& aKeyword)
2411 if (aBookmarkId < 1)
2412 return NS_ERROR_INVALID_ARG; // invalid bookmark id
2414 // Shortcuts are always lowercased internally.
2415 nsAutoString kwd(aKeyword);
2416 ToLowerCase(kwd);
2418 mozStorageTransaction transaction(DBConn(), PR_FALSE);
2419 nsresult rv;
2420 PRBool results;
2421 PRInt64 keywordId = 0;
2423 if (!kwd.IsEmpty()) {
2424 // Attempt to find pre-existing keyword record
2425 nsCOMPtr<mozIStorageStatement> getKeywordStmnt;
2426 rv = DBConn()->CreateStatement(NS_LITERAL_CSTRING(
2427 "SELECT id from moz_keywords WHERE keyword = ?1"),
2428 getter_AddRefs(getKeywordStmnt));
2429 NS_ENSURE_SUCCESS(rv, rv);
2430 rv = getKeywordStmnt->BindStringParameter(0, kwd);
2431 NS_ENSURE_SUCCESS(rv, rv);
2433 rv = getKeywordStmnt->ExecuteStep(&results);
2434 NS_ENSURE_SUCCESS(rv, rv);
2436 if (results) {
2437 rv = getKeywordStmnt->GetInt64(0, &keywordId);
2438 NS_ENSURE_SUCCESS(rv, rv);
2440 else {
2441 // If not already in the db, create new keyword record
2442 nsCOMPtr<mozIStorageStatement> addKeywordStmnt;
2443 rv = DBConn()->CreateStatement(NS_LITERAL_CSTRING(
2444 "INSERT INTO moz_keywords (keyword) VALUES (?1)"),
2445 getter_AddRefs(addKeywordStmnt));
2446 NS_ENSURE_SUCCESS(rv, rv);
2447 rv = addKeywordStmnt->BindStringParameter(0, kwd);
2448 NS_ENSURE_SUCCESS(rv, rv);
2449 rv = addKeywordStmnt->Execute();
2450 NS_ENSURE_SUCCESS(rv, rv);
2452 nsCOMPtr<mozIStorageStatement> idStmt;
2453 rv = DBConn()->CreateStatement(NS_LITERAL_CSTRING(
2454 "SELECT id "
2455 "FROM moz_keywords "
2456 "ORDER BY ROWID DESC "
2457 "LIMIT 1"),
2458 getter_AddRefs(idStmt));
2459 NS_ENSURE_SUCCESS(rv, rv);
2461 PRBool hasResult;
2462 rv = idStmt->ExecuteStep(&hasResult);
2463 NS_ENSURE_SUCCESS(rv, rv);
2464 NS_ASSERTION(hasResult, "hasResult is false but the call succeeded?");
2465 keywordId = idStmt->AsInt64(0);
2469 // Update bookmark record w/ the keyword's id, or null
2470 nsCOMPtr<mozIStorageStatement> updateKeywordStmnt;
2471 rv = DBConn()->CreateStatement(NS_LITERAL_CSTRING(
2472 "UPDATE moz_bookmarks SET keyword_id = ?1 WHERE id = ?2"),
2473 getter_AddRefs(updateKeywordStmnt));
2474 NS_ENSURE_SUCCESS(rv, rv);
2475 rv = updateKeywordStmnt->BindInt64Parameter(0, keywordId);
2476 NS_ENSURE_SUCCESS(rv, rv);
2477 rv = updateKeywordStmnt->BindInt64Parameter(1, aBookmarkId);
2478 NS_ENSURE_SUCCESS(rv, rv);
2479 rv = updateKeywordStmnt->Execute();
2480 NS_ENSURE_SUCCESS(rv, rv);
2482 rv = SetItemDateInternal(mDBSetItemLastModified, aBookmarkId, PR_Now());
2483 NS_ENSURE_SUCCESS(rv, rv);
2485 transaction.Commit();
2487 // Pass the new keyword to OnItemChanged.
2488 ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver,
2489 OnItemChanged(aBookmarkId, NS_LITERAL_CSTRING("keyword"),
2490 PR_FALSE, NS_ConvertUTF16toUTF8(aKeyword)))
2492 return NS_OK;
2495 NS_IMETHODIMP
2496 nsNavBookmarks::GetKeywordForURI(nsIURI* aURI, nsAString& aKeyword)
2498 aKeyword.Truncate(0);
2500 mozStorageStatementScoper scoper(mDBGetKeywordForURI);
2501 nsresult rv = BindStatementURI(mDBGetKeywordForURI, 0, aURI);
2502 NS_ENSURE_SUCCESS(rv, rv);
2504 PRBool hasMore = PR_FALSE;
2505 rv = mDBGetKeywordForURI->ExecuteStep(&hasMore);
2506 if (NS_FAILED(rv) || ! hasMore) {
2507 aKeyword.SetIsVoid(PR_TRUE);
2508 return NS_OK; // not found: return void keyword string
2511 // found, get the keyword
2512 return mDBGetKeywordForURI->GetString(0, aKeyword);
2515 NS_IMETHODIMP
2516 nsNavBookmarks::GetKeywordForBookmark(PRInt64 aBookmarkId, nsAString& aKeyword)
2518 aKeyword.Truncate(0);
2520 mozStorageStatementScoper scoper(mDBGetKeywordForBookmark);
2521 nsresult rv = mDBGetKeywordForBookmark->BindInt64Parameter(0, aBookmarkId);
2522 NS_ENSURE_SUCCESS(rv, rv);
2524 PRBool hasMore = PR_FALSE;
2525 rv = mDBGetKeywordForBookmark->ExecuteStep(&hasMore);
2526 if (NS_FAILED(rv) || ! hasMore) {
2527 aKeyword.SetIsVoid(PR_TRUE);
2528 return NS_OK; // not found: return void keyword string
2531 // found, get the keyword
2532 return mDBGetKeywordForBookmark->GetString(0, aKeyword);
2535 NS_IMETHODIMP
2536 nsNavBookmarks::GetURIForKeyword(const nsAString& aKeyword, nsIURI** aURI)
2538 *aURI = nsnull;
2539 if (aKeyword.IsEmpty())
2540 return NS_ERROR_INVALID_ARG;
2542 // Shortcuts are always lowercased internally.
2543 nsAutoString kwd(aKeyword);
2544 ToLowerCase(kwd);
2546 mozStorageStatementScoper scoper(mDBGetURIForKeyword);
2547 nsresult rv = mDBGetURIForKeyword->BindStringParameter(0, kwd);
2548 NS_ENSURE_SUCCESS(rv, rv);
2550 PRBool hasMore = PR_FALSE;
2551 rv = mDBGetURIForKeyword->ExecuteStep(&hasMore);
2552 if (NS_FAILED(rv) || ! hasMore)
2553 return NS_OK; // not found: leave URI null
2555 // found, get the URI
2556 nsCAutoString spec;
2557 rv = mDBGetURIForKeyword->GetUTF8String(0, spec);
2558 NS_ENSURE_SUCCESS(rv, rv);
2559 return NS_NewURI(aURI, spec);
2562 // See RunInBatchMode
2563 nsresult
2564 nsNavBookmarks::BeginUpdateBatch()
2566 if (mBatchLevel++ == 0) {
2567 mozIStorageConnection* conn = DBConn();
2568 PRBool transactionInProgress = PR_TRUE; // default to no transaction on err
2569 conn->GetTransactionInProgress(&transactionInProgress);
2570 mBatchHasTransaction = ! transactionInProgress;
2571 if (mBatchHasTransaction)
2572 conn->BeginTransaction();
2574 ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver,
2575 OnBeginUpdateBatch())
2577 return NS_OK;
2580 nsresult
2581 nsNavBookmarks::EndUpdateBatch()
2583 if (--mBatchLevel == 0) {
2584 if (mBatchHasTransaction)
2585 DBConn()->CommitTransaction();
2586 mBatchHasTransaction = PR_FALSE;
2587 ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver,
2588 OnEndUpdateBatch())
2590 return NS_OK;
2593 NS_IMETHODIMP
2594 nsNavBookmarks::RunInBatchMode(nsINavHistoryBatchCallback* aCallback,
2595 nsISupports* aUserData) {
2596 NS_ENSURE_ARG_POINTER(aCallback);
2598 BeginUpdateBatch();
2599 nsresult rv = aCallback->RunBatched(aUserData);
2600 EndUpdateBatch();
2602 return rv;
2605 NS_IMETHODIMP
2606 nsNavBookmarks::AddObserver(nsINavBookmarkObserver *aObserver,
2607 PRBool aOwnsWeak)
2609 return mObservers.AppendWeakElement(aObserver, aOwnsWeak);
2612 NS_IMETHODIMP
2613 nsNavBookmarks::RemoveObserver(nsINavBookmarkObserver *aObserver)
2615 return mObservers.RemoveWeakElement(aObserver);
2619 * Called by the History service when shutting down
2621 nsresult
2622 nsNavBookmarks::OnQuit()
2624 return NS_OK;
2627 // nsNavBookmarks::nsINavHistoryObserver
2629 NS_IMETHODIMP
2630 nsNavBookmarks::OnBeginUpdateBatch()
2632 // These aren't passed through to bookmark observers currently.
2633 return NS_OK;
2636 NS_IMETHODIMP
2637 nsNavBookmarks::OnEndUpdateBatch()
2639 // These aren't passed through to bookmark observers currently.
2640 return NS_OK;
2643 NS_IMETHODIMP
2644 nsNavBookmarks::OnVisit(nsIURI *aURI, PRInt64 aVisitID, PRTime aTime,
2645 PRInt64 aSessionID, PRInt64 aReferringID,
2646 PRUint32 aTransitionType, PRUint32* aAdded)
2648 // If the page is bookmarked, we need to notify observers
2649 PRBool bookmarked = PR_FALSE;
2650 IsBookmarked(aURI, &bookmarked);
2651 if (bookmarked) {
2652 // query for all bookmarks for that URI, notify for each
2653 nsTArray<PRInt64> bookmarks;
2655 nsresult rv = GetBookmarkIdsForURITArray(aURI, &bookmarks);
2656 NS_ENSURE_SUCCESS(rv, rv);
2658 if (bookmarks.Length()) {
2659 for (PRUint32 i = 0; i < bookmarks.Length(); i++)
2660 ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver,
2661 OnItemVisited(bookmarks[i], aVisitID, aTime))
2664 return NS_OK;
2667 NS_IMETHODIMP
2668 nsNavBookmarks::OnDeleteURI(nsIURI *aURI)
2670 // If the page is bookmarked, we need to notify observers
2671 PRBool bookmarked = PR_FALSE;
2672 IsBookmarked(aURI, &bookmarked);
2673 if (bookmarked) {
2674 // query for all bookmarks for that URI, notify for each
2675 nsTArray<PRInt64> bookmarks;
2677 nsresult rv = GetBookmarkIdsForURITArray(aURI, &bookmarks);
2678 NS_ENSURE_SUCCESS(rv, rv);
2680 if (bookmarks.Length()) {
2681 for (PRUint32 i = 0; i < bookmarks.Length(); i ++)
2682 ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver,
2683 OnItemChanged(bookmarks[i], NS_LITERAL_CSTRING("cleartime"),
2684 PR_FALSE, EmptyCString()))
2687 return NS_OK;
2690 NS_IMETHODIMP
2691 nsNavBookmarks::OnClearHistory()
2693 // TODO(bryner): we should notify on visited-time change for all URIs
2694 return NS_OK;
2697 NS_IMETHODIMP
2698 nsNavBookmarks::OnTitleChanged(nsIURI* aURI, const nsAString& aPageTitle)
2700 // NOOP. We don't consume page titles from moz_places anymore.
2701 // Title-change notifications are sent from SetItemTitle.
2702 return NS_OK;
2705 NS_IMETHODIMP
2706 nsNavBookmarks::OnPageChanged(nsIURI *aURI, PRUint32 aWhat,
2707 const nsAString &aValue)
2709 nsresult rv;
2710 if (aWhat == nsINavHistoryObserver::ATTRIBUTE_FAVICON) {
2711 // Favicons may be set to either pure URIs or to folder URIs
2712 PRBool isPlaceURI;
2713 rv = aURI->SchemeIs("place", &isPlaceURI);
2714 NS_ENSURE_SUCCESS(rv, rv);
2715 if (isPlaceURI) {
2716 nsCAutoString spec;
2717 rv = aURI->GetSpec(spec);
2718 NS_ENSURE_SUCCESS(rv, rv);
2720 nsNavHistory* history = History();
2721 NS_ENSURE_TRUE(history, NS_ERROR_UNEXPECTED);
2723 nsCOMArray<nsNavHistoryQuery> queries;
2724 nsCOMPtr<nsNavHistoryQueryOptions> options;
2725 rv = history->QueryStringToQueryArray(spec, &queries, getter_AddRefs(options));
2726 NS_ENSURE_SUCCESS(rv, rv);
2728 NS_ENSURE_STATE(queries.Count() == 1);
2729 NS_ENSURE_STATE(queries[0]->Folders().Length() == 1);
2731 ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver,
2732 OnItemChanged(queries[0]->Folders()[0], NS_LITERAL_CSTRING("favicon"),
2733 PR_FALSE, NS_ConvertUTF16toUTF8(aValue)));
2735 else {
2736 // query for all bookmarks for that URI, notify for each
2737 nsTArray<PRInt64> bookmarks;
2738 rv = GetBookmarkIdsForURITArray(aURI, &bookmarks);
2739 NS_ENSURE_SUCCESS(rv, rv);
2741 if (bookmarks.Length()) {
2742 for (PRUint32 i = 0; i < bookmarks.Length(); i ++)
2743 ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver,
2744 OnItemChanged(bookmarks[i], NS_LITERAL_CSTRING("favicon"),
2745 PR_FALSE, NS_ConvertUTF16toUTF8(aValue)));
2749 return NS_OK;
2752 NS_IMETHODIMP
2753 nsNavBookmarks::OnPageExpired(nsIURI* aURI, PRTime aVisitTime,
2754 PRBool aWholeEntry)
2756 // pages that are bookmarks shouldn't expire, so we don't need to handle it
2757 return NS_OK;
2760 // nsIAnnotationObserver
2762 NS_IMETHODIMP
2763 nsNavBookmarks::OnPageAnnotationSet(nsIURI* aPage, const nsACString& aName)
2765 return NS_OK;
2768 NS_IMETHODIMP
2769 nsNavBookmarks::OnItemAnnotationSet(PRInt64 aItemId, const nsACString& aName)
2771 nsresult rv = SetItemDateInternal(mDBSetItemLastModified, aItemId, PR_Now());
2772 NS_ENSURE_SUCCESS(rv, rv);
2774 ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver,
2775 OnItemChanged(aItemId, aName, PR_TRUE, EmptyCString()));
2777 return NS_OK;
2781 NS_IMETHODIMP
2782 nsNavBookmarks::OnPageAnnotationRemoved(nsIURI* aPage, const nsACString& aName)
2784 return NS_OK;
2787 NS_IMETHODIMP
2788 nsNavBookmarks::OnItemAnnotationRemoved(PRInt64 aItemId, const nsACString& aName)
2790 nsresult rv = SetItemDateInternal(mDBSetItemLastModified, aItemId, PR_Now());
2791 NS_ENSURE_SUCCESS(rv, rv);
2793 ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver,
2794 OnItemChanged(aItemId, aName, PR_TRUE, EmptyCString()));
2796 return NS_OK;
2799 PRBool
2800 nsNavBookmarks::ItemExists(PRInt64 aItemId) {
2801 mozStorageStatementScoper scope(mDBGetItemProperties);
2802 nsresult rv = mDBGetItemProperties->BindInt64Parameter(0, aItemId);
2803 NS_ENSURE_SUCCESS(rv, PR_FALSE);
2805 PRBool results;
2806 rv = mDBGetItemProperties->ExecuteStep(&results);
2807 NS_ENSURE_SUCCESS(rv, PR_FALSE);
2809 if (!results)
2810 return PR_FALSE;
2812 return PR_TRUE;