Bug 462366 - correct bogus importBookmarksHTML behaviour, r=dietrich a=beltzner
[wine-gecko.git] / toolkit / components / places / src / nsNavBookmarks.cpp
blobfbf2d28a40f13b90f13315ff2a1b6f4becc96cec
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_PlaceID = 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_PlaceID = 13;
67 const PRInt32 nsNavBookmarks::kGetChildrenIndex_ServiceContractId = 14;
69 const PRInt32 nsNavBookmarks::kGetItemPropertiesIndex_ID = 0;
70 const PRInt32 nsNavBookmarks::kGetItemPropertiesIndex_URI = 1;
71 const PRInt32 nsNavBookmarks::kGetItemPropertiesIndex_Title = 2;
72 const PRInt32 nsNavBookmarks::kGetItemPropertiesIndex_Position = 3;
73 const PRInt32 nsNavBookmarks::kGetItemPropertiesIndex_PlaceID = 4;
74 const PRInt32 nsNavBookmarks::kGetItemPropertiesIndex_Parent = 5;
75 const PRInt32 nsNavBookmarks::kGetItemPropertiesIndex_Type = 6;
76 const PRInt32 nsNavBookmarks::kGetItemPropertiesIndex_ServiceContractId = 7;
77 const PRInt32 nsNavBookmarks::kGetItemPropertiesIndex_DateAdded = 8;
78 const PRInt32 nsNavBookmarks::kGetItemPropertiesIndex_LastModified = 9;
80 nsNavBookmarks* nsNavBookmarks::sInstance = nsnull;
82 #define BOOKMARKS_ANNO_PREFIX "bookmarks/"
83 #define BOOKMARKS_TOOLBAR_FOLDER_ANNO NS_LITERAL_CSTRING(BOOKMARKS_ANNO_PREFIX "toolbarFolder")
84 #define GUID_ANNO NS_LITERAL_CSTRING("placesInternal/GUID")
85 #define READ_ONLY_ANNO NS_LITERAL_CSTRING("placesInternal/READ_ONLY")
87 nsNavBookmarks::nsNavBookmarks()
88 : mItemCount(0), mRoot(0), mBookmarksRoot(0), mTagRoot(0), mToolbarFolder(0), mBatchLevel(0),
89 mBatchHasTransaction(PR_FALSE)
91 NS_ASSERTION(!sInstance, "Multiple nsNavBookmarks instances!");
92 sInstance = this;
95 nsNavBookmarks::~nsNavBookmarks()
97 NS_ASSERTION(sInstance == this, "Expected sInstance == this");
98 sInstance = nsnull;
101 NS_IMPL_ISUPPORTS3(nsNavBookmarks,
102 nsINavBookmarksService,
103 nsINavHistoryObserver,
104 nsIAnnotationObserver)
106 nsresult
107 nsNavBookmarks::Init()
109 nsNavHistory *history = History();
110 NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY);
111 mDBConn = history->GetStorageConnection();
112 mozStorageTransaction transaction(mDBConn, PR_FALSE);
114 nsresult rv = InitStatements();
115 NS_ENSURE_SUCCESS(rv, rv);
117 rv = FillBookmarksHash();
118 NS_ENSURE_SUCCESS(rv, rv);
120 rv = InitRoots();
121 NS_ENSURE_SUCCESS(rv, rv);
123 rv = transaction.Commit();
124 NS_ENSURE_SUCCESS(rv, rv);
126 // Add observers
127 nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService();
128 NS_ENSURE_TRUE(annosvc, NS_ERROR_OUT_OF_MEMORY);
129 annosvc->AddObserver(this);
131 // allows us to notify on title changes. MUST BE LAST so it is impossible
132 // to fail after this call, or the history service will have a reference to
133 // us and we won't go away.
134 history->AddObserver(this, PR_FALSE);
136 // DO NOT PUT STUFF HERE that can fail. See observer comment above.
138 return NS_OK;
141 // nsNavBookmarks::InitTables
143 // All commands that initialize the schema of the DB go in here. This is
144 // called from history init before the dummy DB connection is started that
145 // will prevent us from modifying the schema.
147 nsresult // static
148 nsNavBookmarks::InitTables(mozIStorageConnection* aDBConn)
150 PRBool exists;
151 nsresult rv = aDBConn->TableExists(NS_LITERAL_CSTRING("moz_bookmarks"), &exists);
152 NS_ENSURE_SUCCESS(rv, rv);
153 if (! exists) {
154 rv = aDBConn->ExecuteSimpleSQL(CREATE_MOZ_BOOKMARKS);
155 NS_ENSURE_SUCCESS(rv, rv);
157 // This index will make it faster to determine if a given item is
158 // bookmarked (used by history queries and vacuuming, for example).
159 // Making it compound with "type" speeds up type-differentiation
160 // queries, such as expiration and search.
161 rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
162 "CREATE INDEX moz_bookmarks_itemindex ON moz_bookmarks (fk, type)"));
163 NS_ENSURE_SUCCESS(rv, rv);
165 // The most common operation is to find the children given a parent and position.
166 rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
167 "CREATE INDEX moz_bookmarks_parentindex "
168 "ON moz_bookmarks (parent, position)"));
169 NS_ENSURE_SUCCESS(rv, rv);
171 // fast access to lastModified is useful during sync and to get
172 // last modified bookmark title for tags container's children.
173 rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
174 "CREATE INDEX moz_bookmarks_itemlastmodifiedindex "
175 "ON moz_bookmarks (fk, lastModified)"));
176 NS_ENSURE_SUCCESS(rv, rv);
179 // moz_bookmarks_roots
180 rv = aDBConn->TableExists(NS_LITERAL_CSTRING("moz_bookmarks_roots"), &exists);
181 NS_ENSURE_SUCCESS(rv, rv);
182 if (!exists) {
183 rv = aDBConn->ExecuteSimpleSQL(CREATE_MOZ_BOOKMARKS_ROOTS);
184 NS_ENSURE_SUCCESS(rv, rv);
187 // moz_keywords
188 rv = aDBConn->TableExists(NS_LITERAL_CSTRING("moz_keywords"), &exists);
189 NS_ENSURE_SUCCESS(rv, rv);
190 if (! exists) {
191 rv = aDBConn->ExecuteSimpleSQL(CREATE_MOZ_KEYWORDS);
192 NS_ENSURE_SUCCESS(rv, rv);
194 // Create trigger to update as well
195 rv = aDBConn->ExecuteSimpleSQL(CREATE_KEYWORD_VALIDITY_TRIGGER);
196 NS_ENSURE_SUCCESS(rv, rv);
199 return NS_OK;
203 // nsNavBookmarks::InitStatements
205 // Create common statements to query the database
206 nsresult
207 nsNavBookmarks::InitStatements()
209 // mDBFindURIBookmarks
210 // NOTE: Do not modify the ORDER BY segment of the query, as certain
211 // features depend on it. See bug 398914 for an example.
212 nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
213 "SELECT b.id "
214 "FROM moz_bookmarks b "
215 "JOIN ( "
216 "SELECT id FROM moz_places_temp "
217 "WHERE url = ?1 "
218 "UNION ALL "
219 "SELECT id FROM moz_places "
220 "WHERE url = ?1 "
221 "AND +id NOT IN (SELECT id FROM moz_places_temp) "
222 ") AS h ON b.fk = h.id "
223 "WHERE b.type = ?2 "
224 "ORDER BY MAX(IFNULL(b.lastModified, 0), b.dateAdded) DESC, b.id DESC"),
225 getter_AddRefs(mDBFindURIBookmarks));
226 NS_ENSURE_SUCCESS(rv, rv);
228 // Construct a result where the first columns exactly match those returned by
229 // mDBGetURLPageInfo, and additionally contains columns for position,
230 // item_child, and folder_child from moz_bookmarks.
231 // Results are kGetInfoIndex_*
233 // mDBGetChildren: select all children of a given folder, sorted by position
234 // This is a LEFT OUTER JOIN with moz_places since folders does not have
235 // a reference into that table.
236 rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
237 "SELECT * FROM ( "
238 "SELECT h.id, h.url, COALESCE(b.title, h.title), "
239 "h.rev_host, h.visit_count, "
240 SQL_STR_FRAGMENT_MAX_VISIT_DATE( "h.id" )
241 ", f.url, null, b.id, b.dateAdded, b.lastModified, "
242 "b.position, b.type, b.fk, b.folder_type "
243 "FROM moz_bookmarks b "
244 "JOIN moz_places_temp h ON b.fk = h.id "
245 "LEFT JOIN moz_favicons f ON h.favicon_id = f.id "
246 "WHERE b.parent = ?1 "
247 "UNION ALL "
248 "SELECT h.id, h.url, COALESCE(b.title, h.title), "
249 "h.rev_host, h.visit_count, "
250 SQL_STR_FRAGMENT_MAX_VISIT_DATE( "h.id" )
251 ", f.url, null, b.id, b.dateAdded, b.lastModified, "
252 "b.position, b.type, b.fk, b.folder_type "
253 "FROM moz_bookmarks b "
254 "LEFT JOIN moz_places h ON b.fk = h.id "
255 "LEFT JOIN moz_favicons f ON h.favicon_id = f.id "
256 "WHERE b.parent = ?1 "
257 "AND (b.fk ISNULL OR b.fk NOT IN (select id FROM moz_places_temp)) "
258 ") "
259 "ORDER BY 12 ASC"), /* position */
260 getter_AddRefs(mDBGetChildren));
261 NS_ENSURE_SUCCESS(rv, rv);
263 // mDBFolderCount: count all of the children of a given folder
264 rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
265 "SELECT COUNT(*) FROM moz_bookmarks WHERE parent = ?1"),
266 getter_AddRefs(mDBFolderCount));
267 NS_ENSURE_SUCCESS(rv, rv);
269 rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
270 "SELECT position FROM moz_bookmarks WHERE id = ?1"),
271 getter_AddRefs(mDBGetItemIndex));
272 NS_ENSURE_SUCCESS(rv, rv);
274 rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
275 "SELECT id, fk, type FROM moz_bookmarks WHERE parent = ?1 AND position = ?2"),
276 getter_AddRefs(mDBGetChildAt));
277 NS_ENSURE_SUCCESS(rv, rv);
279 // get bookmark/folder/separator properties
280 rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
281 "SELECT b.id, "
282 "IFNULL( "
283 "(SELECT url FROM moz_places_temp "
284 "WHERE id = (SELECT fk FROM moz_bookmarks WHERE id = ?1)) "
285 ", "
286 "(SELECT url FROM moz_places "
287 "WHERE id = (SELECT fk FROM moz_bookmarks WHERE id = ?1)) "
288 "), b.title, b.position, b.fk, b.parent, b.type, b.folder_type, "
289 "b.dateAdded, b.lastModified "
290 "FROM moz_bookmarks b "
291 "WHERE b.id = ?1"),
292 getter_AddRefs(mDBGetItemProperties));
293 NS_ENSURE_SUCCESS(rv, rv);
295 rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
296 "SELECT item_id FROM moz_items_annos "
297 "WHERE content = ?1 "
298 "LIMIT 1"),
299 getter_AddRefs(mDBGetItemIdForGUID));
300 NS_ENSURE_SUCCESS(rv, rv);
302 // mDBGetRedirectDestinations
303 // input = page ID, time threshold; output = unique ID input has redirected to
304 rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
305 "SELECT DISTINCT dest_v.place_id "
306 "FROM moz_historyvisits_temp source_v "
307 "JOIN moz_historyvisits_temp dest_v ON dest_v.from_visit = source_v.id "
308 "WHERE source_v.place_id = ?1 "
309 "AND source_v.visit_date >= ?2 "
310 "AND dest_v.visit_type IN (") +
311 nsPrintfCString("%d,%d",
312 nsINavHistoryService::TRANSITION_REDIRECT_PERMANENT,
313 nsINavHistoryService::TRANSITION_REDIRECT_TEMPORARY) +
314 NS_LITERAL_CSTRING(") "
315 "UNION "
316 "SELECT DISTINCT dest_v.place_id "
317 "FROM moz_historyvisits_temp source_v "
318 "JOIN moz_historyvisits dest_v ON dest_v.from_visit = source_v.id "
319 "WHERE source_v.place_id = ?1 "
320 "AND source_v.visit_date >= ?2 "
321 "AND dest_v.visit_type IN (") +
322 nsPrintfCString("%d,%d",
323 nsINavHistoryService::TRANSITION_REDIRECT_PERMANENT,
324 nsINavHistoryService::TRANSITION_REDIRECT_TEMPORARY) +
325 NS_LITERAL_CSTRING(") "
326 "UNION "
327 "SELECT DISTINCT dest_v.place_id "
328 "FROM moz_historyvisits source_v "
329 "JOIN moz_historyvisits_temp dest_v ON dest_v.from_visit = source_v.id "
330 "WHERE source_v.place_id = ?1 "
331 "AND source_v.visit_date >= ?2 "
332 "AND dest_v.visit_type IN (") +
333 nsPrintfCString("%d,%d",
334 nsINavHistoryService::TRANSITION_REDIRECT_PERMANENT,
335 nsINavHistoryService::TRANSITION_REDIRECT_TEMPORARY) +
336 NS_LITERAL_CSTRING(") "
337 "UNION "
338 "SELECT DISTINCT dest_v.place_id "
339 "FROM moz_historyvisits source_v "
340 "JOIN moz_historyvisits dest_v ON dest_v.from_visit = source_v.id "
341 "WHERE source_v.place_id = ?1 "
342 "AND source_v.visit_date >= ?2 "
343 "AND dest_v.visit_type IN (") +
344 nsPrintfCString("%d,%d",
345 nsINavHistoryService::TRANSITION_REDIRECT_PERMANENT,
346 nsINavHistoryService::TRANSITION_REDIRECT_TEMPORARY) +
347 NS_LITERAL_CSTRING(") "),
348 getter_AddRefs(mDBGetRedirectDestinations));
349 NS_ENSURE_SUCCESS(rv, rv);
351 // mDBInsertBookmark
352 rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
353 "INSERT INTO moz_bookmarks "
354 "(fk, type, parent, position, title, dateAdded) "
355 "VALUES (?1, ?2, ?3, ?4, ?5, ?6)"),
356 getter_AddRefs(mDBInsertBookmark));
357 NS_ENSURE_SUCCESS(rv, rv);
359 // mDBIsBookmarkedInDatabase
360 // Just select position since it's just an int32 and may be faster.
361 // We don't actually care about the data, just whether there is any.
362 rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
363 "SELECT position FROM moz_bookmarks WHERE fk = ?1 AND type = ?2"),
364 getter_AddRefs(mDBIsBookmarkedInDatabase));
365 NS_ENSURE_SUCCESS(rv, rv);
367 // mDBGetLastBookmarkID
368 rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
369 "SELECT id "
370 "FROM moz_bookmarks "
371 "ORDER BY ROWID DESC "
372 "LIMIT 1"),
373 getter_AddRefs(mDBGetLastBookmarkID));
374 NS_ENSURE_SUCCESS(rv, rv);
376 // mDBSetItemDateAdded
377 rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
378 "UPDATE moz_bookmarks SET dateAdded = ?1 WHERE id = ?2"),
379 getter_AddRefs(mDBSetItemDateAdded));
380 NS_ENSURE_SUCCESS(rv, rv);
382 // mDBSetItemLastModified
383 rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
384 "UPDATE moz_bookmarks SET lastModified = ?1 WHERE id = ?2"),
385 getter_AddRefs(mDBSetItemLastModified));
386 NS_ENSURE_SUCCESS(rv, rv);
388 // mDBSetItemIndex
389 rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
390 "UPDATE moz_bookmarks SET position = ?2 WHERE id = ?1"),
391 getter_AddRefs(mDBSetItemIndex));
392 NS_ENSURE_SUCCESS(rv, rv);
394 // get keyword text for bookmark id
395 rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
396 "SELECT k.keyword FROM moz_bookmarks b "
397 "JOIN moz_keywords k ON k.id = b.keyword_id "
398 "WHERE b.id = ?1"),
399 getter_AddRefs(mDBGetKeywordForBookmark));
400 NS_ENSURE_SUCCESS(rv, rv);
401 // get keyword text for URI (must be a bookmarked URI)
402 rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
403 "SELECT k.keyword "
404 "FROM ( "
405 "SELECT id FROM moz_places_temp "
406 "WHERE url = ?1 "
407 "UNION ALL "
408 "SELECT id FROM moz_places "
409 "WHERE +id NOT IN (SELECT id FROM moz_places_temp) "
410 "AND url = ?1 "
411 ") AS h "
412 "JOIN moz_bookmarks b ON b.fk = h.id "
413 "JOIN moz_keywords k ON k.id = b.keyword_id"),
414 getter_AddRefs(mDBGetKeywordForURI));
415 NS_ENSURE_SUCCESS(rv, rv);
417 // get URI for keyword
418 rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
419 "SELECT url FROM moz_keywords k "
420 "JOIN moz_bookmarks b ON b.keyword_id = k.id "
421 "JOIN moz_places_temp h ON b.fk = h.id "
422 "WHERE k.keyword = ?1 "
423 "UNION ALL "
424 "SELECT url FROM moz_keywords k "
425 "JOIN moz_bookmarks b ON b.keyword_id = k.id "
426 "JOIN moz_places h ON b.fk = h.id "
427 "WHERE k.keyword = ?1 "
428 "AND h.id NOT IN (SELECT id FROM moz_places_temp)"),
429 getter_AddRefs(mDBGetURIForKeyword));
430 NS_ENSURE_SUCCESS(rv, rv);
432 return NS_OK;
436 // nsNavBookmarks::InitRoots
438 // This locates and creates if necessary the root items in the bookmarks
439 // folder hierarchy. These items are stored in a special roots table that
440 // maps short predefined names to folder IDs.
442 // Normally, these folders will exist already and we will save their IDs
443 // which are exposed through the bookmark service interface.
445 // If the root does not exist, a folder is created for it and the ID is
446 // saved in the root table. No user-visible name is given to these folders
447 // and they have no parent or other attributes.
449 // These attributes are set when the default_places.html file is imported.
450 // It defines the hierarchy, and has special attributes that tell us when
451 // a folder is one of our well-known roots. We then insert the root in the
452 // defined point in the hierarchy and set its attributes from this.
454 // This should be called as the last part of the init process so that
455 // all of the statements are set up and the service is ready to use.
457 nsresult
458 nsNavBookmarks::InitRoots()
460 nsCOMPtr<mozIStorageStatement> getRootStatement;
461 nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
462 "SELECT folder_id FROM moz_bookmarks_roots WHERE root_name = ?1"),
463 getter_AddRefs(getRootStatement));
464 NS_ENSURE_SUCCESS(rv, rv);
466 PRBool createdPlacesRoot = PR_FALSE;
467 rv = CreateRoot(getRootStatement, NS_LITERAL_CSTRING("places"), &mRoot, 0, &createdPlacesRoot);
468 NS_ENSURE_SUCCESS(rv, rv);
470 getRootStatement->Reset();
471 rv = CreateRoot(getRootStatement, NS_LITERAL_CSTRING("menu"), &mBookmarksRoot, mRoot, nsnull);
472 NS_ENSURE_SUCCESS(rv, rv);
474 PRBool createdToolbarFolder;
475 getRootStatement->Reset();
476 rv = CreateRoot(getRootStatement, NS_LITERAL_CSTRING("toolbar"), &mToolbarFolder, mRoot, &createdToolbarFolder);
477 NS_ENSURE_SUCCESS(rv, rv);
479 // Once toolbar was not a root, we may need to move over the items and
480 // delete the custom folder
481 if (!createdPlacesRoot && createdToolbarFolder) {
482 nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService();
483 NS_ENSURE_TRUE(annosvc, NS_ERROR_OUT_OF_MEMORY);
485 nsTArray<PRInt64> folders;
486 annosvc->GetItemsWithAnnotationTArray(BOOKMARKS_TOOLBAR_FOLDER_ANNO,
487 &folders);
488 if (folders.Length() > 0) {
489 nsCOMPtr<mozIStorageStatement> moveItems;
490 rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
491 "UPDATE moz_bookmarks SET parent = ?1 WHERE parent=?2"),
492 getter_AddRefs(moveItems));
493 rv = moveItems->BindInt64Parameter(0, mToolbarFolder);
494 NS_ENSURE_SUCCESS(rv, rv);
495 rv = moveItems->BindInt64Parameter(1, folders[0]);
496 NS_ENSURE_SUCCESS(rv, rv);
497 rv = moveItems->Execute();
498 NS_ENSURE_SUCCESS(rv, rv);
499 rv = RemoveFolder(folders[0]);
500 NS_ENSURE_SUCCESS(rv, rv);
504 getRootStatement->Reset();
505 rv = CreateRoot(getRootStatement, NS_LITERAL_CSTRING("tags"), &mTagRoot, mRoot, nsnull);
506 NS_ENSURE_SUCCESS(rv, rv);
508 getRootStatement->Reset();
509 rv = CreateRoot(getRootStatement, NS_LITERAL_CSTRING("unfiled"), &mUnfiledRoot, mRoot, nsnull);
510 NS_ENSURE_SUCCESS(rv, rv);
512 // Set titles for special folders
513 // We cannot rely on createdPlacesRoot due to Fx3beta->final migration path
514 nsCOMPtr<nsIPrefService> prefService =
515 do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
516 NS_ENSURE_SUCCESS(rv, rv);
518 nsCOMPtr<nsIPrefBranch> prefBranch;
519 rv = prefService->GetBranch("", getter_AddRefs(prefBranch));
520 NS_ENSURE_SUCCESS(rv, rv);
522 PRUint16 databaseStatus = nsINavHistoryService::DATABASE_STATUS_OK;
523 rv = History()->GetDatabaseStatus(&databaseStatus);
524 NS_ENSURE_SUCCESS(rv, rv);
526 if (NS_FAILED(rv) ||
527 databaseStatus != nsINavHistoryService::DATABASE_STATUS_OK) {
528 rv = InitDefaults();
529 NS_ENSURE_SUCCESS(rv, rv);
532 return NS_OK;
535 // nsNavBookmarks::InitDefaults
537 // Initializes default bookmarks and containers.
538 // Pulls from places.propertes for l10n.
539 // Replaces the old default_places.html file.
540 nsresult
541 nsNavBookmarks::InitDefaults()
543 nsIStringBundle *bundle = History()->GetBundle();
544 NS_ENSURE_TRUE(bundle, NS_ERROR_OUT_OF_MEMORY);
546 // Bookmarks Menu
547 nsXPIDLString bookmarksTitle;
548 nsresult rv = bundle->GetStringFromName(NS_LITERAL_STRING("BookmarksMenuFolderTitle").get(),
549 getter_Copies(bookmarksTitle));
550 NS_ENSURE_SUCCESS(rv, rv);
551 rv = SetItemTitle(mBookmarksRoot, NS_ConvertUTF16toUTF8(bookmarksTitle));
552 NS_ENSURE_SUCCESS(rv, rv);
554 // Bookmarks Toolbar
555 nsXPIDLString toolbarTitle;
556 rv = bundle->GetStringFromName(NS_LITERAL_STRING("BookmarksToolbarFolderTitle").get(),
557 getter_Copies(toolbarTitle));
558 NS_ENSURE_SUCCESS(rv, rv);
559 rv = SetItemTitle(mToolbarFolder, NS_ConvertUTF16toUTF8(toolbarTitle));
560 NS_ENSURE_SUCCESS(rv, rv);
562 // Unsorted Bookmarks
563 nsXPIDLString unfiledTitle;
564 rv = bundle->GetStringFromName(NS_LITERAL_STRING("UnsortedBookmarksFolderTitle").get(),
565 getter_Copies(unfiledTitle));
566 NS_ENSURE_SUCCESS(rv, rv);
567 rv = SetItemTitle(mUnfiledRoot, NS_ConvertUTF16toUTF8(unfiledTitle));
568 NS_ENSURE_SUCCESS(rv, rv);
570 // Tags
571 nsXPIDLString tagsTitle;
572 rv = bundle->GetStringFromName(NS_LITERAL_STRING("TagsFolderTitle").get(),
573 getter_Copies(tagsTitle));
574 NS_ENSURE_SUCCESS(rv, rv);
575 rv = SetItemTitle(mTagRoot, NS_ConvertUTF16toUTF8(tagsTitle));
576 NS_ENSURE_SUCCESS(rv, rv);
578 return NS_OK;
581 // nsNavBookmarks::CreateRoot
583 // This gets or creates a root folder of the given type. aWasCreated
584 // (optional) is true if the folder had to be created, false if we just used
585 // an old one. The statement that gets a folder ID from a root name is
586 // passed in so the DB only needs to parse the statement once, and we don't
587 // have to have a global for this. Creation is less optimized because it
588 // happens rarely.
590 nsresult
591 nsNavBookmarks::CreateRoot(mozIStorageStatement* aGetRootStatement,
592 const nsCString& name, PRInt64* aID,
593 PRInt64 aParentID, PRBool* aWasCreated)
595 PRBool hasResult = PR_FALSE;
596 nsresult rv = aGetRootStatement->BindUTF8StringParameter(0, name);
597 NS_ENSURE_SUCCESS(rv, rv);
598 rv = aGetRootStatement->ExecuteStep(&hasResult);
599 NS_ENSURE_SUCCESS(rv, rv);
600 if (hasResult) {
601 if (aWasCreated)
602 *aWasCreated = PR_FALSE;
603 rv = aGetRootStatement->GetInt64(0, aID);
604 NS_ENSURE_SUCCESS(rv, rv);
605 NS_ASSERTION(*aID != 0, "Root is 0 for some reason, folders can't have 0 ID");
606 return NS_OK;
608 if (aWasCreated)
609 *aWasCreated = PR_TRUE;
611 // create folder with no name or attributes
612 nsCOMPtr<mozIStorageStatement> insertStatement;
613 rv = CreateFolder(aParentID, EmptyCString(), nsINavBookmarksService::DEFAULT_INDEX, aID);
614 NS_ENSURE_SUCCESS(rv, rv);
616 // save root ID
617 rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
618 "INSERT INTO moz_bookmarks_roots (root_name, folder_id) VALUES (?1, ?2)"),
619 getter_AddRefs(insertStatement));
620 NS_ENSURE_SUCCESS(rv, rv);
621 rv = insertStatement->BindUTF8StringParameter(0, name);
622 NS_ENSURE_SUCCESS(rv, rv);
623 rv = insertStatement->BindInt64Parameter(1, *aID);
624 NS_ENSURE_SUCCESS(rv, rv);
625 rv = insertStatement->Execute();
626 NS_ENSURE_SUCCESS(rv, rv);
628 return NS_OK;
632 // nsNavBookmarks::FillBookmarksHash
634 // This initializes the bookmarks hashtable that tells us which bookmark
635 // a given URI redirects to. This hashtable includes all URIs that
636 // redirect to bookmarks.
638 // This is called from the bookmark init function and so is wrapped
639 // in that transaction (for better performance).
641 nsresult
642 nsNavBookmarks::FillBookmarksHash()
644 PRBool hasMore;
646 // first init the hashtable
647 NS_ENSURE_TRUE(mBookmarksHash.Init(1024), NS_ERROR_OUT_OF_MEMORY);
649 // first populate the table with all bookmarks
650 nsCOMPtr<mozIStorageStatement> statement;
651 nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
652 "SELECT h.id "
653 "FROM moz_bookmarks b "
654 "LEFT JOIN moz_places h ON b.fk = h.id where b.type = ?1"),
655 getter_AddRefs(statement));
656 NS_ENSURE_SUCCESS(rv, rv);
657 rv = statement->BindInt32Parameter(0, TYPE_BOOKMARK);
658 NS_ENSURE_SUCCESS(rv, rv);
659 while (NS_SUCCEEDED(statement->ExecuteStep(&hasMore)) && hasMore) {
660 PRInt64 pageID;
661 rv = statement->GetInt64(0, &pageID);
662 NS_ENSURE_TRUE(mBookmarksHash.Put(pageID, pageID), NS_ERROR_OUT_OF_MEMORY);
665 // Find all pages h2 that have been redirected to from a bookmarked URI:
666 // bookmarked -> url (h1) url (h2)
667 // | ^
668 // . |
669 // visit (v1) -> destination visit (v2)
670 // This should catch most redirects, which are only one level. More levels of
671 // redirection will be handled separately.
672 rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
673 "SELECT v1.place_id, v2.place_id "
674 "FROM moz_bookmarks b "
675 "LEFT JOIN moz_historyvisits_temp v1 on b.fk = v1.place_id "
676 "LEFT JOIN moz_historyvisits v2 on v2.from_visit = v1.id "
677 "WHERE b.fk IS NOT NULL AND b.type = ?1 "
678 "AND v2.visit_type IN (") +
679 nsPrintfCString("%d,%d",
680 nsINavHistoryService::TRANSITION_REDIRECT_PERMANENT,
681 nsINavHistoryService::TRANSITION_REDIRECT_TEMPORARY) +
682 NS_LITERAL_CSTRING(") GROUP BY v2.place_id "
683 "UNION "
684 "SELECT v1.place_id, v2.place_id "
685 "FROM moz_bookmarks b "
686 "LEFT JOIN moz_historyvisits v1 on b.fk = v1.place_id "
687 "LEFT JOIN moz_historyvisits_temp v2 on v2.from_visit = v1.id "
688 "WHERE b.fk IS NOT NULL AND b.type = ?1 "
689 "AND v2.visit_type IN (") +
690 nsPrintfCString("%d,%d",
691 nsINavHistoryService::TRANSITION_REDIRECT_PERMANENT,
692 nsINavHistoryService::TRANSITION_REDIRECT_TEMPORARY) +
693 NS_LITERAL_CSTRING(") GROUP BY v2.place_id "
694 "UNION "
695 "SELECT v1.place_id, v2.place_id "
696 "FROM moz_bookmarks b "
697 "LEFT JOIN moz_historyvisits v1 on b.fk = v1.place_id "
698 "LEFT JOIN moz_historyvisits v2 on v2.from_visit = v1.id "
699 "WHERE b.fk IS NOT NULL AND b.type = ?1 "
700 "AND v2.visit_type IN (") +
701 nsPrintfCString("%d,%d",
702 nsINavHistoryService::TRANSITION_REDIRECT_PERMANENT,
703 nsINavHistoryService::TRANSITION_REDIRECT_TEMPORARY) +
704 NS_LITERAL_CSTRING(") GROUP BY v2.place_id "
705 "UNION "
706 "SELECT v1.place_id, v2.place_id "
707 "FROM moz_bookmarks b "
708 "LEFT JOIN moz_historyvisits_temp v1 on b.fk = v1.place_id "
709 "LEFT JOIN moz_historyvisits_temp v2 on v2.from_visit = v1.id "
710 "WHERE b.fk IS NOT NULL AND b.type = ?1 "
711 "AND v2.visit_type IN (") +
712 nsPrintfCString("%d,%d",
713 nsINavHistoryService::TRANSITION_REDIRECT_PERMANENT,
714 nsINavHistoryService::TRANSITION_REDIRECT_TEMPORARY) +
715 NS_LITERAL_CSTRING(") GROUP BY v2.place_id "),
716 getter_AddRefs(statement));
717 NS_ENSURE_SUCCESS(rv, rv);
718 rv = statement->BindInt64Parameter(0, TYPE_BOOKMARK);
719 NS_ENSURE_SUCCESS(rv, rv);
720 while (NS_SUCCEEDED(statement->ExecuteStep(&hasMore)) && hasMore) {
721 PRInt64 fromId, toId;
722 statement->GetInt64(0, &fromId);
723 statement->GetInt64(1, &toId);
725 NS_ENSURE_TRUE(mBookmarksHash.Put(toId, fromId), NS_ERROR_OUT_OF_MEMORY);
727 // handle redirects deeper than one level
728 rv = RecursiveAddBookmarkHash(fromId, toId, 0);
729 NS_ENSURE_SUCCESS(rv, rv);
732 return NS_OK;
736 // nsNavBookmarks::AddBookmarkToHash
738 // Given a bookmark that was potentially added, this goes through all
739 // redirects that this page may have resulted in and adds them to our hash.
740 // Note that this takes the ID of the URL in the history system, which we
741 // generally have when calling this function and which makes it faster.
743 // For better performance, this call should be in a DB transaction.
745 // @see RecursiveAddBookmarkHash
747 nsresult
748 nsNavBookmarks::AddBookmarkToHash(PRInt64 aPlaceId, PRTime aMinTime)
750 // this function might be called before our hashtable is initialized (for
751 // example, on history import), just ignore these, we'll pick up the add when
752 // the hashtable is initialized later
753 if (! mBookmarksHash.IsInitialized())
754 return NS_OK;
755 if (! mBookmarksHash.Put(aPlaceId, aPlaceId))
756 return NS_ERROR_OUT_OF_MEMORY;
757 return RecursiveAddBookmarkHash(aPlaceId, aPlaceId, aMinTime);
761 // nsNavBookmkars::RecursiveAddBookmarkHash
763 // Used to add a new level of redirect information to the bookmark hash.
764 // Given a source bookmark 'aBookmark' and 'aCurrentSource' that has already
765 // been added to the hashtable, this will add all redirect destinations of
766 // 'aCurrentSource'. Will call itself recursively to walk down the chain.
768 // 'aMinTime' is the minimum time to consider visits from. Visits previous
769 // to this will not be considered. This allows the search to be much more
770 // efficient if you know something happened recently. Use 0 for the min time
771 // to search all history for redirects.
773 nsresult
774 nsNavBookmarks::RecursiveAddBookmarkHash(PRInt64 aPlaceID,
775 PRInt64 aCurrentSource,
776 PRTime aMinTime)
778 nsresult rv;
779 nsTArray<PRInt64> found;
781 // scope for the DB statement. The statement must be reset by the time we
782 // recursively call ourselves again, because our recursive call will use the
783 // same statement.
785 mozStorageStatementScoper scoper(mDBGetRedirectDestinations);
786 rv = mDBGetRedirectDestinations->BindInt64Parameter(0, aCurrentSource);
787 NS_ENSURE_SUCCESS(rv, rv);
788 rv = mDBGetRedirectDestinations->BindInt64Parameter(1, aMinTime);
789 NS_ENSURE_SUCCESS(rv, rv);
791 PRBool hasMore;
792 while (NS_SUCCEEDED(mDBGetRedirectDestinations->ExecuteStep(&hasMore)) &&
793 hasMore) {
795 // add this newly found redirect destination to the hashtable
796 PRInt64 curID;
797 rv = mDBGetRedirectDestinations->GetInt64(0, &curID);
798 NS_ENSURE_SUCCESS(rv, rv);
800 // It is very important we ignore anything already in our hashtable. It
801 // is actually pretty common to get loops of redirects. For example,
802 // a restricted page will redirect you to a login page, which will
803 // redirect you to the restricted page again with the proper cookie.
804 PRInt64 alreadyExistingOne;
805 if (mBookmarksHash.Get(curID, &alreadyExistingOne))
806 continue;
808 if (! mBookmarksHash.Put(curID, aPlaceID))
809 return NS_ERROR_OUT_OF_MEMORY;
811 // save for recursion later
812 found.AppendElement(curID);
816 // recurse on each found item now that we're done with the statement
817 for (PRUint32 i = 0; i < found.Length(); i ++) {
818 rv = RecursiveAddBookmarkHash(aPlaceID, found[i], aMinTime);
819 NS_ENSURE_SUCCESS(rv, rv);
822 return NS_OK;
826 // nsNavBookmarks::UpdateBookmarkHashOnRemove
828 // Call this when a bookmark is removed. It will see if the bookmark still
829 // exists anywhere in the system, and, if not, remove all references to it
830 // in the bookmark hashtable.
832 // The callback takes a pointer to what bookmark is being removed (as
833 // an Int64 history page ID) as the userArg and removes all redirect
834 // destinations that reference it.
836 static PLDHashOperator
837 RemoveBookmarkHashCallback(nsTrimInt64HashKey::KeyType aKey,
838 PRInt64& aPlaceId, void* aUserArg)
840 const PRInt64* removeThisOne = reinterpret_cast<const PRInt64*>(aUserArg);
841 if (aPlaceId == *removeThisOne)
842 return PL_DHASH_REMOVE;
843 return PL_DHASH_NEXT;
845 nsresult
846 nsNavBookmarks::UpdateBookmarkHashOnRemove(PRInt64 aPlaceId)
848 // note we have to use the DB version here since the hashtable may be
849 // out-of-date
850 PRBool inDB;
851 nsresult rv = IsBookmarkedInDatabase(aPlaceId, &inDB);
852 NS_ENSURE_SUCCESS(rv, rv);
853 if (inDB)
854 return NS_OK; // bookmark still exists, don't need to update hashtable
856 // remove it
857 mBookmarksHash.Enumerate(RemoveBookmarkHashCallback,
858 reinterpret_cast<void*>(&aPlaceId));
859 return NS_OK;
863 // nsNavBookmarks::IsBookmarkedInDatabase
865 // This checks to see if the specified URI is actually bookmarked, bypassing
866 // our hashtable. Normal IsBookmarked checks just use the hashtable.
868 nsresult
869 nsNavBookmarks::IsBookmarkedInDatabase(PRInt64 aPlaceId,
870 PRBool *aIsBookmarked)
872 mozStorageStatementScoper scope(mDBIsBookmarkedInDatabase);
873 nsresult rv = mDBIsBookmarkedInDatabase->BindInt64Parameter(0, aPlaceId);
874 NS_ENSURE_SUCCESS(rv, rv);
876 rv = mDBIsBookmarkedInDatabase->BindInt32Parameter(1, TYPE_BOOKMARK);
877 NS_ENSURE_SUCCESS(rv, rv);
879 return mDBIsBookmarkedInDatabase->ExecuteStep(aIsBookmarked);
883 nsresult
884 nsNavBookmarks::AdjustIndices(PRInt64 aFolder,
885 PRInt32 aStartIndex, PRInt32 aEndIndex,
886 PRInt32 aDelta)
888 NS_ASSERTION(aStartIndex <= aEndIndex, "start index must be <= end index");
890 nsCAutoString buffer;
891 buffer.AssignLiteral("UPDATE moz_bookmarks SET position = position + ");
892 buffer.AppendInt(aDelta);
893 buffer.AppendLiteral(" WHERE parent = ");
894 buffer.AppendInt(aFolder);
896 if (aStartIndex != 0) {
897 buffer.AppendLiteral(" AND position >= ");
898 buffer.AppendInt(aStartIndex);
900 if (aEndIndex != PR_INT32_MAX) {
901 buffer.AppendLiteral(" AND position <= ");
902 buffer.AppendInt(aEndIndex);
905 nsresult rv = mDBConn->ExecuteSimpleSQL(buffer);
906 NS_ENSURE_SUCCESS(rv, rv);
908 return NS_OK;
911 NS_IMETHODIMP
912 nsNavBookmarks::GetPlacesRoot(PRInt64 *aRoot)
914 *aRoot = mRoot;
915 return NS_OK;
918 NS_IMETHODIMP
919 nsNavBookmarks::GetBookmarksMenuFolder(PRInt64 *aRoot)
921 *aRoot = mBookmarksRoot;
922 return NS_OK;
925 NS_IMETHODIMP
926 nsNavBookmarks::GetToolbarFolder(PRInt64 *aFolderId)
928 *aFolderId = mToolbarFolder;
929 return NS_OK;
932 NS_IMETHODIMP
933 nsNavBookmarks::GetTagsFolder(PRInt64 *aRoot)
935 *aRoot = mTagRoot;
936 return NS_OK;
939 NS_IMETHODIMP
940 nsNavBookmarks::GetUnfiledBookmarksFolder(PRInt64 *aRoot)
942 *aRoot = mUnfiledRoot;
943 return NS_OK;
946 NS_IMETHODIMP
947 nsNavBookmarks::InsertBookmark(PRInt64 aFolder, nsIURI *aItem, PRInt32 aIndex,
948 const nsACString& aTitle,
949 PRInt64 *aNewBookmarkId)
951 // You can pass -1 to indicate append, but no other negative number is allowed
952 if (aIndex < nsINavBookmarksService::DEFAULT_INDEX)
953 return NS_ERROR_INVALID_ARG;
954 NS_ENSURE_ARG_POINTER(aNewBookmarkId);
956 mozStorageTransaction transaction(mDBConn, PR_FALSE);
958 // This is really a place ID
959 PRInt64 childID;
960 nsresult rv = History()->GetUrlIdFor(aItem, &childID, PR_TRUE);
961 NS_ENSURE_SUCCESS(rv, rv);
963 PRInt32 index;
964 if (aIndex == nsINavBookmarksService::DEFAULT_INDEX) {
965 index = FolderCount(aFolder);
966 } else {
967 index = aIndex;
968 rv = AdjustIndices(aFolder, index, PR_INT32_MAX, 1);
969 NS_ENSURE_SUCCESS(rv, rv);
973 mozStorageStatementScoper scope(mDBInsertBookmark);
974 rv = mDBInsertBookmark->BindInt64Parameter(0, childID);
975 NS_ENSURE_SUCCESS(rv, rv);
976 rv = mDBInsertBookmark->BindInt32Parameter(1, TYPE_BOOKMARK);
977 NS_ENSURE_SUCCESS(rv, rv);
978 rv = mDBInsertBookmark->BindInt64Parameter(2, aFolder);
979 NS_ENSURE_SUCCESS(rv, rv);
980 rv = mDBInsertBookmark->BindInt32Parameter(3, index);
981 NS_ENSURE_SUCCESS(rv, rv);
983 if (aTitle.IsVoid())
984 rv = mDBInsertBookmark->BindNullParameter(4);
985 else
986 rv = mDBInsertBookmark->BindUTF8StringParameter(4, aTitle);
987 NS_ENSURE_SUCCESS(rv, rv);
989 rv = mDBInsertBookmark->BindInt64Parameter(5, PR_Now());
990 NS_ENSURE_SUCCESS(rv, rv);
992 rv = mDBInsertBookmark->Execute();
993 NS_ENSURE_SUCCESS(rv, rv);
996 // get row id of the new bookmark
997 PRInt64 rowId;
999 mozStorageStatementScoper scoper(mDBGetLastBookmarkID);
1001 PRBool hasResult;
1002 rv = mDBGetLastBookmarkID->ExecuteStep(&hasResult);
1003 NS_ENSURE_SUCCESS(rv, rv);
1004 NS_ASSERTION(hasResult, "hasResult is false but the call succeeded?");
1005 rowId = *aNewBookmarkId = mDBGetLastBookmarkID->AsInt64(0);
1008 // XXX
1009 // 0n import / fx 2 migration, is the frecency work going to slow us down?
1010 // We might want to skip this stuff, as well as the frecency work
1011 // caused by GetUrlIdFor() which calls InternalAddNewPage().
1012 // If we do skip this, after import, we will
1013 // need to call FixInvalidFrecenciesForExcludedPlaces().
1014 // We might need to call it anyways, if items aren't properly annotated
1015 // as livemarks feeds yet.
1017 nsCAutoString url;
1018 rv = aItem->GetSpec(url);
1019 NS_ENSURE_SUCCESS(rv, rv);
1021 // prevent place: queries from showing up in the URL bar autocomplete results
1022 PRBool isBookmark = !IsQueryURI(url);
1024 if (isBookmark) {
1025 // if it is a livemark item (the parent is a livemark),
1026 // we pass in false for isBookmark. otherwise, unvisited livemark
1027 // items will appear in URL autocomplete before we visit them.
1028 PRBool parentIsLivemark;
1029 nsCOMPtr<nsILivemarkService> lms =
1030 do_GetService(NS_LIVEMARKSERVICE_CONTRACTID, &rv);
1031 NS_ENSURE_SUCCESS(rv, rv);
1033 rv = lms->IsLivemark(aFolder, &parentIsLivemark);
1034 NS_ENSURE_SUCCESS(rv, rv);
1036 isBookmark = !parentIsLivemark;
1039 // when we created the moz_place entry for the new bookmark
1040 // (a side effect of calling GetUrlIdFor()) frecency -1;
1041 // now we re-calculate the frecency for this moz_place entry.
1042 rv = History()->UpdateFrecency(childID, isBookmark);
1043 NS_ENSURE_SUCCESS(rv, rv);
1045 rv = SetItemDateInternal(mDBSetItemLastModified, aFolder, PR_Now());
1046 NS_ENSURE_SUCCESS(rv, rv);
1048 rv = transaction.Commit();
1049 NS_ENSURE_SUCCESS(rv, rv);
1051 AddBookmarkToHash(childID, 0);
1053 ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver,
1054 OnItemAdded(rowId, aFolder, index))
1056 // If the bookmark has been added to a tag container, notify all
1057 // bookmark-folder result nodes which contain a bookmark for the new
1058 // bookmark's url
1059 PRInt64 grandParentId;
1060 rv = GetFolderIdForItem(aFolder, &grandParentId);
1061 NS_ENSURE_SUCCESS(rv, rv);
1062 if (grandParentId == mTagRoot) {
1063 // query for all bookmarks for that URI, notify for each
1064 nsTArray<PRInt64> bookmarks;
1066 rv = GetBookmarkIdsForURITArray(aItem, &bookmarks);
1067 NS_ENSURE_SUCCESS(rv, rv);
1069 if (bookmarks.Length()) {
1070 for (PRUint32 i = 0; i < bookmarks.Length(); i++) {
1071 ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver,
1072 OnItemChanged(bookmarks[i], NS_LITERAL_CSTRING("tags"),
1073 PR_FALSE, EmptyCString()))
1077 return NS_OK;
1080 NS_IMETHODIMP
1081 nsNavBookmarks::RemoveItem(PRInt64 aItemId)
1083 nsresult rv;
1084 PRInt32 childIndex;
1085 PRInt64 placeId, folderId;
1086 PRUint16 itemType;
1087 nsCAutoString buffer;
1088 nsCAutoString spec;
1090 { // scoping to ensure the statement gets reset
1091 mozStorageStatementScoper scope(mDBGetItemProperties);
1092 mDBGetItemProperties->BindInt64Parameter(0, aItemId);
1094 PRBool results;
1095 rv = mDBGetItemProperties->ExecuteStep(&results);
1096 NS_ENSURE_SUCCESS(rv, rv);
1098 if (!results)
1099 return NS_ERROR_INVALID_ARG; // invalid bookmark id
1101 childIndex = mDBGetItemProperties->AsInt32(kGetItemPropertiesIndex_Position);
1102 placeId = mDBGetItemProperties->AsInt64(kGetItemPropertiesIndex_PlaceID);
1103 folderId = mDBGetItemProperties->AsInt64(kGetItemPropertiesIndex_Parent);
1104 itemType = (PRUint16) mDBGetItemProperties->AsInt32(kGetItemPropertiesIndex_Type);
1105 if (itemType == TYPE_BOOKMARK) {
1106 rv = mDBGetItemProperties->GetUTF8String(kGetItemPropertiesIndex_URI, spec);
1107 NS_ENSURE_SUCCESS(rv, rv);
1111 if (itemType == TYPE_FOLDER) {
1112 rv = RemoveFolder(aItemId);
1113 NS_ENSURE_SUCCESS(rv, rv);
1114 return NS_OK;
1117 mozStorageTransaction transaction(mDBConn, PR_FALSE);
1119 // First, remove item annotations
1120 nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService();
1121 NS_ENSURE_TRUE(annosvc, NS_ERROR_OUT_OF_MEMORY);
1122 rv = annosvc->RemoveItemAnnotations(aItemId);
1123 NS_ENSURE_SUCCESS(rv, rv);
1125 buffer.AssignLiteral("DELETE FROM moz_bookmarks WHERE id = ");
1126 buffer.AppendInt(aItemId);
1128 rv = mDBConn->ExecuteSimpleSQL(buffer);
1129 NS_ENSURE_SUCCESS(rv, rv);
1131 if (childIndex != -1) {
1132 rv = AdjustIndices(folderId, childIndex + 1, PR_INT32_MAX, -1);
1133 NS_ENSURE_SUCCESS(rv, rv);
1136 rv = SetItemDateInternal(mDBSetItemLastModified, folderId, PR_Now());
1137 NS_ENSURE_SUCCESS(rv, rv);
1139 rv = transaction.Commit();
1140 NS_ENSURE_SUCCESS(rv, rv);
1142 rv = UpdateBookmarkHashOnRemove(placeId);
1143 NS_ENSURE_SUCCESS(rv, rv);
1145 // XXX is this too expensive when updating livemarks?
1146 // UpdateBookmarkHashOnRemove() does a sanity check using
1147 // IsBookmarkedInDatabase(), so it might not have actually
1148 // removed the bookmark. should we have a boolean out param
1149 // for if we actually removed it, and use that to decide if we call
1150 // UpdateFrecency() and the rest of this code?
1151 if (itemType == TYPE_BOOKMARK) {
1152 rv = History()->UpdateFrecency(placeId, PR_FALSE /* isBookmark */);
1153 NS_ENSURE_SUCCESS(rv, rv);
1156 ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver,
1157 OnItemRemoved(aItemId, folderId, childIndex))
1159 if (itemType == TYPE_BOOKMARK) {
1160 // If the removed bookmark was a child of a tag container, notify all
1161 // bookmark-folder result nodes which contain a bookmark for the removed
1162 // bookmark's url.
1163 PRInt64 grandParentId;
1164 rv = GetFolderIdForItem(folderId, &grandParentId);
1165 NS_ENSURE_SUCCESS(rv, rv);
1166 if (grandParentId == mTagRoot) {
1167 nsCOMPtr<nsIURI> uri;
1168 rv = NS_NewURI(getter_AddRefs(uri), spec);
1169 NS_ENSURE_SUCCESS(rv, rv);
1170 nsTArray<PRInt64> bookmarks;
1172 rv = GetBookmarkIdsForURITArray(uri, &bookmarks);
1173 NS_ENSURE_SUCCESS(rv, rv);
1175 if (bookmarks.Length()) {
1176 for (PRUint32 i = 0; i < bookmarks.Length(); i++) {
1177 ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver,
1178 OnItemChanged(bookmarks[i], NS_LITERAL_CSTRING("tags"),
1179 PR_FALSE, EmptyCString()))
1184 return NS_OK;
1188 NS_IMETHODIMP
1189 nsNavBookmarks::CreateFolder(PRInt64 aParent, const nsACString &aName,
1190 PRInt32 aIndex, PRInt64 *aNewFolder)
1192 // CreateContainerWithID returns the index of the new folder, but that's not
1193 // used here. To avoid any risk of corrupting data should this function
1194 // be changed, we'll use a local variable to hold it. The PR_TRUE argument
1195 // will cause notifications to be sent to bookmark observers.
1196 PRInt32 localIndex = aIndex;
1197 return CreateContainerWithID(-1, aParent, aName, EmptyString(), PR_TRUE,
1198 &localIndex, aNewFolder);
1201 NS_IMETHODIMP
1202 nsNavBookmarks::CreateDynamicContainer(PRInt64 aParent, const nsACString &aName,
1203 const nsAString &aContractId,
1204 PRInt32 aIndex,
1205 PRInt64 *aNewFolder)
1207 if (aContractId.IsEmpty())
1208 return NS_ERROR_INVALID_ARG;
1210 return CreateContainerWithID(-1, aParent, aName, aContractId, PR_FALSE,
1211 &aIndex, aNewFolder);
1214 NS_IMETHODIMP
1215 nsNavBookmarks::GetFolderReadonly(PRInt64 aFolder, PRBool *aResult)
1217 nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService();
1218 NS_ENSURE_TRUE(annosvc, NS_ERROR_OUT_OF_MEMORY);
1219 return annosvc->ItemHasAnnotation(aFolder, READ_ONLY_ANNO, aResult);
1222 NS_IMETHODIMP
1223 nsNavBookmarks::SetFolderReadonly(PRInt64 aFolder, PRBool aReadOnly)
1225 nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService();
1226 NS_ENSURE_TRUE(annosvc, NS_ERROR_OUT_OF_MEMORY);
1227 if (aReadOnly) {
1228 return annosvc->SetItemAnnotationInt32(aFolder, READ_ONLY_ANNO, 1,
1230 nsAnnotationService::EXPIRE_NEVER);
1232 else {
1233 PRBool hasAnno;
1234 nsresult rv = annosvc->ItemHasAnnotation(aFolder, READ_ONLY_ANNO, &hasAnno);
1235 NS_ENSURE_SUCCESS(rv, rv);
1236 if (hasAnno)
1237 return annosvc->RemoveItemAnnotation(aFolder, READ_ONLY_ANNO);
1239 return NS_OK;
1242 nsresult
1243 nsNavBookmarks::CreateContainerWithID(PRInt64 aItemId, PRInt64 aParent,
1244 const nsACString& aName,
1245 const nsAString& aContractId,
1246 PRBool aIsBookmarkFolder,
1247 PRInt32* aIndex, PRInt64* aNewFolder)
1249 // You can pass -1 to indicate append, but no other negative number is allowed
1250 if (*aIndex < -1)
1251 return NS_ERROR_INVALID_ARG;
1253 mozStorageTransaction transaction(mDBConn, PR_FALSE);
1255 PRInt32 index;
1256 nsresult rv;
1257 if (*aIndex == nsINavBookmarksService::DEFAULT_INDEX) {
1258 index = FolderCount(aParent);
1259 } else {
1260 index = *aIndex;
1261 rv = AdjustIndices(aParent, index, PR_INT32_MAX, 1);
1262 NS_ENSURE_SUCCESS(rv, rv);
1265 nsCOMPtr<mozIStorageStatement> statement;
1266 if (aItemId == -1) {
1267 rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
1268 "INSERT INTO moz_bookmarks "
1269 "(title, type, parent, position, folder_type, dateAdded) "
1270 "VALUES (?1, ?2, ?3, ?4, ?5, ?6)"),
1271 getter_AddRefs(statement));
1272 NS_ENSURE_SUCCESS(rv, rv);
1274 else {
1275 rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
1276 "INSERT INTO moz_bookmarks "
1277 "(id, title, type, parent, position, folder_type, dateAdded) "
1278 "VALUES (?7, ?1, ?2, ?3, ?4, ?5, ?6)"),
1279 getter_AddRefs(statement));
1280 NS_ENSURE_SUCCESS(rv, rv);
1282 rv = statement->BindInt64Parameter(6, aItemId);
1283 NS_ENSURE_SUCCESS(rv, rv);
1286 rv = statement->BindUTF8StringParameter(0, aName);
1287 NS_ENSURE_SUCCESS(rv, rv);
1289 PRInt32 containerType =
1290 aIsBookmarkFolder ? TYPE_FOLDER : TYPE_DYNAMIC_CONTAINER;
1292 rv = statement->BindInt32Parameter(1, containerType);
1293 NS_ENSURE_SUCCESS(rv, rv);
1294 rv = statement->BindInt64Parameter(2, aParent);
1295 NS_ENSURE_SUCCESS(rv, rv);
1296 rv = statement->BindInt32Parameter(3, index);
1297 NS_ENSURE_SUCCESS(rv, rv);
1298 rv = statement->BindStringParameter(4, aContractId);
1299 NS_ENSURE_SUCCESS(rv, rv);
1300 rv = statement->BindInt64Parameter(5, PR_Now());
1301 NS_ENSURE_SUCCESS(rv, rv);
1303 rv = statement->Execute();
1304 NS_ENSURE_SUCCESS(rv, rv);
1306 PRInt64 id;
1307 if (aItemId == -1) {
1308 mozStorageStatementScoper scoper(mDBGetLastBookmarkID);
1310 PRBool hasResult;
1311 rv = mDBGetLastBookmarkID->ExecuteStep(&hasResult);
1312 NS_ENSURE_SUCCESS(rv, rv);
1313 NS_ASSERTION(hasResult, "hasResult is false but the call succeeded?");
1314 id = mDBGetLastBookmarkID->AsInt64(0);
1316 else {
1317 id = aItemId;
1320 rv = SetItemDateInternal(mDBSetItemLastModified, aParent, PR_Now());
1321 NS_ENSURE_SUCCESS(rv, rv);
1323 rv = transaction.Commit();
1324 NS_ENSURE_SUCCESS(rv, rv);
1326 ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver,
1327 OnItemAdded(id, aParent, index))
1329 *aIndex = index;
1330 *aNewFolder = id;
1331 return NS_OK;
1334 NS_IMETHODIMP
1335 nsNavBookmarks::InsertSeparator(PRInt64 aParent, PRInt32 aIndex,
1336 PRInt64* aNewItemId)
1338 // You can pass -1 to indicate append, but no other negative number is
1339 // allowed
1340 if (aIndex < -1)
1341 return NS_ERROR_INVALID_ARG;
1343 mozStorageTransaction transaction(mDBConn, PR_FALSE);
1345 PRInt32 index;
1346 nsresult rv;
1347 if (aIndex == nsINavBookmarksService::DEFAULT_INDEX) {
1348 index = FolderCount(aParent);
1349 } else {
1350 index = aIndex;
1351 rv = AdjustIndices(aParent, index, PR_INT32_MAX, 1);
1352 NS_ENSURE_SUCCESS(rv, rv);
1355 nsCOMPtr<mozIStorageStatement> statement;
1356 rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
1357 "INSERT INTO moz_bookmarks "
1358 "(type, parent, position, dateAdded) VALUES (?1, ?2, ?3, ?4)"),
1359 getter_AddRefs(statement));
1360 NS_ENSURE_SUCCESS(rv, rv);
1362 rv = statement->BindInt64Parameter(0, TYPE_SEPARATOR);
1363 NS_ENSURE_SUCCESS(rv, rv);
1364 rv = statement->BindInt64Parameter(1, aParent);
1365 NS_ENSURE_SUCCESS(rv, rv);
1366 rv = statement->BindInt32Parameter(2, index);
1367 NS_ENSURE_SUCCESS(rv, rv);
1368 rv = statement->BindInt64Parameter(3, PR_Now());
1369 NS_ENSURE_SUCCESS(rv, rv);
1371 rv = statement->Execute();
1372 NS_ENSURE_SUCCESS(rv, rv);
1374 PRInt64 rowId;
1376 mozStorageStatementScoper scoper(mDBGetLastBookmarkID);
1378 PRBool hasResult;
1379 rv = mDBGetLastBookmarkID->ExecuteStep(&hasResult);
1380 NS_ENSURE_SUCCESS(rv, rv);
1381 NS_ASSERTION(hasResult, "hasResult is false but the call succeeded?");
1382 rowId = *aNewItemId = mDBGetLastBookmarkID->AsInt64(0);
1385 rv = SetItemDateInternal(mDBSetItemLastModified, aParent, PR_Now());
1386 NS_ENSURE_SUCCESS(rv, rv);
1388 rv = transaction.Commit();
1389 NS_ENSURE_SUCCESS(rv, rv);
1391 ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver,
1392 OnItemAdded(rowId, aParent, index))
1394 return NS_OK;
1397 nsresult
1398 nsNavBookmarks::GetLastChildId(PRInt64 aFolder, PRInt64* aItemId)
1400 nsCOMPtr<mozIStorageStatement> statement;
1401 nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
1402 "SELECT id FROM moz_bookmarks WHERE parent = ?1 "
1403 "ORDER BY position DESC LIMIT 1"), getter_AddRefs(statement));
1404 NS_ENSURE_SUCCESS(rv, rv);
1406 rv = statement->BindInt64Parameter(0, aFolder);
1407 NS_ENSURE_SUCCESS(rv, rv);
1409 PRBool hasMore;
1410 rv = statement->ExecuteStep(&hasMore);
1411 NS_ENSURE_SUCCESS(rv, rv);
1412 if (!hasMore) {
1413 // Item doesn't exist
1414 return NS_ERROR_INVALID_ARG;
1417 *aItemId = statement->AsInt64(0);
1418 return NS_OK;
1421 NS_IMETHODIMP
1422 nsNavBookmarks::GetIdForItemAt(PRInt64 aFolder, PRInt32 aIndex, PRInt64* aItemId)
1424 nsresult rv;
1425 if (aIndex == nsINavBookmarksService::DEFAULT_INDEX) {
1426 // we want the last item within aFolder
1427 return GetLastChildId(aFolder, aItemId);
1428 } else {
1430 // get the item in aFolder with position aIndex
1431 mozStorageStatementScoper scope(mDBGetChildAt);
1433 rv = mDBGetChildAt->BindInt64Parameter(0, aFolder);
1434 NS_ENSURE_SUCCESS(rv, rv);
1435 rv = mDBGetChildAt->BindInt32Parameter(1, aIndex);
1436 NS_ENSURE_SUCCESS(rv, rv);
1438 PRBool hasMore;
1439 rv = mDBGetChildAt->ExecuteStep(&hasMore);
1440 NS_ENSURE_SUCCESS(rv, rv);
1441 if (!hasMore) {
1442 // Item doesn't exist
1443 return NS_ERROR_INVALID_ARG;
1445 // actually found an item
1446 *aItemId = mDBGetChildAt->AsInt64(0);
1449 return NS_OK;
1452 NS_IMETHODIMP
1453 nsNavBookmarks::RemoveChildAt(PRInt64 aParent, PRInt32 aIndex)
1455 mozStorageTransaction transaction(mDBConn, PR_FALSE);
1456 nsresult rv;
1457 PRInt64 id;
1458 PRInt32 type;
1461 mozStorageStatementScoper scope(mDBGetChildAt);
1462 rv = mDBGetChildAt->BindInt64Parameter(0, aParent);
1463 NS_ENSURE_SUCCESS(rv, rv);
1464 rv = mDBGetChildAt->BindInt32Parameter(1, aIndex);
1465 NS_ENSURE_SUCCESS(rv, rv);
1467 PRBool hasMore;
1468 rv = mDBGetChildAt->ExecuteStep(&hasMore);
1469 NS_ENSURE_SUCCESS(rv, rv);
1470 if (!hasMore) {
1471 // Child doesn't exist
1472 return NS_ERROR_INVALID_ARG;
1475 id = mDBGetChildAt->AsInt64(0);
1476 type = mDBGetChildAt->AsInt32(2);
1479 if (type == TYPE_BOOKMARK || type == TYPE_SEPARATOR) {
1480 // Commit this transaction so that we don't notify observers mid-tranaction
1481 rv = transaction.Commit();
1482 NS_ENSURE_SUCCESS(rv, rv);
1484 return RemoveItem(id);
1486 else if (type == TYPE_FOLDER) {
1487 // Commit this transaction so that we don't notify observers mid-tranaction
1488 rv = transaction.Commit();
1489 NS_ENSURE_SUCCESS(rv, rv);
1491 return RemoveFolder(id);
1493 return NS_ERROR_INVALID_ARG;
1496 nsresult
1497 nsNavBookmarks::GetParentAndIndexOfFolder(PRInt64 aFolder, PRInt64* aParent,
1498 PRInt32* aIndex)
1500 nsCAutoString buffer;
1501 buffer.AssignLiteral("SELECT parent, position FROM moz_bookmarks WHERE id = ");
1502 buffer.AppendInt(aFolder);
1504 nsCOMPtr<mozIStorageStatement> statement;
1505 nsresult rv = mDBConn->CreateStatement(buffer, getter_AddRefs(statement));
1506 NS_ENSURE_SUCCESS(rv, rv);
1508 PRBool results;
1509 rv = statement->ExecuteStep(&results);
1510 NS_ENSURE_SUCCESS(rv, rv);
1511 if (!results) {
1512 return NS_ERROR_INVALID_ARG; // folder is not in the hierarchy
1515 *aParent = statement->AsInt64(0);
1516 *aIndex = statement->AsInt32(1);
1518 return NS_OK;
1521 NS_IMETHODIMP
1522 nsNavBookmarks::RemoveFolder(PRInt64 aFolderId)
1524 mozStorageTransaction transaction(mDBConn, PR_FALSE);
1526 nsresult rv;
1528 PRInt64 parent;
1529 PRInt32 index, type;
1530 nsCAutoString folderType;
1532 mozStorageStatementScoper scope(mDBGetItemProperties);
1533 rv = mDBGetItemProperties->BindInt64Parameter(0, aFolderId);
1534 NS_ENSURE_SUCCESS(rv, rv);
1536 PRBool results;
1537 rv = mDBGetItemProperties->ExecuteStep(&results);
1538 NS_ENSURE_SUCCESS(rv, rv);
1539 if (!results) {
1540 return NS_ERROR_INVALID_ARG; // folder is not in the hierarchy
1543 type = mDBGetItemProperties->AsInt32(kGetItemPropertiesIndex_Type);
1544 parent = mDBGetItemProperties->AsInt64(kGetItemPropertiesIndex_Parent);
1545 index = mDBGetItemProperties->AsInt32(kGetItemPropertiesIndex_Position);
1546 rv = mDBGetItemProperties->GetUTF8String(kGetItemPropertiesIndex_ServiceContractId, folderType);
1547 NS_ENSURE_SUCCESS(rv, rv);
1550 if (type != TYPE_FOLDER) {
1551 NS_WARNING("RemoveFolder(): aFolderId is not a folder!");
1552 return NS_ERROR_INVALID_ARG; // aFolderId is not a folder!
1555 // First, remove item annotations
1556 nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService();
1557 NS_ENSURE_TRUE(annosvc, NS_ERROR_OUT_OF_MEMORY);
1558 rv = annosvc->RemoveItemAnnotations(aFolderId);
1559 NS_ENSURE_SUCCESS(rv, rv);
1561 // If this is a container bookmark, try to notify its service.
1562 if (folderType.Length() > 0) {
1563 // There is a type associated with this folder.
1564 nsCOMPtr<nsIDynamicContainer> bmcServ = do_GetService(folderType.get());
1565 if (bmcServ) {
1566 rv = bmcServ->OnContainerRemoving(aFolderId);
1567 if (NS_FAILED(rv))
1568 NS_WARNING("Remove folder container notification failed.");
1572 // Remove all of the folder's children
1573 rv = RemoveFolderChildren(aFolderId);
1574 NS_ENSURE_SUCCESS(rv, rv);
1576 // Remove the folder from its parent
1577 nsCAutoString buffer;
1578 buffer.AssignLiteral("DELETE FROM moz_bookmarks WHERE id = ");
1579 buffer.AppendInt(aFolderId);
1580 rv = mDBConn->ExecuteSimpleSQL(buffer);
1581 NS_ENSURE_SUCCESS(rv, rv);
1583 rv = AdjustIndices(parent, index + 1, PR_INT32_MAX, -1);
1584 NS_ENSURE_SUCCESS(rv, rv);
1586 rv = SetItemDateInternal(mDBSetItemLastModified, parent, PR_Now());
1587 NS_ENSURE_SUCCESS(rv, rv);
1589 rv = transaction.Commit();
1590 NS_ENSURE_SUCCESS(rv, rv);
1592 if (aFolderId == mToolbarFolder) {
1593 mToolbarFolder = 0;
1596 ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver,
1597 OnItemRemoved(aFolderId, parent, index))
1599 return NS_OK;
1602 NS_IMPL_ISUPPORTS1(nsNavBookmarks::RemoveFolderTransaction, nsITransaction)
1604 NS_IMETHODIMP
1605 nsNavBookmarks::GetRemoveFolderTransaction(PRInt64 aFolder, nsITransaction** aResult)
1607 // Create and initialize a RemoveFolderTransaction object that can be used to
1608 // recreate the folder safely later.
1610 RemoveFolderTransaction* rft =
1611 new RemoveFolderTransaction(aFolder);
1612 if (!rft)
1613 return NS_ERROR_OUT_OF_MEMORY;
1615 NS_ADDREF(*aResult = rft);
1616 return NS_OK;
1619 nsresult
1620 nsNavBookmarks::GetDescendantChildren(PRInt64 aFolderId,
1621 PRInt64 aGrandParentId,
1622 nsTArray<folderChildrenInfo>& aFolderChildrenArray) {
1623 // New children will be added from this index on.
1624 PRUint32 startIndex = aFolderChildrenArray.Length();
1625 nsresult rv;
1627 // Collect children informations.
1628 mozStorageStatementScoper scope(mDBGetChildren);
1629 rv = mDBGetChildren->BindInt64Parameter(0, aFolderId);
1630 NS_ENSURE_SUCCESS(rv, rv);
1632 PRBool hasMore;
1633 while (NS_SUCCEEDED(mDBGetChildren->ExecuteStep(&hasMore)) && hasMore) {
1634 folderChildrenInfo child;
1635 child.itemId = mDBGetChildren->AsInt64(nsNavHistory::kGetInfoIndex_ItemId);
1636 child.parentId = aFolderId;
1637 child.grandParentId = aGrandParentId;
1638 child.itemType = (PRUint16) mDBGetChildren->AsInt32(kGetChildrenIndex_Type);
1639 child.placeId = mDBGetChildren->AsInt64(kGetChildrenIndex_PlaceID);
1640 child.index = mDBGetChildren->AsInt32(kGetChildrenIndex_Position);
1642 if (child.itemType == TYPE_BOOKMARK) {
1643 nsCAutoString URIString;
1644 rv = mDBGetChildren->GetUTF8String(nsNavHistory::kGetInfoIndex_URL,
1645 URIString);
1646 NS_ENSURE_SUCCESS(rv, rv);
1647 child.url = URIString;
1649 else if (child.itemType == TYPE_FOLDER) {
1650 nsCAutoString folderType;
1651 rv = mDBGetChildren->GetUTF8String(kGetChildrenIndex_ServiceContractId,
1652 folderType);
1653 NS_ENSURE_SUCCESS(rv, rv);
1654 child.folderType = folderType;
1656 // Append item to children's array.
1657 aFolderChildrenArray.AppendElement(child);
1661 // Recursively call GetDescendantChildren for added folders.
1662 // We start at startIndex since previous folders are checked
1663 // by previous calls to this method.
1664 PRUint32 childCount = aFolderChildrenArray.Length();
1665 for (PRUint32 i = startIndex; i < childCount; i++) {
1666 if (aFolderChildrenArray[i].itemType == TYPE_FOLDER) {
1667 GetDescendantChildren(aFolderChildrenArray[i].itemId,
1668 aFolderId,
1669 aFolderChildrenArray);
1673 return NS_OK;
1676 NS_IMETHODIMP
1677 nsNavBookmarks::RemoveFolderChildren(PRInt64 aFolderId)
1679 nsresult rv;
1680 PRUint16 itemType;
1681 PRInt64 grandParentId;
1683 mozStorageStatementScoper scope(mDBGetItemProperties);
1684 rv = mDBGetItemProperties->BindInt64Parameter(0, aFolderId);
1685 NS_ENSURE_SUCCESS(rv, rv);
1687 // Sanity check: ensure that item exists.
1688 PRBool folderExists;
1689 if (NS_FAILED(mDBGetItemProperties->ExecuteStep(&folderExists)) || !folderExists)
1690 return NS_ERROR_INVALID_ARG;
1692 // Sanity check: ensure that this is a folder.
1693 itemType = (PRUint16) mDBGetItemProperties->AsInt32(kGetItemPropertiesIndex_Type);
1694 if (itemType != TYPE_FOLDER)
1695 return NS_ERROR_INVALID_ARG;
1697 // Get the grandParent.
1698 // We have to do this only once since recursion will give us other
1699 // grandParents without the need of additional queries.
1700 grandParentId = mDBGetItemProperties->AsInt64(kGetItemPropertiesIndex_Parent);
1703 // Fill folder children array recursively.
1704 nsTArray<folderChildrenInfo> folderChildrenArray;
1705 rv = GetDescendantChildren(aFolderId, grandParentId, folderChildrenArray);
1706 NS_ENSURE_SUCCESS(rv, rv);
1708 // Build a string of folders whose children will be removed.
1709 nsCString foldersToRemove;
1710 for (PRUint32 i = 0; i < folderChildrenArray.Length(); i++) {
1711 folderChildrenInfo child = folderChildrenArray[i];
1712 if (child.itemType == TYPE_FOLDER) {
1713 foldersToRemove.AppendLiteral(",");
1714 foldersToRemove.AppendInt(child.itemId);
1716 // If this is a dynamic container, try to notify its service that we
1717 // are going to remove it.
1718 if (child.folderType.Length() > 0) {
1719 nsCOMPtr<nsIDynamicContainer> bmcServ =
1720 do_GetService(child.folderType.get());
1721 if (bmcServ) {
1722 rv = bmcServ->OnContainerRemoving(child.itemId);
1723 if (NS_FAILED(rv))
1724 NS_WARNING("Remove folder container notification failed.");
1730 // Delete items from the database now.
1731 mozStorageTransaction transaction(mDBConn, PR_FALSE);
1733 rv = mDBConn->ExecuteSimpleSQL(
1734 NS_LITERAL_CSTRING(
1735 "DELETE FROM moz_bookmarks "
1736 "WHERE parent IN (") +
1737 nsPrintfCString("%d", aFolderId) +
1738 foldersToRemove +
1739 NS_LITERAL_CSTRING(")"));
1740 NS_ENSURE_SUCCESS(rv, rv);
1742 // Clean up orphan items annotations.
1743 rv = mDBConn->ExecuteSimpleSQL(
1744 NS_LITERAL_CSTRING(
1745 "DELETE FROM moz_items_annos "
1746 "WHERE id IN ("
1747 "SELECT a.id from moz_items_annos a "
1748 "LEFT JOIN moz_bookmarks b ON a.item_id = b.id "
1749 "WHERE b.id ISNULL)"));
1750 NS_ENSURE_SUCCESS(rv, rv);
1752 // Set the lastModified date.
1753 rv = SetItemDateInternal(mDBSetItemLastModified, aFolderId, PR_Now());
1754 NS_ENSURE_SUCCESS(rv, rv);
1756 for (PRUint32 i = 0; i < folderChildrenArray.Length(); i++) {
1757 folderChildrenInfo child = folderChildrenArray[i];
1758 if (child.itemType == TYPE_BOOKMARK) {
1759 PRInt64 placeId = child.placeId;
1760 UpdateBookmarkHashOnRemove(placeId);
1762 // XXX is this too expensive when updating livemarks?
1763 // UpdateBookmarkHashOnRemove() does a sanity check using
1764 // IsBookmarkedInDatabase(), so it might not have actually
1765 // removed the bookmark. should we have a boolean out param
1766 // for if we actually removed it, and use that to decide if we call
1767 // UpdateFrecency() and the rest of this code?
1768 rv = History()->UpdateFrecency(placeId, PR_FALSE /* isBookmark */);
1769 NS_ENSURE_SUCCESS(rv, rv);
1773 rv = transaction.Commit();
1774 NS_ENSURE_SUCCESS(rv, rv);
1776 // Call observers in reverse order to serve children before their parent.
1777 for (PRInt32 i = folderChildrenArray.Length() - 1; i >= 0 ; i--) {
1778 folderChildrenInfo child = folderChildrenArray[i];
1780 ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver,
1781 OnItemRemoved(child.itemId,
1782 child.parentId,
1783 child.index));
1785 if (child.itemType == TYPE_BOOKMARK) {
1786 // If the removed bookmark was a child of a tag container, notify all
1787 // bookmark-folder result nodes which contain a bookmark for the removed
1788 // bookmark's url.
1790 if (child.grandParentId == mTagRoot) {
1791 nsCOMPtr<nsIURI> uri;
1792 rv = NS_NewURI(getter_AddRefs(uri), child.url);
1793 NS_ENSURE_SUCCESS(rv, rv);
1795 nsTArray<PRInt64> bookmarks;
1796 rv = GetBookmarkIdsForURITArray(uri, &bookmarks);
1797 NS_ENSURE_SUCCESS(rv, rv);
1799 if (bookmarks.Length()) {
1800 for (PRUint32 i = 0; i < bookmarks.Length(); i++) {
1801 ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver,
1802 OnItemChanged(bookmarks[i], NS_LITERAL_CSTRING("tags"),
1803 PR_FALSE, EmptyCString()))
1810 return NS_OK;
1813 NS_IMETHODIMP
1814 nsNavBookmarks::MoveItem(PRInt64 aItemId, PRInt64 aNewParent, PRInt32 aIndex)
1816 // You can pass -1 to indicate append, but no other negative number is allowed
1817 if (aIndex < -1)
1818 return NS_ERROR_INVALID_ARG;
1820 // Disallow making an item its own parent.
1821 if (aItemId == aNewParent)
1822 return NS_ERROR_INVALID_ARG;
1824 mozStorageTransaction transaction(mDBConn, PR_FALSE);
1826 // get item properties
1827 nsresult rv;
1828 PRInt64 oldParent;
1829 PRInt32 oldIndex;
1830 PRUint16 itemType;
1831 nsCAutoString folderType;
1833 mozStorageStatementScoper scope(mDBGetItemProperties);
1834 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);
1840 if (!results) {
1841 return NS_ERROR_INVALID_ARG; // folder is not in the hierarchy
1844 oldParent = mDBGetItemProperties->AsInt64(kGetItemPropertiesIndex_Parent);
1845 oldIndex = mDBGetItemProperties->AsInt32(kGetItemPropertiesIndex_Position);
1846 itemType = (PRUint16) mDBGetItemProperties->AsInt32(kGetItemPropertiesIndex_Type);
1847 if (itemType == TYPE_FOLDER) {
1848 rv = mDBGetItemProperties->GetUTF8String(kGetItemPropertiesIndex_ServiceContractId,
1849 folderType);
1850 NS_ENSURE_SUCCESS(rv, rv);
1854 // if parent and index are the same, nothing to do
1855 if (oldParent == aNewParent && oldIndex == aIndex)
1856 return NS_OK;
1858 // Make sure aNewParent is not aFolder or a subfolder of aFolder
1859 if (itemType == TYPE_FOLDER) {
1860 PRInt64 p = aNewParent;
1862 while (p) {
1863 mozStorageStatementScoper scope(mDBGetItemProperties);
1864 if (p == aItemId) {
1865 return NS_ERROR_INVALID_ARG;
1868 rv = mDBGetItemProperties->BindInt64Parameter(0, p);
1869 NS_ENSURE_SUCCESS(rv, rv);
1871 PRBool results;
1872 rv = mDBGetItemProperties->ExecuteStep(&results);
1873 NS_ENSURE_SUCCESS(rv, rv);
1874 p = results ? mDBGetItemProperties->AsInt64(kGetItemPropertiesIndex_Parent) : 0;
1878 // calculate new index
1879 PRInt32 newIndex;
1880 if (aIndex == nsINavBookmarksService::DEFAULT_INDEX) {
1881 newIndex = FolderCount(aNewParent);
1882 // If the parent remains the same, then the folder is really being moved
1883 // to count - 1 (since it's being removed from the old position)
1884 if (oldParent == aNewParent) {
1885 --newIndex;
1887 } else {
1888 newIndex = aIndex;
1890 if (oldParent == aNewParent && newIndex > oldIndex) {
1891 // when an item is being moved lower in the same folder, the new index
1892 // refers to the index before it was removed. Removal causes everything
1893 // to shift up.
1894 --newIndex;
1898 // this is like the previous check, except this covers if
1899 // the specified index was -1 (append), and the calculated
1900 // new index is the same as the existing index
1901 if (aNewParent == oldParent && newIndex == oldIndex) {
1902 // Nothing to do!
1903 return NS_OK;
1906 // adjust indices to account for the move
1907 // do this before we update the parent/index fields
1908 // or we'll re-adjust the index for the item we are moving
1909 if (oldParent == aNewParent) {
1910 // We can optimize the updates if moving within the same container.
1911 // We only shift the items between the old and new positions, since the
1912 // insertion will offset the deletion.
1913 if (oldIndex > newIndex) {
1914 rv = AdjustIndices(oldParent, newIndex, oldIndex - 1, 1);
1915 } else {
1916 rv = AdjustIndices(oldParent, oldIndex + 1, newIndex, -1);
1918 } else {
1919 // We're moving between containers, so this happens in two steps.
1920 // First, fill the hole from the removal from the old parent.
1921 rv = AdjustIndices(oldParent, oldIndex + 1, PR_INT32_MAX, -1);
1922 NS_ENSURE_SUCCESS(rv, rv);
1923 // Now, make room in the new parent for the insertion.
1924 rv = AdjustIndices(aNewParent, newIndex, PR_INT32_MAX, 1);
1926 NS_ENSURE_SUCCESS(rv, rv);
1928 // update parent/index fields
1929 nsCAutoString buffer;
1930 buffer.AssignLiteral("UPDATE moz_bookmarks SET ");
1931 if (aNewParent != oldParent) {
1932 buffer.AppendLiteral(" parent = ");
1933 buffer.AppendInt(aNewParent);
1935 if (newIndex != oldIndex) {
1936 if (aNewParent != oldParent)
1937 buffer.AppendLiteral(", ");
1938 buffer.AppendLiteral(" position = ");
1939 buffer.AppendInt(newIndex);
1941 buffer.AppendLiteral(" WHERE id = ");
1942 buffer.AppendInt(aItemId);
1943 rv = mDBConn->ExecuteSimpleSQL(buffer);
1944 NS_ENSURE_SUCCESS(rv, rv);
1946 PRTime now = PR_Now();
1947 rv = SetItemDateInternal(mDBSetItemLastModified, oldParent, now);
1948 NS_ENSURE_SUCCESS(rv, rv);
1949 rv = SetItemDateInternal(mDBSetItemLastModified, aNewParent, now);
1950 NS_ENSURE_SUCCESS(rv, rv);
1952 rv = transaction.Commit();
1953 NS_ENSURE_SUCCESS(rv, rv);
1955 // notify bookmark observers
1956 ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver,
1957 OnItemMoved(aItemId, oldParent, oldIndex, aNewParent,
1958 newIndex))
1960 // notify dynamic container provider if there is one
1961 if (!folderType.IsEmpty()) {
1962 nsCOMPtr<nsIDynamicContainer> container =
1963 do_GetService(folderType.get(), &rv);
1964 if (NS_SUCCEEDED(rv)) {
1965 rv = container->OnContainerMoved(aItemId, aNewParent, newIndex);
1966 NS_ENSURE_SUCCESS(rv, rv);
1969 return NS_OK;
1972 NS_IMETHODIMP
1973 nsNavBookmarks::GetChildFolder(PRInt64 aFolder, const nsAString& aSubFolder,
1974 PRInt64* _result)
1976 // note: we allow empty folder names
1977 nsresult rv;
1978 if (aFolder == 0)
1979 return NS_ERROR_INVALID_ARG;
1981 // If this gets used a lot, we'll want a precompiled statement
1982 nsCAutoString getChildFolderQuery =
1983 NS_LITERAL_CSTRING("SELECT id "
1984 "FROM moz_bookmarks "
1985 "WHERE parent = ?1 AND type = ") +
1986 nsPrintfCString("%d", TYPE_FOLDER) +
1987 NS_LITERAL_CSTRING(" AND title = ?2");
1988 nsCOMPtr<mozIStorageStatement> statement;
1989 rv = mDBConn->CreateStatement(getChildFolderQuery, getter_AddRefs(statement));
1990 NS_ENSURE_SUCCESS(rv, rv);
1991 statement->BindInt64Parameter(0, aFolder);
1992 statement->BindStringParameter(1, aSubFolder);
1994 PRBool hasResult = PR_FALSE;
1995 rv = statement->ExecuteStep(&hasResult);
1996 NS_ENSURE_SUCCESS(rv, rv);
1998 if (! hasResult) {
1999 // item not found
2000 *_result = 0;
2001 return NS_OK;
2004 return statement->GetInt64(0, _result);
2007 nsresult
2008 nsNavBookmarks::SetItemDateInternal(mozIStorageStatement* aStatement, PRInt64 aItemId, PRTime aValue)
2010 mozStorageStatementScoper scope(aStatement);
2011 nsresult rv = aStatement->BindInt64Parameter(0, aValue);
2012 NS_ENSURE_SUCCESS(rv, rv);
2013 rv = aStatement->BindInt64Parameter(1, aItemId);
2014 NS_ENSURE_SUCCESS(rv, rv);
2016 rv = aStatement->Execute();
2017 NS_ENSURE_SUCCESS(rv, rv);
2019 // note, we are not notifying the observers
2020 // that the item has changed.
2022 return NS_OK;
2025 NS_IMETHODIMP
2026 nsNavBookmarks::SetItemDateAdded(PRInt64 aItemId, PRTime aDateAdded)
2028 nsresult rv = SetItemDateInternal(mDBSetItemDateAdded, aItemId, aDateAdded);
2029 NS_ENSURE_SUCCESS(rv, rv);
2031 ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver,
2032 OnItemChanged(aItemId, NS_LITERAL_CSTRING("dateAdded"),
2033 PR_FALSE, nsPrintfCString(16, "%lld", aDateAdded)));
2034 return NS_OK;
2037 NS_IMETHODIMP
2038 nsNavBookmarks::GetItemDateAdded(PRInt64 aItemId, PRTime *aDateAdded)
2040 NS_ENSURE_ARG_POINTER(aDateAdded);
2042 mozStorageStatementScoper scope(mDBGetItemProperties);
2043 nsresult rv = mDBGetItemProperties->BindInt64Parameter(0, aItemId);
2044 NS_ENSURE_SUCCESS(rv, rv);
2046 PRBool results;
2047 rv = mDBGetItemProperties->ExecuteStep(&results);
2048 NS_ENSURE_SUCCESS(rv, rv);
2050 if (!results)
2051 return NS_ERROR_INVALID_ARG; // invalid item id
2053 return mDBGetItemProperties->GetInt64(kGetItemPropertiesIndex_DateAdded, aDateAdded);
2056 NS_IMETHODIMP
2057 nsNavBookmarks::SetItemLastModified(PRInt64 aItemId, PRTime aLastModified)
2059 nsresult rv = SetItemDateInternal(mDBSetItemLastModified, aItemId, aLastModified);
2060 NS_ENSURE_SUCCESS(rv, rv);
2062 ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver,
2063 OnItemChanged(aItemId, NS_LITERAL_CSTRING("lastModified"),
2064 PR_FALSE, nsPrintfCString(16, "%lld", aLastModified)));
2065 return NS_OK;
2068 NS_IMETHODIMP
2069 nsNavBookmarks::GetItemLastModified(PRInt64 aItemId, PRTime *aLastModified)
2071 NS_ENSURE_ARG_POINTER(aLastModified);
2073 mozStorageStatementScoper scope(mDBGetItemProperties);
2074 nsresult rv = mDBGetItemProperties->BindInt64Parameter(0, aItemId);
2075 NS_ENSURE_SUCCESS(rv, rv);
2077 PRBool results;
2078 rv = mDBGetItemProperties->ExecuteStep(&results);
2079 NS_ENSURE_SUCCESS(rv, rv);
2081 if (!results)
2082 return NS_ERROR_INVALID_ARG; // invalid item id
2084 return mDBGetItemProperties->GetInt64(kGetItemPropertiesIndex_LastModified, aLastModified);
2087 nsresult
2088 nsNavBookmarks::GetGUIDBase(nsAString &aGUIDBase)
2090 if (!mGUIDBase.IsEmpty()) {
2091 aGUIDBase = mGUIDBase;
2092 return NS_OK;
2095 // generate a new GUID base for this session
2096 nsCOMPtr<nsIUUIDGenerator> uuidgen =
2097 do_GetService("@mozilla.org/uuid-generator;1");
2098 NS_ENSURE_TRUE(uuidgen, NS_ERROR_OUT_OF_MEMORY);
2099 nsID GUID;
2100 nsresult rv = uuidgen->GenerateUUIDInPlace(&GUID);
2101 NS_ENSURE_SUCCESS(rv, rv);
2102 char GUIDChars[NSID_LENGTH];
2103 GUID.ToProvidedString(GUIDChars);
2104 CopyASCIItoUTF16(GUIDChars, mGUIDBase);
2105 aGUIDBase = mGUIDBase;
2106 return NS_OK;
2109 NS_IMETHODIMP
2110 nsNavBookmarks::GetItemGUID(PRInt64 aItemId, nsAString &aGUID)
2112 nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService();
2113 NS_ENSURE_TRUE(annosvc, NS_ERROR_OUT_OF_MEMORY);
2114 nsresult rv = annosvc->GetItemAnnotationString(aItemId, GUID_ANNO, aGUID);
2116 if (NS_SUCCEEDED(rv) || rv != NS_ERROR_NOT_AVAILABLE)
2117 return rv;
2119 nsAutoString tmp;
2120 tmp.AppendInt(mItemCount++);
2121 aGUID.SetCapacity(NSID_LENGTH - 1 + tmp.Length());
2122 nsString GUIDBase;
2123 rv = GetGUIDBase(GUIDBase);
2124 NS_ENSURE_SUCCESS(rv, rv);
2125 aGUID.Assign(GUIDBase);
2126 aGUID.Append(tmp);
2128 return SetItemGUID(aItemId, aGUID);
2131 NS_IMETHODIMP
2132 nsNavBookmarks::SetItemGUID(PRInt64 aItemId, const nsAString &aGUID)
2134 PRInt64 checkId;
2135 GetItemIdForGUID(aGUID, &checkId);
2136 if (checkId != -1)
2137 return NS_ERROR_INVALID_ARG; // invalid GUID, already exists
2139 nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService();
2140 NS_ENSURE_TRUE(annosvc, NS_ERROR_OUT_OF_MEMORY);
2141 return annosvc->SetItemAnnotationString(aItemId, GUID_ANNO, aGUID, 0,
2142 nsIAnnotationService::EXPIRE_NEVER);
2145 NS_IMETHODIMP
2146 nsNavBookmarks::GetItemIdForGUID(const nsAString &aGUID, PRInt64 *aItemId)
2148 mozStorageStatementScoper scoper(mDBGetItemIdForGUID);
2149 nsresult rv = mDBGetItemIdForGUID->BindStringParameter(0, aGUID);
2150 NS_ENSURE_SUCCESS(rv, rv);
2152 PRBool hasMore = PR_FALSE;
2153 rv = mDBGetItemIdForGUID->ExecuteStep(&hasMore);
2154 if (NS_FAILED(rv) || ! hasMore) {
2155 *aItemId = -1;
2156 return NS_OK; // not found: return -1
2159 // found, get the itemId
2160 return mDBGetItemIdForGUID->GetInt64(0, aItemId);
2163 NS_IMETHODIMP
2164 nsNavBookmarks::SetItemTitle(PRInt64 aItemId, const nsACString &aTitle)
2166 nsCOMPtr<mozIStorageStatement> statement;
2167 nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
2168 "UPDATE moz_bookmarks SET title = ?1, lastModified = ?2 WHERE id = ?3"),
2169 getter_AddRefs(statement));
2170 NS_ENSURE_SUCCESS(rv, rv);
2171 rv = statement->BindUTF8StringParameter(0, aTitle);
2172 NS_ENSURE_SUCCESS(rv, rv);
2173 rv = statement->BindInt64Parameter(1, PR_Now());
2174 NS_ENSURE_SUCCESS(rv, rv);
2175 rv = statement->BindInt64Parameter(2, aItemId);
2176 NS_ENSURE_SUCCESS(rv, rv);
2178 rv = statement->Execute();
2179 NS_ENSURE_SUCCESS(rv, rv);
2181 ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver,
2182 OnItemChanged(aItemId, NS_LITERAL_CSTRING("title"),
2183 PR_FALSE, aTitle));
2184 return NS_OK;
2187 NS_IMETHODIMP
2188 nsNavBookmarks::GetItemTitle(PRInt64 aItemId, nsACString &aTitle)
2190 mozStorageStatementScoper scope(mDBGetItemProperties);
2192 nsresult rv = mDBGetItemProperties->BindInt64Parameter(0, aItemId);
2193 NS_ENSURE_SUCCESS(rv, rv);
2195 PRBool results;
2196 rv = mDBGetItemProperties->ExecuteStep(&results);
2197 NS_ENSURE_SUCCESS(rv, rv);
2199 if (!results)
2200 return NS_ERROR_INVALID_ARG; // invalid bookmark id
2202 return mDBGetItemProperties->GetUTF8String(kGetItemPropertiesIndex_Title, aTitle);
2205 NS_IMETHODIMP
2206 nsNavBookmarks::GetBookmarkURI(PRInt64 aItemId, nsIURI **aURI)
2208 NS_ENSURE_ARG_POINTER(aURI);
2210 mozStorageStatementScoper scope(mDBGetItemProperties);
2211 nsresult rv = mDBGetItemProperties->BindInt64Parameter(0, aItemId);
2212 NS_ENSURE_SUCCESS(rv, rv);
2214 PRBool results;
2215 rv = mDBGetItemProperties->ExecuteStep(&results);
2216 NS_ENSURE_SUCCESS(rv, rv);
2218 if (!results)
2219 return NS_ERROR_INVALID_ARG; // invalid bookmark id
2221 PRInt32 type = mDBGetItemProperties->AsInt32(kGetItemPropertiesIndex_Type);
2222 if (type != TYPE_BOOKMARK)
2223 return NS_ERROR_INVALID_ARG; // invalid type (only for bookmarks)
2225 nsCAutoString spec;
2226 rv = mDBGetItemProperties->GetUTF8String(kGetItemPropertiesIndex_URI, spec);
2227 NS_ENSURE_SUCCESS(rv, rv);
2229 rv = NS_NewURI(aURI, spec);
2230 NS_ENSURE_SUCCESS(rv, rv);
2232 return NS_OK;
2235 NS_IMETHODIMP
2236 nsNavBookmarks::GetItemType(PRInt64 aItemId, PRUint16 *aType)
2238 mozStorageStatementScoper scope(mDBGetItemProperties);
2240 nsresult rv = mDBGetItemProperties->BindInt64Parameter(0, aItemId);
2241 NS_ENSURE_SUCCESS(rv, rv);
2243 PRBool results;
2244 rv = mDBGetItemProperties->ExecuteStep(&results);
2245 NS_ENSURE_SUCCESS(rv, rv);
2247 if (!results) {
2248 return NS_ERROR_INVALID_ARG; // invalid bookmark id
2251 *aType = (PRUint16)mDBGetItemProperties->AsInt32(kGetItemPropertiesIndex_Type);
2252 return NS_OK;
2255 nsresult
2256 nsNavBookmarks::GetFolderType(PRInt64 aFolder, nsACString &aType)
2258 mozStorageStatementScoper scope(mDBGetItemProperties);
2259 nsresult rv = mDBGetItemProperties->BindInt64Parameter(0, aFolder);
2260 NS_ENSURE_SUCCESS(rv, rv);
2262 PRBool results;
2263 rv = mDBGetItemProperties->ExecuteStep(&results);
2264 NS_ENSURE_SUCCESS(rv, rv);
2266 if (!results) {
2267 return NS_ERROR_INVALID_ARG;
2270 return mDBGetItemProperties->GetUTF8String(kGetItemPropertiesIndex_ServiceContractId, aType);
2273 nsresult
2274 nsNavBookmarks::ResultNodeForContainer(PRInt64 aID,
2275 nsNavHistoryQueryOptions *aOptions,
2276 nsNavHistoryResultNode **aNode)
2278 mozStorageStatementScoper scope(mDBGetItemProperties);
2279 mDBGetItemProperties->BindInt64Parameter(0, aID);
2281 PRBool results;
2282 nsresult rv = mDBGetItemProperties->ExecuteStep(&results);
2283 NS_ENSURE_SUCCESS(rv, rv);
2284 NS_ASSERTION(results, "ResultNodeForContainer expects a valid item id");
2286 // contract id
2287 nsCAutoString contractId;
2288 rv = mDBGetItemProperties->GetUTF8String(kGetItemPropertiesIndex_ServiceContractId,
2289 contractId);
2290 NS_ENSURE_SUCCESS(rv, rv);
2292 // title
2293 nsCAutoString title;
2294 rv = mDBGetItemProperties->GetUTF8String(kGetItemPropertiesIndex_Title, title);
2296 PRUint16 itemType = (PRUint16) mDBGetItemProperties->AsInt32(kGetItemPropertiesIndex_Type);
2297 if (itemType == TYPE_DYNAMIC_CONTAINER) {
2298 *aNode = new nsNavHistoryContainerResultNode(EmptyCString(), title, EmptyCString(),
2299 nsINavHistoryResultNode::RESULT_TYPE_DYNAMIC_CONTAINER,
2300 PR_TRUE,
2301 contractId,
2302 aOptions);
2303 (*aNode)->mItemId = aID;
2304 } else { // TYPE_FOLDER
2305 *aNode = new nsNavHistoryFolderResultNode(title, aOptions, aID, contractId);
2307 if (!*aNode)
2308 return NS_ERROR_OUT_OF_MEMORY;
2310 (*aNode)->mDateAdded =
2311 mDBGetItemProperties->AsInt64(kGetItemPropertiesIndex_DateAdded);
2312 (*aNode)->mLastModified =
2313 mDBGetItemProperties->AsInt64(kGetItemPropertiesIndex_LastModified);
2315 NS_ADDREF(*aNode);
2316 return NS_OK;
2319 nsresult
2320 nsNavBookmarks::QueryFolderChildren(PRInt64 aFolderId,
2321 nsNavHistoryQueryOptions *aOptions,
2322 nsCOMArray<nsNavHistoryResultNode> *aChildren)
2324 mozStorageStatementScoper scope(mDBGetChildren);
2326 nsresult rv = mDBGetChildren->BindInt64Parameter(0, aFolderId);
2327 NS_ENSURE_SUCCESS(rv, rv);
2329 PRBool results;
2331 nsCOMPtr<nsNavHistoryQueryOptions> options = do_QueryInterface(aOptions, &rv);
2332 NS_ENSURE_SUCCESS(rv, rv);
2334 PRInt32 index = -1;
2335 while (NS_SUCCEEDED(mDBGetChildren->ExecuteStep(&results)) && results) {
2337 // The results will be in order of index. Even if we don't add a node
2338 // because it was excluded, we need to count its index, so do that
2339 // before doing anything else. Index was initialized to -1 above, so
2340 // it will start counting at 0 the first time through the loop.
2341 index ++;
2343 PRUint16 itemType = (PRUint16) mDBGetChildren->AsInt32(kGetChildrenIndex_Type);
2344 PRInt64 id = mDBGetChildren->AsInt64(nsNavHistory::kGetInfoIndex_ItemId);
2345 nsRefPtr<nsNavHistoryResultNode> node;
2346 if (itemType == TYPE_BOOKMARK) {
2347 rv = History()->RowToResult(mDBGetChildren, options,
2348 getter_AddRefs(node));
2349 NS_ENSURE_SUCCESS(rv, rv);
2351 PRUint32 nodeType;
2352 node->GetType(&nodeType);
2353 if ((nodeType == nsINavHistoryResultNode::RESULT_TYPE_QUERY &&
2354 aOptions->ExcludeQueries()) ||
2355 (nodeType != nsINavHistoryResultNode::RESULT_TYPE_QUERY &&
2356 nodeType != nsINavHistoryResultNode::RESULT_TYPE_FOLDER_SHORTCUT &&
2357 aOptions->ExcludeItems())) {
2358 continue;
2360 } else if (itemType == TYPE_FOLDER || itemType == TYPE_DYNAMIC_CONTAINER) {
2361 if (options->ExcludeReadOnlyFolders()) {
2362 // see if it's read only and skip it
2363 PRBool readOnly = PR_FALSE;
2364 GetFolderReadonly(id, &readOnly);
2365 if (readOnly)
2366 continue; // skip
2369 rv = ResultNodeForContainer(id, aOptions, getter_AddRefs(node));
2370 if (NS_FAILED(rv))
2371 continue;
2372 } else {
2373 // separator
2374 if (aOptions->ExcludeItems()) {
2375 continue;
2377 node = new nsNavHistorySeparatorResultNode();
2378 NS_ENSURE_TRUE(node, NS_ERROR_OUT_OF_MEMORY);
2380 // add the item identifier (RowToResult does so for bookmark items in
2381 // the next else block);
2382 node->mItemId =
2383 mDBGetChildren->AsInt64(nsNavHistory::kGetInfoIndex_ItemId);
2385 // date-added and last-modified
2386 node->mDateAdded =
2387 mDBGetChildren->AsInt64(nsNavHistory::kGetInfoIndex_ItemDateAdded);
2388 node->mLastModified =
2389 mDBGetChildren->AsInt64(nsNavHistory::kGetInfoIndex_ItemLastModified);
2392 // this method fills all bookmark queries, so we store the index of the
2393 // item in its parent
2394 node->mBookmarkIndex = index;
2396 NS_ENSURE_TRUE(aChildren->AppendObject(node), NS_ERROR_OUT_OF_MEMORY);
2398 return NS_OK;
2401 PRInt32
2402 nsNavBookmarks::FolderCount(PRInt64 aFolder)
2404 mozStorageStatementScoper scope(mDBFolderCount);
2406 nsresult rv = mDBFolderCount->BindInt64Parameter(0, aFolder);
2407 NS_ENSURE_SUCCESS(rv, 0);
2409 PRBool results;
2410 rv = mDBFolderCount->ExecuteStep(&results);
2411 NS_ENSURE_SUCCESS(rv, rv);
2413 return mDBFolderCount->AsInt32(0);
2416 NS_IMETHODIMP
2417 nsNavBookmarks::IsBookmarked(nsIURI *aURI, PRBool *aBookmarked)
2419 NS_ENSURE_ARG(aURI);
2421 nsNavHistory* history = History();
2422 NS_ENSURE_TRUE(history, NS_ERROR_UNEXPECTED);
2424 // convert the URL to an ID
2425 PRInt64 urlID;
2426 nsresult rv = history->GetUrlIdFor(aURI, &urlID, PR_FALSE);
2427 NS_ENSURE_SUCCESS(rv, rv);
2428 if (! urlID) {
2429 // never seen this before, not even in history
2430 *aBookmarked = PR_FALSE;
2431 return NS_OK;
2434 PRInt64 bookmarkedID;
2435 PRBool foundOne = mBookmarksHash.Get(urlID, &bookmarkedID);
2437 // IsBookmarked only tests if this exact URI is bookmarked, so we need to
2438 // check that the destination matches
2439 if (foundOne)
2440 *aBookmarked = (urlID == bookmarkedID);
2441 else
2442 *aBookmarked = PR_FALSE;
2444 #ifdef DEBUG
2445 // sanity check for the bookmark hashtable
2446 PRBool realBookmarked;
2447 rv = IsBookmarkedInDatabase(urlID, &realBookmarked);
2448 NS_ASSERTION(realBookmarked == *aBookmarked,
2449 "Bookmark hash table out-of-sync with the database");
2450 #endif
2452 return NS_OK;
2455 NS_IMETHODIMP
2456 nsNavBookmarks::GetBookmarkedURIFor(nsIURI* aURI, nsIURI** _retval)
2458 *_retval = nsnull;
2460 nsNavHistory* history = History();
2461 NS_ENSURE_TRUE(history, NS_ERROR_UNEXPECTED);
2463 // convert the URL to an ID
2464 PRInt64 urlID;
2465 nsresult rv = history->GetUrlIdFor(aURI, &urlID, PR_FALSE);
2466 NS_ENSURE_SUCCESS(rv, rv);
2467 if (! urlID) {
2468 // never seen this before, not even in history, leave result NULL
2469 return NS_OK;
2472 PRInt64 bookmarkID;
2473 if (mBookmarksHash.Get(urlID, &bookmarkID)) {
2474 // found one, convert ID back to URL. This statement is NOT refcounted
2475 mozIStorageStatement* statement = history->DBGetIdPageInfo();
2476 NS_ENSURE_TRUE(statement, NS_ERROR_UNEXPECTED);
2477 mozStorageStatementScoper scoper(statement);
2479 rv = statement->BindInt64Parameter(0, bookmarkID);
2480 NS_ENSURE_SUCCESS(rv, rv);
2482 PRBool hasMore;
2483 if (NS_SUCCEEDED(statement->ExecuteStep(&hasMore)) && hasMore) {
2484 nsCAutoString spec;
2485 statement->GetUTF8String(nsNavHistory::kGetInfoIndex_URL, spec);
2486 return NS_NewURI(_retval, spec);
2489 return NS_OK;
2492 NS_IMETHODIMP
2493 nsNavBookmarks::ChangeBookmarkURI(PRInt64 aBookmarkId, nsIURI *aNewURI)
2495 NS_ENSURE_ARG(aNewURI);
2497 mozStorageTransaction transaction(mDBConn, PR_FALSE);
2499 PRInt64 placeId;
2500 nsresult rv = History()->GetUrlIdFor(aNewURI, &placeId, PR_TRUE);
2501 NS_ENSURE_SUCCESS(rv, rv);
2502 if (!placeId)
2503 return NS_ERROR_INVALID_ARG;
2505 nsCOMPtr<mozIStorageStatement> statement;
2506 rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
2507 "UPDATE moz_bookmarks SET fk = ?1 WHERE id = ?2"),
2508 getter_AddRefs(statement));
2509 statement->BindInt64Parameter(0, placeId);
2510 statement->BindInt64Parameter(1, aBookmarkId);
2512 rv = statement->Execute();
2513 NS_ENSURE_SUCCESS(rv, rv);
2515 rv = SetItemDateInternal(mDBSetItemLastModified, aBookmarkId, PR_Now());
2516 NS_ENSURE_SUCCESS(rv, rv);
2518 rv = transaction.Commit();
2519 NS_ENSURE_SUCCESS(rv, rv);
2521 // upon changing the uri for a bookmark, update the frecency for the new place
2522 // no need to check if this is a livemark, because...
2523 rv = History()->UpdateFrecency(placeId, PR_TRUE /* isBookmark */);
2524 NS_ENSURE_SUCCESS(rv, rv);
2526 #if 0
2527 // upon changing the uri for a bookmark, update the frecency for the old place
2528 // XXX todo, we need to get the oldPlaceId (fk) before changing it above
2529 // and then here, we need to determine if that oldPlaceId is still a bookmark (and not a livemark)
2530 rv = History()->UpdateFrecency(oldPlaceId, PR_FALSE /* isBookmark */);
2531 NS_ENSURE_SUCCESS(rv, rv);
2532 #endif
2534 nsCAutoString spec;
2535 rv = aNewURI->GetSpec(spec);
2536 NS_ENSURE_SUCCESS(rv, rv);
2538 // Pass the new URI to OnItemChanged.
2539 ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver,
2540 OnItemChanged(aBookmarkId, NS_LITERAL_CSTRING("uri"), PR_FALSE, spec))
2542 return NS_OK;
2545 NS_IMETHODIMP
2546 nsNavBookmarks::GetFolderIdForItem(PRInt64 aItemId, PRInt64 *aFolderId)
2548 NS_ENSURE_ARG_POINTER(aFolderId);
2550 mozStorageStatementScoper scope(mDBGetItemProperties);
2551 nsresult rv = mDBGetItemProperties->BindInt64Parameter(0, aItemId);
2552 NS_ENSURE_SUCCESS(rv, rv);
2554 PRBool results;
2555 rv = mDBGetItemProperties->ExecuteStep(&results);
2556 NS_ENSURE_SUCCESS(rv, rv);
2558 if (!results)
2559 return NS_ERROR_INVALID_ARG; // invalid item id
2561 rv = mDBGetItemProperties->GetInt64(kGetItemPropertiesIndex_Parent, aFolderId);
2562 NS_ENSURE_SUCCESS(rv, rv);
2564 // this should not happen, but see bug #400448 for details
2565 NS_ENSURE_TRUE(aItemId != *aFolderId, NS_ERROR_UNEXPECTED);
2566 return NS_OK;
2569 NS_IMETHODIMP
2570 nsNavBookmarks::GetBookmarkIdsForURITArray(nsIURI *aURI,
2571 nsTArray<PRInt64> *aResult)
2573 mozStorageStatementScoper scope(mDBFindURIBookmarks);
2575 nsresult rv = BindStatementURI(mDBFindURIBookmarks, 0, aURI);
2576 NS_ENSURE_SUCCESS(rv, rv);
2577 mDBFindURIBookmarks->BindInt32Parameter(1, TYPE_BOOKMARK);
2579 PRBool more;
2580 while (NS_SUCCEEDED((rv = mDBFindURIBookmarks->ExecuteStep(&more))) && more) {
2581 if (! aResult->AppendElement(
2582 mDBFindURIBookmarks->AsInt64(kFindBookmarksIndex_ID)))
2583 return NS_ERROR_OUT_OF_MEMORY;
2586 NS_ENSURE_SUCCESS(rv, rv);
2587 return NS_OK;
2590 NS_IMETHODIMP
2591 nsNavBookmarks::GetBookmarkIdsForURI(nsIURI *aURI, PRUint32 *aCount,
2592 PRInt64 **aBookmarks)
2594 *aCount = 0;
2595 *aBookmarks = nsnull;
2596 nsTArray<PRInt64> bookmarks;
2598 // Get the information from the DB as a TArray
2599 nsresult rv = GetBookmarkIdsForURITArray(aURI, &bookmarks);
2600 NS_ENSURE_SUCCESS(rv, rv);
2602 // Copy the results into a new array for output
2603 if (bookmarks.Length()) {
2604 *aBookmarks = static_cast<PRInt64*>
2605 (nsMemory::Alloc(sizeof(PRInt64) * bookmarks.Length()));
2606 if (! *aBookmarks)
2607 return NS_ERROR_OUT_OF_MEMORY;
2608 for (PRUint32 i = 0; i < bookmarks.Length(); i ++)
2609 (*aBookmarks)[i] = bookmarks[i];
2611 *aCount = bookmarks.Length();
2613 return NS_OK;
2616 NS_IMETHODIMP
2617 nsNavBookmarks::GetItemIndex(PRInt64 aItemId, PRInt32 *aIndex)
2619 mozStorageStatementScoper scope(mDBGetItemIndex);
2620 mDBGetItemIndex->BindInt64Parameter(0, aItemId);
2621 PRBool results;
2622 nsresult rv = mDBGetItemIndex->ExecuteStep(&results);
2623 NS_ENSURE_SUCCESS(rv, rv);
2624 if (!results) {
2625 *aIndex = -1;
2626 return NS_OK;
2629 *aIndex = mDBGetItemIndex->AsInt32(0);
2630 return NS_OK;
2633 NS_IMETHODIMP
2634 nsNavBookmarks::SetItemIndex(PRInt64 aItemId, PRInt32 aNewIndex)
2636 nsresult rv;
2637 PRInt32 oldIndex = 0;
2638 PRInt64 parent = 0;
2641 mozStorageStatementScoper scopeGet(mDBGetItemProperties);
2642 rv = mDBGetItemProperties->BindInt64Parameter(0, aItemId);
2643 NS_ENSURE_SUCCESS(rv, rv);
2645 PRBool results;
2646 rv = mDBGetItemProperties->ExecuteStep(&results);
2647 NS_ENSURE_SUCCESS(rv, rv);
2648 if (!results)
2649 return NS_OK;
2651 oldIndex = mDBGetItemProperties->AsInt32(kGetItemPropertiesIndex_Position);
2652 parent = mDBGetItemProperties->AsInt64(kGetItemPropertiesIndex_Parent);
2655 mozStorageStatementScoper scoper(mDBSetItemIndex);
2656 rv = mDBSetItemIndex->BindInt64Parameter(0, aItemId);
2657 NS_ENSURE_SUCCESS(rv, rv);
2658 rv = mDBSetItemIndex->BindInt32Parameter(1, aNewIndex);
2659 NS_ENSURE_SUCCESS(rv, rv);
2661 rv = mDBSetItemIndex->Execute();
2662 NS_ENSURE_SUCCESS(rv, rv);
2664 ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver,
2665 OnItemRemoved(aItemId, parent, oldIndex))
2666 ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver,
2667 OnItemAdded(aItemId, parent, aNewIndex))
2669 return NS_OK;
2672 NS_IMETHODIMP
2673 nsNavBookmarks::SetKeywordForBookmark(PRInt64 aBookmarkId, const nsAString& aKeyword)
2675 if (aBookmarkId < 1)
2676 return NS_ERROR_INVALID_ARG; // invalid bookmark id
2678 // Shortcuts are always lowercased internally.
2679 nsAutoString kwd(aKeyword);
2680 ToLowerCase(kwd);
2682 mozStorageTransaction transaction(mDBConn, PR_FALSE);
2683 nsresult rv;
2684 PRBool results;
2685 PRInt64 keywordId = 0;
2687 if (!kwd.IsEmpty()) {
2688 // Attempt to find pre-existing keyword record
2689 nsCOMPtr<mozIStorageStatement> getKeywordStmnt;
2690 rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
2691 "SELECT id from moz_keywords WHERE keyword = ?1"),
2692 getter_AddRefs(getKeywordStmnt));
2693 NS_ENSURE_SUCCESS(rv, rv);
2694 rv = getKeywordStmnt->BindStringParameter(0, kwd);
2695 NS_ENSURE_SUCCESS(rv, rv);
2697 rv = getKeywordStmnt->ExecuteStep(&results);
2698 NS_ENSURE_SUCCESS(rv, rv);
2700 if (results) {
2701 rv = getKeywordStmnt->GetInt64(0, &keywordId);
2702 NS_ENSURE_SUCCESS(rv, rv);
2704 else {
2705 // If not already in the db, create new keyword record
2706 nsCOMPtr<mozIStorageStatement> addKeywordStmnt;
2707 rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
2708 "INSERT INTO moz_keywords (keyword) VALUES (?1)"),
2709 getter_AddRefs(addKeywordStmnt));
2710 NS_ENSURE_SUCCESS(rv, rv);
2711 rv = addKeywordStmnt->BindStringParameter(0, kwd);
2712 NS_ENSURE_SUCCESS(rv, rv);
2713 rv = addKeywordStmnt->Execute();
2714 NS_ENSURE_SUCCESS(rv, rv);
2716 nsCOMPtr<mozIStorageStatement> idStmt;
2717 rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
2718 "SELECT id "
2719 "FROM moz_keywords "
2720 "ORDER BY ROWID DESC "
2721 "LIMIT 1"),
2722 getter_AddRefs(idStmt));
2723 NS_ENSURE_SUCCESS(rv, rv);
2725 PRBool hasResult;
2726 rv = idStmt->ExecuteStep(&hasResult);
2727 NS_ENSURE_SUCCESS(rv, rv);
2728 NS_ASSERTION(hasResult, "hasResult is false but the call succeeded?");
2729 keywordId = idStmt->AsInt64(0);
2733 // Update bookmark record w/ the keyword's id, or null
2734 nsCOMPtr<mozIStorageStatement> updateKeywordStmnt;
2735 rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
2736 "UPDATE moz_bookmarks SET keyword_id = ?1 WHERE id = ?2"),
2737 getter_AddRefs(updateKeywordStmnt));
2738 NS_ENSURE_SUCCESS(rv, rv);
2739 rv = updateKeywordStmnt->BindInt64Parameter(0, keywordId);
2740 NS_ENSURE_SUCCESS(rv, rv);
2741 rv = updateKeywordStmnt->BindInt64Parameter(1, aBookmarkId);
2742 NS_ENSURE_SUCCESS(rv, rv);
2743 rv = updateKeywordStmnt->Execute();
2744 NS_ENSURE_SUCCESS(rv, rv);
2746 rv = SetItemDateInternal(mDBSetItemLastModified, aBookmarkId, PR_Now());
2747 NS_ENSURE_SUCCESS(rv, rv);
2749 transaction.Commit();
2751 // Pass the new keyword to OnItemChanged.
2752 ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver,
2753 OnItemChanged(aBookmarkId, NS_LITERAL_CSTRING("keyword"),
2754 PR_FALSE, NS_ConvertUTF16toUTF8(aKeyword)))
2756 return NS_OK;
2759 NS_IMETHODIMP
2760 nsNavBookmarks::GetKeywordForURI(nsIURI* aURI, nsAString& aKeyword)
2762 aKeyword.Truncate(0);
2764 mozStorageStatementScoper scoper(mDBGetKeywordForURI);
2765 nsresult rv = BindStatementURI(mDBGetKeywordForURI, 0, aURI);
2766 NS_ENSURE_SUCCESS(rv, rv);
2768 PRBool hasMore = PR_FALSE;
2769 rv = mDBGetKeywordForURI->ExecuteStep(&hasMore);
2770 if (NS_FAILED(rv) || ! hasMore) {
2771 aKeyword.SetIsVoid(PR_TRUE);
2772 return NS_OK; // not found: return void keyword string
2775 // found, get the keyword
2776 return mDBGetKeywordForURI->GetString(0, aKeyword);
2779 NS_IMETHODIMP
2780 nsNavBookmarks::GetKeywordForBookmark(PRInt64 aBookmarkId, nsAString& aKeyword)
2782 aKeyword.Truncate(0);
2784 mozStorageStatementScoper scoper(mDBGetKeywordForBookmark);
2785 nsresult rv = mDBGetKeywordForBookmark->BindInt64Parameter(0, aBookmarkId);
2786 NS_ENSURE_SUCCESS(rv, rv);
2788 PRBool hasMore = PR_FALSE;
2789 rv = mDBGetKeywordForBookmark->ExecuteStep(&hasMore);
2790 if (NS_FAILED(rv) || ! hasMore) {
2791 aKeyword.SetIsVoid(PR_TRUE);
2792 return NS_OK; // not found: return void keyword string
2795 // found, get the keyword
2796 return mDBGetKeywordForBookmark->GetString(0, aKeyword);
2799 NS_IMETHODIMP
2800 nsNavBookmarks::GetURIForKeyword(const nsAString& aKeyword, nsIURI** aURI)
2802 *aURI = nsnull;
2803 if (aKeyword.IsEmpty())
2804 return NS_ERROR_INVALID_ARG;
2806 // Shortcuts are always lowercased internally.
2807 nsAutoString kwd(aKeyword);
2808 ToLowerCase(kwd);
2810 mozStorageStatementScoper scoper(mDBGetURIForKeyword);
2811 nsresult rv = mDBGetURIForKeyword->BindStringParameter(0, kwd);
2812 NS_ENSURE_SUCCESS(rv, rv);
2814 PRBool hasMore = PR_FALSE;
2815 rv = mDBGetURIForKeyword->ExecuteStep(&hasMore);
2816 if (NS_FAILED(rv) || ! hasMore)
2817 return NS_OK; // not found: leave URI null
2819 // found, get the URI
2820 nsCAutoString spec;
2821 rv = mDBGetURIForKeyword->GetUTF8String(0, spec);
2822 NS_ENSURE_SUCCESS(rv, rv);
2823 return NS_NewURI(aURI, spec);
2826 // See RunInBatchMode
2827 nsresult
2828 nsNavBookmarks::BeginUpdateBatch()
2830 if (mBatchLevel++ == 0) {
2831 mozIStorageConnection* conn = mDBConn;
2832 PRBool transactionInProgress = PR_TRUE; // default to no transaction on err
2833 conn->GetTransactionInProgress(&transactionInProgress);
2834 mBatchHasTransaction = ! transactionInProgress;
2835 if (mBatchHasTransaction)
2836 conn->BeginTransaction();
2838 ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver,
2839 OnBeginUpdateBatch())
2841 return NS_OK;
2844 nsresult
2845 nsNavBookmarks::EndUpdateBatch()
2847 if (--mBatchLevel == 0) {
2848 if (mBatchHasTransaction)
2849 mDBConn->CommitTransaction();
2850 mBatchHasTransaction = PR_FALSE;
2851 ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver,
2852 OnEndUpdateBatch())
2854 return NS_OK;
2857 NS_IMETHODIMP
2858 nsNavBookmarks::RunInBatchMode(nsINavHistoryBatchCallback* aCallback,
2859 nsISupports* aUserData) {
2860 NS_ENSURE_ARG_POINTER(aCallback);
2862 BeginUpdateBatch();
2863 nsresult rv = aCallback->RunBatched(aUserData);
2864 EndUpdateBatch();
2866 return rv;
2869 NS_IMETHODIMP
2870 nsNavBookmarks::AddObserver(nsINavBookmarkObserver *aObserver,
2871 PRBool aOwnsWeak)
2873 return mObservers.AppendWeakElement(aObserver, aOwnsWeak);
2876 NS_IMETHODIMP
2877 nsNavBookmarks::RemoveObserver(nsINavBookmarkObserver *aObserver)
2879 return mObservers.RemoveWeakElement(aObserver);
2883 * Called by the History service when shutting down
2885 nsresult
2886 nsNavBookmarks::OnQuit()
2888 return NS_OK;
2891 // nsNavBookmarks::nsINavHistoryObserver
2893 NS_IMETHODIMP
2894 nsNavBookmarks::OnBeginUpdateBatch()
2896 // These aren't passed through to bookmark observers currently.
2897 return NS_OK;
2900 NS_IMETHODIMP
2901 nsNavBookmarks::OnEndUpdateBatch()
2903 // These aren't passed through to bookmark observers currently.
2904 return NS_OK;
2907 NS_IMETHODIMP
2908 nsNavBookmarks::OnVisit(nsIURI *aURI, PRInt64 aVisitID, PRTime aTime,
2909 PRInt64 aSessionID, PRInt64 aReferringID,
2910 PRUint32 aTransitionType, PRUint32* aAdded)
2912 // If the page is bookmarked, we need to notify observers
2913 PRBool bookmarked = PR_FALSE;
2914 IsBookmarked(aURI, &bookmarked);
2915 if (bookmarked) {
2916 // query for all bookmarks for that URI, notify for each
2917 nsTArray<PRInt64> bookmarks;
2919 nsresult rv = GetBookmarkIdsForURITArray(aURI, &bookmarks);
2920 NS_ENSURE_SUCCESS(rv, rv);
2922 if (bookmarks.Length()) {
2923 for (PRUint32 i = 0; i < bookmarks.Length(); i++)
2924 ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver,
2925 OnItemVisited(bookmarks[i], aVisitID, aTime))
2928 return NS_OK;
2931 NS_IMETHODIMP
2932 nsNavBookmarks::OnDeleteURI(nsIURI *aURI)
2934 // If the page is bookmarked, we need to notify observers
2935 PRBool bookmarked = PR_FALSE;
2936 IsBookmarked(aURI, &bookmarked);
2937 if (bookmarked) {
2938 // query for all bookmarks for that URI, notify for each
2939 nsTArray<PRInt64> bookmarks;
2941 nsresult rv = GetBookmarkIdsForURITArray(aURI, &bookmarks);
2942 NS_ENSURE_SUCCESS(rv, rv);
2944 if (bookmarks.Length()) {
2945 for (PRUint32 i = 0; i < bookmarks.Length(); i ++)
2946 ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver,
2947 OnItemChanged(bookmarks[i], NS_LITERAL_CSTRING("cleartime"),
2948 PR_FALSE, EmptyCString()))
2951 return NS_OK;
2954 NS_IMETHODIMP
2955 nsNavBookmarks::OnClearHistory()
2957 // TODO(bryner): we should notify on visited-time change for all URIs
2958 return NS_OK;
2961 NS_IMETHODIMP
2962 nsNavBookmarks::OnTitleChanged(nsIURI* aURI, const nsAString& aPageTitle)
2964 // NOOP. We don't consume page titles from moz_places anymore.
2965 // Title-change notifications are sent from SetItemTitle.
2966 return NS_OK;
2969 NS_IMETHODIMP
2970 nsNavBookmarks::OnPageChanged(nsIURI *aURI, PRUint32 aWhat,
2971 const nsAString &aValue)
2973 nsresult rv;
2974 if (aWhat == nsINavHistoryObserver::ATTRIBUTE_FAVICON) {
2975 // Favicons may be set to either pure URIs or to folder URIs
2976 PRBool isPlaceURI;
2977 rv = aURI->SchemeIs("place", &isPlaceURI);
2978 NS_ENSURE_SUCCESS(rv, rv);
2979 if (isPlaceURI) {
2980 nsCAutoString spec;
2981 rv = aURI->GetSpec(spec);
2982 NS_ENSURE_SUCCESS(rv, rv);
2984 nsNavHistory* history = History();
2985 NS_ENSURE_TRUE(history, NS_ERROR_UNEXPECTED);
2987 nsCOMArray<nsNavHistoryQuery> queries;
2988 nsCOMPtr<nsNavHistoryQueryOptions> options;
2989 rv = history->QueryStringToQueryArray(spec, &queries, getter_AddRefs(options));
2990 NS_ENSURE_SUCCESS(rv, rv);
2992 NS_ENSURE_STATE(queries.Count() == 1);
2993 NS_ENSURE_STATE(queries[0]->Folders().Length() == 1);
2995 ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver,
2996 OnItemChanged(queries[0]->Folders()[0], NS_LITERAL_CSTRING("favicon"),
2997 PR_FALSE, NS_ConvertUTF16toUTF8(aValue)));
2999 else {
3000 // query for all bookmarks for that URI, notify for each
3001 nsTArray<PRInt64> bookmarks;
3002 rv = GetBookmarkIdsForURITArray(aURI, &bookmarks);
3003 NS_ENSURE_SUCCESS(rv, rv);
3005 if (bookmarks.Length()) {
3006 for (PRUint32 i = 0; i < bookmarks.Length(); i ++)
3007 ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver,
3008 OnItemChanged(bookmarks[i], NS_LITERAL_CSTRING("favicon"),
3009 PR_FALSE, NS_ConvertUTF16toUTF8(aValue)));
3013 return NS_OK;
3016 NS_IMETHODIMP
3017 nsNavBookmarks::OnPageExpired(nsIURI* aURI, PRTime aVisitTime,
3018 PRBool aWholeEntry)
3020 // pages that are bookmarks shouldn't expire, so we don't need to handle it
3021 return NS_OK;
3024 // nsIAnnotationObserver
3026 NS_IMETHODIMP
3027 nsNavBookmarks::OnPageAnnotationSet(nsIURI* aPage, const nsACString& aName)
3029 return NS_OK;
3032 NS_IMETHODIMP
3033 nsNavBookmarks::OnItemAnnotationSet(PRInt64 aItemId, const nsACString& aName)
3035 nsresult rv = SetItemDateInternal(mDBSetItemLastModified, aItemId, PR_Now());
3036 NS_ENSURE_SUCCESS(rv, rv);
3038 ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver,
3039 OnItemChanged(aItemId, aName, PR_TRUE, EmptyCString()));
3041 return NS_OK;
3045 NS_IMETHODIMP
3046 nsNavBookmarks::OnPageAnnotationRemoved(nsIURI* aPage, const nsACString& aName)
3048 return NS_OK;
3051 NS_IMETHODIMP
3052 nsNavBookmarks::OnItemAnnotationRemoved(PRInt64 aItemId, const nsACString& aName)
3054 nsresult rv = SetItemDateInternal(mDBSetItemLastModified, aItemId, PR_Now());
3055 NS_ENSURE_SUCCESS(rv, rv);
3057 ENUMERATE_WEAKARRAY(mObservers, nsINavBookmarkObserver,
3058 OnItemChanged(aItemId, aName, PR_TRUE, EmptyCString()));
3060 return NS_OK;
3063 PRBool
3064 nsNavBookmarks::ItemExists(PRInt64 aItemId) {
3065 mozStorageStatementScoper scope(mDBGetItemProperties);
3066 nsresult rv = mDBGetItemProperties->BindInt64Parameter(0, aItemId);
3067 NS_ENSURE_SUCCESS(rv, PR_FALSE);
3069 PRBool results;
3070 rv = mDBGetItemProperties->ExecuteStep(&results);
3071 NS_ENSURE_SUCCESS(rv, PR_FALSE);
3073 if (!results)
3074 return PR_FALSE;
3076 return PR_TRUE;