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