LCOV - code coverage report
Current view: directory - toolkit/components/places - nsNavBookmarks.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 1359 1310 96.4 %
Date: 2012-06-02 Functions: 103 102 99.0 %

       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
       4                 :  *
       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/
       9                 :  *
      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.
      14                 :  *
      15                 :  * The Original Code is Places.
      16                 :  *
      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.
      21                 :  *
      22                 :  * Contributor(s):
      23                 :  *   Brian Ryner <bryner@brianryner.com> (original author)
      24                 :  *   Dietrich Ayala <dietrich@mozilla.com>
      25                 :  *   Drew Willcoxon <adw@mozilla.com>
      26                 :  *   Marco Bonardo <mak77@bonardo.net>
      27                 :  *
      28                 :  * Alternatively, the contents of this file may be used under the terms of
      29                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      30                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      31                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      32                 :  * of those above. If you wish to allow use of your version of this file only
      33                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      34                 :  * use your version of this file under the terms of the MPL, indicate your
      35                 :  * decision by deleting the provisions above and replace them with the notice
      36                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      37                 :  * the provisions above, a recipient may use your version of this file under
      38                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      39                 :  *
      40                 :  * ***** END LICENSE BLOCK ***** */
      41                 : 
      42                 : #include "nsNavBookmarks.h"
      43                 : 
      44                 : #include "nsNavHistory.h"
      45                 : #include "nsAnnotationService.h"
      46                 : #include "nsPlacesMacros.h"
      47                 : #include "Helpers.h"
      48                 : 
      49                 : #include "nsAppDirectoryServiceDefs.h"
      50                 : #include "nsNetUtil.h"
      51                 : #include "nsUnicharUtils.h"
      52                 : #include "nsPrintfCString.h"
      53                 : #include "nsIUUIDGenerator.h"
      54                 : #include "prprf.h"
      55                 : #include "mozilla/storage.h"
      56                 : #include "mozilla/FunctionTimer.h"
      57                 : #include "mozilla/Util.h"
      58                 : 
      59                 : #include "sampler.h"
      60                 : 
      61                 : #define BOOKMARKS_TO_KEYWORDS_INITIAL_CACHE_SIZE 64
      62                 : #define RECENT_BOOKMARKS_INITIAL_CACHE_SIZE 10
      63                 : // Threashold to expire old bookmarks if the initial cache size is exceeded.
      64                 : #define RECENT_BOOKMARKS_THRESHOLD PRTime((PRInt64)1 * 60 * PR_USEC_PER_SEC)
      65                 : 
      66                 : #define BEGIN_CRITICAL_BOOKMARK_CACHE_SECTION(_itemId_) \
      67                 :   mUncachableBookmarks.PutEntry(_itemId_); \
      68                 :   mRecentBookmarksCache.RemoveEntry(_itemId_)
      69                 : 
      70                 : #define END_CRITICAL_BOOKMARK_CACHE_SECTION(_itemId_) \
      71                 :   MOZ_ASSERT(!mRecentBookmarksCache.GetEntry(_itemId_)); \
      72                 :   MOZ_ASSERT(mUncachableBookmarks.GetEntry(_itemId_)); \
      73                 :   mUncachableBookmarks.RemoveEntry(_itemId_)
      74                 : 
      75                 : #define ADD_TO_BOOKMARK_CACHE(_itemId_, _data_) \
      76                 :   PR_BEGIN_MACRO \
      77                 :   ExpireNonrecentBookmarks(&mRecentBookmarksCache); \
      78                 :   if (!mUncachableBookmarks.GetEntry(_itemId_)) { \
      79                 :     BookmarkKeyClass* key = mRecentBookmarksCache.PutEntry(_itemId_); \
      80                 :     if (key) { \
      81                 :       key->bookmark = _data_; \
      82                 :     } \
      83                 :   } \
      84                 :   PR_END_MACRO
      85                 : 
      86                 : #define TOPIC_PLACES_MAINTENANCE "places-maintenance-finished"
      87                 : 
      88                 : using namespace mozilla;
      89                 : 
      90                 : // These columns sit to the right of the kGetInfoIndex_* columns.
      91                 : const PRInt32 nsNavBookmarks::kGetChildrenIndex_Position = 14;
      92                 : const PRInt32 nsNavBookmarks::kGetChildrenIndex_Type = 15;
      93                 : const PRInt32 nsNavBookmarks::kGetChildrenIndex_PlaceID = 16;
      94                 : const PRInt32 nsNavBookmarks::kGetChildrenIndex_Guid = 17;
      95                 : 
      96                 : using namespace mozilla::places;
      97                 : 
      98             390 : PLACES_FACTORY_SINGLETON_IMPLEMENTATION(nsNavBookmarks, gBookmarksService)
      99                 : 
     100                 : #define BOOKMARKS_ANNO_PREFIX "bookmarks/"
     101                 : #define BOOKMARKS_TOOLBAR_FOLDER_ANNO NS_LITERAL_CSTRING(BOOKMARKS_ANNO_PREFIX "toolbarFolder")
     102                 : #define GUID_ANNO NS_LITERAL_CSTRING("placesInternal/GUID")
     103                 : #define READ_ONLY_ANNO NS_LITERAL_CSTRING("placesInternal/READ_ONLY")
     104                 : 
     105                 : 
     106                 : namespace {
     107                 : 
     108                 : struct keywordSearchData
     109             176 : {
     110                 :   PRInt64 itemId;
     111                 :   nsString keyword;
     112                 : };
     113                 : 
     114                 : PLDHashOperator
     115              45 : SearchBookmarkForKeyword(nsTrimInt64HashKey::KeyType aKey,
     116                 :                          const nsString aValue,
     117                 :                          void* aUserArg)
     118                 : {
     119              45 :   keywordSearchData* data = reinterpret_cast<keywordSearchData*>(aUserArg);
     120              45 :   if (data->keyword.Equals(aValue)) {
     121              33 :     data->itemId = aKey;
     122              33 :     return PL_DHASH_STOP;
     123                 :   }
     124              12 :   return PL_DHASH_NEXT;
     125                 : }
     126                 : 
     127                 : template<typename Method, typename DataType>
     128                 : class AsyncGetBookmarksForURI : public AsyncStatementCallback
     129            5008 : {
     130                 : public:
     131            1252 :   AsyncGetBookmarksForURI(nsNavBookmarks* aBookmarksSvc,
     132                 :                           Method aCallback,
     133                 :                           const DataType& aData)
     134                 :   : mBookmarksSvc(aBookmarksSvc)
     135                 :   , mCallback(aCallback)
     136            1252 :   , mData(aData)
     137                 :   {
     138            1252 :   }
     139                 : 
     140            1252 :   void Init()
     141                 :   {
     142            2504 :     nsRefPtr<Database> DB = Database::GetDatabase();
     143            1252 :     if (DB) {
     144                 :       nsCOMPtr<mozIStorageAsyncStatement> stmt = DB->GetAsyncStatement(
     145                 :         "SELECT b.id, b.guid, b.parent, b.lastModified, t.guid, t.parent "
     146                 :         "FROM moz_bookmarks b "
     147                 :         "JOIN moz_bookmarks t on t.id = b.parent "
     148                 :         "WHERE b.fk = (SELECT id FROM moz_places WHERE url = :page_url) "
     149                 :         "ORDER BY b.lastModified DESC, b.id DESC "
     150            2504 :       );
     151            1252 :       if (stmt) {
     152            1252 :         (void)URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"),
     153                 :                               mData.bookmark.url);
     154            2504 :         nsCOMPtr<mozIStoragePendingStatement> pendingStmt;
     155            1252 :         (void)stmt->ExecuteAsync(this, getter_AddRefs(pendingStmt));
     156                 :       }
     157                 :     }
     158            1252 :   }
     159                 : 
     160             141 :   NS_IMETHOD HandleResult(mozIStorageResultSet* aResultSet)
     161                 :   {
     162             282 :     nsCOMPtr<mozIStorageRow> row;
     163             433 :     while (NS_SUCCEEDED(aResultSet->GetNextRow(getter_AddRefs(row))) && row) {
     164                 :       // Skip tags, for the use-cases of this async getter they are useless.
     165                 :       PRInt64 grandParentId, tagsFolderId;
     166             151 :       nsresult rv = row->GetInt64(5, &grandParentId);
     167             151 :       NS_ENSURE_SUCCESS(rv, rv);
     168             151 :       rv = mBookmarksSvc->GetTagsFolder(&tagsFolderId);
     169             151 :       NS_ENSURE_SUCCESS(rv, rv);
     170             151 :       if (grandParentId == tagsFolderId) {
     171              13 :         continue;
     172                 :       }
     173                 : 
     174             138 :       mData.bookmark.grandParentId = grandParentId;
     175             138 :       rv = row->GetInt64(0, &mData.bookmark.id);
     176             138 :       NS_ENSURE_SUCCESS(rv, rv);
     177             138 :       rv = row->GetUTF8String(1, mData.bookmark.guid);
     178             138 :       NS_ENSURE_SUCCESS(rv, rv);
     179             138 :       rv = row->GetInt64(2, &mData.bookmark.parentId);
     180             138 :       NS_ENSURE_SUCCESS(rv, rv);
     181                 :       // lastModified (3) should not be set for the use-cases of this getter.
     182             138 :       rv = row->GetUTF8String(4, mData.bookmark.parentGuid);
     183             138 :       NS_ENSURE_SUCCESS(rv, rv);
     184                 : 
     185             138 :       if (mCallback) {
     186             138 :         ((*mBookmarksSvc).*mCallback)(mData);
     187                 :       }
     188                 :     }
     189             141 :     return NS_OK;
     190                 :   }
     191                 : 
     192                 : private:
     193                 :   nsRefPtr<nsNavBookmarks> mBookmarksSvc;
     194                 :   Method mCallback;
     195                 :   DataType mData;
     196                 : };
     197                 : 
     198                 : static PLDHashOperator
     199            7813 : ExpireNonrecentBookmarksCallback(BookmarkKeyClass* aKey,
     200                 :                                  void* userArg)
     201                 : {
     202            7813 :   PRInt64* threshold = reinterpret_cast<PRInt64*>(userArg);
     203            7813 :   if (aKey->creationTime < *threshold) {
     204               0 :     return PL_DHASH_REMOVE;
     205                 :   }
     206            7813 :   return PL_DHASH_NEXT;
     207                 : }
     208                 : 
     209                 : static void
     210            2370 : ExpireNonrecentBookmarks(nsTHashtable<BookmarkKeyClass>* hashTable)
     211                 : {
     212            2370 :   if (hashTable->Count() > RECENT_BOOKMARKS_INITIAL_CACHE_SIZE) {
     213             440 :     PRInt64 threshold = PR_Now() - RECENT_BOOKMARKS_THRESHOLD;
     214                 :     (void)hashTable->EnumerateEntries(ExpireNonrecentBookmarksCallback,
     215             440 :                                       reinterpret_cast<void*>(&threshold));
     216                 :   }
     217            2370 : }
     218                 : 
     219                 : static PLDHashOperator
     220            3898 : ExpireRecentBookmarksByParentCallback(BookmarkKeyClass* aKey,
     221                 :                                       void* userArg)
     222                 : {
     223            3898 :   PRInt64* parentId = reinterpret_cast<PRInt64*>(userArg);
     224            3898 :   if (aKey->bookmark.parentId == *parentId) {
     225             551 :     return PL_DHASH_REMOVE;
     226                 :   }
     227            3347 :   return PL_DHASH_NEXT;
     228                 : }
     229                 : 
     230                 : static void
     231             635 : ExpireRecentBookmarksByParent(nsTHashtable<BookmarkKeyClass>* hashTable,
     232                 :                               PRInt64 aParentId)
     233                 : {
     234                 :   (void)hashTable->EnumerateEntries(ExpireRecentBookmarksByParentCallback,
     235             635 :                                     reinterpret_cast<void*>(&aParentId));
     236             635 : }
     237                 : 
     238                 : } // Anonymous namespace.
     239                 : 
     240                 : 
     241             195 : nsNavBookmarks::nsNavBookmarks() : mItemCount(0)
     242                 :                                  , mRoot(0)
     243                 :                                  , mMenuRoot(0)
     244                 :                                  , mTagsRoot(0)
     245                 :                                  , mUnfiledRoot(0)
     246                 :                                  , mToolbarRoot(0)
     247                 :                                  , mCanNotify(false)
     248                 :                                  , mCacheObservers("bookmark-observers")
     249             195 :                                  , mBatching(false)
     250                 : {
     251             195 :   NS_ASSERTION(!gBookmarksService,
     252                 :                "Attempting to create two instances of the service!");
     253             195 :   gBookmarksService = this;
     254             195 : }
     255                 : 
     256                 : 
     257             390 : nsNavBookmarks::~nsNavBookmarks()
     258                 : {
     259             195 :   NS_ASSERTION(gBookmarksService == this,
     260                 :                "Deleting a non-singleton instance of the service");
     261             195 :   if (gBookmarksService == this)
     262             195 :     gBookmarksService = nsnull;
     263             195 : }
     264                 : 
     265                 : 
     266           31054 : NS_IMPL_ISUPPORTS5(nsNavBookmarks
     267                 : , nsINavBookmarksService
     268                 : , nsINavHistoryObserver
     269                 : , nsIAnnotationObserver
     270                 : , nsIObserver
     271                 : , nsISupportsWeakReference
     272                 : )
     273                 : 
     274                 : 
     275                 : nsresult
     276             195 : nsNavBookmarks::Init()
     277                 : {
     278                 :   NS_TIME_FUNCTION;
     279                 : 
     280             195 :   mDB = Database::GetDatabase();
     281             195 :   NS_ENSURE_STATE(mDB);
     282                 : 
     283             195 :   mRecentBookmarksCache.Init(RECENT_BOOKMARKS_INITIAL_CACHE_SIZE);
     284             195 :   mUncachableBookmarks.Init(RECENT_BOOKMARKS_INITIAL_CACHE_SIZE);
     285                 : 
     286             390 :   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
     287             195 :   if (os) {
     288             195 :     (void)os->AddObserver(this, TOPIC_PLACES_MAINTENANCE, true);
     289             195 :     (void)os->AddObserver(this, TOPIC_PLACES_SHUTDOWN, true);
     290             195 :     (void)os->AddObserver(this, TOPIC_PLACES_CONNECTION_CLOSED, true);
     291                 :   }
     292                 : 
     293             195 :   nsresult rv = ReadRoots();
     294             195 :   NS_ENSURE_SUCCESS(rv, rv);
     295                 : 
     296             195 :   mCanNotify = true;
     297                 : 
     298                 :   // Observe annotations.
     299             195 :   nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService();
     300             195 :   NS_ENSURE_TRUE(annosvc, NS_ERROR_OUT_OF_MEMORY);
     301             195 :   annosvc->AddObserver(this);
     302                 : 
     303                 :   // Allows us to notify on title changes. MUST BE LAST so it is impossible
     304                 :   // to fail after this call, or the history service will have a reference to
     305                 :   // us and we won't go away.
     306             195 :   nsNavHistory* history = nsNavHistory::GetHistoryService();
     307             195 :   NS_ENSURE_STATE(history);
     308             195 :   history->AddObserver(this, true);
     309                 : 
     310                 :   // DO NOT PUT STUFF HERE that can fail. See observer comment above.
     311                 : 
     312             195 :   return NS_OK;
     313                 : }
     314                 : 
     315                 : nsresult
     316             195 : nsNavBookmarks::ReadRoots()
     317                 : {
     318             390 :   nsCOMPtr<mozIStorageStatement> stmt;
     319             390 :   nsresult rv = mDB->MainConn()->CreateStatement(NS_LITERAL_CSTRING(
     320                 :     "SELECT root_name, folder_id FROM moz_bookmarks_roots"
     321             390 :   ), getter_AddRefs(stmt));
     322             195 :   NS_ENSURE_SUCCESS(rv, rv);
     323                 : 
     324                 :   bool hasResult;
     325            1365 :   while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
     326            1950 :     nsCAutoString rootName;
     327             975 :     rv = stmt->GetUTF8String(0, rootName);
     328             975 :     NS_ENSURE_SUCCESS(rv, rv);
     329                 :     PRInt64 rootId;
     330             975 :     rv = stmt->GetInt64(1, &rootId);
     331             975 :     NS_ENSURE_SUCCESS(rv, rv);
     332             975 :     NS_ABORT_IF_FALSE(rootId != 0, "Root id is 0, that is an invalid value.");
     333                 : 
     334             975 :     if (rootName.EqualsLiteral("places")) {
     335             195 :       mRoot = rootId;
     336                 :     }
     337             780 :     else if (rootName.EqualsLiteral("menu")) {
     338             195 :       mMenuRoot = rootId;
     339                 :     }
     340             585 :     else if (rootName.EqualsLiteral("toolbar")) {
     341             195 :       mToolbarRoot = rootId;
     342                 :     }
     343             390 :     else if (rootName.EqualsLiteral("tags")) {
     344             195 :       mTagsRoot = rootId;
     345                 :     }
     346             195 :     else if (rootName.EqualsLiteral("unfiled")) {
     347             195 :       mUnfiledRoot = rootId;
     348                 :     }
     349                 :   }
     350                 : 
     351             195 :   if (!mRoot || !mMenuRoot || !mToolbarRoot || !mTagsRoot || !mUnfiledRoot)
     352               0 :     return NS_ERROR_FAILURE;
     353                 : 
     354             195 :   return NS_OK;
     355                 : }
     356                 : 
     357                 : // nsNavBookmarks::IsBookmarkedInDatabase
     358                 : //
     359                 : //    This checks to see if the specified place_id is actually bookmarked.
     360                 : 
     361                 : nsresult
     362               0 : nsNavBookmarks::IsBookmarkedInDatabase(PRInt64 aPlaceId,
     363                 :                                        bool* aIsBookmarked)
     364                 : {
     365                 :   nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
     366                 :     "SELECT 1 FROM moz_bookmarks WHERE fk = :page_id"
     367               0 :   );
     368               0 :   NS_ENSURE_STATE(stmt);
     369               0 :   mozStorageStatementScoper scoper(stmt);
     370                 : 
     371               0 :   nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), aPlaceId);
     372               0 :   NS_ENSURE_SUCCESS(rv, rv);
     373               0 :   rv = stmt->ExecuteStep(aIsBookmarked);
     374               0 :   NS_ENSURE_SUCCESS(rv, rv);
     375               0 :   return NS_OK;
     376                 : }
     377                 : 
     378                 : 
     379                 : nsresult
     380             635 : nsNavBookmarks::AdjustIndices(PRInt64 aFolderId,
     381                 :                               PRInt32 aStartIndex,
     382                 :                               PRInt32 aEndIndex,
     383                 :                               PRInt32 aDelta)
     384                 : {
     385             635 :   NS_ASSERTION(aStartIndex >= 0 && aEndIndex <= PR_INT32_MAX &&
     386                 :                aStartIndex <= aEndIndex, "Bad indices");
     387                 : 
     388                 :   // Expire all cached items for this parent, since all positions are going to
     389                 :   // change.
     390             635 :   ExpireRecentBookmarksByParent(&mRecentBookmarksCache, aFolderId);
     391                 : 
     392                 :   nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
     393                 :     "UPDATE moz_bookmarks SET position = position + :delta "
     394                 :       "WHERE parent = :parent "
     395                 :         "AND position BETWEEN :from_index AND :to_index"
     396            1270 :   );
     397             635 :   NS_ENSURE_STATE(stmt);
     398            1270 :   mozStorageStatementScoper scoper(stmt);
     399                 : 
     400             635 :   nsresult rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("delta"), aDelta);
     401             635 :   NS_ENSURE_SUCCESS(rv, rv);
     402             635 :   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aFolderId);
     403             635 :   NS_ENSURE_SUCCESS(rv, rv);
     404             635 :   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("from_index"), aStartIndex);
     405             635 :   NS_ENSURE_SUCCESS(rv, rv);
     406             635 :   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("to_index"), aEndIndex);
     407             635 :   NS_ENSURE_SUCCESS(rv, rv);
     408                 : 
     409             635 :   rv = stmt->Execute();
     410             635 :   NS_ENSURE_SUCCESS(rv, rv);
     411                 :  
     412             635 :   return NS_OK;
     413                 : }
     414                 : 
     415                 : 
     416                 : NS_IMETHODIMP
     417            1184 : nsNavBookmarks::GetPlacesRoot(PRInt64* aRoot)
     418                 : {
     419            1184 :   *aRoot = mRoot;
     420            1184 :   return NS_OK;
     421                 : }
     422                 : 
     423                 : 
     424                 : NS_IMETHODIMP
     425             824 : nsNavBookmarks::GetBookmarksMenuFolder(PRInt64* aRoot)
     426                 : {
     427             824 :   *aRoot = mMenuRoot;
     428             824 :   return NS_OK;
     429                 : }
     430                 : 
     431                 : 
     432                 : NS_IMETHODIMP
     433             633 : nsNavBookmarks::GetToolbarFolder(PRInt64* aFolderId)
     434                 : {
     435             633 :   *aFolderId = mToolbarRoot;
     436             633 :   return NS_OK;
     437                 : }
     438                 : 
     439                 : 
     440                 : NS_IMETHODIMP
     441            1488 : nsNavBookmarks::GetTagsFolder(PRInt64* aRoot)
     442                 : {
     443            1488 :   *aRoot = mTagsRoot;
     444            1488 :   return NS_OK;
     445                 : }
     446                 : 
     447                 : 
     448                 : NS_IMETHODIMP
     449             725 : nsNavBookmarks::GetUnfiledBookmarksFolder(PRInt64* aRoot)
     450                 : {
     451             725 :   *aRoot = mUnfiledRoot;
     452             725 :   return NS_OK;
     453                 : }
     454                 : 
     455                 : 
     456                 : nsresult
     457            1633 : nsNavBookmarks::InsertBookmarkInDB(PRInt64 aPlaceId,
     458                 :                                    enum ItemType aItemType,
     459                 :                                    PRInt64 aParentId,
     460                 :                                    PRInt32 aIndex,
     461                 :                                    const nsACString& aTitle,
     462                 :                                    PRTime aDateAdded,
     463                 :                                    PRTime aLastModified,
     464                 :                                    const nsACString& aParentGuid,
     465                 :                                    PRInt64 aGrandParentId,
     466                 :                                    nsIURI* aURI,
     467                 :                                    PRInt64* _itemId,
     468                 :                                    nsACString& _guid)
     469                 : {
     470                 :   // Check for a valid itemId.
     471            1633 :   MOZ_ASSERT(_itemId && (*_itemId == -1 || *_itemId > 0));
     472                 :   // Check for a valid placeId.
     473            1633 :   MOZ_ASSERT(aPlaceId && (aPlaceId == -1 || aPlaceId > 0));
     474                 : 
     475                 :   nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
     476                 :     "INSERT INTO moz_bookmarks "
     477                 :       "(id, fk, type, parent, position, title, "
     478                 :        "dateAdded, lastModified, guid) "
     479                 :     "VALUES (:item_id, :page_id, :item_type, :parent, :item_index, "
     480                 :             ":item_title, :date_added, :last_modified, "
     481                 :             "GENERATE_GUID())"
     482            3266 :   );
     483            1633 :   NS_ENSURE_STATE(stmt);
     484            3266 :   mozStorageStatementScoper scoper(stmt);
     485                 : 
     486                 :   nsresult rv;
     487            1633 :   if (*_itemId != -1)
     488               7 :     rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), *_itemId);
     489                 :   else
     490            1626 :     rv = stmt->BindNullByName(NS_LITERAL_CSTRING("item_id"));
     491            1633 :   NS_ENSURE_SUCCESS(rv, rv);
     492                 : 
     493            1633 :   if (aPlaceId != -1)
     494            1159 :     rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), aPlaceId);
     495                 :   else
     496             474 :     rv = stmt->BindNullByName(NS_LITERAL_CSTRING("page_id"));
     497            1633 :   NS_ENSURE_SUCCESS(rv, rv);
     498                 : 
     499            1633 :   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("item_type"), aItemType);
     500            1633 :   NS_ENSURE_SUCCESS(rv, rv);
     501            1633 :   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aParentId);
     502            1633 :   NS_ENSURE_SUCCESS(rv, rv);
     503            1633 :   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("item_index"), aIndex);
     504            1633 :   NS_ENSURE_SUCCESS(rv, rv);
     505                 : 
     506                 :   // Support NULL titles.
     507            1633 :   if (aTitle.IsVoid())
     508             394 :     rv = stmt->BindNullByName(NS_LITERAL_CSTRING("item_title"));
     509                 :   else
     510            1239 :     rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("item_title"), aTitle);
     511            1633 :   NS_ENSURE_SUCCESS(rv, rv);
     512                 : 
     513            1633 :   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("date_added"), aDateAdded);
     514            1633 :   NS_ENSURE_SUCCESS(rv, rv);
     515                 : 
     516            1633 :   if (aLastModified) {
     517               0 :     rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("last_modified"),
     518               0 :                                aLastModified);
     519                 :   }
     520                 :   else {
     521            1633 :     rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("last_modified"), aDateAdded);
     522                 :   }
     523            1633 :   NS_ENSURE_SUCCESS(rv, rv);
     524                 : 
     525            1633 :   rv = stmt->Execute();
     526            1633 :   NS_ENSURE_SUCCESS(rv, rv);
     527                 : 
     528            1633 :   if (*_itemId == -1) {
     529                 :     // Get the newly inserted item id and GUID.
     530                 :     nsCOMPtr<mozIStorageStatement> lastInsertIdStmt = mDB->GetStatement(
     531                 :       "SELECT id, guid "
     532                 :       "FROM moz_bookmarks "
     533                 :       "ORDER BY ROWID DESC "
     534                 :       "LIMIT 1"
     535            3252 :     );
     536            1626 :     NS_ENSURE_STATE(lastInsertIdStmt);
     537            3252 :     mozStorageStatementScoper lastInsertIdScoper(lastInsertIdStmt);
     538                 : 
     539                 :     bool hasResult;
     540            1626 :     rv = lastInsertIdStmt->ExecuteStep(&hasResult);
     541            1626 :     NS_ENSURE_SUCCESS(rv, rv);
     542            1626 :     NS_ENSURE_TRUE(hasResult, NS_ERROR_UNEXPECTED);
     543            1626 :     rv = lastInsertIdStmt->GetInt64(0, _itemId);
     544            1626 :     NS_ENSURE_SUCCESS(rv, rv);
     545            1626 :     rv = lastInsertIdStmt->GetUTF8String(1, _guid);
     546            1626 :     NS_ENSURE_SUCCESS(rv, rv);
     547                 :   }
     548                 : 
     549            1633 :   if (aParentId > 0) {
     550                 :     // Update last modified date of the ancestors.
     551                 :     // TODO (bug 408991): Doing this for all ancestors would be slow without a
     552                 :     //                    nested tree, so for now update only the parent.
     553            1632 :     rv = SetItemDateInternal(LAST_MODIFIED, aParentId, aDateAdded);
     554            1632 :     NS_ENSURE_SUCCESS(rv, rv);
     555                 :   }
     556                 : 
     557                 :   // Add a cache entry since we know everything about this bookmark.
     558            3266 :   BookmarkData bookmark;
     559            1633 :   bookmark.id = *_itemId;
     560            1633 :   bookmark.guid.Assign(_guid);
     561            1633 :   if (aTitle.IsVoid()) {
     562             394 :     bookmark.title.SetIsVoid(true);
     563                 :   }
     564                 :   else {
     565            1239 :     bookmark.title.Assign(aTitle);
     566                 :   }
     567            1633 :   bookmark.position = aIndex;
     568            1633 :   bookmark.placeId = aPlaceId;
     569            1633 :   bookmark.parentId = aParentId;
     570            1633 :   bookmark.type = aItemType;
     571            1633 :   bookmark.dateAdded = aDateAdded;
     572            1633 :   if (aLastModified)
     573               0 :     bookmark.lastModified = aLastModified;
     574                 :   else
     575            1633 :     bookmark.lastModified = aDateAdded;
     576            1633 :   if (aURI) {
     577            1159 :     rv = aURI->GetSpec(bookmark.url);
     578            1159 :     NS_ENSURE_SUCCESS(rv, rv);
     579                 :   }
     580            1633 :   bookmark.parentGuid = aParentGuid;
     581            1633 :   bookmark.grandParentId = aGrandParentId;
     582                 : 
     583            1633 :   ADD_TO_BOOKMARK_CACHE(*_itemId, bookmark);
     584                 : 
     585            1633 :   return NS_OK;
     586                 : }
     587                 : 
     588                 : 
     589                 : NS_IMETHODIMP
     590            1164 : nsNavBookmarks::InsertBookmark(PRInt64 aFolder,
     591                 :                                nsIURI* aURI,
     592                 :                                PRInt32 aIndex,
     593                 :                                const nsACString& aTitle,
     594                 :                                PRInt64* aNewBookmarkId)
     595                 : {
     596            1164 :   NS_ENSURE_ARG(aURI);
     597            1163 :   NS_ENSURE_ARG_POINTER(aNewBookmarkId);
     598            1163 :   NS_ENSURE_ARG_MIN(aIndex, nsINavBookmarksService::DEFAULT_INDEX);
     599                 : 
     600            2318 :   mozStorageTransaction transaction(mDB->MainConn(), false);
     601                 : 
     602            1159 :   nsNavHistory* history = nsNavHistory::GetHistoryService();
     603            1159 :   NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY);
     604                 :   PRInt64 placeId;
     605            2318 :   nsCAutoString placeGuid;
     606            1159 :   nsresult rv = history->GetOrCreateIdForPage(aURI, &placeId, placeGuid);
     607            1159 :   NS_ENSURE_SUCCESS(rv, rv);
     608                 : 
     609                 :   // Get the correct index for insertion.  This also ensures the parent exists.
     610                 :   PRInt32 index, folderCount;
     611                 :   PRInt64 grandParentId;
     612            2318 :   nsCAutoString folderGuid;
     613            1159 :   rv = FetchFolderInfo(aFolder, &folderCount, folderGuid, &grandParentId);
     614            1159 :   NS_ENSURE_SUCCESS(rv, rv);
     615            1159 :   if (aIndex == nsINavBookmarksService::DEFAULT_INDEX ||
     616                 :       aIndex >= folderCount) {
     617            1086 :     index = folderCount;
     618                 :   }
     619                 :   else {
     620              73 :     index = aIndex;
     621                 :     // Create space for the insertion.
     622              73 :     rv = AdjustIndices(aFolder, index, PR_INT32_MAX, 1);
     623              73 :     NS_ENSURE_SUCCESS(rv, rv);
     624                 :   }
     625                 : 
     626            1159 :   *aNewBookmarkId = -1;
     627            1159 :   PRTime dateAdded = PR_Now();
     628            2318 :   nsCAutoString guid;
     629            2318 :   nsCString title;
     630            1159 :   TruncateTitle(aTitle, title);
     631                 : 
     632                 :   rv = InsertBookmarkInDB(placeId, BOOKMARK, aFolder, index, title, dateAdded,
     633                 :                           nsnull, folderGuid, grandParentId, aURI,
     634            1159 :                           aNewBookmarkId, guid);
     635            1159 :   NS_ENSURE_SUCCESS(rv, rv);
     636                 : 
     637                 :   // If not a tag, recalculate frecency for this entry, since it changed.
     638            1159 :   if (grandParentId != mTagsRoot) {
     639             903 :     rv = history->UpdateFrecency(placeId);
     640             903 :     NS_ENSURE_SUCCESS(rv, rv);
     641                 :   }
     642                 : 
     643            1159 :   rv = transaction.Commit();
     644            1159 :   NS_ENSURE_SUCCESS(rv, rv);
     645                 : 
     646            1159 :   NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
     647                 :                    nsINavBookmarkObserver,
     648                 :                    OnItemAdded(*aNewBookmarkId, aFolder, index, TYPE_BOOKMARK,
     649                 :                                aURI, title, dateAdded, guid, folderGuid));
     650                 : 
     651                 :   // If the bookmark has been added to a tag container, notify all
     652                 :   // bookmark-folder result nodes which contain a bookmark for the new
     653                 :   // bookmark's url.
     654            1159 :   if (grandParentId == mTagsRoot) {
     655                 :     // Notify a tags change to all bookmarks for this URI.
     656             512 :     nsTArray<BookmarkData> bookmarks;
     657             256 :     rv = GetBookmarksForURI(aURI, bookmarks);
     658             256 :     NS_ENSURE_SUCCESS(rv, rv);
     659                 : 
     660             470 :     for (PRUint32 i = 0; i < bookmarks.Length(); ++i) {
     661                 :       // Check that bookmarks doesn't include the current tag itemId.
     662             214 :       MOZ_ASSERT(bookmarks[i].id != *aNewBookmarkId);
     663                 : 
     664             214 :       NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
     665                 :                        nsINavBookmarkObserver,
     666                 :                        OnItemChanged(bookmarks[i].id,
     667                 :                                      NS_LITERAL_CSTRING("tags"),
     668                 :                                      false,
     669                 :                                      EmptyCString(),
     670                 :                                      bookmarks[i].lastModified,
     671                 :                                      TYPE_BOOKMARK,
     672                 :                                      bookmarks[i].parentId,
     673                 :                                      bookmarks[i].guid,
     674                 :                                      bookmarks[i].parentGuid));
     675                 :     }
     676                 :   }
     677                 : 
     678            1159 :   return NS_OK;
     679                 : }
     680                 : 
     681                 : 
     682                 : NS_IMETHODIMP
     683             466 : nsNavBookmarks::RemoveItem(PRInt64 aItemId)
     684                 : {
     685             932 :   SAMPLE_LABEL("bookmarks", "RemoveItem");
     686             466 :   NS_ENSURE_ARG(aItemId != mRoot);
     687                 : 
     688             932 :   BookmarkData bookmark;
     689             466 :   nsresult rv = FetchItemInfo(aItemId, bookmark);
     690             466 :   NS_ENSURE_SUCCESS(rv, rv);
     691                 : 
     692             454 :   NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
     693                 :                    nsINavBookmarkObserver,
     694                 :                    OnBeforeItemRemoved(bookmark.id,
     695                 :                                        bookmark.type,
     696                 :                                        bookmark.parentId,
     697                 :                                        bookmark.guid,
     698                 :                                        bookmark.parentGuid));
     699                 : 
     700             908 :   mozStorageTransaction transaction(mDB->MainConn(), false);
     701                 : 
     702                 :   // First, if not a tag, remove item annotations.
     703             454 :   if (bookmark.parentId != mTagsRoot &&
     704                 :       bookmark.grandParentId != mTagsRoot) {
     705             176 :     nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService();
     706             176 :     NS_ENSURE_TRUE(annosvc, NS_ERROR_OUT_OF_MEMORY);
     707             176 :     rv = annosvc->RemoveItemAnnotations(bookmark.id);
     708             176 :     NS_ENSURE_SUCCESS(rv, rv);
     709                 :   }
     710                 : 
     711             454 :   if (bookmark.type == TYPE_FOLDER) {
     712                 :     // Remove all of the folder's children.
     713             180 :     rv = RemoveFolderChildren(bookmark.id);
     714             180 :     NS_ENSURE_SUCCESS(rv, rv);
     715                 :   }
     716                 : 
     717             454 :   BEGIN_CRITICAL_BOOKMARK_CACHE_SECTION(bookmark.id);
     718                 : 
     719                 :   nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
     720                 :     "DELETE FROM moz_bookmarks WHERE id = :item_id"
     721             908 :   );
     722             454 :   NS_ENSURE_STATE(stmt);
     723             908 :   mozStorageStatementScoper scoper(stmt);
     724                 : 
     725             454 :   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), bookmark.id);
     726             454 :   NS_ENSURE_SUCCESS(rv, rv);
     727             454 :   rv = stmt->Execute();
     728             454 :   NS_ENSURE_SUCCESS(rv, rv);
     729                 : 
     730                 :   // Fix indices in the parent.
     731             454 :   if (bookmark.position != DEFAULT_INDEX) {
     732                 :     rv = AdjustIndices(bookmark.parentId,
     733             454 :                        bookmark.position + 1, PR_INT32_MAX, -1);
     734             454 :     NS_ENSURE_SUCCESS(rv, rv);
     735                 :   }
     736                 : 
     737             454 :   bookmark.lastModified = PR_Now();
     738                 :   rv = SetItemDateInternal(LAST_MODIFIED, bookmark.parentId,
     739             454 :                            bookmark.lastModified);
     740             454 :   NS_ENSURE_SUCCESS(rv, rv);
     741                 : 
     742             454 :   rv = transaction.Commit();
     743             454 :   NS_ENSURE_SUCCESS(rv, rv);
     744                 : 
     745             454 :   END_CRITICAL_BOOKMARK_CACHE_SECTION(bookmark.id);
     746                 : 
     747             908 :   nsCOMPtr<nsIURI> uri;
     748             454 :   if (bookmark.type == TYPE_BOOKMARK) {
     749                 :     // If not a tag, recalculate frecency for this entry, since it changed.
     750             261 :     if (bookmark.grandParentId != mTagsRoot) {
     751             116 :       nsNavHistory* history = nsNavHistory::GetHistoryService();
     752             116 :       NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY);
     753             116 :       rv = history->UpdateFrecency(bookmark.placeId);
     754             116 :       NS_ENSURE_SUCCESS(rv, rv);
     755                 :     }
     756                 : 
     757             261 :     rv = UpdateKeywordsHashForRemovedBookmark(aItemId);
     758             261 :     NS_ENSURE_SUCCESS(rv, rv);
     759                 : 
     760                 :     // A broken url should not interrupt the removal process.
     761             261 :     (void)NS_NewURI(getter_AddRefs(uri), bookmark.url);
     762                 :   }
     763                 : 
     764             454 :   NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
     765                 :                    nsINavBookmarkObserver,
     766                 :                    OnItemRemoved(bookmark.id,
     767                 :                                  bookmark.parentId,
     768                 :                                  bookmark.position,
     769                 :                                  bookmark.type,
     770                 :                                  uri,
     771                 :                                  bookmark.guid,
     772                 :                                  bookmark.parentGuid));
     773                 : 
     774             599 :   if (bookmark.type == TYPE_BOOKMARK && bookmark.grandParentId == mTagsRoot &&
     775             145 :       uri) {
     776                 :     // If the removed bookmark was child of a tag container, notify a tags
     777                 :     // change to all bookmarks for this URI.
     778             290 :     nsTArray<BookmarkData> bookmarks;
     779             145 :     rv = GetBookmarksForURI(uri, bookmarks);
     780             145 :     NS_ENSURE_SUCCESS(rv, rv);
     781                 : 
     782             201 :     for (PRUint32 i = 0; i < bookmarks.Length(); ++i) {
     783              56 :       NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
     784                 :                        nsINavBookmarkObserver,
     785                 :                        OnItemChanged(bookmarks[i].id,
     786                 :                                      NS_LITERAL_CSTRING("tags"),
     787                 :                                      false,
     788                 :                                      EmptyCString(),
     789                 :                                      bookmarks[i].lastModified,
     790                 :                                      TYPE_BOOKMARK,
     791                 :                                      bookmarks[i].parentId,
     792                 :                                      bookmarks[i].guid,
     793                 :                                      bookmarks[i].parentGuid));
     794                 :     }
     795                 : 
     796                 :   }
     797                 : 
     798             454 :   return NS_OK;
     799                 : }
     800                 : 
     801                 : 
     802                 : NS_IMETHODIMP
     803             424 : nsNavBookmarks::CreateFolder(PRInt64 aParent, const nsACString& aName,
     804                 :                              PRInt32 aIndex, PRInt64* aNewFolder)
     805                 : {
     806                 :   // NOTE: aParent can be null for root creation, so not checked
     807             424 :   NS_ENSURE_ARG_POINTER(aNewFolder);
     808                 : 
     809                 :   // CreateContainerWithID returns the index of the new folder, but that's not
     810                 :   // used here.  To avoid any risk of corrupting data should this function
     811                 :   // be changed, we'll use a local variable to hold it.  The true argument
     812                 :   // will cause notifications to be sent to bookmark observers.
     813             424 :   PRInt32 localIndex = aIndex;
     814                 :   nsresult rv = CreateContainerWithID(-1, aParent, aName, true, &localIndex,
     815             424 :                                       aNewFolder);
     816             424 :   NS_ENSURE_SUCCESS(rv, rv);
     817             423 :   return NS_OK;
     818                 : }
     819                 : 
     820                 : NS_IMETHODIMP
     821               4 : nsNavBookmarks::GetFolderReadonly(PRInt64 aFolder, bool* aResult)
     822                 : {
     823               4 :   NS_ENSURE_ARG_MIN(aFolder, 1);
     824               3 :   NS_ENSURE_ARG_POINTER(aResult);
     825                 : 
     826               3 :   nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService();
     827               3 :   NS_ENSURE_TRUE(annosvc, NS_ERROR_OUT_OF_MEMORY);
     828               3 :   nsresult rv = annosvc->ItemHasAnnotation(aFolder, READ_ONLY_ANNO, aResult);
     829               3 :   NS_ENSURE_SUCCESS(rv, rv);
     830               3 :   return NS_OK;
     831                 : }
     832                 : 
     833                 : 
     834                 : NS_IMETHODIMP
     835              53 : nsNavBookmarks::SetFolderReadonly(PRInt64 aFolder, bool aReadOnly)
     836                 : {
     837              53 :   NS_ENSURE_ARG_MIN(aFolder, 1);
     838                 : 
     839              52 :   nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService();
     840              52 :   NS_ENSURE_TRUE(annosvc, NS_ERROR_OUT_OF_MEMORY);
     841                 :   nsresult rv;
     842              52 :   if (aReadOnly) {
     843              52 :     rv = annosvc->SetItemAnnotationInt32(aFolder, READ_ONLY_ANNO, 1, 0,
     844              52 :                                          nsAnnotationService::EXPIRE_NEVER);
     845              52 :     NS_ENSURE_SUCCESS(rv, rv);
     846                 :   }
     847                 :   else {
     848                 :     bool hasAnno;
     849               0 :     rv = annosvc->ItemHasAnnotation(aFolder, READ_ONLY_ANNO, &hasAnno);
     850               0 :     NS_ENSURE_SUCCESS(rv, rv);
     851               0 :     if (hasAnno) {
     852               0 :       rv = annosvc->RemoveItemAnnotation(aFolder, READ_ONLY_ANNO);
     853               0 :       NS_ENSURE_SUCCESS(rv, rv);
     854                 :     }
     855                 :   }
     856              52 :   return NS_OK;
     857                 : }
     858                 : 
     859                 : 
     860                 : nsresult
     861             431 : nsNavBookmarks::CreateContainerWithID(PRInt64 aItemId,
     862                 :                                       PRInt64 aParent,
     863                 :                                       const nsACString& aTitle,
     864                 :                                       bool aIsBookmarkFolder,
     865                 :                                       PRInt32* aIndex,
     866                 :                                       PRInt64* aNewFolder)
     867                 : {
     868             431 :   NS_ENSURE_ARG_MIN(*aIndex, nsINavBookmarksService::DEFAULT_INDEX);
     869                 : 
     870                 :   // Get the correct index for insertion.  This also ensures the parent exists.
     871                 :   PRInt32 index, folderCount;
     872                 :   PRInt64 grandParentId;
     873             862 :   nsCAutoString folderGuid;
     874             431 :   nsresult rv = FetchFolderInfo(aParent, &folderCount, folderGuid, &grandParentId);
     875             431 :   NS_ENSURE_SUCCESS(rv, rv);
     876                 : 
     877             860 :   mozStorageTransaction transaction(mDB->MainConn(), false);
     878                 : 
     879             430 :   if (*aIndex == nsINavBookmarksService::DEFAULT_INDEX ||
     880                 :       *aIndex >= folderCount) {
     881             410 :     index = folderCount;
     882                 :   } else {
     883              20 :     index = *aIndex;
     884                 :     // Create space for the insertion.
     885              20 :     rv = AdjustIndices(aParent, index, PR_INT32_MAX, 1);
     886              20 :     NS_ENSURE_SUCCESS(rv, rv);
     887                 :   }
     888                 : 
     889             430 :   *aNewFolder = aItemId;
     890             430 :   PRTime dateAdded = PR_Now();
     891             860 :   nsCAutoString guid;
     892             860 :   nsCString title;
     893             430 :   TruncateTitle(aTitle, title);
     894                 : 
     895                 :   rv = InsertBookmarkInDB(-1, FOLDER, aParent, index,
     896                 :                           title, dateAdded, nsnull, folderGuid, grandParentId,
     897             430 :                           nsnull, aNewFolder, guid);
     898             430 :   NS_ENSURE_SUCCESS(rv, rv);
     899                 : 
     900             430 :   rv = transaction.Commit();
     901             430 :   NS_ENSURE_SUCCESS(rv, rv);
     902                 : 
     903             430 :   NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
     904                 :                    nsINavBookmarkObserver,
     905                 :                    OnItemAdded(*aNewFolder, aParent, index, FOLDER,
     906                 :                                nsnull, title, dateAdded, guid, folderGuid));
     907                 : 
     908             430 :   *aIndex = index;
     909             430 :   return NS_OK;
     910                 : }
     911                 : 
     912                 : 
     913                 : NS_IMETHODIMP
     914              45 : nsNavBookmarks::InsertSeparator(PRInt64 aParent,
     915                 :                                 PRInt32 aIndex,
     916                 :                                 PRInt64* aNewItemId)
     917                 : {
     918              45 :   NS_ENSURE_ARG_MIN(aParent, 1);
     919              44 :   NS_ENSURE_ARG_MIN(aIndex, nsINavBookmarksService::DEFAULT_INDEX);
     920              44 :   NS_ENSURE_ARG_POINTER(aNewItemId);
     921                 : 
     922                 :   // Get the correct index for insertion.  This also ensures the parent exists.
     923                 :   PRInt32 index, folderCount;
     924                 :   PRInt64 grandParentId;
     925              88 :   nsCAutoString folderGuid;
     926              44 :   nsresult rv = FetchFolderInfo(aParent, &folderCount, folderGuid, &grandParentId);
     927              44 :   NS_ENSURE_SUCCESS(rv, rv);
     928                 : 
     929              88 :   mozStorageTransaction transaction(mDB->MainConn(), false);
     930                 : 
     931              44 :   if (aIndex == nsINavBookmarksService::DEFAULT_INDEX ||
     932                 :       aIndex >= folderCount) {
     933              21 :     index = folderCount;
     934                 :   }
     935                 :   else {
     936              23 :     index = aIndex;
     937                 :     // Create space for the insertion.
     938              23 :     rv = AdjustIndices(aParent, index, PR_INT32_MAX, 1);
     939              23 :     NS_ENSURE_SUCCESS(rv, rv);
     940                 :   }
     941                 : 
     942              44 :   *aNewItemId = -1;
     943                 :   // Set a NULL title rather than an empty string.
     944              88 :   nsCString voidString;
     945              44 :   voidString.SetIsVoid(true);
     946              88 :   nsCAutoString guid;
     947              44 :   PRTime dateAdded = PR_Now();
     948                 :   rv = InsertBookmarkInDB(-1, SEPARATOR, aParent, index, voidString, dateAdded,
     949                 :                           nsnull, folderGuid, grandParentId, nsnull,
     950              44 :                           aNewItemId, guid);
     951              44 :   NS_ENSURE_SUCCESS(rv, rv);
     952                 : 
     953              44 :   rv = transaction.Commit();
     954              44 :   NS_ENSURE_SUCCESS(rv, rv);
     955                 : 
     956              44 :   NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
     957                 :                    nsINavBookmarkObserver,
     958                 :                    OnItemAdded(*aNewItemId, aParent, index, TYPE_SEPARATOR,
     959                 :                                nsnull, voidString, dateAdded, guid, folderGuid));
     960                 : 
     961              44 :   return NS_OK;
     962                 : }
     963                 : 
     964                 : 
     965                 : nsresult
     966               6 : nsNavBookmarks::GetLastChildId(PRInt64 aFolderId, PRInt64* aItemId)
     967                 : {
     968               6 :   NS_ASSERTION(aFolderId > 0, "Invalid folder id");
     969               6 :   *aItemId = -1;
     970                 : 
     971                 :   nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
     972                 :     "SELECT id FROM moz_bookmarks WHERE parent = :parent "
     973                 :     "ORDER BY position DESC LIMIT 1"
     974              12 :   );
     975               6 :   NS_ENSURE_STATE(stmt);
     976              12 :   mozStorageStatementScoper scoper(stmt);
     977                 : 
     978               6 :   nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aFolderId);
     979               6 :   NS_ENSURE_SUCCESS(rv, rv);
     980                 :   bool found;
     981               6 :   rv = stmt->ExecuteStep(&found);
     982               6 :   NS_ENSURE_SUCCESS(rv, rv);
     983               6 :   if (found) {
     984               6 :     rv = stmt->GetInt64(0, aItemId);
     985               6 :     NS_ENSURE_SUCCESS(rv, rv);
     986                 :   }
     987                 : 
     988               6 :   return NS_OK;
     989                 : }
     990                 : 
     991                 : 
     992                 : NS_IMETHODIMP
     993             228 : nsNavBookmarks::GetIdForItemAt(PRInt64 aFolder,
     994                 :                                PRInt32 aIndex,
     995                 :                                PRInt64* aItemId)
     996                 : {
     997             228 :   NS_ENSURE_ARG_MIN(aFolder, 1);
     998             227 :   NS_ENSURE_ARG_POINTER(aItemId);
     999                 : 
    1000             227 :   *aItemId = -1;
    1001                 : 
    1002                 :   nsresult rv;
    1003             227 :   if (aIndex == nsINavBookmarksService::DEFAULT_INDEX) {
    1004                 :     // Get last item within aFolder.
    1005               6 :     rv = GetLastChildId(aFolder, aItemId);
    1006               6 :     NS_ENSURE_SUCCESS(rv, rv);
    1007                 :   }
    1008                 :   else {
    1009                 :     // Get the item in aFolder with position aIndex.
    1010                 :     nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
    1011                 :       "SELECT id, fk, type FROM moz_bookmarks "
    1012                 :       "WHERE parent = :parent AND position = :item_index"
    1013             442 :     );
    1014             221 :     NS_ENSURE_STATE(stmt);
    1015             442 :     mozStorageStatementScoper scoper(stmt);
    1016                 : 
    1017             221 :     rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aFolder);
    1018             221 :     NS_ENSURE_SUCCESS(rv, rv);
    1019             221 :     rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("item_index"), aIndex);
    1020             221 :     NS_ENSURE_SUCCESS(rv, rv);
    1021                 : 
    1022                 :     bool found;
    1023             221 :     rv = stmt->ExecuteStep(&found);
    1024             221 :     NS_ENSURE_SUCCESS(rv, rv);
    1025             221 :     if (found) {
    1026             157 :       rv = stmt->GetInt64(0, aItemId);
    1027             157 :       NS_ENSURE_SUCCESS(rv, rv);
    1028                 :     }
    1029                 :   }
    1030             227 :   return NS_OK;
    1031                 : }
    1032                 : 
    1033              60 : NS_IMPL_ISUPPORTS1(nsNavBookmarks::RemoveFolderTransaction, nsITransaction)
    1034                 : 
    1035                 : NS_IMETHODIMP
    1036               5 : nsNavBookmarks::GetRemoveFolderTransaction(PRInt64 aFolderId, nsITransaction** aResult)
    1037                 : {
    1038               5 :   NS_ENSURE_ARG_MIN(aFolderId, 1);
    1039               4 :   NS_ENSURE_ARG_POINTER(aResult);
    1040                 : 
    1041                 :   // Create and initialize a RemoveFolderTransaction object that can be used to
    1042                 :   // recreate the folder safely later. 
    1043                 : 
    1044                 :   RemoveFolderTransaction* rft = 
    1045               4 :     new RemoveFolderTransaction(aFolderId);
    1046               4 :   if (!rft)
    1047               0 :     return NS_ERROR_OUT_OF_MEMORY;
    1048                 : 
    1049               4 :   NS_ADDREF(*aResult = rft);
    1050               4 :   return NS_OK;
    1051                 : }
    1052                 : 
    1053                 : 
    1054                 : nsresult
    1055             494 : nsNavBookmarks::GetDescendantFolders(PRInt64 aFolderId,
    1056                 :                                      nsTArray<PRInt64>& aDescendantFoldersArray) {
    1057                 :   nsresult rv;
    1058                 :   // New descendant folders will be added from this index on.
    1059             494 :   PRUint32 startIndex = aDescendantFoldersArray.Length();
    1060                 :   {
    1061                 :     nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
    1062                 :       "SELECT id "
    1063                 :       "FROM moz_bookmarks "
    1064                 :       "WHERE parent = :parent "
    1065                 :       "AND type = :item_type "
    1066             988 :     );
    1067             494 :     NS_ENSURE_STATE(stmt);
    1068             988 :     mozStorageStatementScoper scoper(stmt);
    1069                 : 
    1070             494 :     rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aFolderId);
    1071             494 :     NS_ENSURE_SUCCESS(rv, rv);
    1072             494 :     rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("item_type"), TYPE_FOLDER);
    1073             494 :     NS_ENSURE_SUCCESS(rv, rv);
    1074                 : 
    1075             494 :     bool hasMore = false;
    1076             991 :     while (NS_SUCCEEDED(stmt->ExecuteStep(&hasMore)) && hasMore) {
    1077                 :       PRInt64 itemId;
    1078               3 :       rv = stmt->GetInt64(0, &itemId);
    1079               3 :       NS_ENSURE_SUCCESS(rv, rv);
    1080               3 :       aDescendantFoldersArray.AppendElement(itemId);
    1081                 :     }
    1082                 :   }
    1083                 : 
    1084                 :   // Recursively call GetDescendantFolders for added folders.
    1085                 :   // We start at startIndex since previous folders are checked
    1086                 :   // by previous calls to this method.
    1087             494 :   PRUint32 childCount = aDescendantFoldersArray.Length();
    1088             497 :   for (PRUint32 i = startIndex; i < childCount; ++i) {
    1089               3 :     GetDescendantFolders(aDescendantFoldersArray[i], aDescendantFoldersArray);
    1090                 :   }
    1091                 : 
    1092             494 :   return NS_OK;
    1093                 : }
    1094                 : 
    1095                 : 
    1096                 : nsresult
    1097             948 : nsNavBookmarks::GetDescendantChildren(PRInt64 aFolderId,
    1098                 :                                       const nsACString& aFolderGuid,
    1099                 :                                       PRInt64 aGrandParentId,
    1100                 :                                       nsTArray<BookmarkData>& aFolderChildrenArray) {
    1101                 :   // New children will be added from this index on.
    1102             948 :   PRUint32 startIndex = aFolderChildrenArray.Length();
    1103                 :   nsresult rv;
    1104                 :   {
    1105                 :     // Collect children informations.
    1106                 :     // Select all children of a given folder, sorted by position.
    1107                 :     // This is a LEFT JOIN because not all bookmarks types have a place.
    1108                 :     // We construct a result where the first columns exactly match
    1109                 :     // kGetInfoIndex_* order, and additionally contains columns for position,
    1110                 :     // item_child, and folder_child from moz_bookmarks.
    1111                 :     nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
    1112                 :       "SELECT h.id, h.url, IFNULL(b.title, h.title), h.rev_host, h.visit_count, "
    1113                 :              "h.last_visit_date, f.url, null, b.id, b.dateAdded, b.lastModified, "
    1114                 :              "b.parent, null, h.frecency, b.position, b.type, b.fk, b.guid "
    1115                 :       "FROM moz_bookmarks b "
    1116                 :       "LEFT JOIN moz_places h ON b.fk = h.id "
    1117                 :       "LEFT JOIN moz_favicons f ON h.favicon_id = f.id "
    1118                 :       "WHERE b.parent = :parent "
    1119                 :       "ORDER BY b.position ASC"
    1120            1896 :     );
    1121             948 :     NS_ENSURE_STATE(stmt);
    1122            1896 :     mozStorageStatementScoper scoper(stmt);
    1123                 : 
    1124             948 :     rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aFolderId);
    1125             948 :     NS_ENSURE_SUCCESS(rv, rv);
    1126                 : 
    1127                 :     bool hasMore;
    1128            2392 :     while (NS_SUCCEEDED(stmt->ExecuteStep(&hasMore)) && hasMore) {
    1129             992 :       BookmarkData child;
    1130             496 :       rv = stmt->GetInt64(nsNavHistory::kGetInfoIndex_ItemId, &child.id);
    1131             496 :       NS_ENSURE_SUCCESS(rv, rv);
    1132             496 :       child.parentId = aFolderId;
    1133             496 :       child.grandParentId = aGrandParentId;
    1134             496 :       child.parentGuid = aFolderGuid;
    1135             496 :       rv = stmt->GetInt32(kGetChildrenIndex_Type, &child.type);
    1136             496 :       NS_ENSURE_SUCCESS(rv, rv);
    1137             496 :       rv = stmt->GetInt64(kGetChildrenIndex_PlaceID, &child.placeId);
    1138             496 :       NS_ENSURE_SUCCESS(rv, rv);
    1139             496 :       rv = stmt->GetInt32(kGetChildrenIndex_Position, &child.position);
    1140             496 :       NS_ENSURE_SUCCESS(rv, rv);
    1141             496 :       rv = stmt->GetUTF8String(kGetChildrenIndex_Guid, child.guid);
    1142             496 :       NS_ENSURE_SUCCESS(rv, rv);
    1143                 : 
    1144             496 :       if (child.type == TYPE_BOOKMARK) {
    1145             395 :         rv = stmt->GetUTF8String(nsNavHistory::kGetInfoIndex_URL, child.url);
    1146             395 :         NS_ENSURE_SUCCESS(rv, rv);
    1147                 :       }
    1148                 : 
    1149                 :       // Append item to children's array.
    1150             992 :       aFolderChildrenArray.AppendElement(child);
    1151                 :     }
    1152                 :   }
    1153                 : 
    1154                 :   // Recursively call GetDescendantChildren for added folders.
    1155                 :   // We start at startIndex since previous folders are checked
    1156                 :   // by previous calls to this method.
    1157             948 :   PRUint32 childCount = aFolderChildrenArray.Length();
    1158            1444 :   for (PRUint32 i = startIndex; i < childCount; ++i) {
    1159             496 :     if (aFolderChildrenArray[i].type == TYPE_FOLDER) {
    1160                 :       // nsTarray assumes that all children can be memmove()d, thus we can't
    1161                 :       // just pass aFolderChildrenArray[i].guid to a method that will change
    1162                 :       // the array itself.  Otherwise, since it's passed by reference, after a
    1163                 :       // memmove() it could point to garbage and cause intermittent crashes.
    1164             180 :       nsCString guid = aFolderChildrenArray[i].guid;
    1165              90 :       GetDescendantChildren(aFolderChildrenArray[i].id,
    1166                 :                             guid,
    1167                 :                             aFolderId,
    1168              90 :                             aFolderChildrenArray);
    1169                 :     }
    1170                 :   }
    1171                 : 
    1172             948 :   return NS_OK;
    1173                 : }
    1174                 : 
    1175                 : 
    1176                 : NS_IMETHODIMP
    1177             859 : nsNavBookmarks::RemoveFolderChildren(PRInt64 aFolderId)
    1178                 : {
    1179            1718 :   SAMPLE_LABEL("bookmarks", "RemoveFolderChilder");
    1180             859 :   NS_ENSURE_ARG_MIN(aFolderId, 1);
    1181                 : 
    1182            1716 :   BookmarkData folder;
    1183             858 :   nsresult rv = FetchItemInfo(aFolderId, folder);
    1184             858 :   NS_ENSURE_SUCCESS(rv, rv);
    1185             858 :   NS_ENSURE_ARG(folder.type == TYPE_FOLDER);
    1186                 : 
    1187                 :   // Fill folder children array recursively.
    1188            1716 :   nsTArray<BookmarkData> folderChildrenArray;
    1189                 :   rv = GetDescendantChildren(folder.id, folder.guid, folder.parentId,
    1190             858 :                              folderChildrenArray);
    1191             858 :   NS_ENSURE_SUCCESS(rv, rv);
    1192                 : 
    1193                 :   // Build a string of folders whose children will be removed.
    1194            1716 :   nsCString foldersToRemove;
    1195            1354 :   for (PRUint32 i = 0; i < folderChildrenArray.Length(); ++i) {
    1196             496 :     BookmarkData& child = folderChildrenArray[i];
    1197                 : 
    1198                 :     // Notify observers that we are about to remove this child.
    1199             496 :     NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
    1200                 :                      nsINavBookmarkObserver,
    1201                 :                      OnBeforeItemRemoved(child.id,
    1202                 :                                          child.type,
    1203                 :                                          child.parentId,
    1204                 :                                          child.guid,
    1205                 :                                          child.parentGuid));
    1206                 : 
    1207             496 :     if (child.type == TYPE_FOLDER) {
    1208              90 :       foldersToRemove.AppendLiteral(",");
    1209              90 :       foldersToRemove.AppendInt(child.id);
    1210                 :     }
    1211                 : 
    1212             496 :     BEGIN_CRITICAL_BOOKMARK_CACHE_SECTION(child.id);
    1213                 :   }
    1214                 : 
    1215                 :   // Delete items from the database now.
    1216            1716 :   mozStorageTransaction transaction(mDB->MainConn(), false);
    1217                 : 
    1218                 :   nsCOMPtr<mozIStorageStatement> deleteStatement = mDB->GetStatement(
    1219             858 :     NS_LITERAL_CSTRING(
    1220                 :       "DELETE FROM moz_bookmarks "
    1221            1716 :       "WHERE parent IN (:parent") + foldersToRemove + NS_LITERAL_CSTRING(")")
    1222            1716 :   );
    1223             858 :   NS_ENSURE_STATE(deleteStatement);
    1224            1716 :   mozStorageStatementScoper deleteStatementScoper(deleteStatement);
    1225                 : 
    1226             858 :   rv = deleteStatement->BindInt64ByName(NS_LITERAL_CSTRING("parent"), folder.id);
    1227             858 :   NS_ENSURE_SUCCESS(rv, rv);
    1228             858 :   rv = deleteStatement->Execute();
    1229             858 :   NS_ENSURE_SUCCESS(rv, rv);
    1230                 : 
    1231                 :   // Clean up orphan items annotations.
    1232             858 :   rv = mDB->MainConn()->ExecuteSimpleSQL(
    1233             858 :     NS_LITERAL_CSTRING(
    1234                 :       "DELETE FROM moz_items_annos "
    1235                 :       "WHERE id IN ("
    1236                 :         "SELECT a.id from moz_items_annos a "
    1237                 :         "LEFT JOIN moz_bookmarks b ON a.item_id = b.id "
    1238             858 :         "WHERE b.id ISNULL)"));
    1239             858 :   NS_ENSURE_SUCCESS(rv, rv);
    1240                 : 
    1241                 :   // Set the lastModified date.
    1242             858 :   rv = SetItemDateInternal(LAST_MODIFIED, folder.id, PR_Now());
    1243             858 :   NS_ENSURE_SUCCESS(rv, rv);
    1244                 : 
    1245            1354 :   for (PRUint32 i = 0; i < folderChildrenArray.Length(); i++) {
    1246             496 :     BookmarkData& child = folderChildrenArray[i];
    1247             496 :     if (child.type == TYPE_BOOKMARK) {
    1248                 :       // If not a tag, recalculate frecency for this entry, since it changed.
    1249             395 :       if (child.grandParentId != mTagsRoot) {
    1250             392 :         nsNavHistory* history = nsNavHistory::GetHistoryService();
    1251             392 :         NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY);
    1252             392 :         rv = history->UpdateFrecency(child.placeId);
    1253             392 :         NS_ENSURE_SUCCESS(rv, rv);
    1254                 :       }
    1255                 : 
    1256             395 :       rv = UpdateKeywordsHashForRemovedBookmark(child.id);
    1257             395 :       NS_ENSURE_SUCCESS(rv, rv);
    1258                 :     }
    1259             496 :     END_CRITICAL_BOOKMARK_CACHE_SECTION(child.id);
    1260                 :   }
    1261                 : 
    1262             858 :   rv = transaction.Commit();
    1263             858 :   NS_ENSURE_SUCCESS(rv, rv);
    1264                 : 
    1265                 :   // Call observers in reverse order to serve children before their parent.
    1266            1354 :   for (PRInt32 i = folderChildrenArray.Length() - 1; i >= 0; --i) {
    1267             496 :     BookmarkData& child = folderChildrenArray[i];
    1268             992 :     nsCOMPtr<nsIURI> uri;
    1269             496 :     if (child.type == TYPE_BOOKMARK) {
    1270                 :       // A broken url should not interrupt the removal process.
    1271             395 :       (void)NS_NewURI(getter_AddRefs(uri), child.url);
    1272                 :     }
    1273                 : 
    1274             496 :     NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
    1275                 :                      nsINavBookmarkObserver,
    1276                 :                      OnItemRemoved(child.id,
    1277                 :                                    child.parentId,
    1278                 :                                    child.position,
    1279                 :                                    child.type,
    1280                 :                                    uri,
    1281                 :                                    child.guid,
    1282                 :                                    child.parentGuid));
    1283                 : 
    1284             499 :     if (child.type == TYPE_BOOKMARK && child.grandParentId == mTagsRoot &&
    1285               3 :         uri) {
    1286                 :       // If the removed bookmark was a child of a tag container, notify all
    1287                 :       // bookmark-folder result nodes which contain a bookmark for the removed
    1288                 :       // bookmark's url.
    1289               6 :       nsTArray<BookmarkData> bookmarks;
    1290               3 :       rv = GetBookmarksForURI(uri, bookmarks);
    1291               3 :       NS_ENSURE_SUCCESS(rv, rv);
    1292                 : 
    1293               6 :       for (PRUint32 i = 0; i < bookmarks.Length(); ++i) {
    1294               3 :         NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
    1295                 :                          nsINavBookmarkObserver,
    1296                 :                          OnItemChanged(bookmarks[i].id,
    1297                 :                                        NS_LITERAL_CSTRING("tags"),
    1298                 :                                        false,
    1299                 :                                        EmptyCString(),
    1300                 :                                        bookmarks[i].lastModified,
    1301                 :                                        TYPE_BOOKMARK,
    1302                 :                                        bookmarks[i].parentId,
    1303                 :                                        bookmarks[i].guid,
    1304                 :                                        bookmarks[i].parentGuid));
    1305                 :       }
    1306                 :     }
    1307                 :   }
    1308                 : 
    1309             858 :   return NS_OK;
    1310                 : }
    1311                 : 
    1312                 : 
    1313                 : NS_IMETHODIMP
    1314              61 : nsNavBookmarks::MoveItem(PRInt64 aItemId, PRInt64 aNewParent, PRInt32 aIndex)
    1315                 : {
    1316              61 :   NS_ENSURE_TRUE(aItemId != mRoot, NS_ERROR_INVALID_ARG);
    1317              61 :   NS_ENSURE_ARG_MIN(aItemId, 1);
    1318              60 :   NS_ENSURE_ARG_MIN(aNewParent, 1);
    1319                 :   // -1 is append, but no other negative number is allowed.
    1320              60 :   NS_ENSURE_ARG_MIN(aIndex, -1);
    1321                 :   // Disallow making an item its own parent.
    1322              60 :   NS_ENSURE_TRUE(aItemId != aNewParent, NS_ERROR_INVALID_ARG);
    1323                 : 
    1324             118 :   mozStorageTransaction transaction(mDB->MainConn(), false);
    1325                 : 
    1326             118 :   BookmarkData bookmark;
    1327              59 :   nsresult rv = FetchItemInfo(aItemId, bookmark);
    1328              59 :   NS_ENSURE_SUCCESS(rv, rv);
    1329                 : 
    1330                 :   // if parent and index are the same, nothing to do
    1331              59 :   if (bookmark.parentId == aNewParent && bookmark.position == aIndex)
    1332              13 :     return NS_OK;
    1333                 : 
    1334                 :   // Make sure aNewParent is not aFolder or a subfolder of aFolder.
    1335                 :   // TODO: make this performant, maybe with a nested tree (bug 408991).
    1336              46 :   if (bookmark.type == TYPE_FOLDER) {
    1337               4 :     PRInt64 ancestorId = aNewParent;
    1338                 : 
    1339              18 :     while (ancestorId) {
    1340              10 :       if (ancestorId == bookmark.id) {
    1341               0 :         return NS_ERROR_INVALID_ARG;
    1342                 :       }
    1343              10 :       rv = GetFolderIdForItem(ancestorId, &ancestorId);
    1344              10 :       if (NS_FAILED(rv)) {
    1345               0 :         break;
    1346                 :       }
    1347                 :     }
    1348                 :   }
    1349                 : 
    1350                 :   // calculate new index
    1351                 :   PRInt32 newIndex, folderCount;
    1352                 :   PRInt64 grandParentId;
    1353              92 :   nsCAutoString newParentGuid;
    1354              46 :   rv = FetchFolderInfo(aNewParent, &folderCount, newParentGuid, &grandParentId);
    1355              46 :   NS_ENSURE_SUCCESS(rv, rv);
    1356              46 :   if (aIndex == nsINavBookmarksService::DEFAULT_INDEX ||
    1357                 :       aIndex >= folderCount) {
    1358              24 :     newIndex = folderCount;
    1359                 :     // If the parent remains the same, then the folder is really being moved
    1360                 :     // to count - 1 (since it's being removed from the old position)
    1361              48 :     if (bookmark.parentId == aNewParent) {
    1362               7 :       --newIndex;
    1363                 :     }
    1364                 :   } else {
    1365              22 :     newIndex = aIndex;
    1366                 : 
    1367              22 :     if (bookmark.parentId == aNewParent && newIndex > bookmark.position) {
    1368                 :       // when an item is being moved lower in the same folder, the new index
    1369                 :       // refers to the index before it was removed. Removal causes everything
    1370                 :       // to shift up.
    1371               1 :       --newIndex;
    1372                 :     }
    1373                 :   }
    1374                 : 
    1375                 :   // this is like the previous check, except this covers if
    1376                 :   // the specified index was -1 (append), and the calculated
    1377                 :   // new index is the same as the existing index
    1378              46 :   if (aNewParent == bookmark.parentId && newIndex == bookmark.position) {
    1379                 :     // Nothing to do!
    1380               1 :     return NS_OK;
    1381                 :   }
    1382                 : 
    1383                 :   // adjust indices to account for the move
    1384                 :   // do this before we update the parent/index fields
    1385                 :   // or we'll re-adjust the index for the item we are moving
    1386              45 :   if (bookmark.parentId == aNewParent) {
    1387                 :     // We can optimize the updates if moving within the same container.
    1388                 :     // We only shift the items between the old and new positions, since the
    1389                 :     // insertion will offset the deletion.
    1390              25 :     if (bookmark.position > newIndex) {
    1391              18 :       rv = AdjustIndices(bookmark.parentId, newIndex, bookmark.position - 1, 1);
    1392                 :     }
    1393                 :     else {
    1394               7 :       rv = AdjustIndices(bookmark.parentId, bookmark.position + 1, newIndex, -1);
    1395                 :     }
    1396              25 :     NS_ENSURE_SUCCESS(rv, rv);
    1397                 :   }
    1398                 :   else {
    1399                 :     // We're moving between containers, so this happens in two steps.
    1400                 :     // First, fill the hole from the removal from the old parent.
    1401              20 :     rv = AdjustIndices(bookmark.parentId, bookmark.position + 1, PR_INT32_MAX, -1);
    1402              20 :     NS_ENSURE_SUCCESS(rv, rv);
    1403                 :     // Now, make room in the new parent for the insertion.
    1404              20 :     rv = AdjustIndices(aNewParent, newIndex, PR_INT32_MAX, 1);
    1405              20 :     NS_ENSURE_SUCCESS(rv, rv);
    1406                 :   }
    1407                 : 
    1408              45 :   BEGIN_CRITICAL_BOOKMARK_CACHE_SECTION(bookmark.id);
    1409                 : 
    1410                 :   {
    1411                 :     // Update parent and position.
    1412                 :     nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
    1413                 :       "UPDATE moz_bookmarks SET parent = :parent, position = :item_index "
    1414                 :       "WHERE id = :item_id "
    1415              90 :     );
    1416              45 :     NS_ENSURE_STATE(stmt);
    1417              90 :     mozStorageStatementScoper scoper(stmt);
    1418                 : 
    1419              45 :     rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aNewParent);
    1420              45 :     NS_ENSURE_SUCCESS(rv, rv);
    1421              45 :     rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("item_index"), newIndex);
    1422              45 :     NS_ENSURE_SUCCESS(rv, rv);
    1423              45 :     rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), bookmark.id);
    1424              45 :     NS_ENSURE_SUCCESS(rv, rv);
    1425              45 :     rv = stmt->Execute();
    1426              45 :     NS_ENSURE_SUCCESS(rv, rv);
    1427                 :   }
    1428                 : 
    1429              45 :   PRTime now = PR_Now();
    1430              45 :   rv = SetItemDateInternal(LAST_MODIFIED, bookmark.parentId, now);
    1431              45 :   NS_ENSURE_SUCCESS(rv, rv);
    1432              45 :   rv = SetItemDateInternal(LAST_MODIFIED, aNewParent, now);
    1433              45 :   NS_ENSURE_SUCCESS(rv, rv);
    1434                 : 
    1435              45 :   rv = transaction.Commit();
    1436              45 :   NS_ENSURE_SUCCESS(rv, rv);
    1437                 : 
    1438              45 :   END_CRITICAL_BOOKMARK_CACHE_SECTION(bookmark.id);
    1439                 : 
    1440              45 :   NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
    1441                 :                    nsINavBookmarkObserver,
    1442                 :                    OnItemMoved(bookmark.id,
    1443                 :                                bookmark.parentId,
    1444                 :                                bookmark.position,
    1445                 :                                aNewParent,
    1446                 :                                newIndex,
    1447                 :                                bookmark.type,
    1448                 :                                bookmark.guid,
    1449                 :                                bookmark.parentGuid,
    1450                 :                                newParentGuid));
    1451              45 :   return NS_OK;
    1452                 : }
    1453                 : 
    1454                 : nsresult
    1455            5824 : nsNavBookmarks::FetchItemInfo(PRInt64 aItemId,
    1456                 :                               BookmarkData& _bookmark)
    1457                 : {
    1458                 :   // Check if the requested id is in the recent cache and avoid the database
    1459                 :   // lookup if so.  Invalidate the cache after getting data if requested.
    1460            5824 :   BookmarkKeyClass* key = mRecentBookmarksCache.GetEntry(aItemId);
    1461            5824 :   if (key) {
    1462            5023 :     _bookmark = key->bookmark;
    1463            5023 :     return NS_OK;
    1464                 :   }
    1465                 : 
    1466                 :   // LEFT JOIN since not all bookmarks have an associated place.
    1467                 :   nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
    1468                 :     "SELECT b.id, h.url, b.title, b.position, b.fk, b.parent, b.type, "
    1469                 :            "b.dateAdded, b.lastModified, b.guid, t.guid, t.parent "
    1470                 :     "FROM moz_bookmarks b "
    1471                 :     "LEFT JOIN moz_bookmarks t ON t.id = b.parent "
    1472                 :     "LEFT JOIN moz_places h ON h.id = b.fk "
    1473                 :     "WHERE b.id = :item_id"
    1474            1602 :   );
    1475             801 :   NS_ENSURE_STATE(stmt);
    1476            1602 :   mozStorageStatementScoper scoper(stmt);
    1477                 : 
    1478             801 :   nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), aItemId);
    1479             801 :   NS_ENSURE_SUCCESS(rv, rv);
    1480                 : 
    1481                 :   bool hasResult;
    1482             801 :   rv = stmt->ExecuteStep(&hasResult);
    1483             801 :   NS_ENSURE_SUCCESS(rv, rv);
    1484             801 :   if (!hasResult) {
    1485              64 :     return NS_ERROR_INVALID_ARG;
    1486                 :   }
    1487                 : 
    1488             737 :   _bookmark.id = aItemId;
    1489             737 :   rv = stmt->GetUTF8String(1, _bookmark.url);
    1490             737 :   NS_ENSURE_SUCCESS(rv, rv);
    1491                 :   bool isNull;
    1492             737 :   rv = stmt->GetIsNull(2, &isNull);
    1493             737 :   NS_ENSURE_SUCCESS(rv, rv);
    1494             737 :   if (isNull) {
    1495              44 :     _bookmark.title.SetIsVoid(true);
    1496                 :   }
    1497                 :   else {
    1498             693 :     rv = stmt->GetUTF8String(2, _bookmark.title);
    1499             693 :     NS_ENSURE_SUCCESS(rv, rv);
    1500                 :   }
    1501             737 :   rv = stmt->GetInt32(3, &_bookmark.position);
    1502             737 :   NS_ENSURE_SUCCESS(rv, rv);
    1503             737 :   rv = stmt->GetInt64(4, &_bookmark.placeId);
    1504             737 :   NS_ENSURE_SUCCESS(rv, rv);
    1505             737 :   rv = stmt->GetInt64(5, &_bookmark.parentId);
    1506             737 :   NS_ENSURE_SUCCESS(rv, rv);
    1507             737 :   rv = stmt->GetInt32(6, &_bookmark.type);
    1508             737 :   NS_ENSURE_SUCCESS(rv, rv);
    1509             737 :   rv = stmt->GetInt64(7, &_bookmark.dateAdded);
    1510             737 :   NS_ENSURE_SUCCESS(rv, rv);
    1511             737 :   rv = stmt->GetInt64(8, &_bookmark.lastModified);
    1512             737 :   NS_ENSURE_SUCCESS(rv, rv);
    1513             737 :   rv = stmt->GetUTF8String(9, _bookmark.guid);
    1514             737 :   NS_ENSURE_SUCCESS(rv, rv);
    1515                 :   // Getting properties of the root would show no parent.
    1516             737 :   rv = stmt->GetIsNull(10, &isNull);
    1517             737 :   NS_ENSURE_SUCCESS(rv, rv);
    1518             737 :   if (!isNull) {
    1519             692 :     rv = stmt->GetUTF8String(10, _bookmark.parentGuid);
    1520             692 :     NS_ENSURE_SUCCESS(rv, rv);
    1521             692 :     rv = stmt->GetInt64(11, &_bookmark.grandParentId);
    1522             692 :     NS_ENSURE_SUCCESS(rv, rv);
    1523                 :   }
    1524                 :   else {
    1525              45 :     _bookmark.grandParentId = -1;
    1526                 :   }
    1527                 : 
    1528             737 :   ADD_TO_BOOKMARK_CACHE(aItemId, _bookmark);
    1529                 : 
    1530             737 :   return NS_OK;
    1531                 : }
    1532                 : 
    1533                 : nsresult
    1534            4526 : nsNavBookmarks::SetItemDateInternal(enum BookmarkDate aDateType,
    1535                 :                                     PRInt64 aItemId,
    1536                 :                                     PRTime aValue)
    1537                 : {
    1538            9052 :   nsCOMPtr<mozIStorageStatement> stmt;
    1539            4526 :   if (aDateType == DATE_ADDED) {
    1540                 :     // lastModified is set to the same value as dateAdded.  We do this for
    1541                 :     // performance reasons, since it will allow us to use an index to sort items
    1542                 :     // by date.
    1543                 :     stmt = mDB->GetStatement(
    1544                 :       "UPDATE moz_bookmarks SET dateAdded = :date, lastModified = :date "
    1545                 :       "WHERE id = :item_id"
    1546             289 :     );
    1547                 :   }
    1548                 :   else {
    1549                 :     stmt = mDB->GetStatement(
    1550                 :       "UPDATE moz_bookmarks SET lastModified = :date WHERE id = :item_id"
    1551            4237 :     );
    1552                 :   }
    1553            4526 :   NS_ENSURE_STATE(stmt);
    1554            9052 :   mozStorageStatementScoper scoper(stmt);
    1555                 : 
    1556            4526 :   nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("date"), aValue);
    1557            4526 :   NS_ENSURE_SUCCESS(rv, rv);
    1558            4526 :   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), aItemId);
    1559            4526 :   NS_ENSURE_SUCCESS(rv, rv);
    1560                 : 
    1561            4526 :   rv = stmt->Execute();
    1562            4526 :   NS_ENSURE_SUCCESS(rv, rv);
    1563                 : 
    1564                 :   // Update the cache entry, if needed.
    1565            4526 :   BookmarkKeyClass* key = mRecentBookmarksCache.GetEntry(aItemId);
    1566            4526 :   if (key) {
    1567            3595 :     if (aDateType == DATE_ADDED) {
    1568             289 :       key->bookmark.dateAdded = aValue;
    1569                 :     }
    1570                 :     // Set lastModified in both cases.
    1571            3595 :     key->bookmark.lastModified = aValue;
    1572                 :   }
    1573                 : 
    1574                 :   // note, we are not notifying the observers
    1575                 :   // that the item has changed.
    1576                 : 
    1577            4526 :   return NS_OK;
    1578                 : }
    1579                 : 
    1580                 : 
    1581                 : NS_IMETHODIMP
    1582             290 : nsNavBookmarks::SetItemDateAdded(PRInt64 aItemId, PRTime aDateAdded)
    1583                 : {
    1584             290 :   NS_ENSURE_ARG_MIN(aItemId, 1);
    1585                 : 
    1586             578 :   BookmarkData bookmark;
    1587             289 :   nsresult rv = FetchItemInfo(aItemId, bookmark);
    1588             289 :   NS_ENSURE_SUCCESS(rv, rv);
    1589             289 :   bookmark.dateAdded = aDateAdded;
    1590                 : 
    1591             289 :   rv = SetItemDateInternal(DATE_ADDED, bookmark.id, bookmark.dateAdded);
    1592             289 :   NS_ENSURE_SUCCESS(rv, rv);
    1593                 : 
    1594                 :   // Note: mDBSetItemDateAdded also sets lastModified to aDateAdded.
    1595             289 :   NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
    1596                 :                    nsINavBookmarkObserver,
    1597                 :                    OnItemChanged(bookmark.id,
    1598                 :                                  NS_LITERAL_CSTRING("dateAdded"),
    1599                 :                                  false,
    1600                 :                                  nsPrintfCString(16, "%lld", bookmark.dateAdded),
    1601                 :                                  bookmark.dateAdded,
    1602                 :                                  bookmark.type,
    1603                 :                                  bookmark.parentId,
    1604                 :                                  bookmark.guid,
    1605                 :                                  bookmark.parentGuid));
    1606             289 :   return NS_OK;
    1607                 : }
    1608                 : 
    1609                 : 
    1610                 : NS_IMETHODIMP
    1611              56 : nsNavBookmarks::GetItemDateAdded(PRInt64 aItemId, PRTime* _dateAdded)
    1612                 : {
    1613              56 :   NS_ENSURE_ARG_MIN(aItemId, 1);
    1614              55 :   NS_ENSURE_ARG_POINTER(_dateAdded);
    1615                 : 
    1616             110 :   BookmarkData bookmark;
    1617              55 :   nsresult rv = FetchItemInfo(aItemId, bookmark);
    1618              55 :   NS_ENSURE_SUCCESS(rv, rv);
    1619                 : 
    1620              55 :   *_dateAdded = bookmark.dateAdded;
    1621              55 :   return NS_OK;
    1622                 : }
    1623                 : 
    1624                 : 
    1625                 : NS_IMETHODIMP
    1626             386 : nsNavBookmarks::SetItemLastModified(PRInt64 aItemId, PRTime aLastModified)
    1627                 : {
    1628             386 :   NS_ENSURE_ARG_MIN(aItemId, 1);
    1629                 : 
    1630             770 :   BookmarkData bookmark;
    1631             385 :   nsresult rv = FetchItemInfo(aItemId, bookmark);
    1632             385 :   NS_ENSURE_SUCCESS(rv, rv);
    1633             385 :   bookmark.lastModified = aLastModified;
    1634                 : 
    1635             385 :   rv = SetItemDateInternal(LAST_MODIFIED, bookmark.id, bookmark.lastModified);
    1636             385 :   NS_ENSURE_SUCCESS(rv, rv);
    1637                 : 
    1638                 :   // Note: mDBSetItemDateAdded also sets lastModified to aDateAdded.
    1639             385 :   NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
    1640                 :                    nsINavBookmarkObserver,
    1641                 :                    OnItemChanged(bookmark.id,
    1642                 :                                  NS_LITERAL_CSTRING("lastModified"),
    1643                 :                                  false,
    1644                 :                                  nsPrintfCString(16, "%lld", bookmark.lastModified),
    1645                 :                                  bookmark.lastModified,
    1646                 :                                  bookmark.type,
    1647                 :                                  bookmark.parentId,
    1648                 :                                  bookmark.guid,
    1649                 :                                  bookmark.parentGuid));
    1650             385 :   return NS_OK;
    1651                 : }
    1652                 : 
    1653                 : 
    1654                 : NS_IMETHODIMP
    1655              64 : nsNavBookmarks::GetItemLastModified(PRInt64 aItemId, PRTime* _lastModified)
    1656                 : {
    1657              64 :   NS_ENSURE_ARG_MIN(aItemId, 1);
    1658              63 :   NS_ENSURE_ARG_POINTER(_lastModified);
    1659                 : 
    1660             126 :   BookmarkData bookmark;
    1661              63 :   nsresult rv = FetchItemInfo(aItemId, bookmark);
    1662              63 :   NS_ENSURE_SUCCESS(rv, rv);
    1663                 : 
    1664              63 :   *_lastModified = bookmark.lastModified;
    1665              63 :   return NS_OK;
    1666                 : }
    1667                 : 
    1668                 : 
    1669                 : nsresult
    1670              12 : nsNavBookmarks::GetGUIDBase(nsAString &aGUIDBase)
    1671                 : {
    1672              12 :   if (!mGUIDBase.IsEmpty()) {
    1673               8 :     aGUIDBase = mGUIDBase;
    1674               8 :     return NS_OK;
    1675                 :   }
    1676                 : 
    1677                 :   // generate a new GUID base for this session
    1678                 :   nsCOMPtr<nsIUUIDGenerator> uuidgen =
    1679               8 :     do_GetService("@mozilla.org/uuid-generator;1");
    1680               4 :   NS_ENSURE_TRUE(uuidgen, NS_ERROR_OUT_OF_MEMORY);
    1681                 :   nsID GUID;
    1682               4 :   nsresult rv = uuidgen->GenerateUUIDInPlace(&GUID);
    1683               4 :   NS_ENSURE_SUCCESS(rv, rv);
    1684                 :   char GUIDChars[NSID_LENGTH];
    1685               4 :   GUID.ToProvidedString(GUIDChars);
    1686               4 :   CopyASCIItoUTF16(GUIDChars, mGUIDBase);
    1687               4 :   aGUIDBase = mGUIDBase;
    1688               4 :   return NS_OK;
    1689                 : }
    1690                 : 
    1691                 : 
    1692                 : NS_IMETHODIMP
    1693              37 : nsNavBookmarks::GetItemGUID(PRInt64 aItemId, nsAString& aGUID)
    1694                 : {
    1695              37 :   NS_ENSURE_ARG_MIN(aItemId, 1);
    1696                 : 
    1697              36 :   nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService();
    1698              36 :   NS_ENSURE_TRUE(annosvc, NS_ERROR_OUT_OF_MEMORY);
    1699              36 :   nsresult rv = annosvc->GetItemAnnotationString(aItemId, GUID_ANNO, aGUID);
    1700              36 :   if (NS_SUCCEEDED(rv) || rv != NS_ERROR_NOT_AVAILABLE)
    1701              24 :     return rv;
    1702                 : 
    1703              24 :   nsAutoString tmp;
    1704              12 :   tmp.AppendInt(mItemCount++);
    1705              12 :   aGUID.SetCapacity(NSID_LENGTH - 1 + tmp.Length());
    1706              24 :   nsString GUIDBase;
    1707              12 :   rv = GetGUIDBase(GUIDBase);
    1708              12 :   NS_ENSURE_SUCCESS(rv, rv);
    1709              12 :   aGUID.Assign(GUIDBase);
    1710              12 :   aGUID.Append(tmp);
    1711                 : 
    1712              12 :   rv = SetItemGUID(aItemId, aGUID);
    1713              12 :   NS_ENSURE_SUCCESS(rv, rv);
    1714               9 :   return NS_OK;
    1715                 : }
    1716                 : 
    1717                 : 
    1718                 : NS_IMETHODIMP
    1719              22 : nsNavBookmarks::SetItemGUID(PRInt64 aItemId, const nsAString& aGUID)
    1720                 : {
    1721              22 :   NS_ENSURE_ARG_MIN(aItemId, 1);
    1722                 : 
    1723                 :   PRInt64 checkId;
    1724              21 :   GetItemIdForGUID(aGUID, &checkId);
    1725              21 :   if (checkId != -1)
    1726               1 :     return NS_ERROR_INVALID_ARG; // invalid GUID, already exists
    1727                 : 
    1728              20 :   nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService();
    1729              20 :   NS_ENSURE_TRUE(annosvc, NS_ERROR_OUT_OF_MEMORY);
    1730              20 :   nsresult rv = annosvc->SetItemAnnotationString(aItemId, GUID_ANNO, aGUID, 0,
    1731              20 :                                                  nsIAnnotationService::EXPIRE_NEVER);
    1732              20 :   NS_ENSURE_SUCCESS(rv, rv);
    1733              17 :   return NS_OK;
    1734                 : }
    1735                 : 
    1736                 : 
    1737                 : NS_IMETHODIMP
    1738              24 : nsNavBookmarks::GetItemIdForGUID(const nsAString& aGUID, PRInt64* aItemId)
    1739                 : {
    1740              24 :   NS_ENSURE_ARG_POINTER(aItemId);
    1741                 : 
    1742                 :   nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
    1743                 :     "SELECT item_id FROM moz_items_annos "
    1744                 :     "WHERE content = :guid "
    1745                 :     "LIMIT 1"
    1746              48 :   );
    1747              24 :   NS_ENSURE_STATE(stmt);
    1748              48 :   mozStorageStatementScoper scoper(stmt);
    1749                 : 
    1750              24 :   nsresult rv = stmt->BindStringByName(NS_LITERAL_CSTRING("guid"), aGUID);
    1751              24 :   NS_ENSURE_SUCCESS(rv, rv);
    1752                 : 
    1753              24 :   bool hasMore = false;
    1754              24 :   rv = stmt->ExecuteStep(&hasMore);
    1755              24 :   if (NS_FAILED(rv) || ! hasMore) {
    1756              21 :     *aItemId = -1;
    1757              21 :     return NS_OK; // not found: return -1
    1758                 :   }
    1759                 : 
    1760                 :   // found, get the itemId
    1761               3 :   rv = stmt->GetInt64(0, aItemId);
    1762               3 :   NS_ENSURE_SUCCESS(rv, rv);
    1763               3 :   return NS_OK;
    1764                 : }
    1765                 : 
    1766                 : 
    1767                 : NS_IMETHODIMP
    1768             194 : nsNavBookmarks::SetItemTitle(PRInt64 aItemId, const nsACString& aTitle)
    1769                 : {
    1770             194 :   NS_ENSURE_ARG_MIN(aItemId, 1);
    1771                 : 
    1772             386 :   BookmarkData bookmark;
    1773             193 :   nsresult rv = FetchItemInfo(aItemId, bookmark);
    1774             193 :   NS_ENSURE_SUCCESS(rv, rv);
    1775                 : 
    1776                 :   nsCOMPtr<mozIStorageStatement> statement = mDB->GetStatement(
    1777                 :     "UPDATE moz_bookmarks SET title = :item_title, lastModified = :date "
    1778                 :     "WHERE id = :item_id "
    1779             386 :   );
    1780             193 :   NS_ENSURE_STATE(statement);
    1781             386 :   mozStorageStatementScoper scoper(statement);
    1782                 : 
    1783             386 :   nsCString title;
    1784             193 :   TruncateTitle(aTitle, title);
    1785                 : 
    1786                 :   // Support setting a null title, we support this in insertBookmark.
    1787             193 :   if (title.IsVoid()) {
    1788               2 :     rv = statement->BindNullByName(NS_LITERAL_CSTRING("item_title"));
    1789                 :   }
    1790                 :   else {
    1791             382 :     rv = statement->BindUTF8StringByName(NS_LITERAL_CSTRING("item_title"),
    1792             191 :                                          title);
    1793                 :   }
    1794             193 :   NS_ENSURE_SUCCESS(rv, rv);
    1795             193 :   bookmark.lastModified = PR_Now();
    1796             386 :   rv = statement->BindInt64ByName(NS_LITERAL_CSTRING("date"),
    1797             193 :                                   bookmark.lastModified);
    1798             193 :   NS_ENSURE_SUCCESS(rv, rv);
    1799             193 :   rv = statement->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), bookmark.id);
    1800             193 :   NS_ENSURE_SUCCESS(rv, rv);
    1801                 : 
    1802             193 :   rv = statement->Execute();
    1803             193 :   NS_ENSURE_SUCCESS(rv, rv);
    1804                 : 
    1805                 :   // Update the cache entry, if needed.
    1806             193 :   BookmarkKeyClass* key = mRecentBookmarksCache.GetEntry(aItemId);
    1807             193 :   if (key) {
    1808             193 :     if (title.IsVoid()) {
    1809               2 :       key->bookmark.title.SetIsVoid(true);
    1810                 :     }
    1811                 :     else {
    1812             191 :       key->bookmark.title.Assign(title);
    1813                 :     }
    1814             193 :     key->bookmark.lastModified = bookmark.lastModified;
    1815                 :   }
    1816                 : 
    1817             193 :   NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
    1818                 :                    nsINavBookmarkObserver,
    1819                 :                    OnItemChanged(bookmark.id,
    1820                 :                                  NS_LITERAL_CSTRING("title"),
    1821                 :                                  false,
    1822                 :                                  title,
    1823                 :                                  bookmark.lastModified,
    1824                 :                                  bookmark.type,
    1825                 :                                  bookmark.parentId,
    1826                 :                                  bookmark.guid,
    1827                 :                                  bookmark.parentGuid));
    1828             193 :   return NS_OK;
    1829                 : }
    1830                 : 
    1831                 : 
    1832                 : NS_IMETHODIMP
    1833             817 : nsNavBookmarks::GetItemTitle(PRInt64 aItemId,
    1834                 :                              nsACString& _title)
    1835                 : {
    1836             817 :   NS_ENSURE_ARG_MIN(aItemId, 1);
    1837                 : 
    1838            1630 :   BookmarkData bookmark;
    1839             815 :   nsresult rv = FetchItemInfo(aItemId, bookmark);
    1840             815 :   NS_ENSURE_SUCCESS(rv, rv);
    1841                 : 
    1842             814 :   _title = bookmark.title;
    1843             814 :   return NS_OK;
    1844                 : }
    1845                 : 
    1846                 : 
    1847                 : NS_IMETHODIMP
    1848             130 : nsNavBookmarks::GetBookmarkURI(PRInt64 aItemId,
    1849                 :                                nsIURI** _URI)
    1850                 : {
    1851             130 :   NS_ENSURE_ARG_MIN(aItemId, 1);
    1852             129 :   NS_ENSURE_ARG_POINTER(_URI);
    1853                 : 
    1854             258 :   BookmarkData bookmark;
    1855             129 :   nsresult rv = FetchItemInfo(aItemId, bookmark);
    1856             129 :   NS_ENSURE_SUCCESS(rv, rv);
    1857                 : 
    1858             128 :   rv = NS_NewURI(_URI, bookmark.url);
    1859             128 :   NS_ENSURE_SUCCESS(rv, rv);
    1860                 : 
    1861             127 :   return NS_OK;
    1862                 : }
    1863                 : 
    1864                 : 
    1865                 : NS_IMETHODIMP
    1866             233 : nsNavBookmarks::GetItemType(PRInt64 aItemId, PRUint16* _type)
    1867                 : {
    1868             233 :   NS_ENSURE_ARG_MIN(aItemId, 1);
    1869             232 :   NS_ENSURE_ARG_POINTER(_type);
    1870                 : 
    1871             464 :   BookmarkData bookmark;
    1872             232 :   nsresult rv = FetchItemInfo(aItemId, bookmark);
    1873             232 :   NS_ENSURE_SUCCESS(rv, rv);
    1874                 : 
    1875             232 :   *_type = static_cast<PRUint16>(bookmark.type);
    1876             232 :   return NS_OK;
    1877                 : }
    1878                 : 
    1879                 : 
    1880                 : nsresult
    1881             490 : nsNavBookmarks::ResultNodeForContainer(PRInt64 aItemId,
    1882                 :                                        nsNavHistoryQueryOptions* aOptions,
    1883                 :                                        nsNavHistoryResultNode** aNode)
    1884                 : {
    1885             980 :   BookmarkData bookmark;
    1886             490 :   nsresult rv = FetchItemInfo(aItemId, bookmark);
    1887             490 :   NS_ENSURE_SUCCESS(rv, rv);
    1888                 : 
    1889             484 :   if (bookmark.type == TYPE_FOLDER) { // TYPE_FOLDER
    1890                 :     *aNode = new nsNavHistoryFolderResultNode(bookmark.title,
    1891                 :                                               aOptions,
    1892             484 :                                               bookmark.id);
    1893                 :   }
    1894                 :   else {
    1895               0 :     return NS_ERROR_INVALID_ARG;
    1896                 :   }
    1897                 : 
    1898             484 :   (*aNode)->mDateAdded = bookmark.dateAdded;
    1899             484 :   (*aNode)->mLastModified = bookmark.lastModified;
    1900                 : 
    1901             484 :   NS_ADDREF(*aNode);
    1902             484 :   return NS_OK;
    1903                 : }
    1904                 : 
    1905                 : 
    1906                 : nsresult
    1907             766 : nsNavBookmarks::QueryFolderChildren(
    1908                 :   PRInt64 aFolderId,
    1909                 :   nsNavHistoryQueryOptions* aOptions,
    1910                 :   nsCOMArray<nsNavHistoryResultNode>* aChildren)
    1911                 : {
    1912             766 :   NS_ENSURE_ARG_POINTER(aOptions);
    1913             766 :   NS_ENSURE_ARG_POINTER(aChildren);
    1914                 : 
    1915                 :   // Select all children of a given folder, sorted by position.
    1916                 :   // This is a LEFT JOIN because not all bookmarks types have a place.
    1917                 :   // We construct a result where the first columns exactly match those returned
    1918                 :   // by mDBGetURLPageInfo, and additionally contains columns for position,
    1919                 :   // item_child, and folder_child from moz_bookmarks.
    1920                 :   nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
    1921                 :     "SELECT h.id, h.url, IFNULL(b.title, h.title), h.rev_host, h.visit_count, "
    1922                 :            "h.last_visit_date, f.url, null, b.id, b.dateAdded, b.lastModified, "
    1923                 :            "b.parent, null, h.frecency, b.position, b.type, b.fk, "
    1924                 :            "b.guid "
    1925                 :     "FROM moz_bookmarks b "
    1926                 :     "LEFT JOIN moz_places h ON b.fk = h.id "
    1927                 :     "LEFT JOIN moz_favicons f ON h.favicon_id = f.id "
    1928                 :     "WHERE b.parent = :parent "
    1929                 :     "ORDER BY b.position ASC"
    1930            1532 :   );
    1931             766 :   NS_ENSURE_STATE(stmt);
    1932            1532 :   mozStorageStatementScoper scoper(stmt);
    1933                 : 
    1934             766 :   nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aFolderId);
    1935             766 :   NS_ENSURE_SUCCESS(rv, rv);
    1936                 : 
    1937            1532 :   nsCOMPtr<mozIStorageValueArray> row = do_QueryInterface(stmt, &rv);
    1938             766 :   NS_ENSURE_SUCCESS(rv, rv);
    1939                 : 
    1940             766 :   PRInt32 index = -1;
    1941                 :   bool hasResult;
    1942            2718 :   while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
    1943            1186 :     rv = ProcessFolderNodeRow(row, aOptions, aChildren, index);
    1944            1186 :     NS_ENSURE_SUCCESS(rv, rv);
    1945                 :   }
    1946                 : 
    1947             766 :   return NS_OK;
    1948                 : }
    1949                 : 
    1950                 : 
    1951                 : nsresult
    1952            1194 : nsNavBookmarks::ProcessFolderNodeRow(
    1953                 :   mozIStorageValueArray* aRow,
    1954                 :   nsNavHistoryQueryOptions* aOptions,
    1955                 :   nsCOMArray<nsNavHistoryResultNode>* aChildren,
    1956                 :   PRInt32& aCurrentIndex)
    1957                 : {
    1958            1194 :   NS_ENSURE_ARG_POINTER(aRow);
    1959            1194 :   NS_ENSURE_ARG_POINTER(aOptions);
    1960            1194 :   NS_ENSURE_ARG_POINTER(aChildren);
    1961                 : 
    1962                 :   // The results will be in order of aCurrentIndex. Even if we don't add a node
    1963                 :   // because it was excluded, we need to count its index, so do that before
    1964                 :   // doing anything else.
    1965            1194 :   aCurrentIndex++;
    1966                 : 
    1967                 :   PRInt32 itemType;
    1968            1194 :   nsresult rv = aRow->GetInt32(kGetChildrenIndex_Type, &itemType);
    1969            1194 :   NS_ENSURE_SUCCESS(rv, rv);
    1970                 :   PRInt64 id;
    1971            1194 :   rv = aRow->GetInt64(nsNavHistory::kGetInfoIndex_ItemId, &id);
    1972            1194 :   NS_ENSURE_SUCCESS(rv, rv);
    1973                 : 
    1974            2388 :   nsRefPtr<nsNavHistoryResultNode> node;
    1975                 : 
    1976            1194 :   if (itemType == TYPE_BOOKMARK) {
    1977             603 :     nsNavHistory* history = nsNavHistory::GetHistoryService();
    1978             603 :     NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY);
    1979             603 :     rv = history->RowToResult(aRow, aOptions, getter_AddRefs(node));
    1980             603 :     NS_ENSURE_SUCCESS(rv, rv);
    1981                 : 
    1982                 :     PRUint32 nodeType;
    1983             603 :     node->GetType(&nodeType);
    1984            1165 :     if ((nodeType == nsINavHistoryResultNode::RESULT_TYPE_QUERY &&
    1985             125 :          aOptions->ExcludeQueries()) ||
    1986                 :         (nodeType != nsINavHistoryResultNode::RESULT_TYPE_QUERY &&
    1987                 :          nodeType != nsINavHistoryResultNode::RESULT_TYPE_FOLDER_SHORTCUT &&
    1988             437 :          aOptions->ExcludeItems())) {
    1989               0 :       return NS_OK;
    1990                 :     }
    1991                 :   }
    1992             591 :   else if (itemType == TYPE_FOLDER) {
    1993             560 :     if (aOptions->ExcludeReadOnlyFolders()) {
    1994                 :       // If the folder is read-only, skip it.
    1995               2 :       bool readOnly = false;
    1996               2 :       GetFolderReadonly(id, &readOnly);
    1997               2 :       if (readOnly)
    1998               1 :         return NS_OK;
    1999                 :     }
    2000                 : 
    2001            1118 :     nsCAutoString title;
    2002             559 :     rv = aRow->GetUTF8String(nsNavHistory::kGetInfoIndex_Title, title);
    2003             559 :     NS_ENSURE_SUCCESS(rv, rv);
    2004                 : 
    2005             559 :     node = new nsNavHistoryFolderResultNode(title, aOptions, id);
    2006                 : 
    2007                 :     rv = aRow->GetInt64(nsNavHistory::kGetInfoIndex_ItemDateAdded,
    2008             559 :                         &node->mDateAdded);
    2009             559 :     NS_ENSURE_SUCCESS(rv, rv);
    2010                 :     rv = aRow->GetInt64(nsNavHistory::kGetInfoIndex_ItemLastModified,
    2011             559 :                         &node->mLastModified);
    2012             559 :     NS_ENSURE_SUCCESS(rv, rv);
    2013                 :   }
    2014                 :   else {
    2015                 :     // This is a separator.
    2016              31 :     if (aOptions->ExcludeItems()) {
    2017               0 :       return NS_OK;
    2018                 :     }
    2019              31 :     node = new nsNavHistorySeparatorResultNode();
    2020                 : 
    2021              31 :     node->mItemId = id;
    2022                 :     rv = aRow->GetInt64(nsNavHistory::kGetInfoIndex_ItemDateAdded,
    2023              31 :                         &node->mDateAdded);
    2024              31 :     NS_ENSURE_SUCCESS(rv, rv);
    2025                 :     rv = aRow->GetInt64(nsNavHistory::kGetInfoIndex_ItemLastModified,
    2026              31 :                         &node->mLastModified);
    2027              31 :     NS_ENSURE_SUCCESS(rv, rv);
    2028                 :   }
    2029                 : 
    2030                 :   // Store the index of the node within this container.  Note that this is not
    2031                 :   // moz_bookmarks.position.
    2032            1193 :   node->mBookmarkIndex = aCurrentIndex;
    2033                 : 
    2034            1193 :   NS_ENSURE_TRUE(aChildren->AppendObject(node), NS_ERROR_OUT_OF_MEMORY);
    2035                 : 
    2036            1193 :   return NS_OK;
    2037                 : }
    2038                 : 
    2039                 : 
    2040                 : nsresult
    2041               3 : nsNavBookmarks::QueryFolderChildrenAsync(
    2042                 :   nsNavHistoryFolderResultNode* aNode,
    2043                 :   PRInt64 aFolderId,
    2044                 :   mozIStoragePendingStatement** _pendingStmt)
    2045                 : {
    2046               3 :   NS_ENSURE_ARG_POINTER(aNode);
    2047               3 :   NS_ENSURE_ARG_POINTER(_pendingStmt);
    2048                 : 
    2049                 :   // Select all children of a given folder, sorted by position.
    2050                 :   // This is a LEFT JOIN because not all bookmarks types have a place.
    2051                 :   // We construct a result where the first columns exactly match those returned
    2052                 :   // by mDBGetURLPageInfo, and additionally contains columns for position,
    2053                 :   // item_child, and folder_child from moz_bookmarks.
    2054                 :   nsCOMPtr<mozIStorageAsyncStatement> stmt = mDB->GetAsyncStatement(
    2055                 :     "SELECT h.id, h.url, IFNULL(b.title, h.title), h.rev_host, h.visit_count, "
    2056                 :            "h.last_visit_date, f.url, null, b.id, b.dateAdded, b.lastModified, "
    2057                 :            "b.parent, null, h.frecency, b.position, b.type, b.fk, "
    2058                 :            "b.guid "
    2059                 :     "FROM moz_bookmarks b "
    2060                 :     "LEFT JOIN moz_places h ON b.fk = h.id "
    2061                 :     "LEFT JOIN moz_favicons f ON h.favicon_id = f.id "
    2062                 :     "WHERE b.parent = :parent "
    2063                 :     "ORDER BY b.position ASC"
    2064               6 :   );
    2065               3 :   NS_ENSURE_STATE(stmt);
    2066                 : 
    2067               3 :   nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aFolderId);
    2068               3 :   NS_ENSURE_SUCCESS(rv, rv);
    2069                 : 
    2070               6 :   nsCOMPtr<mozIStoragePendingStatement> pendingStmt;
    2071               3 :   rv = stmt->ExecuteAsync(aNode, getter_AddRefs(pendingStmt));
    2072               3 :   NS_ENSURE_SUCCESS(rv, rv);
    2073                 : 
    2074               3 :   NS_IF_ADDREF(*_pendingStmt = pendingStmt);
    2075               3 :   return NS_OK;
    2076                 : }
    2077                 : 
    2078                 : 
    2079                 : nsresult
    2080            1693 : nsNavBookmarks::FetchFolderInfo(PRInt64 aFolderId,
    2081                 :                                 PRInt32* _folderCount,
    2082                 :                                 nsACString& _guid,
    2083                 :                                 PRInt64* _parentId)
    2084                 : {
    2085            1693 :   *_folderCount = 0;
    2086            1693 :   *_parentId = -1;
    2087                 : 
    2088                 :   // This query has to always return results, so it can't be written as a join,
    2089                 :   // though a left join of 2 subqueries would have the same cost.
    2090                 :   nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
    2091                 :     "SELECT count(*), "
    2092                 :             "(SELECT guid FROM moz_bookmarks WHERE id = :parent), "
    2093                 :             "(SELECT parent FROM moz_bookmarks WHERE id = :parent) "
    2094                 :     "FROM moz_bookmarks "
    2095                 :     "WHERE parent = :parent"
    2096            3386 :   );
    2097            1693 :   NS_ENSURE_STATE(stmt);
    2098            3386 :   mozStorageStatementScoper scoper(stmt);
    2099                 : 
    2100            1693 :   nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aFolderId);
    2101            1693 :   NS_ENSURE_SUCCESS(rv, rv);
    2102                 : 
    2103                 :   bool hasResult;
    2104            1693 :   rv = stmt->ExecuteStep(&hasResult);
    2105            1693 :   NS_ENSURE_SUCCESS(rv, rv);
    2106            1693 :   NS_ENSURE_TRUE(hasResult, NS_ERROR_UNEXPECTED);
    2107                 : 
    2108                 :   // Ensure that the folder we are looking for exists.
    2109                 :   // Can't rely only on parent, since the root has parent 0, that doesn't exist.
    2110                 :   bool isNull;
    2111            1693 :   rv = stmt->GetIsNull(2, &isNull);
    2112            1693 :   NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && (!isNull || aFolderId == 0),
    2113                 :                  NS_ERROR_INVALID_ARG);
    2114                 : 
    2115            1692 :   rv = stmt->GetInt32(0, _folderCount);
    2116            1692 :   NS_ENSURE_SUCCESS(rv, rv);
    2117            1692 :   if (!isNull) {
    2118            1691 :     rv = stmt->GetUTF8String(1, _guid);
    2119            1691 :     NS_ENSURE_SUCCESS(rv, rv);
    2120            1691 :     rv = stmt->GetInt64(2, _parentId);
    2121            1691 :     NS_ENSURE_SUCCESS(rv, rv);
    2122                 :   }
    2123                 : 
    2124            1692 :   return NS_OK;
    2125                 : }
    2126                 : 
    2127                 : 
    2128                 : NS_IMETHODIMP
    2129              22 : nsNavBookmarks::IsBookmarked(nsIURI* aURI, bool* aBookmarked)
    2130                 : {
    2131              22 :   NS_ENSURE_ARG(aURI);
    2132              21 :   NS_ENSURE_ARG_POINTER(aBookmarked);
    2133                 : 
    2134                 :   nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
    2135                 :     "SELECT 1 FROM moz_bookmarks b "
    2136                 :     "JOIN moz_places h ON b.fk = h.id "
    2137                 :     "WHERE h.url = :page_url"
    2138              42 :   );
    2139              21 :   NS_ENSURE_STATE(stmt);
    2140              42 :   mozStorageStatementScoper scoper(stmt);
    2141                 : 
    2142              21 :   nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), aURI);
    2143              21 :   NS_ENSURE_SUCCESS(rv, rv);
    2144              21 :   rv = stmt->ExecuteStep(aBookmarked);
    2145              21 :   NS_ENSURE_SUCCESS(rv, rv);
    2146                 : 
    2147              21 :   return NS_OK;
    2148                 : }
    2149                 : 
    2150                 : 
    2151                 : NS_IMETHODIMP
    2152              19 : nsNavBookmarks::GetBookmarkedURIFor(nsIURI* aURI, nsIURI** _retval)
    2153                 : {
    2154              19 :   NS_ENSURE_ARG(aURI);
    2155              18 :   NS_ENSURE_ARG_POINTER(_retval);
    2156                 : 
    2157              18 :   *_retval = nsnull;
    2158                 : 
    2159              18 :   nsNavHistory* history = nsNavHistory::GetHistoryService();
    2160              18 :   NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY);
    2161                 :   PRInt64 placeId;
    2162              36 :   nsCAutoString placeGuid;
    2163              18 :   nsresult rv = history->GetIdForPage(aURI, &placeId, placeGuid);
    2164              18 :   NS_ENSURE_SUCCESS(rv, rv);
    2165              18 :   if (!placeId) {
    2166                 :     // This URI is unknown, just return null.
    2167               2 :     return NS_OK;
    2168                 :   }
    2169                 : 
    2170                 :   // Check if a bookmark exists in the redirects chain for this URI.
    2171                 :   // The query will also check if the page is directly bookmarked, and return
    2172                 :   // the first found bookmark in case.  The check is directly on moz_bookmarks
    2173                 :   // without special filtering.
    2174                 :   // The next query finds the bookmarked ancestors in a redirects chain.
    2175                 :   // It won't go further than 3 levels of redirects (a->b->c->your_place_id).
    2176                 :   // To make this path 100% correct (up to any level) we would need either:
    2177                 :   //  - A separate hash, build through recursive querying of the database.
    2178                 :   //    This solution was previously implemented, but it had a negative effect
    2179                 :   //    on startup since at each startup we have to recursively query the
    2180                 :   //    database to rebuild a hash that is always the same across sessions.
    2181                 :   //    It must be updated at each visit and bookmarks change too.  The code to
    2182                 :   //    manage it is complex and prone to errors, sometimes causing incorrect
    2183                 :   //    data fetches (for example wrong favicon for a redirected bookmark).
    2184                 :   //  - A better way to track redirects for a visit.
    2185                 :   //    We would need a separate table to track redirects, in the table we would
    2186                 :   //    have visit_id, redirect_session.  To get all sources for
    2187                 :   //    a visit then we could just join this table and get all visit_id that
    2188                 :   //    are in the same redirect_session as our visit.  This has the drawback
    2189                 :   //    that we can't ensure data integrity in the downgrade -> upgrade path,
    2190                 :   //    since an old version would not update the table on new visits.
    2191                 :   //
    2192                 :   // For most cases these levels of redirects should be fine though, it's hard
    2193                 :   // to hit a page that is 4 or 5 levels of redirects below a bookmarked page.
    2194                 :   //
    2195                 :   // As a bonus the query also checks first if place_id is already a bookmark,
    2196                 :   // so you don't have to check that apart.
    2197                 : 
    2198                 : #define COALESCE_PLACEID \
    2199                 :   "COALESCE(greatgrandparent.place_id, grandparent.place_id, parent.place_id) "
    2200                 : 
    2201                 :   nsCString redirectsFragment =
    2202                 :     nsPrintfCString(3, "%d,%d",
    2203                 :                     nsINavHistoryService::TRANSITION_REDIRECT_PERMANENT,
    2204              32 :                     nsINavHistoryService::TRANSITION_REDIRECT_TEMPORARY);
    2205                 : 
    2206              16 :   nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(NS_LITERAL_CSTRING(
    2207                 :     "SELECT "
    2208                 :       "(SELECT url FROM moz_places WHERE id = :page_id) "
    2209                 :     "FROM moz_bookmarks b "
    2210                 :     "WHERE b.fk = :page_id "
    2211                 :     "UNION ALL " // Not directly bookmarked.
    2212                 :     "SELECT "
    2213                 :       "(SELECT url FROM moz_places WHERE id = " COALESCE_PLACEID ") "
    2214                 :     "FROM moz_historyvisits self "
    2215                 :     "JOIN moz_bookmarks b ON b.fk = " COALESCE_PLACEID
    2216                 :     "LEFT JOIN moz_historyvisits parent ON parent.id = self.from_visit "
    2217                 :     "LEFT JOIN moz_historyvisits grandparent ON parent.from_visit = grandparent.id "
    2218              32 :       "AND parent.visit_type IN (") + redirectsFragment + NS_LITERAL_CSTRING(") "
    2219                 :     "LEFT JOIN moz_historyvisits greatgrandparent ON grandparent.from_visit = greatgrandparent.id "
    2220              48 :       "AND grandparent.visit_type IN (") + redirectsFragment + NS_LITERAL_CSTRING(") "
    2221              48 :     "WHERE self.visit_type IN (") + redirectsFragment + NS_LITERAL_CSTRING(") "
    2222                 :       "AND self.place_id = :page_id "
    2223                 :     "LIMIT 1 " // Stop at the first result.
    2224              32 :   ));
    2225                 : #undef COALESCE_PLACEID
    2226                 : 
    2227              16 :   NS_ENSURE_STATE(stmt);
    2228              32 :   mozStorageStatementScoper scoper(stmt);
    2229                 : 
    2230              16 :   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), placeId);
    2231              16 :   NS_ENSURE_SUCCESS(rv, rv);
    2232                 :   bool hasBookmarkedOrigin;
    2233              16 :   if (NS_SUCCEEDED(stmt->ExecuteStep(&hasBookmarkedOrigin)) &&
    2234                 :       hasBookmarkedOrigin) {
    2235              26 :     nsCAutoString spec;
    2236              13 :     rv = stmt->GetUTF8String(0, spec);
    2237              13 :     NS_ENSURE_SUCCESS(rv, rv);
    2238              13 :     rv = NS_NewURI(_retval, spec);
    2239              13 :     NS_ENSURE_SUCCESS(rv, rv);
    2240                 :   }
    2241                 : 
    2242                 :   // If there is no bookmarked origin, we will just return null.
    2243              16 :   return NS_OK;
    2244                 : }
    2245                 : 
    2246                 : 
    2247                 : NS_IMETHODIMP
    2248              27 : nsNavBookmarks::ChangeBookmarkURI(PRInt64 aBookmarkId, nsIURI* aNewURI)
    2249                 : {
    2250              27 :   NS_ENSURE_ARG_MIN(aBookmarkId, 1);
    2251              26 :   NS_ENSURE_ARG(aNewURI);
    2252                 : 
    2253              52 :   BookmarkData bookmark;
    2254              26 :   nsresult rv = FetchItemInfo(aBookmarkId, bookmark);
    2255              26 :   NS_ENSURE_SUCCESS(rv, rv);
    2256              24 :   NS_ENSURE_ARG(bookmark.type == TYPE_BOOKMARK);
    2257                 : 
    2258              48 :   mozStorageTransaction transaction(mDB->MainConn(), false);
    2259                 : 
    2260              24 :   nsNavHistory* history = nsNavHistory::GetHistoryService();
    2261              24 :   NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY);
    2262                 :   PRInt64 newPlaceId;
    2263              48 :   nsCAutoString newPlaceGuid;
    2264              24 :   rv = history->GetOrCreateIdForPage(aNewURI, &newPlaceId, newPlaceGuid);
    2265              24 :   NS_ENSURE_SUCCESS(rv, rv);
    2266              24 :   if (!newPlaceId)
    2267               0 :     return NS_ERROR_INVALID_ARG;
    2268                 : 
    2269              24 :   BEGIN_CRITICAL_BOOKMARK_CACHE_SECTION(bookmark.id);
    2270                 : 
    2271                 :   nsCOMPtr<mozIStorageStatement> statement = mDB->GetStatement(
    2272                 :     "UPDATE moz_bookmarks SET fk = :page_id, lastModified = :date "
    2273                 :     "WHERE id = :item_id "
    2274              48 :   );
    2275              24 :   NS_ENSURE_STATE(statement);
    2276              48 :   mozStorageStatementScoper scoper(statement);
    2277                 : 
    2278              24 :   rv = statement->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), newPlaceId);
    2279              24 :   NS_ENSURE_SUCCESS(rv, rv);
    2280              24 :   bookmark.lastModified = PR_Now();
    2281              48 :   rv = statement->BindInt64ByName(NS_LITERAL_CSTRING("date"),
    2282              24 :                                   bookmark.lastModified);
    2283              24 :   NS_ENSURE_SUCCESS(rv, rv);
    2284              24 :   rv = statement->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), bookmark.id);
    2285              24 :   NS_ENSURE_SUCCESS(rv, rv);
    2286              24 :   rv = statement->Execute();
    2287              24 :   NS_ENSURE_SUCCESS(rv, rv);
    2288                 : 
    2289              24 :   rv = transaction.Commit();
    2290              24 :   NS_ENSURE_SUCCESS(rv, rv);
    2291                 : 
    2292              24 :   END_CRITICAL_BOOKMARK_CACHE_SECTION(bookmark.id);
    2293                 : 
    2294              24 :   rv = history->UpdateFrecency(newPlaceId);
    2295              24 :   NS_ENSURE_SUCCESS(rv, rv);
    2296                 : 
    2297                 :   // Upon changing the URI for a bookmark, update the frecency for the old
    2298                 :   // place as well.
    2299              24 :   rv = history->UpdateFrecency(bookmark.placeId);
    2300              24 :   NS_ENSURE_SUCCESS(rv, rv);
    2301                 : 
    2302              48 :   nsCAutoString spec;
    2303              24 :   rv = aNewURI->GetSpec(spec);
    2304              24 :   NS_ENSURE_SUCCESS(rv, rv);
    2305                 : 
    2306              24 :   NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
    2307                 :                    nsINavBookmarkObserver,
    2308                 :                    OnItemChanged(bookmark.id,
    2309                 :                                  NS_LITERAL_CSTRING("uri"),
    2310                 :                                  false,
    2311                 :                                  spec,
    2312                 :                                  bookmark.lastModified,
    2313                 :                                  bookmark.type,
    2314                 :                                  bookmark.parentId,
    2315                 :                                  bookmark.guid,
    2316                 :                                  bookmark.parentGuid));
    2317              24 :   return NS_OK;
    2318                 : }
    2319                 : 
    2320                 : 
    2321                 : NS_IMETHODIMP
    2322             561 : nsNavBookmarks::GetFolderIdForItem(PRInt64 aItemId, PRInt64* _parentId)
    2323                 : {
    2324             561 :   NS_ENSURE_ARG_MIN(aItemId, 1);
    2325             558 :   NS_ENSURE_ARG_POINTER(_parentId);
    2326                 : 
    2327            1116 :   BookmarkData bookmark;
    2328             558 :   nsresult rv = FetchItemInfo(aItemId, bookmark);
    2329             558 :   NS_ENSURE_SUCCESS(rv, rv);
    2330                 : 
    2331                 :   // this should not happen, but see bug #400448 for details
    2332             547 :   NS_ENSURE_TRUE(bookmark.id != bookmark.parentId, NS_ERROR_UNEXPECTED);
    2333                 : 
    2334             547 :   *_parentId = bookmark.parentId;
    2335             547 :   return NS_OK;
    2336                 : }
    2337                 : 
    2338                 : 
    2339                 : nsresult
    2340             116 : nsNavBookmarks::GetBookmarkIdsForURITArray(nsIURI* aURI,
    2341                 :                                            nsTArray<PRInt64>& aResult,
    2342                 :                                            bool aSkipTags)
    2343                 : {
    2344             116 :   NS_ENSURE_ARG(aURI);
    2345                 : 
    2346                 :   // Double ordering covers possible lastModified ties, that could happen when
    2347                 :   // importing, syncing or due to extensions.
    2348                 :   // Note: not using a JOIN is cheaper in this case.
    2349                 :   nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
    2350                 :     "SELECT b.id, b.guid, b.parent, b.lastModified, t.guid, t.parent "
    2351                 :     "FROM moz_bookmarks b "
    2352                 :     "JOIN moz_bookmarks t on t.id = b.parent "
    2353                 :     "WHERE b.fk = (SELECT id FROM moz_places WHERE url = :page_url) "
    2354                 :     "ORDER BY b.lastModified DESC, b.id DESC "
    2355             232 :   );
    2356             116 :   NS_ENSURE_STATE(stmt);
    2357             232 :   mozStorageStatementScoper scoper(stmt);
    2358                 : 
    2359             116 :   nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), aURI);
    2360             116 :   NS_ENSURE_SUCCESS(rv, rv);
    2361                 : 
    2362                 :   bool more;
    2363             446 :   while (NS_SUCCEEDED((rv = stmt->ExecuteStep(&more))) && more) {
    2364             214 :     if (aSkipTags) {
    2365                 :       // Skip tags, for the use-cases of this async getter they are useless.
    2366                 :       PRInt64 grandParentId;
    2367               0 :       nsresult rv = stmt->GetInt64(5, &grandParentId);
    2368               0 :       NS_ENSURE_SUCCESS(rv, rv);
    2369               0 :       if (grandParentId == mTagsRoot) {
    2370               0 :         continue;
    2371                 :       }
    2372                 :     }
    2373                 :     PRInt64 bookmarkId;
    2374             214 :     rv = stmt->GetInt64(0, &bookmarkId);
    2375             214 :     NS_ENSURE_SUCCESS(rv, rv);
    2376             214 :     NS_ENSURE_TRUE(aResult.AppendElement(bookmarkId), NS_ERROR_OUT_OF_MEMORY);
    2377                 :   }
    2378             116 :   NS_ENSURE_SUCCESS(rv, rv);
    2379                 : 
    2380             116 :   return NS_OK;
    2381                 : }
    2382                 : 
    2383                 : nsresult
    2384             404 : nsNavBookmarks::GetBookmarksForURI(nsIURI* aURI,
    2385                 :                                    nsTArray<BookmarkData>& aBookmarks)
    2386                 : {
    2387             404 :   NS_ENSURE_ARG(aURI);
    2388                 : 
    2389                 :   // Double ordering covers possible lastModified ties, that could happen when
    2390                 :   // importing, syncing or due to extensions.
    2391                 :   // Note: not using a JOIN is cheaper in this case.
    2392                 :   nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
    2393                 :     "SELECT b.id, b.guid, b.parent, b.lastModified, t.guid, t.parent "
    2394                 :     "FROM moz_bookmarks b "
    2395                 :     "JOIN moz_bookmarks t on t.id = b.parent "
    2396                 :     "WHERE b.fk = (SELECT id FROM moz_places WHERE url = :page_url) "
    2397                 :     "ORDER BY b.lastModified DESC, b.id DESC "
    2398             808 :   );
    2399             404 :   NS_ENSURE_STATE(stmt);
    2400             808 :   mozStorageStatementScoper scoper(stmt);
    2401                 : 
    2402             404 :   nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), aURI);
    2403             404 :   NS_ENSURE_SUCCESS(rv, rv);
    2404                 : 
    2405                 :   bool more;
    2406             808 :   nsAutoString tags;
    2407            1792 :   while (NS_SUCCEEDED((rv = stmt->ExecuteStep(&more))) && more) {
    2408                 :     // Skip tags.
    2409                 :     PRInt64 grandParentId;
    2410             984 :     nsresult rv = stmt->GetInt64(5, &grandParentId);
    2411             984 :     NS_ENSURE_SUCCESS(rv, rv);
    2412             984 :     if (grandParentId == mTagsRoot) {
    2413             711 :       continue;
    2414                 :     }
    2415                 : 
    2416             546 :     BookmarkData bookmark;
    2417             273 :     bookmark.grandParentId = grandParentId;
    2418             273 :     rv = stmt->GetInt64(0, &bookmark.id);
    2419             273 :     NS_ENSURE_SUCCESS(rv, rv);
    2420             273 :     rv = stmt->GetUTF8String(1, bookmark.guid);
    2421             273 :     NS_ENSURE_SUCCESS(rv, rv);
    2422             273 :     rv = stmt->GetInt64(2, &bookmark.parentId);
    2423             273 :     NS_ENSURE_SUCCESS(rv, rv);
    2424             273 :     rv = stmt->GetInt64(3, &bookmark.lastModified);
    2425             273 :     NS_ENSURE_SUCCESS(rv, rv);
    2426             273 :     rv = stmt->GetUTF8String(4, bookmark.parentGuid);
    2427             273 :     NS_ENSURE_SUCCESS(rv, rv);
    2428                 : 
    2429             273 :     NS_ENSURE_TRUE(aBookmarks.AppendElement(bookmark), NS_ERROR_OUT_OF_MEMORY);
    2430                 :   }
    2431                 : 
    2432             404 :   return NS_OK;
    2433                 : }
    2434                 : 
    2435                 : NS_IMETHODIMP
    2436             117 : nsNavBookmarks::GetBookmarkIdsForURI(nsIURI* aURI, PRUint32* aCount,
    2437                 :                                      PRInt64** aBookmarks)
    2438                 : {
    2439             117 :   NS_ENSURE_ARG(aURI);
    2440             116 :   NS_ENSURE_ARG_POINTER(aCount);
    2441             116 :   NS_ENSURE_ARG_POINTER(aBookmarks);
    2442                 : 
    2443             116 :   *aCount = 0;
    2444             116 :   *aBookmarks = nsnull;
    2445             232 :   nsTArray<PRInt64> bookmarks;
    2446                 : 
    2447                 :   // Get the information from the DB as a TArray
    2448                 :   // TODO (bug 653816): make this API skip tags by default.
    2449             116 :   nsresult rv = GetBookmarkIdsForURITArray(aURI, bookmarks, false);
    2450             116 :   NS_ENSURE_SUCCESS(rv, rv);
    2451                 : 
    2452                 :   // Copy the results into a new array for output
    2453             116 :   if (bookmarks.Length()) {
    2454                 :     *aBookmarks =
    2455              92 :       static_cast<PRInt64*>(nsMemory::Alloc(sizeof(PRInt64) * bookmarks.Length()));
    2456              92 :     if (!*aBookmarks)
    2457               0 :       return NS_ERROR_OUT_OF_MEMORY;
    2458             306 :     for (PRUint32 i = 0; i < bookmarks.Length(); i ++)
    2459             214 :       (*aBookmarks)[i] = bookmarks[i];
    2460                 :   }
    2461                 : 
    2462             116 :   *aCount = bookmarks.Length();
    2463             116 :   return NS_OK;
    2464                 : }
    2465                 : 
    2466                 : 
    2467                 : NS_IMETHODIMP
    2468             288 : nsNavBookmarks::GetItemIndex(PRInt64 aItemId, PRInt32* _index)
    2469                 : {
    2470             288 :   NS_ENSURE_ARG_MIN(aItemId, 1);
    2471             287 :   NS_ENSURE_ARG_POINTER(_index);
    2472                 : 
    2473             574 :   BookmarkData bookmark;
    2474             287 :   nsresult rv = FetchItemInfo(aItemId, bookmark);
    2475                 :   // With respect to the API.
    2476             287 :   if (NS_FAILED(rv)) {
    2477              15 :     *_index = -1;
    2478              15 :     return NS_OK;
    2479                 :   }
    2480                 : 
    2481             272 :   *_index = bookmark.position;
    2482             272 :   return NS_OK;
    2483                 : }
    2484                 : 
    2485                 : 
    2486                 : NS_IMETHODIMP
    2487              16 : nsNavBookmarks::SetItemIndex(PRInt64 aItemId, PRInt32 aNewIndex)
    2488                 : {
    2489              16 :   NS_ENSURE_ARG_MIN(aItemId, 1);
    2490              14 :   NS_ENSURE_ARG_MIN(aNewIndex, 0);
    2491                 : 
    2492              26 :   BookmarkData bookmark;
    2493              13 :   nsresult rv = FetchItemInfo(aItemId, bookmark);
    2494              13 :   NS_ENSURE_SUCCESS(rv, rv);
    2495                 : 
    2496                 :   // Ensure we are not going out of range.
    2497                 :   PRInt32 folderCount;
    2498                 :   PRInt64 grandParentId;
    2499              26 :   nsCAutoString folderGuid;
    2500              13 :   rv = FetchFolderInfo(bookmark.parentId, &folderCount, folderGuid, &grandParentId);
    2501              13 :   NS_ENSURE_SUCCESS(rv, rv);
    2502              13 :   NS_ENSURE_TRUE(aNewIndex < folderCount, NS_ERROR_INVALID_ARG);
    2503                 :   // Check the parent's guid is the expected one.
    2504              13 :   MOZ_ASSERT(bookmark.parentGuid == folderGuid);
    2505                 : 
    2506              13 :   BEGIN_CRITICAL_BOOKMARK_CACHE_SECTION(bookmark.id);
    2507                 : 
    2508                 :   nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
    2509                 :     "UPDATE moz_bookmarks SET position = :item_index WHERE id = :item_id"
    2510              26 :   );
    2511              13 :   NS_ENSURE_STATE(stmt);
    2512              26 :   mozStorageStatementScoper scoper(stmt);
    2513                 : 
    2514              13 :   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), aItemId);
    2515              13 :   NS_ENSURE_SUCCESS(rv, rv);
    2516              13 :   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("item_index"), aNewIndex);
    2517              13 :   NS_ENSURE_SUCCESS(rv, rv);
    2518                 : 
    2519              13 :   rv = stmt->Execute();
    2520              13 :   NS_ENSURE_SUCCESS(rv, rv);
    2521                 : 
    2522              13 :   END_CRITICAL_BOOKMARK_CACHE_SECTION(bookmark.id);
    2523                 : 
    2524              13 :   NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
    2525                 :                    nsINavBookmarkObserver,
    2526                 :                    OnItemMoved(bookmark.id,
    2527                 :                                bookmark.parentId,
    2528                 :                                bookmark.position,
    2529                 :                                bookmark.parentId,
    2530                 :                                aNewIndex,
    2531                 :                                bookmark.type,
    2532                 :                                bookmark.guid,
    2533                 :                                bookmark.parentGuid,
    2534                 :                                bookmark.parentGuid));
    2535                 : 
    2536              13 :   return NS_OK;
    2537                 : }
    2538                 : 
    2539                 : 
    2540                 : nsresult
    2541             656 : nsNavBookmarks::UpdateKeywordsHashForRemovedBookmark(PRInt64 aItemId)
    2542                 : {
    2543            1312 :   nsAutoString keyword;
    2544            1312 :   if (NS_SUCCEEDED(GetKeywordForBookmark(aItemId, keyword)) &&
    2545             656 :       !keyword.IsEmpty()) {
    2546              37 :     nsresult rv = EnsureKeywordsHash();
    2547              37 :     NS_ENSURE_SUCCESS(rv, rv);
    2548              37 :     mBookmarkToKeywordHash.Remove(aItemId);
    2549                 : 
    2550                 :     // If the keyword is unused, remove it from the database.
    2551              74 :     keywordSearchData searchData;
    2552              37 :     searchData.keyword.Assign(keyword);
    2553              37 :     searchData.itemId = -1;
    2554              37 :     mBookmarkToKeywordHash.EnumerateRead(SearchBookmarkForKeyword, &searchData);
    2555              37 :     if (searchData.itemId == -1) {
    2556                 :       nsCOMPtr<mozIStorageAsyncStatement> stmt = mDB->GetAsyncStatement(
    2557                 :         "DELETE FROM moz_keywords "
    2558                 :         "WHERE keyword = :keyword "
    2559                 :         "AND NOT EXISTS ( "
    2560                 :           "SELECT id "
    2561                 :           "FROM moz_bookmarks "
    2562                 :           "WHERE keyword_id = moz_keywords.id "
    2563                 :         ")"
    2564              60 :       );
    2565              30 :       NS_ENSURE_STATE(stmt);
    2566                 : 
    2567              30 :       rv = stmt->BindStringByName(NS_LITERAL_CSTRING("keyword"), keyword);
    2568              30 :       NS_ENSURE_SUCCESS(rv, rv);
    2569              60 :       nsCOMPtr<mozIStoragePendingStatement> pendingStmt;
    2570              30 :       rv = stmt->ExecuteAsync(nsnull, getter_AddRefs(pendingStmt));
    2571              30 :       NS_ENSURE_SUCCESS(rv, rv);
    2572                 :     }
    2573                 :   }
    2574             656 :   return NS_OK;
    2575                 : }
    2576                 : 
    2577                 : 
    2578                 : NS_IMETHODIMP
    2579              66 : nsNavBookmarks::SetKeywordForBookmark(PRInt64 aBookmarkId,
    2580                 :                                       const nsAString& aUserCasedKeyword)
    2581                 : {
    2582              66 :   NS_ENSURE_ARG_MIN(aBookmarkId, 1);
    2583                 : 
    2584                 :   // This also ensures the bookmark is valid.
    2585             130 :   BookmarkData bookmark;
    2586              65 :   nsresult rv = FetchItemInfo(aBookmarkId, bookmark);
    2587              65 :   NS_ENSURE_SUCCESS(rv, rv);
    2588                 : 
    2589              65 :   rv = EnsureKeywordsHash();
    2590              65 :   NS_ENSURE_SUCCESS(rv, rv);
    2591                 : 
    2592                 :   // Shortcuts are always lowercased internally.
    2593             130 :   nsAutoString keyword(aUserCasedKeyword);
    2594              65 :   ToLowerCase(keyword);
    2595                 : 
    2596                 :   // Check if bookmark was already associated to a keyword.
    2597             130 :   nsAutoString oldKeyword;
    2598              65 :   rv = GetKeywordForBookmark(bookmark.id, oldKeyword);
    2599              65 :   NS_ENSURE_SUCCESS(rv, rv);
    2600                 : 
    2601                 :   // Trying to set the same value or to remove a nonexistent keyword is a no-op.
    2602              65 :   if (keyword.Equals(oldKeyword) || (keyword.IsEmpty() && oldKeyword.IsEmpty()))
    2603              11 :     return NS_OK;
    2604                 : 
    2605             108 :   mozStorageTransaction transaction(mDB->MainConn(), false);
    2606                 : 
    2607                 :   nsCOMPtr<mozIStorageStatement> updateBookmarkStmt = mDB->GetStatement(
    2608                 :     "UPDATE moz_bookmarks "
    2609                 :     "SET keyword_id = (SELECT id FROM moz_keywords WHERE keyword = :keyword), "
    2610                 :         "lastModified = :date "
    2611                 :     "WHERE id = :item_id "
    2612             108 :   );
    2613              54 :   NS_ENSURE_STATE(updateBookmarkStmt);
    2614             108 :   mozStorageStatementScoper updateBookmarkScoper(updateBookmarkStmt);
    2615                 : 
    2616              54 :   if (keyword.IsEmpty()) {
    2617                 :     // Remove keyword association from the hash.
    2618               2 :     mBookmarkToKeywordHash.Remove(bookmark.id);
    2619               2 :     rv = updateBookmarkStmt->BindNullByName(NS_LITERAL_CSTRING("keyword"));
    2620                 :   }
    2621                 :    else {
    2622                 :     // We are associating bookmark to a new keyword. Create a new keyword
    2623                 :     // record if needed.
    2624                 :     nsCOMPtr<mozIStorageStatement> newKeywordStmt = mDB->GetStatement(
    2625                 :       "INSERT OR IGNORE INTO moz_keywords (keyword) VALUES (:keyword)"
    2626             104 :     );
    2627              52 :     NS_ENSURE_STATE(newKeywordStmt);
    2628             104 :     mozStorageStatementScoper newKeywordScoper(newKeywordStmt);
    2629                 : 
    2630             104 :     rv = newKeywordStmt->BindStringByName(NS_LITERAL_CSTRING("keyword"),
    2631              52 :                                           keyword);
    2632              52 :     NS_ENSURE_SUCCESS(rv, rv);
    2633              52 :     rv = newKeywordStmt->Execute();
    2634              52 :     NS_ENSURE_SUCCESS(rv, rv);
    2635                 : 
    2636                 :     // Add new keyword association to the hash, removing the old one if needed.
    2637              52 :     if (!oldKeyword.IsEmpty())
    2638               0 :       mBookmarkToKeywordHash.Remove(bookmark.id);
    2639              52 :     mBookmarkToKeywordHash.Put(bookmark.id, keyword);
    2640             104 :     rv = updateBookmarkStmt->BindStringByName(NS_LITERAL_CSTRING("keyword"), keyword);
    2641                 :   }
    2642              54 :   NS_ENSURE_SUCCESS(rv, rv);
    2643              54 :   bookmark.lastModified = PR_Now();
    2644             108 :   rv = updateBookmarkStmt->BindInt64ByName(NS_LITERAL_CSTRING("date"),
    2645              54 :                                            bookmark.lastModified);
    2646              54 :   NS_ENSURE_SUCCESS(rv, rv);
    2647             108 :   rv = updateBookmarkStmt->BindInt64ByName(NS_LITERAL_CSTRING("item_id"),
    2648              54 :                                            bookmark.id);
    2649              54 :   NS_ENSURE_SUCCESS(rv, rv);
    2650              54 :   rv = updateBookmarkStmt->Execute();
    2651              54 :   NS_ENSURE_SUCCESS(rv, rv);
    2652                 : 
    2653              54 :   rv = transaction.Commit();
    2654              54 :   NS_ENSURE_SUCCESS(rv, rv);
    2655                 : 
    2656                 :   // Update the cache entry, if needed.
    2657              54 :   BookmarkKeyClass* key = mRecentBookmarksCache.GetEntry(aBookmarkId);
    2658              54 :   if (key) {
    2659              54 :     key->bookmark.lastModified = bookmark.lastModified;
    2660                 :   }
    2661                 : 
    2662              54 :   NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
    2663                 :                    nsINavBookmarkObserver,
    2664                 :                    OnItemChanged(bookmark.id,
    2665                 :                                  NS_LITERAL_CSTRING("keyword"),
    2666                 :                                  false,
    2667                 :                                  NS_ConvertUTF16toUTF8(keyword),
    2668                 :                                  bookmark.lastModified,
    2669                 :                                  bookmark.type,
    2670                 :                                  bookmark.parentId,
    2671                 :                                  bookmark.guid,
    2672                 :                                  bookmark.parentGuid));
    2673                 : 
    2674              54 :   return NS_OK;
    2675                 : }
    2676                 : 
    2677                 : 
    2678                 : NS_IMETHODIMP
    2679              19 : nsNavBookmarks::GetKeywordForURI(nsIURI* aURI, nsAString& aKeyword)
    2680                 : {
    2681              19 :   NS_ENSURE_ARG(aURI);
    2682              18 :   aKeyword.Truncate(0);
    2683                 : 
    2684                 :   nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
    2685                 :     "SELECT k.keyword "
    2686                 :     "FROM moz_places h "
    2687                 :     "JOIN moz_bookmarks b ON b.fk = h.id "
    2688                 :     "JOIN moz_keywords k ON k.id = b.keyword_id "
    2689                 :     "WHERE h.url = :page_url "
    2690              36 :   );
    2691              18 :   NS_ENSURE_STATE(stmt);
    2692              36 :   mozStorageStatementScoper scoper(stmt);
    2693                 : 
    2694              18 :   nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), aURI);
    2695              18 :   NS_ENSURE_SUCCESS(rv, rv);
    2696                 : 
    2697              18 :   bool hasMore = false;
    2698              18 :   rv = stmt->ExecuteStep(&hasMore);
    2699              18 :   if (NS_FAILED(rv) || !hasMore) {
    2700               4 :     aKeyword.SetIsVoid(true);
    2701               4 :     return NS_OK; // not found: return void keyword string
    2702                 :   }
    2703                 : 
    2704                 :   // found, get the keyword
    2705              14 :   rv = stmt->GetString(0, aKeyword);
    2706              14 :   NS_ENSURE_SUCCESS(rv, rv);
    2707              14 :   return NS_OK;
    2708                 : }
    2709                 : 
    2710                 : 
    2711                 : NS_IMETHODIMP
    2712             991 : nsNavBookmarks::GetKeywordForBookmark(PRInt64 aBookmarkId, nsAString& aKeyword)
    2713                 : {
    2714             991 :   NS_ENSURE_ARG_MIN(aBookmarkId, 1);
    2715             990 :   aKeyword.Truncate(0);
    2716                 : 
    2717             990 :   nsresult rv = EnsureKeywordsHash();
    2718             990 :   NS_ENSURE_SUCCESS(rv, rv);
    2719                 : 
    2720            1980 :   nsAutoString keyword;
    2721             990 :   if (!mBookmarkToKeywordHash.Get(aBookmarkId, &keyword)) {
    2722             880 :     aKeyword.SetIsVoid(true);
    2723                 :   }
    2724                 :   else {
    2725             110 :     aKeyword.Assign(keyword);
    2726                 :   }
    2727                 : 
    2728             990 :   return NS_OK;
    2729                 : }
    2730                 : 
    2731                 : 
    2732                 : NS_IMETHODIMP
    2733              52 : nsNavBookmarks::GetURIForKeyword(const nsAString& aUserCasedKeyword,
    2734                 :                                  nsIURI** aURI)
    2735                 : {
    2736              52 :   NS_ENSURE_ARG_POINTER(aURI);
    2737              52 :   NS_ENSURE_TRUE(!aUserCasedKeyword.IsEmpty(), NS_ERROR_INVALID_ARG);
    2738              51 :   *aURI = nsnull;
    2739                 : 
    2740                 :   // Shortcuts are always lowercased internally.
    2741             102 :   nsAutoString keyword(aUserCasedKeyword);
    2742              51 :   ToLowerCase(keyword);
    2743                 : 
    2744              51 :   nsresult rv = EnsureKeywordsHash();
    2745              51 :   NS_ENSURE_SUCCESS(rv, rv);
    2746                 : 
    2747             102 :   keywordSearchData searchData;
    2748              51 :   searchData.keyword.Assign(keyword);
    2749              51 :   searchData.itemId = -1;
    2750              51 :   mBookmarkToKeywordHash.EnumerateRead(SearchBookmarkForKeyword, &searchData);
    2751                 : 
    2752              51 :   if (searchData.itemId == -1) {
    2753                 :     // Not found.
    2754              25 :     return NS_OK;
    2755                 :   }
    2756                 : 
    2757              26 :   rv = GetBookmarkURI(searchData.itemId, aURI);
    2758              26 :   NS_ENSURE_SUCCESS(rv, rv);
    2759                 : 
    2760              26 :   return NS_OK;
    2761                 : }
    2762                 : 
    2763                 : 
    2764                 : nsresult
    2765            1143 : nsNavBookmarks::EnsureKeywordsHash() {
    2766            1143 :   if (mBookmarkToKeywordHash.IsInitialized())
    2767            1061 :     return NS_OK;
    2768                 : 
    2769              82 :   mBookmarkToKeywordHash.Init(BOOKMARKS_TO_KEYWORDS_INITIAL_CACHE_SIZE);
    2770                 : 
    2771             164 :   nsCOMPtr<mozIStorageStatement> stmt;
    2772             164 :   nsresult rv = mDB->MainConn()->CreateStatement(NS_LITERAL_CSTRING(
    2773                 :     "SELECT b.id, k.keyword "
    2774                 :     "FROM moz_bookmarks b "
    2775                 :     "JOIN moz_keywords k ON k.id = b.keyword_id "
    2776             164 :   ), getter_AddRefs(stmt));
    2777              82 :   NS_ENSURE_SUCCESS(rv, rv);
    2778                 : 
    2779                 :   bool hasMore;
    2780             164 :   while (NS_SUCCEEDED(stmt->ExecuteStep(&hasMore)) && hasMore) {
    2781                 :     PRInt64 itemId;
    2782               0 :     rv = stmt->GetInt64(0, &itemId);
    2783               0 :     NS_ENSURE_SUCCESS(rv, rv);
    2784               0 :     nsAutoString keyword;
    2785               0 :     rv = stmt->GetString(1, keyword);
    2786               0 :     NS_ENSURE_SUCCESS(rv, rv);
    2787                 : 
    2788               0 :     rv = mBookmarkToKeywordHash.Put(itemId, keyword);
    2789               0 :     NS_ENSURE_SUCCESS(rv, rv);
    2790                 :   }
    2791                 : 
    2792              82 :   return NS_OK;
    2793                 : }
    2794                 : 
    2795                 : 
    2796                 : NS_IMETHODIMP
    2797             388 : nsNavBookmarks::RunInBatchMode(nsINavHistoryBatchCallback* aCallback,
    2798                 :                                nsISupports* aUserData) {
    2799             776 :   SAMPLE_LABEL("bookmarks", "RunInBatchMode");
    2800             388 :   NS_ENSURE_ARG(aCallback);
    2801                 : 
    2802             387 :   mBatching = true;
    2803                 : 
    2804                 :   // Just forward the request to history.  History service must exist for
    2805                 :   // bookmarks to work and we are observing it, thus batch notifications will be
    2806                 :   // forwarded to bookmarks observers.
    2807             387 :   nsNavHistory* history = nsNavHistory::GetHistoryService();
    2808             387 :   NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY);
    2809             387 :   nsresult rv = history->RunInBatchMode(aCallback, aUserData);
    2810             387 :   NS_ENSURE_SUCCESS(rv, rv);
    2811                 : 
    2812             387 :   return NS_OK;
    2813                 : }
    2814                 : 
    2815                 : 
    2816                 : NS_IMETHODIMP
    2817             817 : nsNavBookmarks::AddObserver(nsINavBookmarkObserver* aObserver,
    2818                 :                             bool aOwnsWeak)
    2819                 : {
    2820             817 :   NS_ENSURE_ARG(aObserver);
    2821             816 :   return mObservers.AppendWeakElement(aObserver, aOwnsWeak);
    2822                 : }
    2823                 : 
    2824                 : 
    2825                 : NS_IMETHODIMP
    2826             783 : nsNavBookmarks::RemoveObserver(nsINavBookmarkObserver* aObserver)
    2827                 : {
    2828             783 :   return mObservers.RemoveWeakElement(aObserver);
    2829                 : }
    2830                 : 
    2831                 : void
    2832              37 : nsNavBookmarks::NotifyItemVisited(const ItemVisitData& aData)
    2833                 : {
    2834              74 :   nsCOMPtr<nsIURI> uri;
    2835              37 :   (void)NS_NewURI(getter_AddRefs(uri), aData.bookmark.url);
    2836              37 :   NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
    2837                 :                    nsINavBookmarkObserver,
    2838                 :                    OnItemVisited(aData.bookmark.id,
    2839                 :                                  aData.visitId,
    2840                 :                                  aData.time,
    2841                 :                                  aData.transitionType,
    2842                 :                                  uri,
    2843                 :                                  aData.bookmark.parentId,
    2844                 :                                  aData.bookmark.guid,
    2845                 :                                  aData.bookmark.parentGuid));
    2846              37 : }
    2847                 : 
    2848                 : void
    2849             101 : nsNavBookmarks::NotifyItemChanged(const ItemChangeData& aData)
    2850                 : {
    2851                 :   // A guid must always be defined.
    2852             101 :   MOZ_ASSERT(!aData.bookmark.guid.IsEmpty());
    2853             101 :   NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
    2854                 :                    nsINavBookmarkObserver,
    2855                 :                    OnItemChanged(aData.bookmark.id,
    2856                 :                                  aData.property,
    2857                 :                                  aData.isAnnotation,
    2858                 :                                  aData.newValue,
    2859                 :                                  aData.bookmark.lastModified,
    2860                 :                                  aData.bookmark.type,
    2861                 :                                  aData.bookmark.parentId,
    2862                 :                                  aData.bookmark.guid,
    2863                 :                                  aData.bookmark.parentGuid));
    2864             101 : }
    2865                 : 
    2866                 : ////////////////////////////////////////////////////////////////////////////////
    2867                 : //// nsIObserver
    2868                 : 
    2869                 : NS_IMETHODIMP
    2870             417 : nsNavBookmarks::Observe(nsISupports *aSubject, const char *aTopic,
    2871                 :                         const PRUnichar *aData)
    2872                 : {
    2873             417 :   NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread");
    2874                 : 
    2875             417 :   if (strcmp(aTopic, TOPIC_PLACES_MAINTENANCE) == 0) {
    2876                 :     // Maintenance can execute direct writes to the database, thus clear all
    2877                 :     // the cached bookmarks.
    2878              27 :     mRecentBookmarksCache.Clear();
    2879                 :   }
    2880             390 :   else if (strcmp(aTopic, TOPIC_PLACES_SHUTDOWN) == 0) {
    2881                 :     // Stop Observing annotations.
    2882             195 :     nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService();
    2883             195 :     if (annosvc) {
    2884             195 :       annosvc->RemoveObserver(this);
    2885                 :     }
    2886                 :   }
    2887             195 :   else if (strcmp(aTopic, TOPIC_PLACES_CONNECTION_CLOSED) == 0) {
    2888                 :     // Don't even try to notify observers from this point on, the category
    2889                 :     // cache would init services that could try to use our APIs.
    2890             195 :     mCanNotify = false;
    2891                 :   }
    2892                 : 
    2893             417 :   return NS_OK;
    2894                 : }
    2895                 : 
    2896                 : ////////////////////////////////////////////////////////////////////////////////
    2897                 : //// nsINavHistoryObserver
    2898                 : 
    2899                 : NS_IMETHODIMP
    2900             466 : nsNavBookmarks::OnBeginUpdateBatch()
    2901                 : {
    2902             466 :   NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
    2903                 :                    nsINavBookmarkObserver, OnBeginUpdateBatch());
    2904             466 :   return NS_OK;
    2905                 : }
    2906                 : 
    2907                 : 
    2908                 : NS_IMETHODIMP
    2909             475 : nsNavBookmarks::OnEndUpdateBatch()
    2910                 : {
    2911             475 :   if (mBatching) {
    2912             351 :     mBatching = false;
    2913                 :   }
    2914                 : 
    2915             475 :   NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
    2916                 :                    nsINavBookmarkObserver, OnEndUpdateBatch());
    2917             475 :   return NS_OK;
    2918                 : }
    2919                 : 
    2920                 : 
    2921                 : NS_IMETHODIMP
    2922            1151 : nsNavBookmarks::OnVisit(nsIURI* aURI, PRInt64 aVisitId, PRTime aTime,
    2923                 :                         PRInt64 aSessionID, PRInt64 aReferringID,
    2924                 :                         PRUint32 aTransitionType, const nsACString& aGUID,
    2925                 :                         PRUint32* aAdded)
    2926                 : {
    2927                 :   // If the page is bookmarked, notify observers for each associated bookmark.
    2928            2302 :   ItemVisitData visitData;
    2929            1151 :   nsresult rv = aURI->GetSpec(visitData.bookmark.url);
    2930            1151 :   NS_ENSURE_SUCCESS(rv, rv);
    2931            1151 :   visitData.visitId = aVisitId;
    2932            1151 :   visitData.time = aTime;
    2933            1151 :   visitData.transitionType = aTransitionType;
    2934                 : 
    2935                 :   nsRefPtr< AsyncGetBookmarksForURI<ItemVisitMethod, ItemVisitData> > notifier =
    2936            2302 :     new AsyncGetBookmarksForURI<ItemVisitMethod, ItemVisitData>(this, &nsNavBookmarks::NotifyItemVisited, visitData);
    2937            1151 :   notifier->Init();
    2938            1151 :   return NS_OK;
    2939                 : }
    2940                 : 
    2941                 : 
    2942                 : NS_IMETHODIMP
    2943              34 : nsNavBookmarks::OnBeforeDeleteURI(nsIURI* aURI,
    2944                 :                                   const nsACString& aGUID,
    2945                 :                                   PRUint16 aReason)
    2946                 : {
    2947              34 :   return NS_OK;
    2948                 : }
    2949                 : 
    2950                 : 
    2951                 : NS_IMETHODIMP
    2952              73 : nsNavBookmarks::OnDeleteURI(nsIURI* aURI,
    2953                 :                             const nsACString& aGUID,
    2954                 :                             PRUint16 aReason)
    2955                 : {
    2956                 : #ifdef DEBUG
    2957              73 :   nsNavHistory* history = nsNavHistory::GetHistoryService();
    2958                 :   PRInt64 placeId;
    2959             146 :   nsCAutoString placeGuid;
    2960              73 :   NS_ABORT_IF_FALSE(
    2961                 :     history && NS_SUCCEEDED(history->GetIdForPage(aURI, &placeId, placeGuid)) && !placeId,
    2962                 :     "OnDeleteURI was notified for a page that still exists?"
    2963                 :   );
    2964                 : #endif
    2965              73 :   return NS_OK;
    2966                 : }
    2967                 : 
    2968                 : 
    2969                 : NS_IMETHODIMP
    2970             136 : nsNavBookmarks::OnClearHistory()
    2971                 : {
    2972                 :   // TODO(bryner): we should notify on visited-time change for all URIs
    2973             136 :   return NS_OK;
    2974                 : }
    2975                 : 
    2976                 : 
    2977                 : NS_IMETHODIMP
    2978             300 : nsNavBookmarks::OnTitleChanged(nsIURI* aURI,
    2979                 :                                const nsAString& aPageTitle,
    2980                 :                                const nsACString& aGUID)
    2981                 : {
    2982                 :   // NOOP. We don't consume page titles from moz_places anymore.
    2983                 :   // Title-change notifications are sent from SetItemTitle.
    2984             300 :   return NS_OK;
    2985                 : }
    2986                 : 
    2987                 : 
    2988                 : NS_IMETHODIMP
    2989              88 : nsNavBookmarks::OnPageChanged(nsIURI* aURI,
    2990                 :                               PRUint32 aChangedAttribute,
    2991                 :                               const nsAString& aNewValue,
    2992                 :                               const nsACString& aGUID)
    2993                 : {
    2994                 :   nsresult rv;
    2995              88 :   if (aChangedAttribute == nsINavHistoryObserver::ATTRIBUTE_FAVICON) {
    2996             176 :     ItemChangeData changeData;
    2997              88 :     rv = aURI->GetSpec(changeData.bookmark.url);
    2998              88 :     NS_ENSURE_SUCCESS(rv, rv);
    2999              88 :     changeData.property = NS_LITERAL_CSTRING("favicon");
    3000              88 :     changeData.isAnnotation = false;
    3001              88 :     changeData.newValue = NS_ConvertUTF16toUTF8(aNewValue);
    3002              88 :     changeData.bookmark.lastModified = 0;
    3003              88 :     changeData.bookmark.type = TYPE_BOOKMARK;
    3004                 : 
    3005                 :     // Favicons may be set to either pure URIs or to folder URIs
    3006                 :     bool isPlaceURI;
    3007              88 :     rv = aURI->SchemeIs("place", &isPlaceURI);
    3008              88 :     NS_ENSURE_SUCCESS(rv, rv);
    3009              88 :     if (isPlaceURI) {
    3010               0 :       nsNavHistory* history = nsNavHistory::GetHistoryService();
    3011               0 :       NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY);
    3012                 :   
    3013               0 :       nsCOMArray<nsNavHistoryQuery> queries;
    3014               0 :       nsCOMPtr<nsNavHistoryQueryOptions> options;
    3015                 :       rv = history->QueryStringToQueryArray(changeData.bookmark.url,
    3016               0 :                                             &queries, getter_AddRefs(options));
    3017               0 :       NS_ENSURE_SUCCESS(rv, rv);
    3018                 : 
    3019               0 :       if (queries.Count() == 1 && queries[0]->Folders().Length() == 1) {
    3020                 :         // Fetch missing data.
    3021               0 :         rv = FetchItemInfo(queries[0]->Folders()[0], changeData.bookmark);
    3022               0 :         NS_ENSURE_SUCCESS(rv, rv);        
    3023               0 :         NotifyItemChanged(changeData);
    3024                 :       }
    3025                 :     }
    3026                 :     else {
    3027                 :       nsRefPtr< AsyncGetBookmarksForURI<ItemChangeMethod, ItemChangeData> > notifier =
    3028             176 :         new AsyncGetBookmarksForURI<ItemChangeMethod, ItemChangeData>(this, &nsNavBookmarks::NotifyItemChanged, changeData);
    3029              88 :       notifier->Init();
    3030                 :     }
    3031                 :   }
    3032              88 :   return NS_OK;
    3033                 : }
    3034                 : 
    3035                 : 
    3036                 : NS_IMETHODIMP
    3037              62 : nsNavBookmarks::OnDeleteVisits(nsIURI* aURI, PRTime aVisitTime,
    3038                 :                                const nsACString& aGUID,
    3039                 :                                PRUint16 aReason)
    3040                 : {
    3041                 :   // Notify "cleartime" only if all visits to the page have been removed.
    3042              62 :   if (!aVisitTime) {
    3043                 :     // If the page is bookmarked, notify observers for each associated bookmark.
    3044              26 :     ItemChangeData changeData;
    3045              13 :     nsresult rv = aURI->GetSpec(changeData.bookmark.url);
    3046              13 :     NS_ENSURE_SUCCESS(rv, rv);
    3047              13 :     changeData.property = NS_LITERAL_CSTRING("cleartime");
    3048              13 :     changeData.isAnnotation = false;
    3049              13 :     changeData.bookmark.lastModified = 0;
    3050              13 :     changeData.bookmark.type = TYPE_BOOKMARK;
    3051                 : 
    3052                 :     nsRefPtr< AsyncGetBookmarksForURI<ItemChangeMethod, ItemChangeData> > notifier =
    3053              39 :       new AsyncGetBookmarksForURI<ItemChangeMethod, ItemChangeData>(this, &nsNavBookmarks::NotifyItemChanged, changeData);
    3054              13 :     notifier->Init();
    3055                 :   }
    3056              62 :   return NS_OK;
    3057                 : }
    3058                 : 
    3059                 : 
    3060                 : // nsIAnnotationObserver
    3061                 : 
    3062                 : NS_IMETHODIMP
    3063             272 : nsNavBookmarks::OnPageAnnotationSet(nsIURI* aPage, const nsACString& aName)
    3064                 : {
    3065             272 :   return NS_OK;
    3066                 : }
    3067                 : 
    3068                 : 
    3069                 : NS_IMETHODIMP
    3070             834 : nsNavBookmarks::OnItemAnnotationSet(PRInt64 aItemId, const nsACString& aName)
    3071                 : {
    3072            1668 :   BookmarkData bookmark;
    3073             834 :   nsresult rv = FetchItemInfo(aItemId, bookmark);
    3074             834 :   NS_ENSURE_SUCCESS(rv, rv);
    3075                 : 
    3076             818 :   bookmark.lastModified = PR_Now();
    3077             818 :   rv = SetItemDateInternal(LAST_MODIFIED, bookmark.id, bookmark.lastModified);
    3078             818 :   NS_ENSURE_SUCCESS(rv, rv);
    3079                 : 
    3080             818 :   NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
    3081                 :                    nsINavBookmarkObserver,
    3082                 :                    OnItemChanged(bookmark.id,
    3083                 :                                  aName,
    3084                 :                                  true,
    3085                 :                                  EmptyCString(),
    3086                 :                                  bookmark.lastModified,
    3087                 :                                  bookmark.type,
    3088                 :                                  bookmark.parentId,
    3089                 :                                  bookmark.guid,
    3090                 :                                  bookmark.parentGuid));
    3091             818 :   return NS_OK;
    3092                 : }
    3093                 : 
    3094                 : 
    3095                 : NS_IMETHODIMP
    3096               8 : nsNavBookmarks::OnPageAnnotationRemoved(nsIURI* aPage, const nsACString& aName)
    3097                 : {
    3098               8 :   return NS_OK;
    3099                 : }
    3100                 : 
    3101                 : 
    3102                 : NS_IMETHODIMP
    3103             277 : nsNavBookmarks::OnItemAnnotationRemoved(PRInt64 aItemId, const nsACString& aName)
    3104                 : {
    3105                 :   // As of now this is doing the same as OnItemAnnotationSet, so just forward
    3106                 :   // the call.
    3107             277 :   nsresult rv = OnItemAnnotationSet(aItemId, aName);
    3108             277 :   NS_ENSURE_SUCCESS(rv, rv);
    3109             261 :   return NS_OK;
    3110                 : }

Generated by: LCOV version 1.7