LCOV - code coverage report
Current view: directory - toolkit/components/places - nsNavHistoryResult.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 1947 1607 82.5 %
Date: 2012-06-02 Functions: 232 198 85.3 %

       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 Mozilla History System
      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                 :  *   Brett Wilson <brettw@gmail.com> (original author)
      24                 :  *   Dietrich Ayala <dietrich@mozilla.com>
      25                 :  *   Asaf Romano <mano@mozilla.com>
      26                 :  *   Marco Bonardo <mak77@bonardo.net>
      27                 :  *   Drew Willcoxon <adw@mozilla.com>
      28                 :  *
      29                 :  * Alternatively, the contents of this file may be used under the terms of
      30                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      31                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      32                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      33                 :  * of those above. If you wish to allow use of your version of this file only
      34                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      35                 :  * use your version of this file under the terms of the MPL, indicate your
      36                 :  * decision by deleting the provisions above and replace them with the notice
      37                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      38                 :  * the provisions above, a recipient may use your version of this file under
      39                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      40                 :  *
      41                 :  * ***** END LICENSE BLOCK ***** */
      42                 : 
      43                 : #include <stdio.h>
      44                 : #include "nsNavHistory.h"
      45                 : #include "nsNavBookmarks.h"
      46                 : #include "nsFaviconService.h"
      47                 : #include "nsITaggingService.h"
      48                 : #include "nsAnnotationService.h"
      49                 : #include "Helpers.h"
      50                 : 
      51                 : #include "nsDebug.h"
      52                 : #include "nsNetUtil.h"
      53                 : #include "nsString.h"
      54                 : #include "nsReadableUtils.h"
      55                 : #include "nsUnicharUtils.h"
      56                 : #include "prtime.h"
      57                 : #include "prprf.h"
      58                 : 
      59                 : #include "nsCycleCollectionParticipant.h"
      60                 : #include "nsIClassInfo.h"
      61                 : #include "nsIProgrammingLanguage.h"
      62                 : #include "nsIXPCScriptable.h"
      63                 : 
      64                 : #define TO_ICONTAINER(_node)                                                  \
      65                 :     static_cast<nsINavHistoryContainerResultNode*>(_node)                      
      66                 : 
      67                 : #define TO_CONTAINER(_node)                                                   \
      68                 :     static_cast<nsNavHistoryContainerResultNode*>(_node)
      69                 : 
      70                 : #define NOTIFY_RESULT_OBSERVERS_RET(_result, _method, _ret)                   \
      71                 :   PR_BEGIN_MACRO                                                              \
      72                 :   NS_ENSURE_TRUE(_result, _ret);                                              \
      73                 :   if (!_result->mSuppressNotifications) {                                     \
      74                 :     ENUMERATE_WEAKARRAY(_result->mObservers, nsINavHistoryResultObserver,     \
      75                 :                         _method)                                              \
      76                 :   }                                                                           \
      77                 :   PR_END_MACRO
      78                 : 
      79                 : #define NOTIFY_RESULT_OBSERVERS(_result, _method)                             \
      80                 :   NOTIFY_RESULT_OBSERVERS_RET(_result, _method, NS_ERROR_UNEXPECTED)
      81                 : 
      82                 : // What we want is: NS_INTERFACE_MAP_ENTRY(self) for static IID accessors,
      83                 : // but some of our classes (like nsNavHistoryResult) have an ambiguous base
      84                 : // class of nsISupports which prevents this from working (the default macro
      85                 : // converts it to nsISupports, then addrefs it, then returns it). Therefore, we
      86                 : // expand the macro here and change it so that it works. Yuck.
      87                 : #define NS_INTERFACE_MAP_STATIC_AMBIGUOUS(_class) \
      88                 :   if (aIID.Equals(NS_GET_IID(_class))) { \
      89                 :     NS_ADDREF(this); \
      90                 :     *aInstancePtr = this; \
      91                 :     return NS_OK; \
      92                 :   } else
      93                 : 
      94                 : // Number of changes to handle separately in a batch.  If more changes are
      95                 : // requested the node will switch to full refresh mode.
      96                 : #define MAX_BATCH_CHANGES_BEFORE_REFRESH 5
      97                 : 
      98                 : // Emulate string comparison (used for sorting) for PRTime and int.
      99            1054 : inline PRInt32 ComparePRTime(PRTime a, PRTime b)
     100                 : {
     101            1054 :   if (LL_CMP(a, <, b))
     102             506 :     return -1;
     103             548 :   else if (LL_CMP(a, >, b))
     104             491 :     return 1;
     105              57 :   return 0;
     106                 : }
     107             450 : inline PRInt32 CompareIntegers(PRUint32 a, PRUint32 b)
     108                 : {
     109             450 :   return a - b;
     110                 : }
     111                 : 
     112                 : namespace mozilla {
     113                 :   namespace places {
     114                 :     // Class-info and the scriptable helper are implemented in order to
     115                 :     // allow the JS frontend code to set expando properties on result nodes.
     116                 :     class ResultNodeClassInfo : public nsIClassInfo
     117                 :                               , public nsIXPCScriptable
     118            1464 :     {
     119                 :       NS_DECL_ISUPPORTS
     120                 :       NS_DECL_NSIXPCSCRIPTABLE
     121                 : 
     122                 :       // TODO: Bug 517718.
     123                 :       NS_IMETHODIMP
     124             140 :       GetInterfaces(PRUint32 *_count, nsIID ***_array)
     125                 :       {
     126             140 :         *_count = 0;
     127             140 :         *_array = nsnull;
     128                 : 
     129             140 :         return NS_OK;
     130                 :       }
     131                 : 
     132                 :       NS_IMETHODIMP
     133            3885 :       GetHelperForLanguage(PRUint32 aLanguage, nsISupports **_helper)
     134                 :       {
     135            3885 :         if (aLanguage == nsIProgrammingLanguage::JAVASCRIPT) {
     136            3885 :           *_helper = static_cast<nsIXPCScriptable *>(this);
     137            3885 :           NS_ADDREF(*_helper);
     138                 :         }
     139                 :         else
     140               0 :           *_helper = nsnull;
     141                 : 
     142            3885 :         return NS_OK;
     143                 :       }
     144                 : 
     145                 :       NS_IMETHODIMP
     146               0 :       GetContractID(char **_contractID)
     147                 :       {
     148               0 :         *_contractID = nsnull;
     149               0 :         return NS_OK;
     150                 :       }
     151                 : 
     152                 :       NS_IMETHODIMP
     153               0 :       GetClassDescription(char **_desc)
     154                 :       {
     155               0 :         *_desc = nsnull;
     156               0 :         return NS_OK;
     157                 :       }
     158                 : 
     159                 :       NS_IMETHODIMP
     160               0 :       GetClassID(nsCID **_id)
     161                 :       {
     162               0 :         *_id = nsnull;
     163               0 :         return NS_OK;
     164                 :       }
     165                 : 
     166                 :       NS_IMETHODIMP
     167               0 :       GetImplementationLanguage(PRUint32 *_language)
     168                 :       {
     169               0 :         *_language = nsIProgrammingLanguage::CPLUSPLUS;
     170               0 :         return NS_OK;
     171                 :       }
     172                 : 
     173                 :       NS_IMETHODIMP
     174            3960 :       GetFlags(PRUint32 *_flags)
     175                 :       {
     176            3960 :         *_flags = 0;
     177            3960 :         return NS_OK;
     178                 :       }
     179                 : 
     180                 :       NS_IMETHODIMP
     181               0 :       GetClassIDNoAlloc(nsCID *_cid)
     182                 :       {
     183               0 :         return NS_ERROR_NOT_AVAILABLE;
     184                 :       }
     185                 :     };
     186                 : 
     187                 :     /**
     188                 :      * As a static implementation of classinfo, we violate XPCOM rules andjust
     189                 :      * pretend to use the refcount mechanism.  See classinfo documentation at
     190                 :      * https://developer.mozilla.org/en/Using_nsIClassInfo
     191                 :      */
     192           16116 :     NS_IMETHODIMP_(nsrefcnt) ResultNodeClassInfo::AddRef()
     193                 :     {
     194           16116 :       return 2;
     195                 :     }
     196           16116 :     NS_IMETHODIMP_(nsrefcnt) ResultNodeClassInfo::Release()
     197                 :     {
     198           16116 :       return 1;
     199                 :     }
     200                 : 
     201           11943 :     NS_IMPL_QUERY_INTERFACE2(ResultNodeClassInfo, nsIClassInfo, nsIXPCScriptable)
     202                 : 
     203                 : #define XPC_MAP_CLASSNAME ResultNodeClassInfo
     204                 : #define XPC_MAP_QUOTED_CLASSNAME "ResultNodeClassInfo"
     205                 : #define XPC_MAP_FLAGS nsIXPCScriptable::USE_JSSTUB_FOR_ADDPROPERTY | \
     206                 :                       nsIXPCScriptable::USE_JSSTUB_FOR_DELPROPERTY | \
     207                 :                       nsIXPCScriptable::USE_JSSTUB_FOR_SETPROPERTY
     208                 : 
     209                 : // xpc_map_end contains implementation for nsIXPCScriptable, that used the
     210                 : // constant define above
     211                 : #include "xpc_map_end.h"    
     212                 : 
     213            1464 :     static ResultNodeClassInfo sResultNodeClassInfo;
     214                 :   } // namespace places
     215                 : } // namespace mozilla
     216                 : 
     217                 : using namespace mozilla::places;
     218                 : 
     219            1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsNavHistoryResultNode)
     220                 : 
     221             899 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsNavHistoryResultNode)
     222             899 :   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mParent)
     223             899 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END 
     224                 : 
     225             961 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsNavHistoryResultNode)
     226             961 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mParent, nsINavHistoryContainerResultNode);
     227             961 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     228                 : 
     229           52684 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsNavHistoryResultNode)
     230           40295 :   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsINavHistoryResultNode)
     231           31870 :   if (aIID.Equals(NS_GET_IID(nsIClassInfo)))
     232            3885 :     foundInterface = static_cast<nsIClassInfo *>(&mozilla::places::sResultNodeClassInfo);
     233                 :   else
     234           27985 :   NS_INTERFACE_MAP_ENTRY(nsINavHistoryResultNode)
     235           24867 : NS_INTERFACE_MAP_END
     236                 : 
     237           38826 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsNavHistoryResultNode)
     238           38826 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsNavHistoryResultNode)
     239                 : 
     240            4499 : nsNavHistoryResultNode::nsNavHistoryResultNode(
     241                 :     const nsACString& aURI, const nsACString& aTitle, PRUint32 aAccessCount,
     242                 :     PRTime aTime, const nsACString& aIconURI) :
     243                 :   mParent(nsnull),
     244                 :   mURI(aURI),
     245                 :   mTitle(aTitle),
     246                 :   mAreTagsSorted(false),
     247                 :   mAccessCount(aAccessCount),
     248                 :   mTime(aTime),
     249                 :   mFaviconURI(aIconURI),
     250                 :   mBookmarkIndex(-1),
     251                 :   mItemId(-1),
     252                 :   mFolderId(-1),
     253                 :   mDateAdded(0),
     254                 :   mLastModified(0),
     255                 :   mIndentLevel(-1),
     256            4499 :   mFrecency(0)
     257                 : {
     258            4499 :   mTags.SetIsVoid(true);
     259            4499 : }
     260                 : 
     261                 : 
     262                 : NS_IMETHODIMP
     263               0 : nsNavHistoryResultNode::GetIcon(nsACString& aIcon)
     264                 : {
     265               0 :   if (mFaviconURI.IsEmpty()) {
     266               0 :     aIcon.Truncate();
     267               0 :     return NS_OK;
     268                 :   }
     269                 : 
     270               0 :   nsFaviconService* faviconService = nsFaviconService::GetFaviconService();
     271               0 :   NS_ENSURE_TRUE(faviconService, NS_ERROR_OUT_OF_MEMORY);
     272               0 :   faviconService->GetFaviconSpecForIconString(mFaviconURI, aIcon);
     273               0 :   return NS_OK;
     274                 : }
     275                 : 
     276                 : 
     277                 : NS_IMETHODIMP
     278            1642 : nsNavHistoryResultNode::GetParent(nsINavHistoryContainerResultNode** aParent)
     279                 : {
     280            1642 :   NS_IF_ADDREF(*aParent = mParent);
     281            1642 :   return NS_OK;
     282                 : }
     283                 : 
     284                 : 
     285                 : NS_IMETHODIMP
     286              48 : nsNavHistoryResultNode::GetParentResult(nsINavHistoryResult** aResult)
     287                 : {
     288              48 :   *aResult = nsnull;
     289              48 :   if (IsContainer())
     290              48 :     NS_IF_ADDREF(*aResult = GetAsContainer()->mResult);
     291               0 :   else if (mParent)
     292               0 :     NS_IF_ADDREF(*aResult = mParent->mResult);
     293                 : 
     294              48 :   NS_ENSURE_STATE(*aResult);
     295              48 :   return NS_OK;
     296                 : }
     297                 : 
     298                 : 
     299                 : NS_IMETHODIMP
     300             128 : nsNavHistoryResultNode::GetTags(nsAString& aTags) {
     301                 :   // Only URI-nodes may be associated with tags
     302             128 :   if (!IsURI()) {
     303               0 :     aTags.Truncate();
     304               0 :     return NS_OK;
     305                 :   }
     306                 : 
     307                 :   // Initially, the tags string is set to a void string (see constructor).  We
     308                 :   // then build it the first time this method called is called (and by that,
     309                 :   // implicitly unset the void flag). Result observers may re-set the void flag
     310                 :   // in order to force rebuilding of the tags string.
     311             128 :   if (!mTags.IsVoid()) {
     312                 :     // If mTags is assigned by a history query it is unsorted for performance
     313                 :     // reasons, it must be sorted by name on first read access.
     314              36 :     if (!mAreTagsSorted) {
     315               0 :       nsTArray<nsCString> tags;
     316               0 :       ParseString(NS_ConvertUTF16toUTF8(mTags), ',', tags);
     317               0 :       tags.Sort();
     318               0 :       mTags.SetIsVoid(true);
     319               0 :       for (nsTArray<nsCString>::index_type i = 0; i < tags.Length(); ++i) {
     320               0 :         AppendUTF8toUTF16(tags[i], mTags);
     321               0 :         if (i < tags.Length() - 1 )
     322               0 :           mTags.AppendLiteral(", ");
     323                 :       }
     324               0 :       mAreTagsSorted = true;
     325                 :     }
     326              36 :     aTags.Assign(mTags);
     327              36 :     return NS_OK;
     328                 :   }
     329                 : 
     330                 :   // Fetch the tags
     331             184 :   nsRefPtr<Database> DB = Database::GetDatabase();
     332              92 :   NS_ENSURE_STATE(DB);
     333                 :   nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
     334                 :     "/* do not warn (bug 487594) */ "
     335                 :     "SELECT GROUP_CONCAT(tag_title, ', ') "
     336                 :     "FROM ( "
     337                 :       "SELECT t.title AS tag_title "
     338                 :       "FROM moz_bookmarks b "
     339                 :       "JOIN moz_bookmarks t ON t.id = +b.parent "
     340                 :       "WHERE b.fk = (SELECT id FROM moz_places WHERE url = :page_url) "
     341                 :         "AND t.parent = :tags_folder "
     342                 :       "ORDER BY t.title COLLATE NOCASE ASC "
     343                 :     ") "
     344             184 :   );
     345              92 :   NS_ENSURE_STATE(stmt);
     346             184 :   mozStorageStatementScoper scoper(stmt);
     347                 : 
     348              92 :   nsNavHistory* history = nsNavHistory::GetHistoryService();
     349              92 :   NS_ENSURE_STATE(history);
     350             184 :   nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("tags_folder"),
     351              92 :                                       history->GetTagsFolder());
     352              92 :   NS_ENSURE_SUCCESS(rv, rv);
     353              92 :   rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), mURI);
     354              92 :   NS_ENSURE_SUCCESS(rv, rv);
     355                 : 
     356              92 :   bool hasTags = false;
     357              92 :   if (NS_SUCCEEDED(stmt->ExecuteStep(&hasTags)) && hasTags) {
     358              92 :     rv = stmt->GetString(0, mTags);
     359              92 :     NS_ENSURE_SUCCESS(rv, rv);
     360              92 :     aTags.Assign(mTags);
     361              92 :     mAreTagsSorted = true;
     362                 :   }
     363                 : 
     364                 :   // If this node is a child of a history query, we need to make sure changes
     365                 :   // to tags are properly live-updated.
     366             146 :   if (mParent && mParent->IsQuery() &&
     367              54 :       mParent->mOptions->QueryType() == nsINavHistoryQueryOptions::QUERY_TYPE_HISTORY) {
     368              54 :     nsNavHistoryQueryResultNode* query = mParent->GetAsQuery();
     369              54 :     nsNavHistoryResult* result = query->GetResult();
     370              54 :     NS_ENSURE_STATE(result);
     371              54 :     result->AddAllBookmarksObserver(query);
     372                 :   }
     373                 : 
     374              92 :   return NS_OK;
     375                 : }
     376                 : 
     377                 : 
     378                 : void
     379            3572 : nsNavHistoryResultNode::OnRemoving()
     380                 : {
     381            3572 :   mParent = nsnull;
     382            3572 : }
     383                 : 
     384                 : 
     385                 : /**
     386                 :  * This will find the result for this node.  We can ask the nearest container
     387                 :  * for this value (either ourselves or our parents should be a container,
     388                 :  * and all containers have result pointers).
     389                 :  */
     390                 : nsNavHistoryResult*
     391            8506 : nsNavHistoryResultNode::GetResult()
     392                 : {
     393            8506 :   nsNavHistoryResultNode* node = this;
     394             169 :   do {
     395            8675 :     if (node->IsContainer()) {
     396            8506 :       nsNavHistoryContainerResultNode* container = TO_CONTAINER(node);
     397            8506 :       NS_ASSERTION(container->mResult, "Containers must have valid results");
     398            8506 :       return container->mResult;
     399                 :     }
     400             169 :     node = node->mParent;
     401                 :   } while (node);
     402               0 :   NS_NOTREACHED("No container node found in hierarchy!");
     403               0 :   return nsnull;
     404                 : }
     405                 : 
     406                 : 
     407                 : /**
     408                 :  * Searches up the tree for the closest ancestor node that has an options
     409                 :  * structure.  This will tell us the options that were used to generate this
     410                 :  * node.
     411                 :  *
     412                 :  * Be careful, this function walks up the tree, so it can not be used when
     413                 :  * result nodes are created because they have no parent.  Only call this
     414                 :  * function after the tree has been built.
     415                 :  */
     416                 : nsNavHistoryQueryOptions*
     417            2257 : nsNavHistoryResultNode::GetGeneratingOptions()
     418                 : {
     419            2257 :   if (!mParent) {
     420                 :     // When we have no parent, it either means we haven't built the tree yet,
     421                 :     // in which case calling this function is a bug, or this node is the root
     422                 :     // of the tree.  When we are the root of the tree, our own options are the
     423                 :     // generating options.
     424            1467 :     if (IsContainer())
     425            1467 :       return GetAsContainer()->mOptions;
     426                 : 
     427               0 :     NS_NOTREACHED("Can't find a generating node for this container, perhaps FillStats has not been called on this tree yet?");
     428               0 :     return nsnull;
     429                 :   }
     430                 : 
     431                 :   // Look up the tree.  We want the options that were used to create this node,
     432                 :   // and since it has a parent, it's the options of an ancestor, not of the node
     433                 :   // itself.  So start at the parent.
     434             790 :   nsNavHistoryContainerResultNode* cur = mParent;
     435            1580 :   while (cur) {
     436             790 :     if (cur->IsContainer())
     437             790 :       return cur->GetAsContainer()->mOptions;
     438               0 :     cur = cur->mParent;
     439                 :   }
     440                 : 
     441                 :   // We should always find a container node as an ancestor.
     442               0 :   NS_NOTREACHED("Can't find a generating node for this container, the tree seemes corrupted.");
     443               0 :   return nsnull;
     444                 : }
     445                 : 
     446                 : 
     447            4678 : NS_IMPL_ISUPPORTS_INHERITED1(nsNavHistoryVisitResultNode,
     448                 :                              nsNavHistoryResultNode,
     449                 :                              nsINavHistoryVisitResultNode)
     450                 : 
     451             191 : nsNavHistoryVisitResultNode::nsNavHistoryVisitResultNode(
     452                 :     const nsACString& aURI, const nsACString& aTitle, PRUint32 aAccessCount,
     453                 :     PRTime aTime, const nsACString& aIconURI, PRInt64 aSession) :
     454                 :   nsNavHistoryResultNode(aURI, aTitle, aAccessCount, aTime, aIconURI),
     455             191 :   mSessionId(aSession)
     456                 : {
     457             191 : }
     458                 : 
     459                 : 
     460               0 : NS_IMPL_ISUPPORTS_INHERITED1(nsNavHistoryFullVisitResultNode,
     461                 :                              nsNavHistoryVisitResultNode,
     462                 :                              nsINavHistoryFullVisitResultNode)
     463                 : 
     464               0 : nsNavHistoryFullVisitResultNode::nsNavHistoryFullVisitResultNode(
     465                 :     const nsACString& aURI, const nsACString& aTitle, PRUint32 aAccessCount,
     466                 :     PRTime aTime, const nsACString& aIconURI, PRInt64 aSession,
     467                 :     PRInt64 aVisitId, PRInt64 aReferringVisitId, PRInt32 aTransitionType) :
     468                 :   nsNavHistoryVisitResultNode(aURI, aTitle, aAccessCount, aTime, aIconURI,
     469                 :                               aSession),
     470                 :   mVisitId(aVisitId),
     471                 :   mReferringVisitId(aReferringVisitId),
     472               0 :   mTransitionType(aTransitionType)
     473                 : {
     474               0 : }
     475                 : 
     476                 : 
     477            1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsNavHistoryContainerResultNode)
     478                 : 
     479             889 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsNavHistoryContainerResultNode, nsNavHistoryResultNode)
     480             889 :   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mResult)
     481             889 :   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mChildren)
     482             889 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END 
     483                 : 
     484             951 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsNavHistoryContainerResultNode, nsNavHistoryResultNode)
     485             951 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mResult, nsINavHistoryResult)
     486             951 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mChildren)
     487             951 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     488                 : 
     489           26959 : NS_IMPL_ADDREF_INHERITED(nsNavHistoryContainerResultNode, nsNavHistoryResultNode)
     490           26959 : NS_IMPL_RELEASE_INHERITED(nsNavHistoryContainerResultNode, nsNavHistoryResultNode)
     491                 : 
     492           47249 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsNavHistoryContainerResultNode)
     493           33489 :   NS_INTERFACE_MAP_STATIC_AMBIGUOUS(nsNavHistoryContainerResultNode)
     494           33489 :   NS_INTERFACE_MAP_ENTRY(nsINavHistoryContainerResultNode)
     495           30297 : NS_INTERFACE_MAP_END_INHERITING(nsNavHistoryResultNode)
     496                 : 
     497            1532 : nsNavHistoryContainerResultNode::nsNavHistoryContainerResultNode(
     498                 :     const nsACString& aURI, const nsACString& aTitle,
     499                 :     const nsACString& aIconURI, PRUint32 aContainerType, bool aReadOnly,
     500                 :     nsNavHistoryQueryOptions* aOptions) :
     501                 :   nsNavHistoryResultNode(aURI, aTitle, 0, 0, aIconURI),
     502                 :   mResult(nsnull),
     503                 :   mContainerType(aContainerType),
     504                 :   mExpanded(false),
     505                 :   mChildrenReadOnly(aReadOnly),
     506                 :   mOptions(aOptions),
     507            1532 :   mAsyncCanceledState(NOT_CANCELED)
     508                 : {
     509            1532 : }
     510                 : 
     511             768 : nsNavHistoryContainerResultNode::nsNavHistoryContainerResultNode(
     512                 :     const nsACString& aURI, const nsACString& aTitle,
     513                 :     PRTime aTime,
     514                 :     const nsACString& aIconURI, PRUint32 aContainerType, bool aReadOnly,
     515                 :     nsNavHistoryQueryOptions* aOptions) :
     516                 :   nsNavHistoryResultNode(aURI, aTitle, 0, aTime, aIconURI),
     517                 :   mResult(nsnull),
     518                 :   mContainerType(aContainerType),
     519                 :   mExpanded(false),
     520                 :   mChildrenReadOnly(aReadOnly),
     521                 :   mOptions(aOptions),
     522             768 :   mAsyncCanceledState(NOT_CANCELED)
     523                 : {
     524             768 : }
     525                 : 
     526                 : 
     527            4600 : nsNavHistoryContainerResultNode::~nsNavHistoryContainerResultNode()
     528                 : {
     529                 :   // Explicitly clean up array of children of this container.  We must ensure
     530                 :   // all references are gone and all of their destructors are called.
     531            2300 :   mChildren.Clear();
     532            4600 : }
     533                 : 
     534                 : 
     535                 : /**
     536                 :  * Containers should notify their children that they are being removed when the
     537                 :  * container is being removed.
     538                 :  */
     539                 : void
     540               0 : nsNavHistoryContainerResultNode::OnRemoving()
     541                 : {
     542               0 :   nsNavHistoryResultNode::OnRemoving();
     543               0 :   for (PRInt32 i = 0; i < mChildren.Count(); ++i)
     544               0 :     mChildren[i]->OnRemoving();
     545               0 :   mChildren.Clear();
     546               0 : }
     547                 : 
     548                 : 
     549                 : bool
     550             759 : nsNavHistoryContainerResultNode::AreChildrenVisible()
     551                 : {
     552             759 :   nsNavHistoryResult* result = GetResult();
     553             759 :   if (!result) {
     554               0 :     NS_NOTREACHED("Invalid result");
     555               0 :     return false;
     556                 :   }
     557                 : 
     558             759 :   if (!mExpanded)
     559               2 :     return false;
     560                 : 
     561                 :   // Now check if any ancestor is closed.
     562             757 :   nsNavHistoryContainerResultNode* ancestor = mParent;
     563            1547 :   while (ancestor) {
     564              33 :     if (!ancestor->mExpanded)
     565               0 :       return false;
     566                 : 
     567              33 :     ancestor = ancestor->mParent;
     568                 :   }
     569                 : 
     570             757 :   return true;
     571                 : }
     572                 : 
     573                 : 
     574                 : NS_IMETHODIMP
     575             627 : nsNavHistoryContainerResultNode::GetContainerOpen(bool *aContainerOpen)
     576                 : {
     577             627 :   *aContainerOpen = mExpanded;
     578             627 :   return NS_OK;
     579                 : }
     580                 : 
     581                 : 
     582                 : NS_IMETHODIMP
     583            3234 : nsNavHistoryContainerResultNode::SetContainerOpen(bool aContainerOpen)
     584                 : {
     585            3234 :   if (aContainerOpen) {
     586            1639 :     if (!mExpanded) {
     587            1635 :       nsNavHistoryQueryOptions* options = GetGeneratingOptions();
     588            1635 :       if (options && options->AsyncEnabled())
     589               4 :         OpenContainerAsync();
     590                 :       else
     591            1631 :         OpenContainer();
     592                 :     }
     593                 :   }
     594                 :   else {
     595            1595 :     if (mExpanded)
     596            1593 :       CloseContainer();
     597               2 :     else if (mAsyncPendingStmt)
     598               1 :       CancelAsyncOpen(false);
     599                 :   }
     600                 : 
     601            3234 :   return NS_OK;
     602                 : }
     603                 : 
     604                 : 
     605                 : /**
     606                 :  * Notifies the result's observers of a change in the container's state.  The
     607                 :  * notification includes both the old and new states:  The old is aOldState, and
     608                 :  * the new is the container's current state.
     609                 :  *
     610                 :  * @param aOldState
     611                 :  *        The state being transitioned out of.
     612                 :  */
     613                 : nsresult
     614            3227 : nsNavHistoryContainerResultNode::NotifyOnStateChange(PRUint16 aOldState)
     615                 : {
     616            3227 :   nsNavHistoryResult* result = GetResult();
     617            3227 :   NS_ENSURE_STATE(result);
     618                 : 
     619                 :   nsresult rv;
     620                 :   PRUint16 currState;
     621            3227 :   rv = GetState(&currState);
     622            3227 :   NS_ENSURE_SUCCESS(rv, rv);
     623                 : 
     624                 :   // Notify via the new ContainerStateChanged observer method.
     625            3227 :   NOTIFY_RESULT_OBSERVERS(result,
     626                 :                           ContainerStateChanged(this, aOldState, currState));
     627            3227 :   return NS_OK;
     628                 : }
     629                 : 
     630                 : 
     631                 : NS_IMETHODIMP
     632            4839 : nsNavHistoryContainerResultNode::GetState(PRUint16* _state)
     633                 : {
     634            4839 :   NS_ENSURE_ARG_POINTER(_state);
     635                 : 
     636                 :   *_state = mExpanded ? (PRUint16)STATE_OPENED
     637                 :                       : mAsyncPendingStmt ? (PRUint16)STATE_LOADING
     638            4839 :                                           : (PRUint16)STATE_CLOSED;
     639                 : 
     640            4839 :   return NS_OK;
     641                 : }
     642                 : 
     643                 : 
     644                 : /**
     645                 :  * This handles the generic container case.  Other container types should
     646                 :  * override this to do their own handling.
     647                 :  */
     648                 : nsresult
     649               0 : nsNavHistoryContainerResultNode::OpenContainer()
     650                 : {
     651               0 :   NS_ASSERTION(!mExpanded, "Container must not be expanded to open it");
     652               0 :   mExpanded = true;
     653                 : 
     654               0 :   nsresult rv = NotifyOnStateChange(STATE_CLOSED);
     655               0 :   NS_ENSURE_SUCCESS(rv, rv);
     656                 : 
     657               0 :   return NS_OK;
     658                 : }
     659                 : 
     660                 : 
     661                 : /**
     662                 :  * Unset aSuppressNotifications to notify observers on this change.  That is
     663                 :  * the normal operation.  This is set to false for the recursive calls since the
     664                 :  * root container that is being closed will handle recomputation of the visible
     665                 :  * elements for its entire subtree.
     666                 :  */
     667                 : nsresult
     668            1602 : nsNavHistoryContainerResultNode::CloseContainer(bool aSuppressNotifications)
     669                 : {
     670            1602 :   NS_ASSERTION((mExpanded && !mAsyncPendingStmt) ||
     671                 :                (!mExpanded && mAsyncPendingStmt),
     672                 :                "Container must be expanded or loading to close it");
     673                 : 
     674                 :   nsresult rv;
     675                 :   PRUint16 oldState;
     676            1602 :   rv = GetState(&oldState);
     677            1602 :   NS_ENSURE_SUCCESS(rv, rv);
     678                 : 
     679            1602 :   if (mExpanded) {
     680                 :     // Recursively close all child containers.
     681            4859 :     for (PRInt32 i = 0; i < mChildren.Count(); ++i) {
     682            4539 :       if (mChildren[i]->IsContainer() &&
     683            1281 :           mChildren[i]->GetAsContainer()->mExpanded)
     684               8 :         mChildren[i]->GetAsContainer()->CloseContainer(true);
     685                 :     }
     686                 : 
     687            1601 :     mExpanded = false;
     688                 :   }
     689                 : 
     690                 :   // Be sure to set this to null before notifying observers.  It signifies that
     691                 :   // the container is no longer loading (if it was in the first place).
     692            1602 :   mAsyncPendingStmt = nsnull;
     693                 : 
     694            1602 :   if (!aSuppressNotifications) {
     695            1594 :     rv = NotifyOnStateChange(oldState);
     696            1594 :     NS_ENSURE_SUCCESS(rv, rv);
     697                 :   }
     698                 : 
     699                 :   // If this is the root container of a result, we can tell the result to stop
     700                 :   // observing changes, otherwise the result will stay in memory and updates
     701                 :   // itself till it is cycle collected.
     702            1602 :   nsNavHistoryResult* result = GetResult();
     703            1602 :   NS_ENSURE_STATE(result);
     704            1602 :   if (result->mRootNode == this) {
     705            1006 :     result->StopObserving();
     706                 :     // When reopening this node its result will be out of sync.
     707                 :     // We must clear our children to ensure we will call FillChildren
     708                 :     // again in such a case.
     709            1006 :     if (this->IsQuery())
     710             628 :       this->GetAsQuery()->ClearChildren(true);
     711             378 :     else if (this->IsFolder())
     712             378 :       this->GetAsFolder()->ClearChildren(true);
     713                 :   }
     714                 : 
     715            1602 :   return NS_OK;
     716                 : }
     717                 : 
     718                 : 
     719                 : /**
     720                 :  * The async version of OpenContainer.
     721                 :  */
     722                 : nsresult
     723               0 : nsNavHistoryContainerResultNode::OpenContainerAsync()
     724                 : {
     725               0 :   return NS_ERROR_NOT_IMPLEMENTED;
     726                 : }
     727                 : 
     728                 : 
     729                 : /**
     730                 :  * Cancels the pending asynchronous Storage execution triggered by
     731                 :  * FillChildrenAsync, if it exists.  This method doesn't do much, because after
     732                 :  * cancelation Storage will call this node's HandleCompletion callback, where
     733                 :  * the real work is done.
     734                 :  *
     735                 :  * @param aRestart
     736                 :  *        If true, async execution will be restarted by HandleCompletion.
     737                 :  */
     738                 : void
     739               1 : nsNavHistoryContainerResultNode::CancelAsyncOpen(bool aRestart)
     740                 : {
     741               1 :   NS_ASSERTION(mAsyncPendingStmt, "Async execution canceled but not pending");
     742                 : 
     743               1 :   mAsyncCanceledState = aRestart ? CANCELED_RESTART_NEEDED : CANCELED;
     744                 : 
     745                 :   // Cancel will fail if the pending statement has already been canceled.
     746                 :   // That's OK since this method may be called multiple times, and multiple
     747                 :   // cancels don't harm anything.
     748               1 :   (void)mAsyncPendingStmt->Cancel();
     749               1 : }
     750                 : 
     751                 : 
     752                 : /**
     753                 :  * This builds up tree statistics from the bottom up.  Call with a container
     754                 :  * and the indent level of that container.  To init the full tree, call with
     755                 :  * the root container.  The default indent level is -1, which is appropriate
     756                 :  * for the root level.
     757                 :  *
     758                 :  * CALL THIS AFTER FILLING ANY CONTAINER to update the parent and result node
     759                 :  * pointers, even if you don't care about visit counts and last visit dates.
     760                 :  */
     761                 : void
     762            3957 : nsNavHistoryContainerResultNode::FillStats()
     763                 : {
     764            3957 :   PRUint32 accessCount = 0;
     765            3957 :   PRTime newTime = 0;
     766                 : 
     767            7396 :   for (PRInt32 i = 0; i < mChildren.Count(); ++i) {
     768            3439 :     nsNavHistoryResultNode* node = mChildren[i];
     769            3439 :     node->mParent = this;
     770            3439 :     node->mIndentLevel = mIndentLevel + 1;
     771            3439 :     if (node->IsContainer()) {
     772            1366 :       nsNavHistoryContainerResultNode* container = node->GetAsContainer();
     773            1366 :       container->mResult = mResult;
     774            1366 :       container->FillStats();
     775                 :     }
     776            3439 :     accessCount += node->mAccessCount;
     777                 :     // this is how container nodes get sorted by date
     778                 :     // The container gets the most recent time of the child nodes.
     779            3439 :     if (node->mTime > newTime)
     780            1003 :       newTime = node->mTime;
     781                 :   }
     782                 : 
     783            3957 :   if (mExpanded) {
     784             889 :     mAccessCount = accessCount;
     785             889 :     if (!IsQuery() || newTime > mTime)
     786             402 :       mTime = newTime;
     787                 :   }
     788            3957 : }
     789                 : 
     790                 : 
     791                 : /**
     792                 :  * This is used when one container changes to do a minimal update of the tree
     793                 :  * structure.  When something changes, you want to call FillStats if necessary
     794                 :  * and update this container completely.  Then call this function which will
     795                 :  * walk up the tree and fill in the previous containers.
     796                 :  *
     797                 :  * Note that you have to tell us by how much our access count changed.  Our
     798                 :  * access count should already be set to the new value; this is used tochange
     799                 :  * the parents without having to re-count all their children.
     800                 :  *
     801                 :  * This does NOT update the last visit date downward.  Therefore, if you are
     802                 :  * deleting a node that has the most recent last visit date, the parents will
     803                 :  * not get their last visit dates downshifted accordingly.  This is a rather
     804                 :  * unusual case: we don't often delete things, and we usually don't even show
     805                 :  * the last visit date for folders.  Updating would be slower because we would
     806                 :  * have to recompute it from scratch.
     807                 :  */
     808                 : nsresult
     809             375 : nsNavHistoryContainerResultNode::ReverseUpdateStats(PRInt32 aAccessCountChange)
     810                 : {
     811             375 :   if (mParent) {
     812              29 :     nsNavHistoryResult* result = GetResult();
     813              29 :     bool shouldNotify = result && mParent->mParent &&
     814              58 :                           mParent->mParent->AreChildrenVisible();
     815                 : 
     816              29 :     mParent->mAccessCount += aAccessCountChange;
     817              29 :     bool timeChanged = false;
     818              29 :     if (mTime > mParent->mTime) {
     819               1 :       timeChanged = true;
     820               1 :       mParent->mTime = mTime;
     821                 :     }
     822                 : 
     823              29 :     if (shouldNotify) {
     824               0 :       NOTIFY_RESULT_OBSERVERS(result,
     825                 :                               NodeHistoryDetailsChanged(TO_ICONTAINER(mParent),
     826                 :                                                         mParent->mTime,
     827                 :                                                         mParent->mAccessCount));
     828                 :     }
     829                 : 
     830                 :     // check sorting, the stats may have caused this node to move if the
     831                 :     // sorting depended on something we are changing.
     832              29 :     PRUint16 sortMode = mParent->GetSortType();
     833                 :     bool sortingByVisitCount =
     834                 :       sortMode == nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_ASCENDING ||
     835              29 :       sortMode == nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_DESCENDING;
     836                 :     bool sortingByTime =
     837                 :       sortMode == nsINavHistoryQueryOptions::SORT_BY_DATE_ASCENDING ||
     838              29 :       sortMode == nsINavHistoryQueryOptions::SORT_BY_DATE_DESCENDING;
     839                 : 
     840              29 :     if ((sortingByVisitCount && aAccessCountChange != 0) ||
     841                 :         (sortingByTime && timeChanged)) {
     842               0 :       PRUint32 ourIndex = mParent->FindChild(this);
     843               0 :       EnsureItemPosition(ourIndex);
     844                 :     }
     845                 : 
     846              29 :     nsresult rv = mParent->ReverseUpdateStats(aAccessCountChange);
     847              29 :     NS_ENSURE_SUCCESS(rv, rv);
     848                 :   }
     849                 : 
     850             375 :   return NS_OK;
     851                 : }
     852                 : 
     853                 : 
     854                 : /**
     855                 :  * This walks up the tree until we find a query result node or the root to get
     856                 :  * the sorting type.
     857                 :  */
     858                 : PRUint16
     859            1661 : nsNavHistoryContainerResultNode::GetSortType()
     860                 : {
     861            1661 :   if (mParent)
     862             418 :     return mParent->GetSortType();
     863            1243 :   if (mResult)
     864            1243 :     return mResult->mSortingMode;
     865                 : 
     866               0 :   NS_NOTREACHED("We should always have a result");
     867               0 :   return nsINavHistoryQueryOptions::SORT_BY_NONE;
     868                 : }
     869                 : 
     870                 : 
     871               0 : nsresult nsNavHistoryContainerResultNode::Refresh() {
     872               0 :   NS_WARNING("Refresh() is supported by queries or folders, not generic containers.");
     873               0 :   return NS_OK;
     874                 : }
     875                 : 
     876                 : void
     877            1509 : nsNavHistoryContainerResultNode::GetSortingAnnotation(nsACString& aAnnotation)
     878                 : {
     879            1509 :   if (mParent)
     880             418 :     mParent->GetSortingAnnotation(aAnnotation);
     881            1091 :   else if (mResult)
     882            1091 :     aAnnotation.Assign(mResult->mSortingAnnotation);
     883                 :   else
     884               0 :     NS_NOTREACHED("We should always have a result");
     885            1509 : }
     886                 : 
     887                 : /**
     888                 :  * @return the sorting comparator function for the give sort type, or null if
     889                 :  * there is no comparator.
     890                 :  */
     891                 : nsNavHistoryContainerResultNode::SortComparator
     892            1839 : nsNavHistoryContainerResultNode::GetSortingComparator(PRUint16 aSortType)
     893                 : {
     894            1839 :   switch (aSortType)
     895                 :   {
     896                 :     case nsINavHistoryQueryOptions::SORT_BY_NONE:
     897            1394 :       return &SortComparison_Bookmark;
     898                 :     case nsINavHistoryQueryOptions::SORT_BY_TITLE_ASCENDING:
     899              92 :       return &SortComparison_TitleLess;
     900                 :     case nsINavHistoryQueryOptions::SORT_BY_TITLE_DESCENDING:
     901              65 :       return &SortComparison_TitleGreater;
     902                 :     case nsINavHistoryQueryOptions::SORT_BY_DATE_ASCENDING:
     903              73 :       return &SortComparison_DateLess;
     904                 :     case nsINavHistoryQueryOptions::SORT_BY_DATE_DESCENDING:
     905             111 :       return &SortComparison_DateGreater;
     906                 :     case nsINavHistoryQueryOptions::SORT_BY_URI_ASCENDING:
     907              14 :       return &SortComparison_URILess;
     908                 :     case nsINavHistoryQueryOptions::SORT_BY_URI_DESCENDING:
     909               1 :       return &SortComparison_URIGreater;
     910                 :     case nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_ASCENDING:
     911               8 :       return &SortComparison_VisitCountLess;
     912                 :     case nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_DESCENDING:
     913              28 :       return &SortComparison_VisitCountGreater;
     914                 :     case nsINavHistoryQueryOptions::SORT_BY_KEYWORD_ASCENDING:
     915               6 :       return &SortComparison_KeywordLess;
     916                 :     case nsINavHistoryQueryOptions::SORT_BY_KEYWORD_DESCENDING:
     917               1 :       return &SortComparison_KeywordGreater;
     918                 :     case nsINavHistoryQueryOptions::SORT_BY_ANNOTATION_ASCENDING:
     919               4 :       return &SortComparison_AnnotationLess;
     920                 :     case nsINavHistoryQueryOptions::SORT_BY_ANNOTATION_DESCENDING:
     921               6 :       return &SortComparison_AnnotationGreater;
     922                 :     case nsINavHistoryQueryOptions::SORT_BY_DATEADDED_ASCENDING:
     923              11 :       return &SortComparison_DateAddedLess;
     924                 :     case nsINavHistoryQueryOptions::SORT_BY_DATEADDED_DESCENDING:
     925              13 :       return &SortComparison_DateAddedGreater;
     926                 :     case nsINavHistoryQueryOptions::SORT_BY_LASTMODIFIED_ASCENDING:
     927               3 :       return &SortComparison_LastModifiedLess;
     928                 :     case nsINavHistoryQueryOptions::SORT_BY_LASTMODIFIED_DESCENDING:
     929               3 :       return &SortComparison_LastModifiedGreater;
     930                 :     case nsINavHistoryQueryOptions::SORT_BY_TAGS_ASCENDING:
     931               1 :       return &SortComparison_TagsLess;
     932                 :     case nsINavHistoryQueryOptions::SORT_BY_TAGS_DESCENDING:
     933               1 :       return &SortComparison_TagsGreater;
     934                 :     case nsINavHistoryQueryOptions::SORT_BY_FRECENCY_ASCENDING:
     935               2 :       return &SortComparison_FrecencyLess;
     936                 :     case nsINavHistoryQueryOptions::SORT_BY_FRECENCY_DESCENDING:
     937               2 :       return &SortComparison_FrecencyGreater;
     938                 :     default:
     939               0 :       NS_NOTREACHED("Bad sorting type");
     940               0 :       return nsnull;
     941                 :   }
     942                 : }
     943                 : 
     944                 : 
     945                 : /**
     946                 :  * This is used by Result::SetSortingMode and QueryResultNode::FillChildren to
     947                 :  * sort the child list.
     948                 :  *
     949                 :  * This does NOT update any visibility or tree information.  The caller will
     950                 :  * have to completely rebuild the visible list after this.
     951                 :  */
     952                 : void
     953            1394 : nsNavHistoryContainerResultNode::RecursiveSort(
     954                 :     const char* aData, SortComparator aComparator)
     955                 : {
     956            1394 :   void* data = const_cast<void*>(static_cast<const void*>(aData));
     957                 : 
     958            1394 :   mChildren.Sort(aComparator, data);
     959            2664 :   for (PRInt32 i = 0; i < mChildren.Count(); ++i) {
     960            1270 :     if (mChildren[i]->IsContainer())
     961             780 :       mChildren[i]->GetAsContainer()->RecursiveSort(aData, aComparator);
     962                 :   }
     963            1394 : }
     964                 : 
     965                 : 
     966                 : /**
     967                 :  * @return the index that the given item would fall on if it were to be
     968                 :  * inserted using the given sorting.
     969                 :  */
     970                 : PRUint32
     971              34 : nsNavHistoryContainerResultNode::FindInsertionPoint(
     972                 :     nsNavHistoryResultNode* aNode, SortComparator aComparator,
     973                 :     const char* aData, bool* aItemExists)
     974                 : {
     975              34 :   if (aItemExists)
     976              29 :     (*aItemExists) = false;
     977                 : 
     978              34 :   if (mChildren.Count() == 0)
     979               0 :     return 0;
     980                 : 
     981              34 :   void* data = const_cast<void*>(static_cast<const void*>(aData));
     982                 : 
     983                 :   // The common case is the beginning or the end because this is used to insert
     984                 :   // new items that are added to history, which is usually sorted by date.
     985                 :   PRInt32 res;
     986              34 :   res = aComparator(aNode, mChildren[0], data);
     987              34 :   if (res <= 0) {
     988               7 :     if (aItemExists && res == 0)
     989               1 :       (*aItemExists) = true;
     990               7 :     return 0;
     991                 :   }
     992              27 :   res = aComparator(aNode, mChildren[mChildren.Count() - 1], data);
     993              27 :   if (res >= 0) {
     994              11 :     if (aItemExists && res == 0)
     995               3 :       (*aItemExists) = true;
     996              11 :     return mChildren.Count();
     997                 :   }
     998                 : 
     999              16 :   PRUint32 beginRange = 0; // inclusive
    1000              16 :   PRUint32 endRange = mChildren.Count(); // exclusive
    1001              51 :   while (1) {
    1002              67 :     if (beginRange == endRange)
    1003              16 :       return endRange;
    1004              51 :     PRUint32 center = beginRange + (endRange - beginRange) / 2;
    1005              51 :     PRInt32 res = aComparator(aNode, mChildren[center], data);
    1006              51 :     if (res <= 0) {
    1007              29 :       endRange = center; // left side
    1008              29 :       if (aItemExists && res == 0)
    1009               2 :         (*aItemExists) = true;
    1010                 :     }
    1011                 :     else {
    1012              22 :       beginRange = center + 1; // right site
    1013                 :     }
    1014                 :   }
    1015                 : }
    1016                 : 
    1017                 : 
    1018                 : /**
    1019                 :  * This checks the child node at the given index to see if its sorting is
    1020                 :  * correct.  This is called when nodes are updated and we need to see whether
    1021                 :  * we need to move it.
    1022                 :  *
    1023                 :  * @returns true if not and it should be resorted.
    1024                 : */
    1025                 : bool
    1026             323 : nsNavHistoryContainerResultNode::DoesChildNeedResorting(PRUint32 aIndex,
    1027                 :     SortComparator aComparator, const char* aData)
    1028                 : {
    1029             323 :   NS_ASSERTION(aIndex >= 0 && aIndex < PRUint32(mChildren.Count()),
    1030                 :                "Input index out of range");
    1031             323 :   if (mChildren.Count() == 1)
    1032             129 :     return false;
    1033                 : 
    1034             194 :   void* data = const_cast<void*>(static_cast<const void*>(aData));
    1035                 : 
    1036             194 :   if (aIndex > 0) {
    1037                 :     // compare to previous item
    1038             169 :     if (aComparator(mChildren[aIndex - 1], mChildren[aIndex], data) > 0)
    1039               3 :       return true;
    1040                 :   }
    1041             191 :   if (aIndex < PRUint32(mChildren.Count()) - 1) {
    1042                 :     // compare to next item
    1043              34 :     if (aComparator(mChildren[aIndex], mChildren[aIndex + 1], data) > 0)
    1044               2 :       return true;
    1045                 :   }
    1046             189 :   return false;
    1047                 : }
    1048                 : 
    1049                 : 
    1050                 : /* static */
    1051             500 : PRInt32 nsNavHistoryContainerResultNode::SortComparison_StringLess(
    1052                 :     const nsAString& a, const nsAString& b) {
    1053                 : 
    1054             500 :   nsNavHistory* history = nsNavHistory::GetHistoryService();
    1055             500 :   NS_ENSURE_TRUE(history, 0);
    1056             500 :   nsICollation* collation = history->GetCollation();
    1057             500 :   NS_ENSURE_TRUE(collation, 0);
    1058                 : 
    1059             500 :   PRInt32 res = 0;
    1060             500 :   collation->CompareString(nsICollation::kCollationCaseInSensitive, a, b, &res);
    1061             500 :   return res;
    1062                 : }
    1063                 : 
    1064                 : 
    1065                 : /**
    1066                 :  * When there are bookmark indices, we should never have ties, so we don't
    1067                 :  * need to worry about tiebreaking.  When there are no bookmark indices,
    1068                 :  * everything will be -1 and we don't worry about sorting.
    1069                 :  */
    1070            1210 : PRInt32 nsNavHistoryContainerResultNode::SortComparison_Bookmark(
    1071                 :     nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure)
    1072                 : {
    1073            1210 :   return a->mBookmarkIndex - b->mBookmarkIndex;
    1074                 : }
    1075                 : 
    1076                 : /**
    1077                 :  * These are a little more complicated because they do a localization
    1078                 :  * conversion.  If this is too slow, we can compute the sort keys once in
    1079                 :  * advance, sort that array, and then reorder the real array accordingly.
    1080                 :  * This would save some key generations.
    1081                 :  *
    1082                 :  * The collation object must be allocated before sorting on title!
    1083                 :  */
    1084             379 : PRInt32 nsNavHistoryContainerResultNode::SortComparison_TitleLess(
    1085                 :     nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure)
    1086                 : {
    1087                 :   PRUint32 aType;
    1088             379 :   a->GetType(&aType);
    1089                 : 
    1090             379 :   PRInt32 value = SortComparison_StringLess(NS_ConvertUTF8toUTF16(a->mTitle),
    1091             758 :                                             NS_ConvertUTF8toUTF16(b->mTitle));
    1092             379 :   if (value == 0) {
    1093                 :     // resolve by URI
    1094              79 :     if (a->IsURI()) {
    1095              79 :       value = a->mURI.Compare(b->mURI.get());
    1096                 :     }
    1097              79 :     if (value == 0) {
    1098                 :       // resolve by date
    1099               0 :       value = ComparePRTime(a->mTime, b->mTime);
    1100               0 :       if (value == 0)
    1101               0 :         value = nsNavHistoryContainerResultNode::SortComparison_Bookmark(a, b, closure);
    1102                 :     }
    1103                 :   }
    1104             379 :   return value;
    1105                 : }
    1106             145 : PRInt32 nsNavHistoryContainerResultNode::SortComparison_TitleGreater(
    1107                 :     nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure)
    1108                 : {
    1109             145 :   return -SortComparison_TitleLess(a, b, closure);
    1110                 : }
    1111                 : 
    1112                 : /**
    1113                 :  * Equal times will be very unusual, but it is important that there is some
    1114                 :  * deterministic ordering of the results so they don't move around.
    1115                 :  */
    1116             921 : PRInt32 nsNavHistoryContainerResultNode::SortComparison_DateLess(
    1117                 :     nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure)
    1118                 : {
    1119             921 :   PRInt32 value = ComparePRTime(a->mTime, b->mTime);
    1120             921 :   if (value == 0) {
    1121              22 :     value = SortComparison_StringLess(NS_ConvertUTF8toUTF16(a->mTitle),
    1122              44 :                                       NS_ConvertUTF8toUTF16(b->mTitle));
    1123              22 :     if (value == 0)
    1124               9 :       value = nsNavHistoryContainerResultNode::SortComparison_Bookmark(a, b, closure);
    1125                 :   }
    1126             921 :   return value;
    1127                 : }
    1128             813 : PRInt32 nsNavHistoryContainerResultNode::SortComparison_DateGreater(
    1129                 :     nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure)
    1130                 : {
    1131             813 :   return -nsNavHistoryContainerResultNode::SortComparison_DateLess(a, b, closure);
    1132                 : }
    1133                 : 
    1134                 : 
    1135              34 : PRInt32 nsNavHistoryContainerResultNode::SortComparison_DateAddedLess(
    1136                 :     nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure)
    1137                 : {
    1138              34 :   PRInt32 value = ComparePRTime(a->mDateAdded, b->mDateAdded);
    1139              34 :   if (value == 0) {
    1140              14 :     value = SortComparison_StringLess(NS_ConvertUTF8toUTF16(a->mTitle),
    1141              28 :                                       NS_ConvertUTF8toUTF16(b->mTitle));
    1142              14 :     if (value == 0)
    1143               0 :       value = nsNavHistoryContainerResultNode::SortComparison_Bookmark(a, b, closure);
    1144                 :   }
    1145              34 :   return value;
    1146                 : }
    1147              20 : PRInt32 nsNavHistoryContainerResultNode::SortComparison_DateAddedGreater(
    1148                 :     nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure)
    1149                 : {
    1150              20 :   return -nsNavHistoryContainerResultNode::SortComparison_DateAddedLess(a, b, closure);
    1151                 : }
    1152                 : 
    1153                 : 
    1154              25 : PRInt32 nsNavHistoryContainerResultNode::SortComparison_LastModifiedLess(
    1155                 :     nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure)
    1156                 : {
    1157              25 :   PRInt32 value = ComparePRTime(a->mLastModified, b->mLastModified);
    1158              25 :   if (value == 0) {
    1159               5 :     value = SortComparison_StringLess(NS_ConvertUTF8toUTF16(a->mTitle),
    1160              10 :                                       NS_ConvertUTF8toUTF16(b->mTitle));
    1161               5 :     if (value == 0)
    1162               0 :       value = nsNavHistoryContainerResultNode::SortComparison_Bookmark(a, b, closure);
    1163                 :   }
    1164              25 :   return value;
    1165                 : }
    1166              15 : PRInt32 nsNavHistoryContainerResultNode::SortComparison_LastModifiedGreater(
    1167                 :     nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure)
    1168                 : {
    1169              15 :   return -nsNavHistoryContainerResultNode::SortComparison_LastModifiedLess(a, b, closure);
    1170                 : }
    1171                 : 
    1172                 : 
    1173                 : /**
    1174                 :  * Certain types of parent nodes are treated specially because URIs are not
    1175                 :  * valid (like days or hosts).
    1176                 :  */
    1177             109 : PRInt32 nsNavHistoryContainerResultNode::SortComparison_URILess(
    1178                 :     nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure)
    1179                 : {
    1180                 :   PRInt32 value;
    1181             109 :   if (a->IsURI() && b->IsURI()) {
    1182                 :     // normal URI or visit
    1183              95 :     value = a->mURI.Compare(b->mURI.get());
    1184                 :   } else {
    1185                 :     // for everything else, use title (= host name)
    1186              14 :     value = SortComparison_StringLess(NS_ConvertUTF8toUTF16(a->mTitle),
    1187              28 :                                       NS_ConvertUTF8toUTF16(b->mTitle));
    1188                 :   }
    1189                 : 
    1190             109 :   if (value == 0) {
    1191              11 :     value = ComparePRTime(a->mTime, b->mTime);
    1192              11 :     if (value == 0)
    1193              11 :       value = nsNavHistoryContainerResultNode::SortComparison_Bookmark(a, b, closure);
    1194                 :   }
    1195             109 :   return value;
    1196                 : }
    1197              14 : PRInt32 nsNavHistoryContainerResultNode::SortComparison_URIGreater(
    1198                 :     nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure)
    1199                 : {
    1200              14 :   return -SortComparison_URILess(a, b, closure);
    1201                 : }
    1202                 : 
    1203                 : 
    1204              34 : PRInt32 nsNavHistoryContainerResultNode::SortComparison_KeywordLess(
    1205                 :     nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure)
    1206                 : {
    1207              34 :   PRInt32 value = 0;
    1208              34 :   if (a->mItemId != -1 || b->mItemId != -1) {
    1209                 :     // compare the keywords
    1210             102 :     nsAutoString keywordA, keywordB;
    1211              34 :     nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService();
    1212              34 :     NS_ENSURE_TRUE(bookmarks, 0);
    1213                 : 
    1214                 :     nsresult rv;
    1215              34 :     if (a->mItemId != -1) {
    1216              34 :       rv = bookmarks->GetKeywordForBookmark(a->mItemId, keywordA);
    1217              34 :       NS_ENSURE_SUCCESS(rv, 0);
    1218                 :     }
    1219              34 :     if (b->mItemId != -1) {
    1220              34 :       rv = bookmarks->GetKeywordForBookmark(b->mItemId, keywordB);
    1221              34 :       NS_ENSURE_SUCCESS(rv, 0);
    1222                 :     }
    1223                 : 
    1224              68 :     value = SortComparison_StringLess(keywordA, keywordB);
    1225                 :   }
    1226                 : 
    1227                 :   // Fall back to title sorting.
    1228              34 :   if (value == 0)
    1229               7 :     value = SortComparison_TitleLess(a, b, closure);
    1230                 : 
    1231              34 :   return value;
    1232                 : }
    1233                 : 
    1234              10 : PRInt32 nsNavHistoryContainerResultNode::SortComparison_KeywordGreater(
    1235                 :     nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure)
    1236                 : {
    1237              10 :   return -SortComparison_KeywordLess(a, b, closure);
    1238                 : }
    1239                 : 
    1240              29 : PRInt32 nsNavHistoryContainerResultNode::SortComparison_AnnotationLess(
    1241                 :     nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure)
    1242                 : {
    1243              58 :   nsCAutoString annoName(static_cast<char*>(closure));
    1244              29 :   NS_ENSURE_TRUE(!annoName.IsEmpty(), 0);
    1245                 : 
    1246              29 :   bool a_itemAnno = false;
    1247              29 :   bool b_itemAnno = false;
    1248                 : 
    1249                 :   // Not used for item annos
    1250              58 :   nsCOMPtr<nsIURI> a_uri, b_uri;
    1251              29 :   if (a->mItemId != -1) {
    1252               4 :     a_itemAnno = true;
    1253                 :   } else {
    1254              50 :     nsCAutoString spec;
    1255              25 :     if (NS_SUCCEEDED(a->GetUri(spec)))
    1256              25 :       NS_NewURI(getter_AddRefs(a_uri), spec);
    1257              25 :     NS_ENSURE_TRUE(a_uri, 0);
    1258                 :   }
    1259                 : 
    1260              29 :   if (b->mItemId != -1) {
    1261               4 :     b_itemAnno = true;
    1262                 :   } else {
    1263              50 :     nsCAutoString spec;
    1264              25 :     if (NS_SUCCEEDED(b->GetUri(spec)))
    1265              25 :       NS_NewURI(getter_AddRefs(b_uri), spec);
    1266              25 :     NS_ENSURE_TRUE(b_uri, 0);
    1267                 :   }
    1268                 : 
    1269              29 :   nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService();
    1270              29 :   NS_ENSURE_TRUE(annosvc, 0);
    1271                 : 
    1272                 :   bool a_hasAnno, b_hasAnno;
    1273              29 :   if (a_itemAnno) {
    1274               4 :     NS_ENSURE_SUCCESS(annosvc->ItemHasAnnotation(a->mItemId, annoName,
    1275                 :                                                  &a_hasAnno), 0);
    1276                 :   } else {
    1277              25 :     NS_ENSURE_SUCCESS(annosvc->PageHasAnnotation(a_uri, annoName,
    1278                 :                                                  &a_hasAnno), 0);
    1279                 :   }
    1280              29 :   if (b_itemAnno) {
    1281               4 :     NS_ENSURE_SUCCESS(annosvc->ItemHasAnnotation(b->mItemId, annoName,
    1282                 :                                                  &b_hasAnno), 0);
    1283                 :   } else {
    1284              25 :     NS_ENSURE_SUCCESS(annosvc->PageHasAnnotation(b_uri, annoName,
    1285                 :                                                  &b_hasAnno), 0);    
    1286                 :   }
    1287                 : 
    1288              29 :   PRInt32 value = 0;
    1289              29 :   if (a_hasAnno || b_hasAnno) {
    1290                 :     PRUint16 annoType;
    1291              29 :     if (a_hasAnno) {
    1292              29 :       if (a_itemAnno) {
    1293               4 :         NS_ENSURE_SUCCESS(annosvc->GetItemAnnotationType(a->mItemId,
    1294                 :                                                          annoName,
    1295                 :                                                          &annoType), 0);
    1296                 :       } else {
    1297              25 :         NS_ENSURE_SUCCESS(annosvc->GetPageAnnotationType(a_uri, annoName,
    1298                 :                                                          &annoType), 0);
    1299                 :       }
    1300                 :     }
    1301              29 :     if (b_hasAnno) {
    1302                 :       PRUint16 b_type;
    1303              28 :       if (b_itemAnno) {
    1304               3 :         NS_ENSURE_SUCCESS(annosvc->GetItemAnnotationType(b->mItemId,
    1305                 :                                                          annoName,
    1306                 :                                                          &b_type), 0);
    1307                 :       } else {
    1308              25 :         NS_ENSURE_SUCCESS(annosvc->GetPageAnnotationType(b_uri, annoName,
    1309                 :                                                          &b_type), 0);
    1310                 :       }
    1311                 :       // We better make the API not support this state, really
    1312                 :       // XXXmano: this is actually wrong for double<->int and int64<->int32
    1313              28 :       if (a_hasAnno && b_type != annoType)
    1314               0 :         return 0;
    1315              28 :       annoType = b_type;
    1316                 :     }
    1317                 : 
    1318                 : #define GET_ANNOTATIONS_VALUES(METHOD_ITEM, METHOD_PAGE, A_VAL, B_VAL)        \
    1319                 :         if (a_hasAnno) {                                                      \
    1320                 :           if (a_itemAnno) {                                                   \
    1321                 :             NS_ENSURE_SUCCESS(annosvc->METHOD_ITEM(a->mItemId, annoName,      \
    1322                 :                                                    A_VAL), 0);                \
    1323                 :           } else {                                                            \
    1324                 :             NS_ENSURE_SUCCESS(annosvc->METHOD_PAGE(a_uri, annoName,           \
    1325                 :                                                    A_VAL), 0);                \
    1326                 :           }                                                                   \
    1327                 :         }                                                                     \
    1328                 :         if (b_hasAnno) {                                                      \
    1329                 :           if (b_itemAnno) {                                                   \
    1330                 :             NS_ENSURE_SUCCESS(annosvc->METHOD_ITEM(b->mItemId, annoName,      \
    1331                 :                                                    B_VAL), 0);                \
    1332                 :           } else {                                                            \
    1333                 :             NS_ENSURE_SUCCESS(annosvc->METHOD_PAGE(b_uri, annoName,           \
    1334                 :                                                    B_VAL), 0);                \
    1335                 :           }                                                                   \
    1336                 :         }
    1337                 : 
    1338                 :     // Surprising as it is, we don't support sorting by a binary annotation
    1339              29 :     if (annoType != nsIAnnotationService::TYPE_BINARY) {
    1340              29 :       if (annoType == nsIAnnotationService::TYPE_STRING) {
    1341              27 :         nsAutoString a_val, b_val;
    1342               9 :         GET_ANNOTATIONS_VALUES(GetItemAnnotationString,
    1343                 :                                GetPageAnnotationString, a_val, b_val);
    1344              18 :         value = SortComparison_StringLess(a_val, b_val);
    1345                 :       }
    1346              20 :       else if (annoType == nsIAnnotationService::TYPE_INT32) {
    1347              10 :         PRInt32 a_val = 0, b_val = 0;
    1348              10 :         GET_ANNOTATIONS_VALUES(GetItemAnnotationInt32,
    1349                 :                                GetPageAnnotationInt32, &a_val, &b_val);
    1350              10 :         value = (a_val < b_val) ? -1 : (a_val > b_val) ? 1 : 0;
    1351                 :       }
    1352              10 :       else if (annoType == nsIAnnotationService::TYPE_INT64) {
    1353               0 :         PRInt64 a_val = 0, b_val = 0;
    1354               0 :         GET_ANNOTATIONS_VALUES(GetItemAnnotationInt64,
    1355                 :                                GetPageAnnotationInt64, &a_val, &b_val);
    1356               0 :         value = (a_val < b_val) ? -1 : (a_val > b_val) ? 1 : 0;
    1357                 :       }
    1358              10 :       else if (annoType == nsIAnnotationService::TYPE_DOUBLE) {
    1359              10 :         double a_val = 0, b_val = 0;
    1360              10 :         GET_ANNOTATIONS_VALUES(GetItemAnnotationDouble,
    1361                 :                                GetPageAnnotationDouble, &a_val, &b_val);
    1362              10 :         value = (a_val < b_val) ? -1 : (a_val > b_val) ? 1 : 0;
    1363                 :       }
    1364                 :     }
    1365                 :   }
    1366                 : 
    1367                 :   // Note we also fall back to the title-sorting route one of the items didn't
    1368                 :   // have the annotation set or if both had it set but in a different storage
    1369                 :   // type
    1370              29 :   if (value == 0)
    1371               2 :     return SortComparison_TitleLess(a, b, nsnull);
    1372                 : 
    1373              27 :   return value;
    1374                 : }
    1375              19 : PRInt32 nsNavHistoryContainerResultNode::SortComparison_AnnotationGreater(
    1376                 :     nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure)
    1377                 : {
    1378              19 :   return -SortComparison_AnnotationLess(a, b, closure);
    1379                 : }
    1380                 : 
    1381                 : /**
    1382                 :  * Fall back on dates for conflict resolution
    1383                 :  */
    1384             438 : PRInt32 nsNavHistoryContainerResultNode::SortComparison_VisitCountLess(
    1385                 :     nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure)
    1386                 : {
    1387             438 :   PRInt32 value = CompareIntegers(a->mAccessCount, b->mAccessCount);
    1388             438 :   if (value == 0) {
    1389              51 :     value = ComparePRTime(a->mTime, b->mTime);
    1390              51 :     if (value == 0)
    1391               3 :       value = nsNavHistoryContainerResultNode::SortComparison_Bookmark(a, b, closure);
    1392                 :   }
    1393             438 :   return value;
    1394                 : }
    1395             368 : PRInt32 nsNavHistoryContainerResultNode::SortComparison_VisitCountGreater(
    1396                 :     nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure)
    1397                 : {
    1398             368 :   return -nsNavHistoryContainerResultNode::SortComparison_VisitCountLess(a, b, closure);
    1399                 : }
    1400                 : 
    1401                 : 
    1402              23 : PRInt32 nsNavHistoryContainerResultNode::SortComparison_TagsLess(
    1403                 :     nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure)
    1404                 : {
    1405              23 :   PRInt32 value = 0;
    1406              46 :   nsAutoString aTags, bTags;
    1407                 : 
    1408              23 :   nsresult rv = a->GetTags(aTags);
    1409              23 :   NS_ENSURE_SUCCESS(rv, 0);
    1410                 : 
    1411              23 :   rv = b->GetTags(bTags);
    1412              23 :   NS_ENSURE_SUCCESS(rv, 0);
    1413                 : 
    1414              23 :   value = SortComparison_StringLess(aTags, bTags);
    1415                 : 
    1416                 :   // fall back to title sorting
    1417              23 :   if (value == 0)
    1418               4 :     value = SortComparison_TitleLess(a, b, closure);
    1419                 : 
    1420              23 :   return value;
    1421                 : }
    1422                 : 
    1423              10 : PRInt32 nsNavHistoryContainerResultNode::SortComparison_TagsGreater(
    1424                 :     nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure)
    1425                 : {
    1426              10 :   return -SortComparison_TagsLess(a, b, closure);
    1427                 : }
    1428                 : 
    1429                 : /**
    1430                 :  * Fall back on date and bookmarked status, for conflict resolution.
    1431                 :  */
    1432                 : PRInt32
    1433              12 : nsNavHistoryContainerResultNode::SortComparison_FrecencyLess(
    1434                 :   nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure
    1435                 : )
    1436                 : {
    1437              12 :   PRInt32 value = CompareIntegers(a->mFrecency, b->mFrecency);
    1438              12 :   if (value == 0) {
    1439              12 :     value = ComparePRTime(a->mTime, b->mTime);
    1440              12 :     if (value == 0) {
    1441               2 :       value = nsNavHistoryContainerResultNode::SortComparison_Bookmark(a, b, closure);
    1442                 :     }
    1443                 :   }
    1444              12 :   return value;
    1445                 : }
    1446                 : PRInt32
    1447               6 : nsNavHistoryContainerResultNode::SortComparison_FrecencyGreater(
    1448                 :   nsNavHistoryResultNode* a, nsNavHistoryResultNode* b, void* closure
    1449                 : )
    1450                 : {
    1451               6 :   return -nsNavHistoryContainerResultNode::SortComparison_FrecencyLess(a, b, closure);
    1452                 : }
    1453                 : 
    1454                 : /**
    1455                 :  * Searches this folder for a node with the given URI.  Returns null if not
    1456                 :  * found.
    1457                 :  *
    1458                 :  * @note Does not addref the node!
    1459                 :  */
    1460                 : nsNavHistoryResultNode*
    1461               8 : nsNavHistoryContainerResultNode::FindChildURI(const nsACString& aSpec,
    1462                 :     PRUint32* aNodeIndex)
    1463                 : {
    1464              45 :   for (PRInt32 i = 0; i < mChildren.Count(); ++i) {
    1465              42 :     if (mChildren[i]->IsURI()) {
    1466              42 :       if (aSpec.Equals(mChildren[i]->mURI)) {
    1467               5 :         *aNodeIndex = i;
    1468               5 :         return mChildren[i];
    1469                 :       }
    1470                 :     }
    1471                 :   }
    1472               3 :   return nsnull;
    1473                 : }
    1474                 : 
    1475                 : 
    1476                 : /**
    1477                 :  * Searches this container for a subfolder with the given name.  This is used
    1478                 :  * to find host and "day" nodes.
    1479                 :  *
    1480                 :  * @return null if not found.
    1481                 :  * @note Does not addref the node!
    1482                 :  */
    1483                 : nsNavHistoryContainerResultNode*
    1484               0 : nsNavHistoryContainerResultNode::FindChildContainerByName(
    1485                 :     const nsACString& aTitle, PRUint32* aNodeIndex)
    1486                 : {
    1487               0 :   for (PRInt32 i = 0; i < mChildren.Count(); ++i) {
    1488               0 :     if (mChildren[i]->IsContainer()) {
    1489                 :       nsNavHistoryContainerResultNode* container =
    1490               0 :           mChildren[i]->GetAsContainer();
    1491               0 :       if (container->mTitle.Equals(aTitle)) {
    1492               0 :         *aNodeIndex = i;
    1493               0 :         return container;
    1494                 :       }
    1495                 :     }
    1496                 :   }
    1497               0 :   return nsnull;
    1498                 : }
    1499                 : 
    1500                 : 
    1501                 : /**
    1502                 :  * This does the work of adding a child to the container.  The child can be
    1503                 :  * either a container or or a single item that may even be collapsed with the
    1504                 :  * adjacent ones.
    1505                 :  *
    1506                 :  * Some inserts are "temporary" meaning that they are happening immediately
    1507                 :  * after a temporary remove.  We do this when movings elements when they
    1508                 :  * change to keep them in the proper sorting position.  In these cases, we
    1509                 :  * don't need to recompute any statistics.
    1510                 :  */
    1511                 : nsresult
    1512             163 : nsNavHistoryContainerResultNode::InsertChildAt(nsNavHistoryResultNode* aNode,
    1513                 :                                                PRInt32 aIndex,
    1514                 :                                                bool aIsTemporary)
    1515                 : {
    1516             163 :   nsNavHistoryResult* result = GetResult();
    1517             163 :   NS_ENSURE_STATE(result);
    1518                 : 
    1519             163 :   aNode->mParent = this;
    1520             163 :   aNode->mIndentLevel = mIndentLevel + 1;
    1521             163 :   if (!aIsTemporary && aNode->IsContainer()) {
    1522                 :     // need to update all the new item's children
    1523              61 :     nsNavHistoryContainerResultNode* container = aNode->GetAsContainer();
    1524              61 :     container->mResult = result;
    1525              61 :     container->FillStats();
    1526                 :   }
    1527                 : 
    1528             163 :   if (!mChildren.InsertObjectAt(aNode, aIndex))
    1529               0 :     return NS_ERROR_OUT_OF_MEMORY;
    1530                 : 
    1531                 :   // Update our stats and notify the result's observers.
    1532             163 :   if (!aIsTemporary) {
    1533             135 :     mAccessCount += aNode->mAccessCount;
    1534             135 :     if (mTime < aNode->mTime)
    1535               6 :       mTime = aNode->mTime;
    1536             135 :     if (!mParent || mParent->AreChildrenVisible()) {
    1537             135 :       NOTIFY_RESULT_OBSERVERS(result,
    1538                 :                               NodeHistoryDetailsChanged(TO_ICONTAINER(this),
    1539                 :                                                         mTime,
    1540                 :                                                         mAccessCount));
    1541                 :     }
    1542                 : 
    1543             135 :     nsresult rv = ReverseUpdateStats(aNode->mAccessCount);
    1544             135 :     NS_ENSURE_SUCCESS(rv, rv);
    1545                 :   }
    1546                 : 
    1547                 :   // Update tree if we are visible.  Note that we could be here and not
    1548                 :   // expanded, like when there is a bookmark folder being updated because its
    1549                 :   // parent is visible.
    1550             163 :   if (AreChildrenVisible())
    1551             163 :     NOTIFY_RESULT_OBSERVERS(result, NodeInserted(this, aNode, aIndex));
    1552                 : 
    1553             163 :   return NS_OK;
    1554                 : }
    1555                 : 
    1556                 : 
    1557                 : /**
    1558                 :  * This locates the proper place for insertion according to the current sort
    1559                 :  * and calls InsertChildAt
    1560                 :  */
    1561                 : nsresult
    1562              38 : nsNavHistoryContainerResultNode::InsertSortedChild(
    1563                 :     nsNavHistoryResultNode* aNode, 
    1564                 :     bool aIsTemporary, bool aIgnoreDuplicates)
    1565                 : {
    1566                 : 
    1567              38 :   if (mChildren.Count() == 0)
    1568               9 :     return InsertChildAt(aNode, 0, aIsTemporary);
    1569                 : 
    1570              29 :   SortComparator comparator = GetSortingComparator(GetSortType());
    1571              29 :   if (comparator) {
    1572                 :     // When inserting a new node, it must have proper statistics because we use
    1573                 :     // them to find the correct insertion point.  The insert function will then
    1574                 :     // recompute these statistics and fill in the proper parents and hierarchy
    1575                 :     // level.  Doing this twice shouldn't be a large performance penalty because
    1576                 :     // when we are inserting new containers, they typically contain only one
    1577                 :     // item (because we've browsed a new page).
    1578              29 :     if (!aIsTemporary && aNode->IsContainer()) {
    1579                 :       // need to update all the new item's children
    1580               0 :       nsNavHistoryContainerResultNode* container = aNode->GetAsContainer();
    1581               0 :       container->mResult = mResult;
    1582               0 :       container->FillStats();
    1583                 :     }
    1584                 : 
    1585              58 :     nsCAutoString sortingAnnotation;
    1586              29 :     GetSortingAnnotation(sortingAnnotation);
    1587                 :     bool itemExists;
    1588                 :     PRUint32 position = FindInsertionPoint(aNode, comparator, 
    1589                 :                                            sortingAnnotation.get(), 
    1590              29 :                                            &itemExists);
    1591              29 :     if (aIgnoreDuplicates && itemExists)
    1592               0 :       return NS_OK;
    1593                 : 
    1594              29 :     return InsertChildAt(aNode, position, aIsTemporary);
    1595                 :   }
    1596               0 :   return InsertChildAt(aNode, mChildren.Count(), aIsTemporary);
    1597                 : }
    1598                 : 
    1599                 : /**
    1600                 :  * This checks if the item at aIndex is located correctly given the sorting
    1601                 :  * move.  If it's not, the item is moved, and the result's observers are
    1602                 :  * notified.
    1603                 :  *
    1604                 :  * @return true if the item position has been changed, false otherwise.
    1605                 :  */
    1606                 : bool
    1607             323 : nsNavHistoryContainerResultNode::EnsureItemPosition(PRUint32 aIndex) {
    1608             323 :   NS_ASSERTION(aIndex >= 0 && aIndex < (PRUint32)mChildren.Count(), "Invalid index");
    1609             323 :   if (aIndex < 0 || aIndex >= (PRUint32)mChildren.Count())
    1610               0 :     return false;
    1611                 : 
    1612             323 :   SortComparator comparator = GetSortingComparator(GetSortType());
    1613             323 :   if (!comparator)
    1614               0 :     return false;
    1615                 : 
    1616             646 :   nsCAutoString sortAnno;
    1617             323 :   GetSortingAnnotation(sortAnno);
    1618             323 :   if (!DoesChildNeedResorting(aIndex, comparator, sortAnno.get()))
    1619             318 :     return false;
    1620                 : 
    1621              10 :   nsRefPtr<nsNavHistoryResultNode> node(mChildren[aIndex]);
    1622               5 :   mChildren.RemoveObjectAt(aIndex);
    1623                 : 
    1624                 :   PRUint32 newIndex = FindInsertionPoint(
    1625               5 :                           node, comparator,sortAnno.get(), nsnull);
    1626               5 :   mChildren.InsertObjectAt(node.get(), newIndex);
    1627                 : 
    1628               5 :   if (AreChildrenVisible()) {
    1629               5 :     nsNavHistoryResult* result = GetResult();
    1630               5 :     NOTIFY_RESULT_OBSERVERS_RET(result,
    1631                 :                                 NodeMoved(node, this, aIndex, this, newIndex),
    1632                 :                                 false);
    1633                 :   }
    1634                 : 
    1635               5 :   return true;
    1636                 : }
    1637                 : 
    1638                 : 
    1639                 : /**
    1640                 :  * This takes a list of nodes and merges them into the current result set.
    1641                 :  * Any containers that are added must already be sorted.
    1642                 :  *
    1643                 :  * This assumes that the items in 'aAddition' are new visits or replacement
    1644                 :  * URIs.  We do not update visits.
    1645                 :  *
    1646                 :  * @note In the future, we can do more updates incrementally using.  When a URI
    1647                 :  * changes in a way we can't easily handle, construct a query with each query
    1648                 :  * object specifying an exact match for the URI in question.  Then remove all
    1649                 :  * instances of that URI in the result and call this function.
    1650                 :  */
    1651                 : void
    1652              15 : nsNavHistoryContainerResultNode::MergeResults(
    1653                 :     nsCOMArray<nsNavHistoryResultNode>* aAddition)
    1654                 : {
    1655                 :   // Generally we will have very few (one) entries in the addition list, so
    1656                 :   // just iterate through it.  If we find we may have a lot, we may want to do
    1657                 :   // some hashing to help with the merge.
    1658              30 :   for (PRUint32 i = 0; i < PRUint32(aAddition->Count()); ++i) {
    1659              15 :     nsNavHistoryResultNode* curAddition = (*aAddition)[i];
    1660              15 :     if (curAddition->IsContainer()) {
    1661                 :       PRUint32 containerIndex;
    1662                 :       nsNavHistoryContainerResultNode* container =
    1663               0 :         FindChildContainerByName(curAddition->mTitle, &containerIndex);
    1664               0 :       if (container) {
    1665                 :         // recursively merge with the existing container
    1666               0 :         container->MergeResults(&curAddition->GetAsContainer()->mChildren);
    1667                 :       } else {
    1668                 :         // need to add the new container to our result.
    1669               0 :         InsertSortedChild(curAddition);
    1670                 :       }
    1671                 :     } else {
    1672              15 :       if (curAddition->IsVisit()) {
    1673                 :         // insert the new visit
    1674               7 :         InsertSortedChild(curAddition);
    1675                 :       } else {
    1676                 :         // add the URI, replacing a current one if any
    1677                 :         PRUint32 oldIndex;
    1678                 :         nsNavHistoryResultNode* oldNode =
    1679               8 :           FindChildURI(curAddition->mURI, &oldIndex);
    1680               8 :         if (oldNode) {
    1681                 :           // if we don't have a parent (for example, the history
    1682                 :           // sidebar, when sorted by last visited or most visited)
    1683                 :           // we have to manually Remove/Insert instead of Replace
    1684                 :           // see bug #389782 for details
    1685               5 :           if (mParent)
    1686               0 :             ReplaceChildURIAt(oldIndex, curAddition);
    1687                 :           else {
    1688               5 :             RemoveChildAt(oldIndex, true);
    1689               5 :             InsertSortedChild(curAddition, true);
    1690                 :           }
    1691                 :         }
    1692                 :         else
    1693               3 :           InsertSortedChild(curAddition);
    1694                 :       }
    1695                 :     }
    1696                 :   }
    1697              15 : }
    1698                 : 
    1699                 : 
    1700                 : /**
    1701                 :  * This is called to replace a leaf node.  It will update tree stats and notify
    1702                 :  * the result's observers.  You can not use this to replace a container.
    1703                 :  *
    1704                 :  * This assumes that the node is being replaced with a newer version of itself
    1705                 :  * and so its visit count will not go down.  Also, this means that the
    1706                 :  * collapsing of duplicates will not change.
    1707                 :  */
    1708                 : nsresult
    1709               0 : nsNavHistoryContainerResultNode::ReplaceChildURIAt(PRUint32 aIndex,
    1710                 :     nsNavHistoryResultNode* aNode)
    1711                 : {
    1712               0 :   NS_ASSERTION(aIndex < PRUint32(mChildren.Count()),
    1713                 :                "Invalid index for replacement");
    1714               0 :   NS_ASSERTION(mChildren[aIndex]->IsURI(),
    1715                 :                "Can not use ReplaceChildAt for a node of another type");
    1716               0 :   NS_ASSERTION(mChildren[aIndex]->mURI.Equals(aNode->mURI),
    1717                 :                "We must replace a URI with an updated one of the same");
    1718                 : 
    1719               0 :   aNode->mParent = this;
    1720               0 :   aNode->mIndentLevel = mIndentLevel + 1;
    1721                 : 
    1722                 :   // Update tree stats if needed.
    1723               0 :   PRUint32 accessCountChange = aNode->mAccessCount - mChildren[aIndex]->mAccessCount;
    1724               0 :   if (accessCountChange != 0 || mChildren[aIndex]->mTime != aNode->mTime) {
    1725               0 :     NS_ASSERTION(aNode->mAccessCount >= mChildren[aIndex]->mAccessCount,
    1726                 :                  "Replacing a node with one back in time or some nonmatching node");
    1727                 : 
    1728               0 :     mAccessCount += accessCountChange;
    1729               0 :     if (mTime < aNode->mTime)
    1730               0 :       mTime = aNode->mTime;
    1731               0 :     nsresult rv = ReverseUpdateStats(accessCountChange);
    1732               0 :     NS_ENSURE_SUCCESS(rv, rv);
    1733                 :   }
    1734                 : 
    1735                 :   // Hold a reference so it doesn't go away as soon as we remove it from the
    1736                 :   // array.
    1737               0 :   nsRefPtr<nsNavHistoryResultNode> oldItem = mChildren[aIndex];
    1738               0 :   if (!mChildren.ReplaceObjectAt(aNode, aIndex))
    1739               0 :     return NS_ERROR_FAILURE;
    1740                 : 
    1741               0 :   if (AreChildrenVisible()) {
    1742               0 :     nsNavHistoryResult* result = GetResult();
    1743               0 :     NOTIFY_RESULT_OBSERVERS(result,
    1744                 :                             NodeReplaced(this, oldItem, aNode, aIndex));
    1745                 :   }
    1746                 : 
    1747               0 :   mChildren[aIndex]->OnRemoving();
    1748               0 :   return NS_OK;
    1749                 : }
    1750                 : 
    1751                 : 
    1752                 : /**
    1753                 :  * This does all the work of removing a child from this container, including
    1754                 :  * updating the tree if necessary.  Note that we do not need to be open for
    1755                 :  * this to work.
    1756                 :  *
    1757                 :  * Some removes are "temporary" meaning that they'll just get inserted again.
    1758                 :  * We do this for resorting.  In these cases, we don't need to recompute any
    1759                 :  * statistics, and we shouldn't notify those container that they are being
    1760                 :  * removed.
    1761                 :  */
    1762                 : nsresult
    1763             215 : nsNavHistoryContainerResultNode::RemoveChildAt(PRInt32 aIndex,
    1764                 :                                                bool aIsTemporary)
    1765                 : {
    1766             215 :   NS_ASSERTION(aIndex >= 0 && aIndex < mChildren.Count(), "Invalid index");
    1767                 : 
    1768                 :   // Hold an owning reference to keep from expiring while we work with it.
    1769             430 :   nsRefPtr<nsNavHistoryResultNode> oldNode = mChildren[aIndex];
    1770                 : 
    1771                 :   // Update stats.
    1772             215 :   PRUint32 oldAccessCount = 0;
    1773             215 :   if (!aIsTemporary) {
    1774             210 :     oldAccessCount = mAccessCount;
    1775             210 :     mAccessCount -= mChildren[aIndex]->mAccessCount;
    1776                 :     NS_ASSERTION(mAccessCount >= 0, "Invalid access count while updating!");
    1777                 :   }
    1778                 : 
    1779                 :   // Remove it from our list and notify the result's observers.
    1780             215 :   mChildren.RemoveObjectAt(aIndex);
    1781             215 :   if (AreChildrenVisible()) {
    1782             215 :     nsNavHistoryResult* result = GetResult();
    1783             215 :     NOTIFY_RESULT_OBSERVERS(result,
    1784                 :                             NodeRemoved(this, oldNode, aIndex));
    1785                 :   }
    1786                 : 
    1787             215 :   if (!aIsTemporary) {
    1788             210 :     nsresult rv = ReverseUpdateStats(mAccessCount - oldAccessCount);
    1789             210 :     NS_ENSURE_SUCCESS(rv, rv);
    1790             210 :     oldNode->OnRemoving();
    1791                 :   }
    1792             215 :   return NS_OK;
    1793                 : }
    1794                 : 
    1795                 : 
    1796                 : /**
    1797                 :  * Searches for matches for the given URI.  If aOnlyOne is set, it will
    1798                 :  * terminate as soon as it finds a single match.  This would be used when there
    1799                 :  * are URI results so there will only ever be one copy of any URI.
    1800                 :  *
    1801                 :  * When aOnlyOne is false, it will check all elements.  This is for visit
    1802                 :  * style results that may have multiple copies of any given URI.
    1803                 :  */
    1804                 : void
    1805             149 : nsNavHistoryContainerResultNode::RecursiveFindURIs(bool aOnlyOne,
    1806                 :     nsNavHistoryContainerResultNode* aContainer, const nsCString& aSpec,
    1807                 :     nsCOMArray<nsNavHistoryResultNode>* aMatches)
    1808                 : {
    1809             620 :   for (PRInt32 child = 0; child < aContainer->mChildren.Count(); ++child) {
    1810                 :     PRUint32 type;
    1811             532 :     aContainer->mChildren[child]->GetType(&type);
    1812             532 :     if (nsNavHistoryResultNode::IsTypeURI(type)) {
    1813                 :       // compare URIs
    1814             532 :       nsNavHistoryResultNode* uriNode = aContainer->mChildren[child];
    1815             532 :       if (uriNode->mURI.Equals(aSpec)) {
    1816                 :         // found
    1817             123 :         aMatches->AppendObject(uriNode);
    1818             123 :         if (aOnlyOne)
    1819              61 :           return;
    1820                 :       }
    1821                 :     }
    1822                 :   }
    1823                 : }
    1824                 : 
    1825                 : 
    1826                 : /**
    1827                 :  * If aUpdateSort is true, we will also update the sorting of this item.
    1828                 :  * Normally you want this to be true, but it can be false if the thing you are
    1829                 :  * changing can not affect sorting (like favicons).
    1830                 :  *
    1831                 :  * You should NOT change any child lists as part of the callback function.
    1832                 :  */
    1833                 : nsresult
    1834              61 : nsNavHistoryContainerResultNode::UpdateURIs(bool aRecursive, bool aOnlyOne,
    1835                 :     bool aUpdateSort, const nsCString& aSpec,
    1836                 :     nsresult (*aCallback)(nsNavHistoryResultNode*,void*, nsNavHistoryResult*), void* aClosure)
    1837                 : {
    1838              61 :   nsNavHistoryResult* result = GetResult();
    1839              61 :   NS_ENSURE_STATE(result);
    1840                 : 
    1841                 :   // this needs to be owning since sometimes we remove and re-insert nodes
    1842                 :   // in their parents and we don't want them to go away.
    1843             122 :   nsCOMArray<nsNavHistoryResultNode> matches;
    1844                 : 
    1845              61 :   if (aRecursive) {
    1846              61 :     RecursiveFindURIs(aOnlyOne, this, aSpec, &matches);
    1847               0 :   } else if (aOnlyOne) {
    1848                 :     PRUint32 nodeIndex;
    1849               0 :     nsNavHistoryResultNode* node = FindChildURI(aSpec, &nodeIndex);
    1850               0 :     if (node)
    1851               0 :       matches.AppendObject(node);
    1852                 :   } else {
    1853               0 :     NS_NOTREACHED("UpdateURIs does not handle nonrecursive updates of multiple items.");
    1854                 :     // this case easy to add if you need it, just find all the matching URIs
    1855                 :     // at this level.  However, this isn't currently used. History uses
    1856                 :     // recursive, Bookmarks uses one level and knows that the match is unique.
    1857               0 :     return NS_ERROR_FAILURE;
    1858                 :   }
    1859              61 :   if (matches.Count() == 0)
    1860              12 :     return NS_OK;
    1861                 : 
    1862                 :   // PERFORMANCE: This updates each container for each child in it that
    1863                 :   // changes.  In some cases, many elements have changed inside the same
    1864                 :   // container.  It would be better to compose a list of containers, and
    1865                 :   // update each one only once for all the items that have changed in it.
    1866              99 :   for (PRInt32 i = 0; i < matches.Count(); ++i)
    1867                 :   {
    1868              50 :     nsNavHistoryResultNode* node = matches[i];
    1869              50 :     nsNavHistoryContainerResultNode* parent = node->mParent;
    1870              50 :     if (!parent) {
    1871               0 :       NS_NOTREACHED("All URI nodes being updated must have parents");
    1872               0 :       continue;
    1873                 :     }
    1874                 : 
    1875              50 :     PRUint32 oldAccessCount = node->mAccessCount;
    1876              50 :     PRTime oldTime = node->mTime;
    1877              50 :     aCallback(node, aClosure, result);
    1878                 : 
    1879              50 :     if (oldAccessCount != node->mAccessCount || oldTime != node->mTime) {
    1880               0 :       parent->mAccessCount += node->mAccessCount - oldAccessCount;
    1881               0 :       if (node->mTime > parent->mTime)
    1882               0 :         parent->mTime = node->mTime;
    1883               0 :       if (parent->AreChildrenVisible()) {
    1884               0 :           NOTIFY_RESULT_OBSERVERS(result,
    1885                 :                                   NodeHistoryDetailsChanged(
    1886                 :                                     TO_ICONTAINER(parent),
    1887                 :                                     parent->mTime,
    1888                 :                                     parent->mAccessCount));
    1889                 :       }
    1890               0 :       nsresult rv = parent->ReverseUpdateStats(node->mAccessCount - oldAccessCount);
    1891               0 :       NS_ENSURE_SUCCESS(rv, rv);
    1892                 :     }
    1893                 : 
    1894              50 :     if (aUpdateSort) {
    1895               0 :       PRInt32 childIndex = parent->FindChild(node);
    1896               0 :       NS_ASSERTION(childIndex >= 0, "Could not find child we just got a reference to");
    1897               0 :       if (childIndex >= 0)
    1898               0 :         parent->EnsureItemPosition(childIndex);
    1899                 :     }
    1900                 :   }
    1901                 : 
    1902              49 :   return NS_OK;
    1903                 : }
    1904                 : 
    1905                 : 
    1906                 : /**
    1907                 :  * This is used to update the titles in the tree.  This is called from both
    1908                 :  * query and bookmark folder containers to update the tree.  Bookmark folders
    1909                 :  * should be sure to set recursive to false, since child folders will have
    1910                 :  * their own callbacks registered.
    1911                 :  */
    1912              50 : static nsresult setTitleCallback(
    1913                 :     nsNavHistoryResultNode* aNode, void* aClosure,
    1914                 :     nsNavHistoryResult* aResult)
    1915                 : {
    1916              50 :   const nsACString* newTitle = reinterpret_cast<nsACString*>(aClosure);
    1917              50 :   aNode->mTitle = *newTitle;
    1918                 : 
    1919              50 :   if (aResult && (!aNode->mParent || aNode->mParent->AreChildrenVisible()))
    1920              50 :     NOTIFY_RESULT_OBSERVERS(aResult, NodeTitleChanged(aNode, *newTitle));
    1921                 : 
    1922              50 :   return NS_OK;
    1923                 : }
    1924                 : nsresult
    1925              61 : nsNavHistoryContainerResultNode::ChangeTitles(nsIURI* aURI,
    1926                 :                                               const nsACString& aNewTitle,
    1927                 :                                               bool aRecursive,
    1928                 :                                               bool aOnlyOne)
    1929                 : {
    1930                 :   // uri string
    1931             122 :   nsCAutoString uriString;
    1932              61 :   nsresult rv = aURI->GetSpec(uriString);
    1933              61 :   NS_ENSURE_SUCCESS(rv, rv);
    1934                 : 
    1935                 :   // The recursive function will update the result's tree nodes, but only if we
    1936                 :   // give it a non-null pointer.  So if there isn't a tree, just pass NULL so
    1937                 :   // it doesn't bother trying to call the result.
    1938              61 :   nsNavHistoryResult* result = GetResult();
    1939              61 :   NS_ENSURE_STATE(result);
    1940                 : 
    1941              61 :   PRUint16 sortType = GetSortType();
    1942                 :   bool updateSorting =
    1943                 :     (sortType == nsINavHistoryQueryOptions::SORT_BY_TITLE_ASCENDING ||
    1944              61 :      sortType == nsINavHistoryQueryOptions::SORT_BY_TITLE_DESCENDING);
    1945                 : 
    1946                 :   rv = UpdateURIs(aRecursive, aOnlyOne, updateSorting, uriString,
    1947                 :                  setTitleCallback,
    1948              61 :                  const_cast<void*>(reinterpret_cast<const void*>(&aNewTitle)));
    1949              61 :   NS_ENSURE_SUCCESS(rv, rv);
    1950                 : 
    1951              61 :   return NS_OK;
    1952                 : }
    1953                 : 
    1954                 : 
    1955                 : /**
    1956                 :  * Complex containers (folders and queries) will override this.  Here, we
    1957                 :  * handle the case of simple containers (like host groups) where the children
    1958                 :  * are always stored.
    1959                 :  */
    1960                 : NS_IMETHODIMP
    1961               0 : nsNavHistoryContainerResultNode::GetHasChildren(bool *aHasChildren)
    1962                 : {
    1963               0 :   *aHasChildren = (mChildren.Count() > 0);
    1964               0 :   return NS_OK;
    1965                 : }
    1966                 : 
    1967                 : 
    1968                 : /**
    1969                 :  * @throws if this node is closed.
    1970                 :  */
    1971                 : NS_IMETHODIMP
    1972            3776 : nsNavHistoryContainerResultNode::GetChildCount(PRUint32* aChildCount)
    1973                 : {
    1974            3776 :   if (!mExpanded)
    1975               0 :     return NS_ERROR_NOT_AVAILABLE;
    1976            3776 :   *aChildCount = mChildren.Count();
    1977            3776 :   return NS_OK;
    1978                 : }
    1979                 : 
    1980                 : 
    1981                 : NS_IMETHODIMP
    1982            4409 : nsNavHistoryContainerResultNode::GetChild(PRUint32 aIndex,
    1983                 :                                           nsINavHistoryResultNode** _retval)
    1984                 : {
    1985            4409 :   if (!mExpanded)
    1986               0 :     return NS_ERROR_NOT_AVAILABLE;
    1987            4409 :   if (aIndex >= PRUint32(mChildren.Count()))
    1988               0 :     return NS_ERROR_INVALID_ARG;
    1989            4409 :   NS_ADDREF(*_retval = mChildren[aIndex]);
    1990            4409 :   return NS_OK;
    1991                 : }
    1992                 : 
    1993                 : 
    1994                 : NS_IMETHODIMP
    1995               2 : nsNavHistoryContainerResultNode::GetChildIndex(nsINavHistoryResultNode* aNode,
    1996                 :                                                PRUint32* _retval)
    1997                 : {
    1998               2 :   if (!mExpanded)
    1999               0 :     return NS_ERROR_NOT_AVAILABLE;
    2000                 : 
    2001               2 :   PRInt32 nodeIndex = FindChild(static_cast<nsNavHistoryResultNode*>(aNode));
    2002               2 :   if (nodeIndex == -1)
    2003               1 :     return NS_ERROR_INVALID_ARG;
    2004                 : 
    2005               1 :   *_retval = nodeIndex;
    2006               1 :   return NS_OK;
    2007                 : }
    2008                 : 
    2009                 : 
    2010                 : NS_IMETHODIMP
    2011               0 : nsNavHistoryContainerResultNode::FindNodeByDetails(const nsACString& aURIString,
    2012                 :                                                    PRTime aTime,
    2013                 :                                                    PRInt64 aItemId,
    2014                 :                                                    bool aRecursive,
    2015                 :                                                    nsINavHistoryResultNode** _retval) {
    2016               0 :   if (!mExpanded)
    2017               0 :     return NS_ERROR_NOT_AVAILABLE;
    2018                 : 
    2019               0 :   *_retval = nsnull;
    2020               0 :   for (PRInt32 i = 0; i < mChildren.Count(); ++i) {
    2021               0 :     if (mChildren[i]->mURI.Equals(aURIString) &&
    2022               0 :         mChildren[i]->mTime == aTime &&
    2023               0 :         mChildren[i]->mItemId == aItemId) {
    2024               0 :       *_retval = mChildren[i];
    2025               0 :       break;
    2026                 :     }
    2027                 : 
    2028               0 :     if (aRecursive && mChildren[i]->IsContainer()) {
    2029                 :       nsNavHistoryContainerResultNode* asContainer =
    2030               0 :         mChildren[i]->GetAsContainer();
    2031               0 :       if (asContainer->mExpanded) {
    2032                 :         nsresult rv = asContainer->FindNodeByDetails(aURIString, aTime,
    2033                 :                                                      aItemId,
    2034                 :                                                      aRecursive,
    2035               0 :                                                      _retval);
    2036                 :                                                       
    2037               0 :         if (NS_SUCCEEDED(rv) && _retval)
    2038               0 :           break;
    2039                 :       }
    2040                 :     }
    2041                 :   }
    2042               0 :   NS_IF_ADDREF(*_retval);
    2043               0 :   return NS_OK;
    2044                 : }
    2045                 : 
    2046                 : /**
    2047                 :  * @note Overridden for folders to query the bookmarks service directly.
    2048                 :  */
    2049                 : NS_IMETHODIMP
    2050               0 : nsNavHistoryContainerResultNode::GetChildrenReadOnly(bool *aChildrenReadOnly)
    2051                 : {
    2052               0 :   *aChildrenReadOnly = mChildrenReadOnly;
    2053               0 :   return NS_OK;
    2054                 : }
    2055                 : 
    2056                 : /**
    2057                 :  * HOW QUERY UPDATING WORKS
    2058                 :  *
    2059                 :  * Queries are different than bookmark folders in that we can not always do
    2060                 :  * dynamic updates (easily) and updates are more expensive.  Therefore, we do
    2061                 :  * NOT query if we are not open and want to see if we have any children (for
    2062                 :  * drawing a twisty) and always assume we will.
    2063                 :  *
    2064                 :  * When the container is opened, we execute the query and register the
    2065                 :  * listeners.  Like bookmark folders, we stay registered even when closed, and
    2066                 :  * clear ourselves as soon as a message comes in.  This lets us respond quickly
    2067                 :  * if the user closes and reopens the container.
    2068                 :  *
    2069                 :  * We try to handle the most common notifications for the most common query
    2070                 :  * types dynamically, that is, figuring out what should happen in response to
    2071                 :  * a message without doing a requery.  For complex changes or complex queries,
    2072                 :  * we give up and requery.
    2073                 :  */
    2074           47375 : NS_IMPL_ISUPPORTS_INHERITED1(nsNavHistoryQueryResultNode,
    2075                 :                              nsNavHistoryContainerResultNode,
    2076                 :                              nsINavHistoryQueryResultNode)
    2077                 : 
    2078               4 : nsNavHistoryQueryResultNode::nsNavHistoryQueryResultNode(
    2079                 :     const nsACString& aTitle, const nsACString& aIconURI,
    2080                 :     const nsACString& aQueryURI) :
    2081                 :   nsNavHistoryContainerResultNode(aQueryURI, aTitle, aIconURI,
    2082                 :                                   nsNavHistoryResultNode::RESULT_TYPE_QUERY,
    2083                 :                                   true, nsnull),
    2084                 :   mLiveUpdate(QUERYUPDATE_COMPLEX_WITH_BOOKMARKS),
    2085                 :   mHasSearchTerms(false),
    2086                 :   mContentsValid(false),
    2087               4 :   mBatchChanges(0)
    2088                 : {
    2089               4 : }
    2090                 : 
    2091             485 : nsNavHistoryQueryResultNode::nsNavHistoryQueryResultNode(
    2092                 :     const nsACString& aTitle, const nsACString& aIconURI,
    2093                 :     const nsCOMArray<nsNavHistoryQuery>& aQueries,
    2094                 :     nsNavHistoryQueryOptions* aOptions) :
    2095             485 :   nsNavHistoryContainerResultNode(EmptyCString(), aTitle, aIconURI,
    2096                 :                                   nsNavHistoryResultNode::RESULT_TYPE_QUERY,
    2097                 :                                   true, aOptions),
    2098                 :   mQueries(aQueries),
    2099                 :   mContentsValid(false),
    2100             485 :   mBatchChanges(0)
    2101                 : {
    2102             485 :   NS_ASSERTION(aQueries.Count() > 0, "Must have at least one query");
    2103                 : 
    2104             485 :   nsNavHistory* history = nsNavHistory::GetHistoryService();
    2105             485 :   NS_ASSERTION(history, "History service missing");
    2106             485 :   if (history) {
    2107                 :     mLiveUpdate = history->GetUpdateRequirements(mQueries, mOptions,
    2108             485 :                                                  &mHasSearchTerms);
    2109                 :   }
    2110             485 : }
    2111                 : 
    2112             768 : nsNavHistoryQueryResultNode::nsNavHistoryQueryResultNode(
    2113                 :     const nsACString& aTitle, const nsACString& aIconURI,
    2114                 :     PRTime aTime,
    2115                 :     const nsCOMArray<nsNavHistoryQuery>& aQueries,
    2116                 :     nsNavHistoryQueryOptions* aOptions) :
    2117             768 :   nsNavHistoryContainerResultNode(EmptyCString(), aTitle, aTime, aIconURI,
    2118                 :                                   nsNavHistoryResultNode::RESULT_TYPE_QUERY,
    2119                 :                                   true, aOptions),
    2120                 :   mQueries(aQueries),
    2121                 :   mContentsValid(false),
    2122             768 :   mBatchChanges(0)
    2123                 : {
    2124             768 :   NS_ASSERTION(aQueries.Count() > 0, "Must have at least one query");
    2125                 : 
    2126             768 :   nsNavHistory* history = nsNavHistory::GetHistoryService();
    2127             768 :   NS_ASSERTION(history, "History service missing");
    2128             768 :   if (history) {
    2129                 :     mLiveUpdate = history->GetUpdateRequirements(mQueries, mOptions,
    2130             768 :                                                  &mHasSearchTerms);
    2131                 :   }
    2132             768 : }
    2133                 : 
    2134            3771 : nsNavHistoryQueryResultNode::~nsNavHistoryQueryResultNode() {
    2135                 :   // Remove this node from result's observers.  We don't need to be notified
    2136                 :   // anymore.
    2137            2023 :   if (mResult && mResult->mAllBookmarksObservers.IndexOf(this) !=
    2138            2023 :                    mResult->mAllBookmarksObservers.NoIndex)
    2139               0 :     mResult->RemoveAllBookmarksObserver(this);
    2140            2023 :   if (mResult && mResult->mHistoryObservers.IndexOf(this) !=
    2141            2023 :                    mResult->mHistoryObservers.NoIndex)
    2142               0 :     mResult->RemoveHistoryObserver(this);
    2143            5028 : }
    2144                 : 
    2145                 : /**
    2146                 :  * Whoever made us may want non-expanding queries. However, we always expand
    2147                 :  * when we are the root node, or else asking for non-expanding queries would be
    2148                 :  * useless.  A query node is not expandable if excludeItems is set or if
    2149                 :  * expandQueries is unset.
    2150                 :  */
    2151                 : bool
    2152             865 : nsNavHistoryQueryResultNode::CanExpand()
    2153                 : {
    2154             865 :   if (IsContainersQuery())
    2155             239 :     return true;
    2156                 : 
    2157                 :   // If ExcludeItems is set on the root or on the node itself, don't expand.
    2158            1250 :   if ((mResult && mResult->mRootNode->mOptions->ExcludeItems()) ||
    2159             624 :       Options()->ExcludeItems())
    2160               4 :     return false;
    2161                 : 
    2162                 :   // Check the ancestor container.
    2163             622 :   nsNavHistoryQueryOptions* options = GetGeneratingOptions();
    2164             622 :   if (options) {
    2165             622 :     if (options->ExcludeItems())
    2166               0 :       return false;
    2167             622 :     if (options->ExpandQueries())
    2168             622 :       return true;
    2169                 :   }
    2170                 : 
    2171               0 :   if (mResult && mResult->mRootNode == this)
    2172               0 :     return true;
    2173                 : 
    2174               0 :   return false;
    2175                 : }
    2176                 : 
    2177                 : 
    2178                 : /**
    2179                 :  * Some query with a particular result type can contain other queries.  They
    2180                 :  * must be always expandable
    2181                 :  */
    2182                 : bool
    2183            3018 : nsNavHistoryQueryResultNode::IsContainersQuery()
    2184                 : {
    2185            3018 :   PRUint16 resultType = Options()->ResultType();
    2186                 :   return resultType == nsINavHistoryQueryOptions::RESULTS_AS_DATE_QUERY ||
    2187                 :          resultType == nsINavHistoryQueryOptions::RESULTS_AS_DATE_SITE_QUERY ||
    2188                 :          resultType == nsINavHistoryQueryOptions::RESULTS_AS_TAG_QUERY ||
    2189            3018 :          resultType == nsINavHistoryQueryOptions::RESULTS_AS_SITE_QUERY;
    2190                 : }
    2191                 : 
    2192                 : 
    2193                 : /**
    2194                 :  * Here we do not want to call ContainerResultNode::OnRemoving since our own
    2195                 :  * ClearChildren will do the same thing and more (unregister the observers).
    2196                 :  * The base ResultNode::OnRemoving will clear some regular node stats, so it
    2197                 :  * is OK.
    2198                 :  */
    2199                 : void
    2200             766 : nsNavHistoryQueryResultNode::OnRemoving()
    2201                 : {
    2202             766 :   nsNavHistoryResultNode::OnRemoving();
    2203             766 :   ClearChildren(true);
    2204             766 : }
    2205                 : 
    2206                 : 
    2207                 : /**
    2208                 :  * Marks the container as open, rebuilding results if they are invalid.  We
    2209                 :  * may still have valid results if the container was previously open and
    2210                 :  * nothing happened since closing it.
    2211                 :  *
    2212                 :  * We do not handle CloseContainer specially.  The default one just marks the
    2213                 :  * container as closed, but doesn't actually mark the results as invalid.
    2214                 :  * The results will be invalidated by the next history or bookmark
    2215                 :  * notification that comes in.  This means if you open and close the item
    2216                 :  * without anything happening in between, it will be fast (this actually
    2217                 :  * happens when results are used as menus).
    2218                 :  */
    2219                 : nsresult
    2220             861 : nsNavHistoryQueryResultNode::OpenContainer()
    2221                 : {
    2222             861 :   NS_ASSERTION(!mExpanded, "Container must be closed to open it");
    2223             861 :   mExpanded = true;
    2224                 : 
    2225                 :   nsresult rv;
    2226                 : 
    2227             861 :   if (!CanExpand())
    2228               4 :     return NS_OK;
    2229             857 :   if (!mContentsValid) {
    2230             857 :     rv = FillChildren();
    2231             857 :     NS_ENSURE_SUCCESS(rv, rv);
    2232                 :   }
    2233                 : 
    2234             857 :   rv = NotifyOnStateChange(STATE_CLOSED);
    2235             857 :   NS_ENSURE_SUCCESS(rv, rv);
    2236                 : 
    2237             857 :   return NS_OK;
    2238                 : }
    2239                 : 
    2240                 : 
    2241                 : /**
    2242                 :  * When we have valid results we can always give an exact answer.  When we
    2243                 :  * don't we just assume we'll have results, since actually doing the query
    2244                 :  * might be hard.  This is used to draw twisties on the tree, so precise results
    2245                 :  * don't matter.
    2246                 :  */
    2247                 : NS_IMETHODIMP
    2248               4 : nsNavHistoryQueryResultNode::GetHasChildren(bool* aHasChildren)
    2249                 : {
    2250               4 :   *aHasChildren = false;
    2251                 : 
    2252               4 :   if (!CanExpand()) {
    2253               0 :     return NS_OK;
    2254                 :   }
    2255                 : 
    2256               4 :   PRUint16 resultType = mOptions->ResultType();
    2257                 : 
    2258                 :   // Tags are always populated, otherwise they are removed.
    2259               4 :   if (resultType == nsINavHistoryQueryOptions::RESULTS_AS_TAG_CONTENTS) {
    2260               2 :     *aHasChildren = true;
    2261               2 :     return NS_OK;
    2262                 :   }
    2263                 : 
    2264                 :   // For tag containers query we must check if we have any tag
    2265               2 :   if (resultType == nsINavHistoryQueryOptions::RESULTS_AS_TAG_QUERY) {
    2266                 :     nsCOMPtr<nsITaggingService> tagging =
    2267               0 :       do_GetService(NS_TAGGINGSERVICE_CONTRACTID);
    2268               0 :     if (tagging) {
    2269                 :       bool hasTags;
    2270               0 :       *aHasChildren = NS_SUCCEEDED(tagging->GetHasTags(&hasTags)) && hasTags;
    2271                 :     }
    2272               0 :     return NS_OK;
    2273                 :   }
    2274                 : 
    2275                 :   // For history containers query we must check if we have any history
    2276               2 :   if (resultType == nsINavHistoryQueryOptions::RESULTS_AS_DATE_QUERY ||
    2277                 :       resultType == nsINavHistoryQueryOptions::RESULTS_AS_DATE_SITE_QUERY ||
    2278                 :       resultType == nsINavHistoryQueryOptions::RESULTS_AS_SITE_QUERY) {
    2279               0 :     nsNavHistory* history = nsNavHistory::GetHistoryService();
    2280               0 :     NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY);
    2281               0 :     return history->GetHasHistoryEntries(aHasChildren);
    2282                 :   }
    2283                 : 
    2284                 :   //XXX: For other containers queries we must:
    2285                 :   // 1. If it's open, just check mChildren for containers
    2286                 :   // 2. Else null the view (keep it in a var), open container, check mChildren
    2287                 :   //    for containers, close container, reset the view
    2288                 : 
    2289               2 :   if (mContentsValid) {
    2290               0 :     *aHasChildren = (mChildren.Count() > 0);
    2291               0 :     return NS_OK;
    2292                 :   }
    2293               2 :   *aHasChildren = true;
    2294               2 :   return NS_OK;
    2295                 : }
    2296                 : 
    2297                 : 
    2298                 : /**
    2299                 :  * This doesn't just return mURI because in the case of queries that may
    2300                 :  * be lazily constructed from the query objects.
    2301                 :  */
    2302                 : NS_IMETHODIMP
    2303              66 : nsNavHistoryQueryResultNode::GetUri(nsACString& aURI)
    2304                 : {
    2305              66 :   nsresult rv = VerifyQueriesSerialized();
    2306              66 :   NS_ENSURE_SUCCESS(rv, rv);
    2307              66 :   aURI = mURI;
    2308              66 :   return NS_OK;
    2309                 : }
    2310                 : 
    2311                 : 
    2312                 : NS_IMETHODIMP
    2313               0 : nsNavHistoryQueryResultNode::GetFolderItemId(PRInt64* aItemId)
    2314                 : {
    2315               0 :   *aItemId = mItemId;
    2316               0 :   return NS_OK;
    2317                 : }
    2318                 : 
    2319                 : 
    2320                 : NS_IMETHODIMP
    2321               1 : nsNavHistoryQueryResultNode::GetQueries(PRUint32* queryCount,
    2322                 :                                         nsINavHistoryQuery*** queries)
    2323                 : {
    2324               1 :   nsresult rv = VerifyQueriesParsed();
    2325               1 :   NS_ENSURE_SUCCESS(rv, rv);
    2326               1 :   NS_ASSERTION(mQueries.Count() > 0, "Must have >= 1 query");
    2327                 : 
    2328                 :   *queries = static_cast<nsINavHistoryQuery**>
    2329               1 :                         (nsMemory::Alloc(mQueries.Count() * sizeof(nsINavHistoryQuery*)));
    2330               1 :   NS_ENSURE_TRUE(*queries, NS_ERROR_OUT_OF_MEMORY);
    2331                 : 
    2332               2 :   for (PRInt32 i = 0; i < mQueries.Count(); ++i)
    2333               1 :     NS_ADDREF((*queries)[i] = mQueries[i]);
    2334               1 :   *queryCount = mQueries.Count();
    2335               1 :   return NS_OK;
    2336                 : }
    2337                 : 
    2338                 : 
    2339                 : NS_IMETHODIMP
    2340              51 : nsNavHistoryQueryResultNode::GetQueryOptions(
    2341                 :                                       nsINavHistoryQueryOptions** aQueryOptions)
    2342                 : {
    2343              51 :   *aQueryOptions = Options();
    2344              51 :   NS_ADDREF(*aQueryOptions);
    2345              51 :   return NS_OK;
    2346                 : }
    2347                 : 
    2348                 : /**
    2349                 :  * Safe options getter, ensures queries are parsed first.
    2350                 :  */
    2351                 : nsNavHistoryQueryOptions*
    2352            3697 : nsNavHistoryQueryResultNode::Options()
    2353                 : {
    2354            3697 :   nsresult rv = VerifyQueriesParsed();
    2355            3697 :   if (NS_FAILED(rv))
    2356               0 :     return nsnull;
    2357            3697 :   NS_ASSERTION(mOptions, "Options invalid, cannot generate from URI");
    2358            3697 :   return mOptions;
    2359                 : }
    2360                 : 
    2361                 : 
    2362                 : nsresult
    2363            4587 : nsNavHistoryQueryResultNode::VerifyQueriesParsed()
    2364                 : {
    2365            4587 :   if (mQueries.Count() > 0) {
    2366            4583 :     NS_ASSERTION(mOptions, "If a result has queries, it also needs options");
    2367            4583 :     return NS_OK;
    2368                 :   }
    2369               4 :   NS_ASSERTION(!mURI.IsEmpty(),
    2370                 :                "Query nodes must have either a URI or query/options");
    2371                 : 
    2372               4 :   nsNavHistory* history = nsNavHistory::GetHistoryService();
    2373               4 :   NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY);
    2374                 : 
    2375                 :   nsresult rv = history->QueryStringToQueryArray(mURI, &mQueries,
    2376               4 :                                                  getter_AddRefs(mOptions));
    2377               4 :   NS_ENSURE_SUCCESS(rv, rv);
    2378                 : 
    2379                 :   mLiveUpdate = history->GetUpdateRequirements(mQueries, mOptions,
    2380               4 :                                                &mHasSearchTerms);
    2381               4 :   return NS_OK;
    2382                 : }
    2383                 : 
    2384                 : 
    2385                 : nsresult
    2386              66 : nsNavHistoryQueryResultNode::VerifyQueriesSerialized()
    2387                 : {
    2388              66 :   if (!mURI.IsEmpty()) {
    2389              11 :     return NS_OK;
    2390                 :   }
    2391              55 :   NS_ASSERTION(mQueries.Count() > 0 && mOptions,
    2392                 :                "Query nodes must have either a URI or query/options");
    2393                 : 
    2394             110 :   nsTArray<nsINavHistoryQuery*> flatQueries;
    2395              55 :   flatQueries.SetCapacity(mQueries.Count());
    2396             112 :   for (PRInt32 i = 0; i < mQueries.Count(); ++i)
    2397                 :     flatQueries.AppendElement(static_cast<nsINavHistoryQuery*>
    2398              57 :                                          (mQueries.ObjectAt(i)));
    2399                 : 
    2400              55 :   nsNavHistory* history = nsNavHistory::GetHistoryService();
    2401              55 :   NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY);
    2402                 : 
    2403                 :   nsresult rv = history->QueriesToQueryString(flatQueries.Elements(),
    2404                 :                                               flatQueries.Length(),
    2405              55 :                                               mOptions, mURI);
    2406              55 :   NS_ENSURE_SUCCESS(rv, rv);
    2407              55 :   NS_ENSURE_STATE(!mURI.IsEmpty());
    2408              55 :   return NS_OK;
    2409                 : }
    2410                 : 
    2411                 : 
    2412                 : nsresult
    2413             889 : nsNavHistoryQueryResultNode::FillChildren()
    2414                 : {
    2415             889 :   NS_ASSERTION(!mContentsValid,
    2416                 :                "Don't call FillChildren when contents are valid");
    2417             889 :   NS_ASSERTION(mChildren.Count() == 0,
    2418                 :                "We are trying to fill children when there already are some");
    2419                 : 
    2420             889 :   nsNavHistory* history = nsNavHistory::GetHistoryService();
    2421             889 :   NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY);
    2422                 : 
    2423                 :   // get the results from the history service
    2424             889 :   nsresult rv = VerifyQueriesParsed();
    2425             889 :   NS_ENSURE_SUCCESS(rv, rv);
    2426             889 :   rv = history->GetQueryResults(this, mQueries, mOptions, &mChildren);
    2427             889 :   NS_ENSURE_SUCCESS(rv, rv);
    2428                 : 
    2429                 :   // it is important to call FillStats to fill in the parents on all
    2430                 :   // nodes and the result node pointers on the containers
    2431             889 :   FillStats();
    2432                 : 
    2433             889 :   PRUint16 sortType = GetSortType();
    2434                 : 
    2435             889 :   if (mResult->mNeedsToApplySortingMode) {
    2436                 :     // We should repopulate container and then apply sortingMode.  To avoid
    2437                 :     // sorting 2 times we simply do that here.
    2438             324 :     mResult->SetSortingMode(mResult->mSortingMode);
    2439                 :   }
    2440             565 :   else if (mOptions->QueryType() != nsINavHistoryQueryOptions::QUERY_TYPE_HISTORY ||
    2441                 :            sortType != nsINavHistoryQueryOptions::SORT_BY_NONE) {
    2442                 :     // The default SORT_BY_NONE sorts by the bookmark index (position), 
    2443                 :     // which we do not have for history queries.
    2444                 :     // Once we've computed all tree stats, we can sort, because containers will
    2445                 :     // then have proper visit counts and dates.
    2446             379 :     SortComparator comparator = GetSortingComparator(GetSortType());
    2447             379 :     if (comparator) {
    2448             758 :       nsCAutoString sortingAnnotation;
    2449             379 :       GetSortingAnnotation(sortingAnnotation);
    2450                 :       // Usually containers queries results comes already sorted from the
    2451                 :       // database, but some locales could have special rules to sort by title.
    2452                 :       // RecursiveSort won't apply these rules to containers in containers
    2453                 :       // queries because when setting sortingMode on the result we want to sort
    2454                 :       // contained items (bug 473157).
    2455                 :       // Base container RecursiveSort will sort both our children and all
    2456                 :       // descendants, and is used in this case because we have to do manual
    2457                 :       // title sorting.
    2458                 :       // Query RecursiveSort will instead only sort descendants if we are a
    2459                 :       // constinaersQuery, e.g. a grouped query that will return other queries.
    2460                 :       // For other type of queries it will act as the base one.
    2461             415 :       if (IsContainersQuery() &&
    2462              36 :           sortType == mOptions->SortingMode() &&
    2463                 :           (sortType == nsINavHistoryQueryOptions::SORT_BY_TITLE_ASCENDING ||
    2464                 :            sortType == nsINavHistoryQueryOptions::SORT_BY_TITLE_DESCENDING))
    2465              18 :         nsNavHistoryContainerResultNode::RecursiveSort(sortingAnnotation.get(), comparator);
    2466                 :       else
    2467             361 :         RecursiveSort(sortingAnnotation.get(), comparator);
    2468                 :     }
    2469                 :   }
    2470                 : 
    2471                 :   // if we are limiting our results remove items from the end of the
    2472                 :   // mChildren array after sorting. This is done for root node only.
    2473                 :   // note, if count < max results, we won't do anything.
    2474             889 :   if (!mParent && mOptions->MaxResults()) {
    2475             254 :     while ((PRUint32)mChildren.Count() > mOptions->MaxResults())
    2476               0 :       mChildren.RemoveObjectAt(mChildren.Count() - 1);
    2477                 :   }
    2478                 : 
    2479             889 :   nsNavHistoryResult* result = GetResult();
    2480             889 :   NS_ENSURE_STATE(result);
    2481                 : 
    2482            1190 :   if (mOptions->QueryType() == nsINavHistoryQueryOptions::QUERY_TYPE_HISTORY ||
    2483             301 :       mOptions->QueryType() == nsINavHistoryQueryOptions::QUERY_TYPE_UNIFIED) {
    2484                 :     // Date containers that contain site containers have no reason to observe
    2485                 :     // history, if the inside site container is expanded it will update,
    2486                 :     // otherwise we are going to refresh the parent query.
    2487             588 :     if (!mParent || mParent->mOptions->ResultType() != nsINavHistoryQueryOptions::RESULTS_AS_DATE_SITE_QUERY) {
    2488                 :       // register with the result for history updates
    2489             543 :       result->AddHistoryObserver(this);
    2490                 :     }
    2491                 :   }
    2492                 : 
    2493            1477 :   if (mOptions->QueryType() == nsINavHistoryQueryOptions::QUERY_TYPE_BOOKMARKS ||
    2494             588 :       mOptions->QueryType() == nsINavHistoryQueryOptions::QUERY_TYPE_UNIFIED ||
    2495                 :       mLiveUpdate == QUERYUPDATE_COMPLEX_WITH_BOOKMARKS ||
    2496                 :       mHasSearchTerms) {
    2497                 :     // register with the result for bookmark updates
    2498             358 :     result->AddAllBookmarksObserver(this);
    2499                 :   }
    2500                 : 
    2501             889 :   mContentsValid = true;
    2502             889 :   return NS_OK;
    2503                 : }
    2504                 : 
    2505                 : 
    2506                 : /**
    2507                 :  * Call with unregister = false when we are going to update the children (for
    2508                 :  * example, when the container is open).  This will clear the list and notify
    2509                 :  * all the children that they are going away.
    2510                 :  *
    2511                 :  * When the results are becoming invalid and we are not going to refresh them,
    2512                 :  * set unregister = true, which will unregister the listener from the
    2513                 :  * result if any.  We use unregister = false when we are refreshing the list
    2514                 :  * immediately so want to stay a notifier.
    2515                 :  */
    2516                 : void
    2517            1432 : nsNavHistoryQueryResultNode::ClearChildren(bool aUnregister)
    2518                 : {
    2519            3677 :   for (PRInt32 i = 0; i < mChildren.Count(); ++i)
    2520            2245 :     mChildren[i]->OnRemoving();
    2521            1432 :   mChildren.Clear();
    2522                 : 
    2523            1432 :   if (aUnregister && mContentsValid) {
    2524             864 :     nsNavHistoryResult* result = GetResult();
    2525             864 :     if (result) {
    2526             864 :       result->RemoveHistoryObserver(this);
    2527             864 :       result->RemoveAllBookmarksObserver(this);
    2528                 :     }
    2529                 :   }
    2530            1432 :   mContentsValid = false;
    2531            1432 : }
    2532                 : 
    2533                 : 
    2534                 : /**
    2535                 :  * This is called to update the result when something has changed that we
    2536                 :  * can not incrementally update.
    2537                 :  */
    2538                 : nsresult
    2539              97 : nsNavHistoryQueryResultNode::Refresh()
    2540                 : {
    2541              97 :   nsNavHistoryResult* result = GetResult();
    2542              97 :   NS_ENSURE_STATE(result);
    2543              97 :   if (result->mBatchInProgress) {
    2544              59 :     result->requestRefresh(this);
    2545              59 :     return NS_OK;
    2546                 :   }
    2547                 : 
    2548                 :   // This is not a root node but it does not have a parent - this means that 
    2549                 :   // the node has already been cleared and it is now called, because it was 
    2550                 :   // left in a local copy of the observers array.
    2551              38 :   if (mIndentLevel > -1 && !mParent)
    2552               0 :     return NS_OK;
    2553                 : 
    2554                 :   // Do not refresh if we are not expanded or if we are child of a query
    2555                 :   // containing other queries.  In this case calling Refresh for each child
    2556                 :   // query could cause a major slowdown.  We should not refresh nested
    2557                 :   // queries, since we will already refresh the parent one.
    2558              74 :   if (!mExpanded ||
    2559              36 :       (mParent && mParent->IsQuery() &&
    2560               0 :        mParent->GetAsQuery()->IsContainersQuery())) {
    2561                 :     // Don't update, just invalidate and unhook
    2562               6 :     ClearChildren(true);
    2563               6 :     return NS_OK; // no updates in tree state
    2564                 :   }
    2565                 : 
    2566              32 :   if (mLiveUpdate == QUERYUPDATE_COMPLEX_WITH_BOOKMARKS)
    2567               7 :     ClearChildren(true);
    2568                 :   else
    2569              25 :     ClearChildren(false);
    2570                 : 
    2571                 :   // Ignore errors from FillChildren, since we will still want to refresh
    2572                 :   // the tree (there just might not be anything in it on error).
    2573              32 :   (void)FillChildren();
    2574                 : 
    2575              32 :   NOTIFY_RESULT_OBSERVERS(result, InvalidateContainer(TO_CONTAINER(this)));
    2576              32 :   return NS_OK;
    2577                 : }
    2578                 : 
    2579                 : 
    2580                 : /**
    2581                 :  * Here, we override GetSortType to return the current sorting for this
    2582                 :  * query.  GetSortType is used when dynamically inserting query results so we
    2583                 :  * can see which comparator we should use to find the proper insertion point
    2584                 :  * (it shouldn't be called from folder containers which maintain their own
    2585                 :  * sorting).
    2586                 :  *
    2587                 :  * Normally, the container just forwards it up the chain.  This is what we want
    2588                 :  * for host groups, for example.  For queries, we often want to use the query's
    2589                 :  * sorting mode.
    2590                 :  *
    2591                 :  * However, we only use this query node's sorting when it is not the root.
    2592                 :  * When it is the root, we use the result's sorting mode.  This is because
    2593                 :  * there are two cases:
    2594                 :  *   - You are looking at a bookmark hierarchy that contains an embedded
    2595                 :  *     result.  We should always use the query's sort ordering since the result
    2596                 :  *     node's headers have nothing to do with us (and are disabled).
    2597                 :  *   - You are looking at a query in the tree.  In this case, we want the
    2598                 :  *     result sorting to override ours (it should be initialized to the same
    2599                 :  *     sorting mode).
    2600                 :  */
    2601                 : PRUint16
    2602            1359 : nsNavHistoryQueryResultNode::GetSortType()
    2603                 : {
    2604            1359 :   if (mParent)
    2605             282 :     return mOptions->SortingMode();
    2606            1077 :   if (mResult)
    2607            1077 :     return mResult->mSortingMode;
    2608                 : 
    2609               0 :   NS_NOTREACHED("We should always have a result");
    2610               0 :   return nsINavHistoryQueryOptions::SORT_BY_NONE;
    2611                 : }
    2612                 : 
    2613                 : 
    2614                 : void
    2615             408 : nsNavHistoryQueryResultNode::GetSortingAnnotation(nsACString& aAnnotation) {
    2616             408 :   if (mParent) {
    2617                 :     // use our sorting, we are not the root
    2618              46 :     mOptions->GetSortingAnnotation(aAnnotation);
    2619                 :   }
    2620             362 :   else if (mResult) {
    2621             362 :     aAnnotation.Assign(mResult->mSortingAnnotation);
    2622                 :   }
    2623                 :   else
    2624               0 :     NS_NOTREACHED("We should always have a result");
    2625             408 : }
    2626                 : 
    2627                 : void
    2628            1737 : nsNavHistoryQueryResultNode::RecursiveSort(
    2629                 :     const char* aData, SortComparator aComparator)
    2630                 : {
    2631            1737 :   void* data = const_cast<void*>(static_cast<const void*>(aData));
    2632                 : 
    2633            1737 :   if (!IsContainersQuery())
    2634            1116 :     mChildren.Sort(aComparator, data);
    2635                 : 
    2636            3927 :   for (PRInt32 i = 0; i < mChildren.Count(); ++i) {
    2637            2190 :     if (mChildren[i]->IsContainer())
    2638             864 :       mChildren[i]->GetAsContainer()->RecursiveSort(aData, aComparator);
    2639                 :   }
    2640            1737 : }
    2641                 : 
    2642                 : 
    2643                 : NS_IMETHODIMP
    2644             130 : nsNavHistoryQueryResultNode::OnBeginUpdateBatch()
    2645                 : {
    2646             130 :   return NS_OK;
    2647                 : }
    2648                 : 
    2649                 : 
    2650                 : NS_IMETHODIMP
    2651             130 : nsNavHistoryQueryResultNode::OnEndUpdateBatch()
    2652                 : {
    2653                 :   // If the query has no children it's possible it's not yet listening to
    2654                 :   // bookmarks changes, in such a case it's safer to force a refresh to gather
    2655                 :   // eventual new nodes matching query options.
    2656             130 :   if (mChildren.Count() == 0) {
    2657              27 :     nsresult rv = Refresh();
    2658              27 :     NS_ENSURE_SUCCESS(rv, rv);
    2659                 :   }
    2660                 : 
    2661             130 :   mBatchChanges = 0;
    2662             130 :   return NS_OK;
    2663                 : }
    2664                 : 
    2665                 : 
    2666                 : /**
    2667                 :  * Here we need to update all copies of the URI we have with the new visit
    2668                 :  * count, and potentially add a new entry in our query.  This is the most
    2669                 :  * common update operation and it is important that it be as efficient as
    2670                 :  * possible.
    2671                 :  */
    2672                 : NS_IMETHODIMP
    2673              43 : nsNavHistoryQueryResultNode::OnVisit(nsIURI* aURI, PRInt64 aVisitId,
    2674                 :                                      PRTime aTime, PRInt64 aSessionId,
    2675                 :                                      PRInt64 aReferringId,
    2676                 :                                      PRUint32 aTransitionType,
    2677                 :                                      const nsACString& aGUID,
    2678                 :                                      PRUint32* aAdded)
    2679                 : {
    2680              43 :   nsNavHistoryResult* result = GetResult();
    2681              43 :   NS_ENSURE_STATE(result);
    2682              43 :   if (result->mBatchInProgress &&
    2683                 :       ++mBatchChanges > MAX_BATCH_CHANGES_BEFORE_REFRESH) {
    2684               0 :     nsresult rv = Refresh();
    2685               0 :     NS_ENSURE_SUCCESS(rv, rv);
    2686               0 :     return NS_OK;
    2687                 :   }
    2688                 : 
    2689              43 :   nsNavHistory* history = nsNavHistory::GetHistoryService();
    2690              43 :   NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY);
    2691                 : 
    2692                 :   nsresult rv;
    2693              86 :   nsRefPtr<nsNavHistoryResultNode> addition;
    2694              43 :   switch(mLiveUpdate) {
    2695                 : 
    2696                 :     case QUERYUPDATE_HOST: {
    2697                 :       // For these simple yet common cases we can check the host ourselves
    2698                 :       // before doing the overhead of creating a new result node.
    2699              14 :       NS_ASSERTION(mQueries.Count() == 1, 
    2700                 :           "Host updated queries can have only one object");
    2701                 :       nsCOMPtr<nsNavHistoryQuery> queryHost = 
    2702              28 :           do_QueryInterface(mQueries[0], &rv);
    2703              14 :       NS_ENSURE_SUCCESS(rv, rv);
    2704                 : 
    2705                 :       bool hasDomain;
    2706              14 :       queryHost->GetHasDomain(&hasDomain);
    2707              14 :       if (!hasDomain)
    2708               0 :         return NS_OK;
    2709                 : 
    2710              28 :       nsCAutoString host;
    2711              14 :       if (NS_FAILED(aURI->GetAsciiHost(host)))
    2712               0 :         return NS_OK;
    2713                 : 
    2714              14 :       if (!queryHost->Domain().Equals(host))
    2715               3 :         return NS_OK;
    2716                 : 
    2717                 :     } // Let it fall through - we want to check the time too,
    2718                 :       // if the time is not present it will match too.
    2719                 :     case QUERYUPDATE_TIME: {
    2720                 :       // For these simple yet common cases we can check the time ourselves
    2721                 :       // before doing the overhead of creating a new result node.
    2722              19 :       NS_ASSERTION(mQueries.Count() == 1, 
    2723                 :           "Time updated queries can have only one object");
    2724                 :       nsCOMPtr<nsNavHistoryQuery> query = 
    2725              38 :           do_QueryInterface(mQueries[0], &rv);
    2726              19 :       NS_ENSURE_SUCCESS(rv, rv);
    2727                 : 
    2728                 :       bool hasIt;
    2729              19 :       query->GetHasBeginTime(&hasIt);
    2730              19 :       if (hasIt) {
    2731                 :         PRTime beginTime = history->NormalizeTime(query->BeginTimeReference(),
    2732               6 :                                                   query->BeginTime());
    2733               6 :         if (aTime < beginTime)
    2734               0 :           return NS_OK; // before our time range
    2735                 :       }
    2736              19 :       query->GetHasEndTime(&hasIt);
    2737              19 :       if (hasIt) {
    2738                 :         PRTime endTime = history->NormalizeTime(query->EndTimeReference(),
    2739               6 :                                                 query->EndTime());
    2740               6 :         if (aTime > endTime)
    2741               2 :           return NS_OK; // after our time range
    2742                 :       }
    2743                 :       // Now we know that our visit satisfies the time range, fallback to the
    2744                 :       // QUERYUPDATE_SIMPLE case.
    2745                 :     }
    2746                 :     case QUERYUPDATE_SIMPLE: {
    2747                 :       // The history service can tell us whether the new item should appear
    2748                 :       // in the result.  We first have to construct a node for it to check.
    2749                 :       rv = history->VisitIdToResultNode(aVisitId, mOptions,
    2750              37 :                                         getter_AddRefs(addition));
    2751              70 :       if (NS_FAILED(rv) || !addition ||
    2752              33 :           !history->EvaluateQueryForNode(mQueries, mOptions, addition))
    2753              22 :         return NS_OK; // don't need to include in our query
    2754                 :       break;
    2755                 :     }
    2756                 :     case QUERYUPDATE_COMPLEX:
    2757                 :     case QUERYUPDATE_COMPLEX_WITH_BOOKMARKS:
    2758                 :       // need to requery in complex cases
    2759               1 :       return Refresh();
    2760                 :     default:
    2761               0 :       NS_NOTREACHED("Invalid value for mLiveUpdate");
    2762               0 :       return Refresh();
    2763                 :   }
    2764                 : 
    2765                 :   // NOTE: The dynamic updating never deletes any nodes.  Sometimes it replaces
    2766                 :   // URI nodes or adds visits, but never deletes old ones.
    2767                 :   //
    2768                 :   // The only time this might happen in the current implementation is if the
    2769                 :   // title changes and it no longer matches a keyword search.  This is not
    2770                 :   // important enough to handle given that we don't do any other deleting.
    2771                 :   // It is arguably more useful behavior anyway, since you're searching your
    2772                 :   // history and the page used to match.
    2773                 :   //
    2774                 :   // When more queries are possible (show pages I've visited less than 5 times)
    2775                 :   // this will be important to add.
    2776                 : 
    2777              30 :   nsCOMArray<nsNavHistoryResultNode> mergerNode;
    2778                 : 
    2779              15 :   if (!mergerNode.AppendObject(addition))
    2780               0 :     return NS_ERROR_OUT_OF_MEMORY;
    2781                 : 
    2782              15 :   MergeResults(&mergerNode);
    2783                 : 
    2784              15 :   if (aAdded)
    2785              15 :     ++(*aAdded);
    2786                 : 
    2787              15 :   return NS_OK;
    2788                 : }
    2789                 : 
    2790                 : 
    2791                 : /**
    2792                 :  * Find every node that matches this URI and rename it.  We try to do
    2793                 :  * incremental updates here, even when we are closed, because changing titles
    2794                 :  * is easier than requerying if we are invalid.
    2795                 :  *
    2796                 :  * This actually gets called a lot.  Typically, we will get an AddURI message
    2797                 :  * when the user visits the page, and then the title will be set asynchronously
    2798                 :  * when the title element of the page is parsed.
    2799                 :  */
    2800                 : NS_IMETHODIMP
    2801              63 : nsNavHistoryQueryResultNode::OnTitleChanged(nsIURI* aURI,
    2802                 :                                             const nsAString& aPageTitle,
    2803                 :                                             const nsACString& aGUID)
    2804                 : {
    2805              63 :   if (!mExpanded) {
    2806                 :     // When we are not expanded, we don't update, just invalidate and unhook.
    2807                 :     // It would still be pretty easy to traverse the results and update the
    2808                 :     // titles, but when a title changes, its unlikely that it will be the only
    2809                 :     // thing.  Therefore, we just give up.
    2810               0 :     ClearChildren(true);
    2811               0 :     return NS_OK; // no updates in tree state
    2812                 :   }
    2813                 : 
    2814              63 :   nsNavHistoryResult* result = GetResult();
    2815              63 :   NS_ENSURE_STATE(result);
    2816              63 :   if (result->mBatchInProgress &&
    2817                 :       ++mBatchChanges > MAX_BATCH_CHANGES_BEFORE_REFRESH) {
    2818               2 :     nsresult rv = Refresh();
    2819               2 :     NS_ENSURE_SUCCESS(rv, rv);
    2820               2 :     return NS_OK;
    2821                 :   }
    2822                 : 
    2823                 :   // compute what the new title should be
    2824             122 :   NS_ConvertUTF16toUTF8 newTitle(aPageTitle);
    2825                 : 
    2826              61 :   bool onlyOneEntry = (mOptions->ResultType() ==
    2827                 :                          nsINavHistoryQueryOptions::RESULTS_AS_URI ||
    2828              24 :                          mOptions->ResultType() ==
    2829                 :                          nsINavHistoryQueryOptions::RESULTS_AS_TAG_CONTENTS
    2830              85 :                          );
    2831                 : 
    2832                 :   // See if our queries have any search term matching.
    2833              61 :   if (mHasSearchTerms) {
    2834                 :     // Find all matching URI nodes.
    2835              96 :     nsCOMArray<nsNavHistoryResultNode> matches;
    2836              96 :     nsCAutoString spec;
    2837              48 :     nsresult rv = aURI->GetSpec(spec);
    2838              48 :     NS_ENSURE_SUCCESS(rv, rv);
    2839              48 :     RecursiveFindURIs(onlyOneEntry, this, spec, &matches);
    2840              48 :     if (matches.Count() == 0) {
    2841                 :       // This could be a new node matching the query, thus we could need
    2842                 :       // to add it to the result.
    2843              44 :       nsRefPtr<nsNavHistoryResultNode> node;
    2844              22 :       nsNavHistory* history = nsNavHistory::GetHistoryService();
    2845              22 :       NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY);
    2846              22 :       rv = history->URIToResultNode(aURI, mOptions, getter_AddRefs(node));
    2847              22 :       NS_ENSURE_SUCCESS(rv, rv);
    2848              22 :       if (history->EvaluateQueryForNode(mQueries, mOptions, node)) {
    2849              21 :         rv = InsertSortedChild(node, true);
    2850              21 :         NS_ENSURE_SUCCESS(rv, rv);
    2851                 :       }
    2852                 :     }
    2853              86 :     for (PRInt32 i = 0; i < matches.Count(); ++i) {
    2854                 :       // For each matched node we check if it passes the query filter, if not
    2855                 :       // we remove the node from the result, otherwise we'll update the title
    2856                 :       // later.
    2857              38 :       nsNavHistoryResultNode* node = matches[i];
    2858                 :       // We must check the node with the new title.
    2859              38 :       node->mTitle = newTitle;
    2860                 : 
    2861              38 :       nsNavHistory* history = nsNavHistory::GetHistoryService();
    2862              38 :       NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY);
    2863              38 :       if (!history->EvaluateQueryForNode(mQueries, mOptions, node)) {
    2864              23 :         nsNavHistoryContainerResultNode* parent = node->mParent;
    2865                 :         // URI nodes should always have parents
    2866              23 :         NS_ENSURE_TRUE(parent, NS_ERROR_UNEXPECTED);
    2867              23 :         PRInt32 childIndex = parent->FindChild(node);
    2868              23 :         NS_ASSERTION(childIndex >= 0, "Child not found in parent");
    2869              23 :         parent->RemoveChildAt(childIndex);
    2870                 :       }
    2871                 :     }
    2872                 :   }
    2873                 : 
    2874              61 :   return ChangeTitles(aURI, newTitle, true, onlyOneEntry);
    2875                 : }
    2876                 : 
    2877                 : 
    2878                 : NS_IMETHODIMP
    2879               0 : nsNavHistoryQueryResultNode::OnBeforeDeleteURI(nsIURI* aURI,
    2880                 :                                                const nsACString& aGUID,
    2881                 :                                                PRUint16 aReason)
    2882                 : {
    2883               0 :   return NS_OK;
    2884                 : }
    2885                 : 
    2886                 : /**
    2887                 :  * Here, we can always live update by just deleting all occurrences of
    2888                 :  * the given URI.
    2889                 :  */
    2890                 : NS_IMETHODIMP
    2891              11 : nsNavHistoryQueryResultNode::OnDeleteURI(nsIURI* aURI,
    2892                 :                                          const nsACString& aGUID,
    2893                 :                                          PRUint16 aReason)
    2894                 : {
    2895              11 :   nsNavHistoryResult* result = GetResult();
    2896              11 :   NS_ENSURE_STATE(result);
    2897              11 :   if (result->mBatchInProgress &&
    2898                 :       ++mBatchChanges > MAX_BATCH_CHANGES_BEFORE_REFRESH) {
    2899               0 :     nsresult rv = Refresh();
    2900               0 :     NS_ENSURE_SUCCESS(rv, rv);
    2901               0 :     return NS_OK;
    2902                 :   }
    2903                 : 
    2904              11 :   if (IsContainersQuery()) {
    2905                 :     // Incremental updates of query returning queries are pretty much
    2906                 :     // complicated.  In this case it's possible one of the child queries has
    2907                 :     // no more children and it should be removed.  Unfortunately there is no
    2908                 :     // way to know that without executing the child query and counting results.
    2909               7 :     nsresult rv = Refresh();
    2910               7 :     NS_ENSURE_SUCCESS(rv, rv);
    2911               7 :     return NS_OK;
    2912                 :   }
    2913                 : 
    2914               4 :   bool onlyOneEntry = (mOptions->ResultType() ==
    2915                 :                          nsINavHistoryQueryOptions::RESULTS_AS_URI ||
    2916               2 :                          mOptions->ResultType() ==
    2917               6 :                          nsINavHistoryQueryOptions::RESULTS_AS_TAG_CONTENTS);
    2918               8 :   nsCAutoString spec;
    2919               4 :   nsresult rv = aURI->GetSpec(spec);
    2920               4 :   NS_ENSURE_SUCCESS(rv, rv);
    2921                 : 
    2922               8 :   nsCOMArray<nsNavHistoryResultNode> matches;
    2923               4 :   RecursiveFindURIs(onlyOneEntry, this, spec, &matches);
    2924               9 :   for (PRInt32 i = 0; i < matches.Count(); ++i) {
    2925               5 :     nsNavHistoryResultNode* node = matches[i];
    2926               5 :     nsNavHistoryContainerResultNode* parent = node->mParent;
    2927                 :     // URI nodes should always have parents
    2928               5 :     NS_ENSURE_TRUE(parent, NS_ERROR_UNEXPECTED);
    2929                 : 
    2930               5 :     PRInt32 childIndex = parent->FindChild(node);
    2931               5 :     NS_ASSERTION(childIndex >= 0, "Child not found in parent");
    2932               5 :     parent->RemoveChildAt(childIndex);
    2933               5 :     if (parent->mChildren.Count() == 0 && parent->IsQuery() &&
    2934                 :         parent->mIndentLevel > -1) {
    2935                 :       // When query subcontainers (like hosts) get empty we should remove them
    2936                 :       // as well.  If the parent is not the root node, append it to our list
    2937                 :       // and it will get evaluated later in the loop.
    2938               0 :       matches.AppendObject(parent);
    2939                 :     }
    2940                 :   }
    2941               4 :   return NS_OK;
    2942                 : }
    2943                 : 
    2944                 : 
    2945                 : NS_IMETHODIMP
    2946               1 : nsNavHistoryQueryResultNode::OnClearHistory()
    2947                 : {
    2948               1 :   nsresult rv = Refresh();
    2949               1 :   NS_ENSURE_SUCCESS(rv, rv);
    2950               1 :   return NS_OK;
    2951                 : }
    2952                 : 
    2953                 : 
    2954               0 : static nsresult setFaviconCallback(
    2955                 :    nsNavHistoryResultNode* aNode, void* aClosure,
    2956                 :    nsNavHistoryResult* aResult)
    2957                 : {
    2958               0 :   const nsCString* newFavicon = static_cast<nsCString*>(aClosure);
    2959               0 :   aNode->mFaviconURI = *newFavicon;
    2960                 : 
    2961               0 :   if (aResult && (!aNode->mParent || aNode->mParent->AreChildrenVisible()))
    2962               0 :     NOTIFY_RESULT_OBSERVERS(aResult, NodeIconChanged(aNode));
    2963                 : 
    2964               0 :   return NS_OK;
    2965                 : }
    2966                 : 
    2967                 : 
    2968                 : NS_IMETHODIMP
    2969               0 : nsNavHistoryQueryResultNode::OnPageChanged(nsIURI* aURI,
    2970                 :                                            PRUint32 aChangedAttribute,
    2971                 :                                            const nsAString& aNewValue,
    2972                 :                                            const nsACString& aGUID)
    2973                 : {
    2974               0 :   nsCAutoString spec;
    2975               0 :   nsresult rv = aURI->GetSpec(spec);
    2976               0 :   NS_ENSURE_SUCCESS(rv, rv);
    2977                 : 
    2978               0 :   switch (aChangedAttribute) {
    2979                 :     case nsINavHistoryObserver::ATTRIBUTE_FAVICON: {
    2980               0 :       NS_ConvertUTF16toUTF8 newFavicon(aNewValue);
    2981               0 :       bool onlyOneEntry = (mOptions->ResultType() ==
    2982                 :                              nsINavHistoryQueryOptions::RESULTS_AS_URI ||
    2983               0 :                              mOptions->ResultType() ==
    2984               0 :                              nsINavHistoryQueryOptions::RESULTS_AS_TAG_CONTENTS);
    2985                 :       rv = UpdateURIs(true, onlyOneEntry, false, spec, setFaviconCallback,
    2986               0 :                       &newFavicon);
    2987               0 :       NS_ENSURE_SUCCESS(rv, rv);
    2988               0 :       break;
    2989                 :     }
    2990                 :     default:
    2991               0 :       NS_WARNING("Unknown page changed notification");
    2992                 :   }
    2993               0 :   return NS_OK;
    2994                 : }
    2995                 : 
    2996                 : 
    2997                 : NS_IMETHODIMP
    2998               1 : nsNavHistoryQueryResultNode::OnDeleteVisits(nsIURI* aURI,
    2999                 :                                             PRTime aVisitTime,
    3000                 :                                             const nsACString& aGUID,
    3001                 :                                             PRUint16 aReason)
    3002                 : {
    3003               1 :   NS_PRECONDITION(mOptions->QueryType() == nsINavHistoryQueryOptions::QUERY_TYPE_HISTORY,
    3004                 :                   "Bookmarks queries should not get a OnDeleteVisits notification");
    3005               1 :   if (aVisitTime == 0) {
    3006                 :     // All visits for this uri have been removed, but the uri won't be removed
    3007                 :     // from the databse, most likely because it's a bookmark.  For a history
    3008                 :     // query this is equivalent to a onDeleteURI notification.
    3009               1 :     nsresult rv = OnDeleteURI(aURI, aGUID, aReason);
    3010               1 :     NS_ENSURE_SUCCESS(rv, rv);
    3011                 :   }
    3012                 : 
    3013               1 :   return NS_OK;
    3014                 : }
    3015                 : 
    3016                 : nsresult
    3017              36 : nsNavHistoryQueryResultNode::NotifyIfTagsChanged(nsIURI* aURI)
    3018                 : {
    3019              36 :   nsNavHistoryResult* result = GetResult();
    3020              36 :   NS_ENSURE_STATE(result);
    3021              72 :   nsCAutoString spec;
    3022              36 :   nsresult rv = aURI->GetSpec(spec);
    3023              36 :   NS_ENSURE_SUCCESS(rv, rv);
    3024              36 :   bool onlyOneEntry = (mOptions->ResultType() ==
    3025                 :                          nsINavHistoryQueryOptions::RESULTS_AS_URI ||
    3026              18 :                          mOptions->ResultType() ==
    3027                 :                          nsINavHistoryQueryOptions::RESULTS_AS_TAG_CONTENTS
    3028              54 :                          );
    3029                 : 
    3030                 :   // Find matching URI nodes.
    3031              72 :   nsRefPtr<nsNavHistoryResultNode> node;
    3032              36 :   nsNavHistory* history = nsNavHistory::GetHistoryService();
    3033                 : 
    3034              72 :   nsCOMArray<nsNavHistoryResultNode> matches;
    3035              36 :   RecursiveFindURIs(onlyOneEntry, this, spec, &matches);
    3036                 : 
    3037              36 :   if (matches.Count() == 0 && mHasSearchTerms && !mRemovingURI) {
    3038                 :     // A new tag has been added, it's possible it matches our query.
    3039               2 :     NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY);
    3040               2 :     rv = history->URIToResultNode(aURI, mOptions, getter_AddRefs(node));
    3041               2 :     NS_ENSURE_SUCCESS(rv, rv);
    3042               2 :     if (history->EvaluateQueryForNode(mQueries, mOptions, node)) {
    3043               2 :       rv = InsertSortedChild(node, true);
    3044               2 :       NS_ENSURE_SUCCESS(rv, rv);
    3045                 :     }
    3046                 :   }
    3047                 : 
    3048              66 :   for (PRInt32 i = 0; i < matches.Count(); ++i) {
    3049              30 :     nsNavHistoryResultNode* node = matches[i];
    3050                 :     // Force a tags update before checking the node.
    3051              30 :     node->mTags.SetIsVoid(true);
    3052              60 :     nsAutoString tags;
    3053              30 :     rv = node->GetTags(tags);
    3054              30 :     NS_ENSURE_SUCCESS(rv, rv);
    3055                 :     // It's possible now this node does not respect anymore the conditions.
    3056                 :     // In such a case it should be removed.
    3057              48 :     if (mHasSearchTerms &&
    3058              18 :         !history->EvaluateQueryForNode(mQueries, mOptions, node)) {
    3059               6 :       nsNavHistoryContainerResultNode* parent = node->mParent;
    3060                 :       // URI nodes should always have parents
    3061               6 :       NS_ENSURE_TRUE(parent, NS_ERROR_UNEXPECTED);
    3062               6 :       PRInt32 childIndex = parent->FindChild(node);
    3063               6 :       NS_ASSERTION(childIndex >= 0, "Child not found in parent");
    3064               6 :       parent->RemoveChildAt(childIndex);
    3065                 :     }
    3066                 :     else {
    3067              24 :       NOTIFY_RESULT_OBSERVERS(result, NodeTagsChanged(node));
    3068                 :     }
    3069                 :   }
    3070                 : 
    3071              36 :   return NS_OK;
    3072                 : }
    3073                 : 
    3074                 : /**
    3075                 :  * These are the bookmark observer functions for query nodes.  They listen
    3076                 :  * for bookmark events and refresh the results if we have any dependence on
    3077                 :  * the bookmark system.
    3078                 :  */
    3079                 : NS_IMETHODIMP
    3080              88 : nsNavHistoryQueryResultNode::OnItemAdded(PRInt64 aItemId,
    3081                 :                                          PRInt64 aParentId,
    3082                 :                                          PRInt32 aIndex,
    3083                 :                                          PRUint16 aItemType,
    3084                 :                                          nsIURI* aURI,
    3085                 :                                          const nsACString& aTitle,
    3086                 :                                          PRTime aDateAdded,
    3087                 :                                          const nsACString& aGUID,
    3088                 :                                          const nsACString& aParentGUID)
    3089                 : {
    3090              88 :   if (aItemType == nsINavBookmarksService::TYPE_BOOKMARK &&
    3091                 :       mLiveUpdate != QUERYUPDATE_SIMPLE &&  mLiveUpdate != QUERYUPDATE_TIME) {
    3092               6 :     nsresult rv = Refresh();
    3093               6 :     NS_ENSURE_SUCCESS(rv, rv);
    3094                 :   }
    3095              88 :   return NS_OK;
    3096                 : }
    3097                 : 
    3098                 : 
    3099                 : NS_IMETHODIMP
    3100              37 : nsNavHistoryQueryResultNode::OnBeforeItemRemoved(PRInt64 aItemId,
    3101                 :                                                  PRUint16 aItemType,
    3102                 :                                                  PRInt64 aParentId,
    3103                 :                                                  const nsACString& aGUID,
    3104                 :                                                  const nsACString& aParentGUID)
    3105                 : {
    3106              37 :   return NS_OK;
    3107                 : }
    3108                 : 
    3109                 : 
    3110                 : NS_IMETHODIMP
    3111              73 : nsNavHistoryQueryResultNode::OnItemRemoved(PRInt64 aItemId,
    3112                 :                                            PRInt64 aParentId,
    3113                 :                                            PRInt32 aIndex,
    3114                 :                                            PRUint16 aItemType,
    3115                 :                                            nsIURI* aURI,
    3116                 :                                            const nsACString& aGUID,
    3117                 :                                            const nsACString& aParentGUID)
    3118                 : {
    3119              73 :   mRemovingURI = aURI;
    3120              73 :   if (aItemType == nsINavBookmarksService::TYPE_BOOKMARK &&
    3121                 :       mLiveUpdate != QUERYUPDATE_SIMPLE && mLiveUpdate != QUERYUPDATE_TIME) {
    3122               1 :     nsresult rv = Refresh();
    3123               1 :     NS_ENSURE_SUCCESS(rv, rv);
    3124                 :   }
    3125              73 :   return NS_OK;
    3126                 : }
    3127                 : 
    3128                 : 
    3129                 : NS_IMETHODIMP
    3130              61 : nsNavHistoryQueryResultNode::OnItemChanged(PRInt64 aItemId,
    3131                 :                                            const nsACString& aProperty,
    3132                 :                                            bool aIsAnnotationProperty,
    3133                 :                                            const nsACString& aNewValue,
    3134                 :                                            PRTime aLastModified,
    3135                 :                                            PRUint16 aItemType,
    3136                 :                                            PRInt64 aParentId,
    3137                 :                                            const nsACString& aGUID,
    3138                 :                                            const nsACString& aParentGUID)
    3139                 : {
    3140                 :   // History observers should not get OnItemChanged
    3141                 :   // but should get the corresponding history notifications instead.
    3142                 :   // For bookmark queries, "all bookmark" observers should get OnItemChanged.
    3143                 :   // For example, when a title of a bookmark changes, we want that to refresh.
    3144                 : 
    3145              61 :   if (mLiveUpdate == QUERYUPDATE_COMPLEX_WITH_BOOKMARKS) {
    3146              20 :     switch (aItemType) {
    3147                 :       case nsINavBookmarksService::TYPE_SEPARATOR:
    3148                 :         // No separators in queries.
    3149               0 :         return NS_OK;
    3150                 :       case nsINavBookmarksService::TYPE_FOLDER:
    3151                 :         // Queries never result as "folders", but the tags-query results as
    3152                 :         // special "tag" containers, which should follow their corresponding
    3153                 :         // folders titles.
    3154               1 :         if (mOptions->ResultType() != nsINavHistoryQueryOptions::RESULTS_AS_TAG_QUERY)
    3155               0 :           return NS_OK;
    3156                 :       default:
    3157              20 :         (void)Refresh();
    3158                 :     }
    3159                 :   }
    3160                 :   else {
    3161                 :     // Some node could observe both bookmarks and history.  But a node observing
    3162                 :     // only history should never get a bookmark notification.
    3163              41 :     NS_WARN_IF_FALSE(mResult && (mResult->mIsAllBookmarksObserver || mResult->mIsBookmarkFolderObserver),
    3164                 :                      "history observers should not get OnItemChanged, but should get the corresponding history notifications instead");
    3165                 : 
    3166                 :     // Tags in history queries are a special case since tags are per uri and
    3167                 :     // we filter tags based on searchterms.
    3168              82 :     if (aItemType == nsINavBookmarksService::TYPE_BOOKMARK &&
    3169              41 :         aProperty.EqualsLiteral("tags")) {
    3170              36 :       nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService();
    3171              36 :       NS_ENSURE_TRUE(bookmarks, NS_ERROR_OUT_OF_MEMORY);
    3172              72 :       nsCOMPtr<nsIURI> uri;
    3173              36 :       nsresult rv = bookmarks->GetBookmarkURI(aItemId, getter_AddRefs(uri));
    3174              36 :       NS_ENSURE_SUCCESS(rv, rv);
    3175              36 :       rv = NotifyIfTagsChanged(uri);
    3176              36 :       NS_ENSURE_SUCCESS(rv, rv);
    3177                 :     }
    3178                 :   }
    3179                 : 
    3180                 :   return nsNavHistoryResultNode::OnItemChanged(aItemId, aProperty,
    3181                 :                                                aIsAnnotationProperty,
    3182                 :                                                aNewValue, aLastModified,
    3183                 :                                                aItemType, aParentId, aGUID,
    3184              61 :                                                aParentGUID);
    3185                 : }
    3186                 : 
    3187                 : NS_IMETHODIMP
    3188               0 : nsNavHistoryQueryResultNode::OnItemVisited(PRInt64 aItemId,
    3189                 :                                            PRInt64 aVisitId,
    3190                 :                                            PRTime aTime,
    3191                 :                                            PRUint32 aTransitionType,
    3192                 :                                            nsIURI* aURI,
    3193                 :                                            PRInt64 aParentId,
    3194                 :                                            const nsACString& aGUID,
    3195                 :                                            const nsACString& aParentGUID)
    3196                 : {
    3197                 :   // for bookmark queries, "all bookmark" observer should get OnItemVisited
    3198                 :   // but it is ignored.
    3199               0 :   if (mLiveUpdate != QUERYUPDATE_COMPLEX_WITH_BOOKMARKS)
    3200               0 :     NS_WARN_IF_FALSE(mResult && (mResult->mIsAllBookmarksObserver || mResult->mIsBookmarkFolderObserver),
    3201                 :                      "history observers should not get OnItemVisited, but should get OnVisit instead");
    3202               0 :   return NS_OK;
    3203                 : }
    3204                 : 
    3205                 : NS_IMETHODIMP
    3206               0 : nsNavHistoryQueryResultNode::OnItemMoved(PRInt64 aFolder,
    3207                 :                                          PRInt64 aOldParent,
    3208                 :                                          PRInt32 aOldIndex,
    3209                 :                                          PRInt64 aNewParent,
    3210                 :                                          PRInt32 aNewIndex,
    3211                 :                                          PRUint16 aItemType,
    3212                 :                                          const nsACString& aGUID,
    3213                 :                                          const nsACString& aOldParentGUID,
    3214                 :                                          const nsACString& aNewParentGUID)
    3215                 : {
    3216                 :   // 1. The query cannot be affected by the item's position
    3217                 :   // 2. For the time being, we cannot optimize this not to update
    3218                 :   //    queries which are not restricted to some folders, due to way
    3219                 :   //    sub-queries are updated (see Refresh)
    3220               0 :   if (mLiveUpdate == QUERYUPDATE_COMPLEX_WITH_BOOKMARKS &&
    3221                 :       aItemType != nsINavBookmarksService::TYPE_SEPARATOR &&
    3222                 :       aOldParent != aNewParent) {
    3223               0 :     return Refresh();
    3224                 :   }
    3225               0 :   return NS_OK;
    3226                 : }
    3227                 : 
    3228                 : /**
    3229                 :  * HOW DYNAMIC FOLDER UPDATING WORKS
    3230                 :  *
    3231                 :  * When you create a result, it will automatically keep itself in sync with
    3232                 :  * stuff that happens in the system.  For folder nodes, this means changes to
    3233                 :  * bookmarks.
    3234                 :  *
    3235                 :  * A folder will fill its children "when necessary." This means it is being
    3236                 :  * opened or whether we need to see if it is empty for twisty drawing.  It will
    3237                 :  * then register its ID with the main result object that owns it.  This result
    3238                 :  * object will listen for all bookmark notifications and pass those
    3239                 :  * notifications to folder nodes that have registered for that specific folder
    3240                 :  * ID.
    3241                 :  *
    3242                 :  * When a bookmark folder is closed, it will not clear its children.  Instead,
    3243                 :  * it will keep them and also stay registered as a listener.  This means that
    3244                 :  * you can more quickly re-open the same folder without doing any work.  This
    3245                 :  * happens a lot for menus, and bookmarks don't change very often.
    3246                 :  *
    3247                 :  * When a message comes in and the folder is open, we will do the correct
    3248                 :  * operations to keep ourselves in sync with the bookmark service.  If the
    3249                 :  * folder is closed, we just clear our list to mark it as invalid and
    3250                 :  * unregister as a listener.  This means we do not have to keep maintaining
    3251                 :  * an up-to-date list for the entire bookmark menu structure in every place
    3252                 :  * it is used.
    3253                 :  */
    3254           54313 : NS_IMPL_ISUPPORTS_INHERITED1(nsNavHistoryFolderResultNode,
    3255                 :                              nsNavHistoryContainerResultNode,
    3256                 :                              nsINavHistoryQueryResultNode)
    3257                 : 
    3258            1043 : nsNavHistoryFolderResultNode::nsNavHistoryFolderResultNode(
    3259                 :     const nsACString& aTitle, nsNavHistoryQueryOptions* aOptions,
    3260                 :     PRInt64 aFolderId) :
    3261            2086 :   nsNavHistoryContainerResultNode(EmptyCString(), aTitle, EmptyCString(),
    3262                 :                                   nsNavHistoryResultNode::RESULT_TYPE_FOLDER,
    3263                 :                                   false, aOptions),
    3264                 :   mContentsValid(false),
    3265                 :   mQueryItemId(-1),
    3266            2086 :   mIsRegisteredFolderObserver(false)
    3267                 : {
    3268            1043 :   mItemId = aFolderId;
    3269            1043 : }
    3270                 : 
    3271            3129 : nsNavHistoryFolderResultNode::~nsNavHistoryFolderResultNode()
    3272                 : {
    3273            1043 :   if (mIsRegisteredFolderObserver && mResult)
    3274               0 :     mResult->RemoveBookmarkFolderObserver(this, mItemId);
    3275            4172 : }
    3276                 : 
    3277                 : 
    3278                 : /**
    3279                 :  * Here we do not want to call ContainerResultNode::OnRemoving since our own
    3280                 :  * ClearChildren will do the same thing and more (unregister the observers).
    3281                 :  * The base ResultNode::OnRemoving will clear some regular node stats, so it is
    3282                 :  * OK.
    3283                 :  */
    3284                 : void
    3285             646 : nsNavHistoryFolderResultNode::OnRemoving()
    3286                 : {
    3287             646 :   nsNavHistoryResultNode::OnRemoving();
    3288             646 :   ClearChildren(true);
    3289             646 : }
    3290                 : 
    3291                 : 
    3292                 : nsresult
    3293             771 : nsNavHistoryFolderResultNode::OpenContainer()
    3294                 : {
    3295             771 :   NS_ASSERTION(!mExpanded, "Container must be expanded to close it");
    3296                 :   nsresult rv;
    3297                 : 
    3298             771 :   if (!mContentsValid) {
    3299             750 :     rv = FillChildren();
    3300             750 :     NS_ENSURE_SUCCESS(rv, rv);
    3301                 :   }
    3302             771 :   mExpanded = true;
    3303                 : 
    3304             771 :   rv = NotifyOnStateChange(STATE_CLOSED);
    3305             771 :   NS_ENSURE_SUCCESS(rv, rv);
    3306                 : 
    3307             771 :   return NS_OK;
    3308                 : }
    3309                 : 
    3310                 : 
    3311                 : /**
    3312                 :  * The async version of OpenContainer.
    3313                 :  */
    3314                 : nsresult
    3315               4 : nsNavHistoryFolderResultNode::OpenContainerAsync()
    3316                 : {
    3317               4 :   NS_ASSERTION(!mExpanded, "Container already expanded when opening it");
    3318                 : 
    3319                 :   // If the children are valid, open the container synchronously.  This will be
    3320                 :   // the case when the container has already been opened and any other time
    3321                 :   // FillChildren or FillChildrenAsync has previously been called.
    3322               4 :   if (mContentsValid)
    3323               1 :     return OpenContainer();
    3324                 : 
    3325               3 :   nsresult rv = FillChildrenAsync();
    3326               3 :   NS_ENSURE_SUCCESS(rv, rv);
    3327                 : 
    3328               3 :   rv = NotifyOnStateChange(STATE_CLOSED);
    3329               3 :   NS_ENSURE_SUCCESS(rv, rv);
    3330                 : 
    3331               3 :   return NS_OK;
    3332                 : }
    3333                 : 
    3334                 : 
    3335                 : /**
    3336                 :  * @see nsNavHistoryQueryResultNode::HasChildren.  The semantics here are a
    3337                 :  * little different.  Querying the contents of a bookmark folder is relatively
    3338                 :  * fast and it is common to have empty folders.  Therefore, we always want to
    3339                 :  * return the correct result so that twisties are drawn properly.
    3340                 :  */
    3341                 : NS_IMETHODIMP
    3342              22 : nsNavHistoryFolderResultNode::GetHasChildren(bool* aHasChildren)
    3343                 : {
    3344              22 :   if (!mContentsValid) {
    3345              16 :     nsresult rv = FillChildren();
    3346              16 :     NS_ENSURE_SUCCESS(rv, rv);
    3347                 :   }
    3348              22 :   *aHasChildren = (mChildren.Count() > 0);
    3349              22 :   return NS_OK;
    3350                 : }
    3351                 : 
    3352                 : /**
    3353                 :  * @return the id of the item from which the folder node was generated, it
    3354                 :  * could be either a concrete folder-itemId or the id used in a
    3355                 :  * simple-folder-query-bookmark (place:folder=X).
    3356                 :  */
    3357                 : NS_IMETHODIMP
    3358            2419 : nsNavHistoryFolderResultNode::GetItemId(PRInt64* aItemId)
    3359                 : {
    3360            2419 :   *aItemId = mQueryItemId == -1 ? mItemId : mQueryItemId;
    3361            2419 :   return NS_OK;
    3362                 : }
    3363                 : 
    3364                 : /**
    3365                 :  * Here, we override the getter and ignore the value stored in our object.
    3366                 :  * The bookmarks service can tell us whether this folder should be read-only
    3367                 :  * or not.
    3368                 :  *
    3369                 :  * It would be nice to put this code in the folder constructor, but the
    3370                 :  * database was complaining.  I believe it is because most folders are created
    3371                 :  * while enumerating the bookmarks table and having a statement open, and doing
    3372                 :  * another statement might make it unhappy in some cases.
    3373                 :  */
    3374                 : NS_IMETHODIMP
    3375               0 : nsNavHistoryFolderResultNode::GetChildrenReadOnly(bool *aChildrenReadOnly)
    3376                 : {
    3377               0 :   nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService();
    3378               0 :   NS_ENSURE_TRUE(bookmarks, NS_ERROR_UNEXPECTED);
    3379               0 :   return bookmarks->GetFolderReadonly(mItemId, aChildrenReadOnly);
    3380                 : }
    3381                 : 
    3382                 : 
    3383                 : NS_IMETHODIMP
    3384              32 : nsNavHistoryFolderResultNode::GetFolderItemId(PRInt64* aItemId)
    3385                 : {
    3386              32 :   *aItemId = mItemId;
    3387              32 :   return NS_OK;
    3388                 : }
    3389                 : 
    3390                 : /**
    3391                 :  * Lazily computes the URI for this specific folder query with the current
    3392                 :  * options.
    3393                 :  */
    3394                 : NS_IMETHODIMP
    3395              47 : nsNavHistoryFolderResultNode::GetUri(nsACString& aURI)
    3396                 : {
    3397              47 :   if (!mURI.IsEmpty()) {
    3398               0 :     aURI = mURI;
    3399               0 :     return NS_OK;
    3400                 :   }
    3401                 : 
    3402                 :   PRUint32 queryCount;
    3403                 :   nsINavHistoryQuery** queries;
    3404              47 :   nsresult rv = GetQueries(&queryCount, &queries);
    3405              47 :   NS_ENSURE_SUCCESS(rv, rv);
    3406                 : 
    3407              47 :   nsNavHistory* history = nsNavHistory::GetHistoryService();
    3408              47 :   NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY);
    3409                 : 
    3410              47 :   rv = history->QueriesToQueryString(queries, queryCount, mOptions, aURI);
    3411              94 :   for (PRUint32 queryIndex = 0; queryIndex < queryCount; ++queryIndex) {
    3412              47 :     NS_RELEASE(queries[queryIndex]);
    3413                 :   }
    3414              47 :   nsMemory::Free(queries);
    3415              47 :   return rv;
    3416                 : }
    3417                 : 
    3418                 : 
    3419                 : /**
    3420                 :  * @return the queries that give you this bookmarks folder
    3421                 :  */
    3422                 : NS_IMETHODIMP
    3423              47 : nsNavHistoryFolderResultNode::GetQueries(PRUint32* queryCount,
    3424                 :                                          nsINavHistoryQuery*** queries)
    3425                 : {
    3426                 :   // get the query object
    3427              94 :   nsCOMPtr<nsINavHistoryQuery> query;
    3428              47 :   nsNavHistory* history = nsNavHistory::GetHistoryService();
    3429              47 :   NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY);
    3430              47 :   nsresult rv = history->GetNewQuery(getter_AddRefs(query));
    3431              47 :   NS_ENSURE_SUCCESS(rv, rv);
    3432                 : 
    3433                 :   // query just has the folder ID set and nothing else
    3434              47 :   rv = query->SetFolders(&mItemId, 1);
    3435              47 :   NS_ENSURE_SUCCESS(rv, rv);
    3436                 : 
    3437                 :   // make array of our 1 query
    3438                 :   *queries = static_cast<nsINavHistoryQuery**>
    3439              47 :                         (nsMemory::Alloc(sizeof(nsINavHistoryQuery*)));
    3440              47 :   if (!*queries)
    3441               0 :     return NS_ERROR_OUT_OF_MEMORY;
    3442              47 :   NS_ADDREF((*queries)[0] = query);
    3443              47 :   *queryCount = 1;
    3444              47 :   return NS_OK;
    3445                 : }
    3446                 : 
    3447                 : 
    3448                 : /**
    3449                 :  * Options for the query that gives you this bookmarks folder.  This is just
    3450                 :  * the options for the folder with the current folder ID set.
    3451                 :  */
    3452                 : NS_IMETHODIMP
    3453              70 : nsNavHistoryFolderResultNode::GetQueryOptions(
    3454                 :                                       nsINavHistoryQueryOptions** aQueryOptions)
    3455                 : {
    3456              70 :   NS_ASSERTION(mOptions, "Options invalid");
    3457                 : 
    3458              70 :   *aQueryOptions = mOptions;
    3459              70 :   NS_ADDREF(*aQueryOptions);
    3460              70 :   return NS_OK;
    3461                 : }
    3462                 : 
    3463                 : 
    3464                 : nsresult
    3465             766 : nsNavHistoryFolderResultNode::FillChildren()
    3466                 : {
    3467             766 :   NS_ASSERTION(!mContentsValid,
    3468                 :                "Don't call FillChildren when contents are valid");
    3469             766 :   NS_ASSERTION(mChildren.Count() == 0,
    3470                 :                "We are trying to fill children when there already are some");
    3471                 : 
    3472             766 :   nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService();
    3473             766 :   NS_ENSURE_TRUE(bookmarks, NS_ERROR_OUT_OF_MEMORY);
    3474                 : 
    3475                 :   // Actually get the folder children from the bookmark service.
    3476             766 :   nsresult rv = bookmarks->QueryFolderChildren(mItemId, mOptions, &mChildren);
    3477             766 :   NS_ENSURE_SUCCESS(rv, rv);
    3478                 : 
    3479                 :   // PERFORMANCE: it may be better to also fill any child folders at this point
    3480                 :   // so that we can draw tree twisties without doing a separate query later.
    3481                 :   // If we don't end up drawing twisties a lot, it doesn't matter. If we do
    3482                 :   // this, we should wrap everything in a transaction here on the bookmark
    3483                 :   // service's connection.
    3484                 : 
    3485             766 :   return OnChildrenFilled();
    3486                 : }
    3487                 : 
    3488                 : 
    3489                 : /**
    3490                 :  * Performs some tasks after all the children of the container have been added.
    3491                 :  * The container's contents are not valid until this method has been called.
    3492                 :  */
    3493                 : nsresult
    3494             768 : nsNavHistoryFolderResultNode::OnChildrenFilled()
    3495                 : {
    3496                 :   // It is important to call FillStats to fill in the parents on all
    3497                 :   // nodes and the result node pointers on the containers.
    3498             768 :   FillStats();
    3499                 : 
    3500             768 :   if (mResult->mNeedsToApplySortingMode) {
    3501                 :     // We should repopulate container and then apply sortingMode.  To avoid
    3502                 :     // sorting 2 times we simply do that here.
    3503               0 :     mResult->SetSortingMode(mResult->mSortingMode);
    3504                 :   }
    3505                 :   else {
    3506                 :     // Once we've computed all tree stats, we can sort, because containers will
    3507                 :     // then have proper visit counts and dates.
    3508             768 :     SortComparator comparator = GetSortingComparator(GetSortType());
    3509             768 :     if (comparator) {
    3510            1536 :       nsCAutoString sortingAnnotation;
    3511             768 :       GetSortingAnnotation(sortingAnnotation);
    3512             768 :       RecursiveSort(sortingAnnotation.get(), comparator);
    3513                 :     }
    3514                 :   }
    3515                 : 
    3516                 :   // If we are limiting our results remove items from the end of the
    3517                 :   // mChildren array after sorting.  This is done for root node only.
    3518                 :   // Note, if count < max results, we won't do anything.
    3519             768 :   if (!mParent && mOptions->MaxResults()) {
    3520               0 :     while ((PRUint32)mChildren.Count() > mOptions->MaxResults())
    3521               0 :       mChildren.RemoveObjectAt(mChildren.Count() - 1);
    3522                 :   }
    3523                 : 
    3524                 :   // Register with the result for updates.
    3525             768 :   EnsureRegisteredAsFolderObserver();
    3526                 : 
    3527             768 :   mContentsValid = true;
    3528             768 :   return NS_OK;
    3529                 : }
    3530                 : 
    3531                 : 
    3532                 : /**
    3533                 :  * Registers the node with its result as a folder observer if it is not already
    3534                 :  * registered.
    3535                 :  */
    3536                 : void
    3537             771 : nsNavHistoryFolderResultNode::EnsureRegisteredAsFolderObserver()
    3538                 : {
    3539             771 :   if (!mIsRegisteredFolderObserver && mResult) {
    3540             769 :     mResult->AddBookmarkFolderObserver(this, mItemId);
    3541             769 :     mIsRegisteredFolderObserver = true;
    3542                 :   }
    3543             771 : }
    3544                 : 
    3545                 : 
    3546                 : /**
    3547                 :  * The async version of FillChildren.  This begins asynchronous execution by
    3548                 :  * calling nsNavBookmarks::QueryFolderChildrenAsync.  During execution, this
    3549                 :  * node's async Storage callbacks, HandleResult and HandleCompletion, will be
    3550                 :  * called.
    3551                 :  */
    3552                 : nsresult
    3553               3 : nsNavHistoryFolderResultNode::FillChildrenAsync()
    3554                 : {
    3555               3 :   NS_ASSERTION(!mContentsValid, "FillChildrenAsync when contents are valid");
    3556               3 :   NS_ASSERTION(mChildren.Count() == 0, "FillChildrenAsync when children exist");
    3557                 : 
    3558                 :   // ProcessFolderNodeChild, called in HandleResult, increments this for every
    3559                 :   // result row it processes.  Initialize it here as we begin async execution.
    3560               3 :   mAsyncBookmarkIndex = -1;
    3561                 : 
    3562               3 :   nsNavBookmarks* bmSvc = nsNavBookmarks::GetBookmarksService();
    3563               3 :   NS_ENSURE_TRUE(bmSvc, NS_ERROR_OUT_OF_MEMORY);
    3564                 :   nsresult rv =
    3565                 :     bmSvc->QueryFolderChildrenAsync(this, mItemId,
    3566               3 :                                     getter_AddRefs(mAsyncPendingStmt));
    3567               3 :   NS_ENSURE_SUCCESS(rv, rv);
    3568                 : 
    3569                 :   // Register with the result for updates.  All updates during async execution
    3570                 :   // will cause it to be restarted.
    3571               3 :   EnsureRegisteredAsFolderObserver();
    3572                 : 
    3573               3 :   return NS_OK;
    3574                 : }
    3575                 : 
    3576                 : 
    3577                 : /**
    3578                 :  * A mozIStorageStatementCallback method.  Called during the async execution
    3579                 :  * begun by FillChildrenAsync.
    3580                 :  *
    3581                 :  * @param aResultSet
    3582                 :  *        The result set containing the data from the database.
    3583                 :  */
    3584                 : NS_IMETHODIMP
    3585               2 : nsNavHistoryFolderResultNode::HandleResult(mozIStorageResultSet* aResultSet)
    3586                 : {
    3587               2 :   NS_ENSURE_ARG_POINTER(aResultSet);
    3588                 : 
    3589               2 :   nsNavBookmarks* bmSvc = nsNavBookmarks::GetBookmarksService();
    3590               2 :   if (!bmSvc) {
    3591               0 :     CancelAsyncOpen(false);
    3592               0 :     return NS_ERROR_OUT_OF_MEMORY;
    3593                 :   }
    3594                 : 
    3595                 :   // Consume all the currently available rows of the result set.
    3596               4 :   nsCOMPtr<mozIStorageRow> row;
    3597               2 :   while (NS_SUCCEEDED(aResultSet->GetNextRow(getter_AddRefs(row))) && row) {
    3598                 :     nsresult rv = bmSvc->ProcessFolderNodeRow(row, mOptions, &mChildren,
    3599               8 :                                               mAsyncBookmarkIndex);
    3600               8 :     if (NS_FAILED(rv)) {
    3601               0 :       CancelAsyncOpen(false);
    3602               0 :       return rv;
    3603                 :     }
    3604                 :   }
    3605                 : 
    3606               2 :   return NS_OK;
    3607                 : }
    3608                 : 
    3609                 : 
    3610                 : /**
    3611                 :  * A mozIStorageStatementCallback method.  Called during the async execution
    3612                 :  * begun by FillChildrenAsync.
    3613                 :  *
    3614                 :  * @param aReason
    3615                 :  *        Indicates the final state of execution.
    3616                 :  */
    3617                 : NS_IMETHODIMP
    3618               3 : nsNavHistoryFolderResultNode::HandleCompletion(PRUint16 aReason)
    3619                 : {
    3620               3 :   if (aReason == mozIStorageStatementCallback::REASON_FINISHED &&
    3621                 :       mAsyncCanceledState == NOT_CANCELED) {
    3622                 :     // Async execution successfully completed.  The container is ready to open.
    3623                 : 
    3624               2 :     nsresult rv = OnChildrenFilled();
    3625               2 :     NS_ENSURE_SUCCESS(rv, rv);
    3626                 : 
    3627               2 :     mExpanded = true;
    3628               2 :     mAsyncPendingStmt = nsnull;
    3629                 : 
    3630                 :     // Notify observers only after mExpanded and mAsyncPendingStmt are set.
    3631               2 :     rv = NotifyOnStateChange(STATE_LOADING);
    3632               2 :     NS_ENSURE_SUCCESS(rv, rv);
    3633                 :   }
    3634                 : 
    3635               1 :   else if (mAsyncCanceledState == CANCELED_RESTART_NEEDED) {
    3636                 :     // Async execution was canceled and needs to be restarted.
    3637               0 :     mAsyncCanceledState = NOT_CANCELED;
    3638               0 :     ClearChildren(false);
    3639               0 :     FillChildrenAsync();
    3640                 :   }
    3641                 : 
    3642                 :   else {
    3643                 :     // Async execution failed or was canceled without restart.  Remove all
    3644                 :     // children and close the container, notifying observers.
    3645               1 :     mAsyncCanceledState = NOT_CANCELED;
    3646               1 :     ClearChildren(true);
    3647               1 :     CloseContainer();
    3648                 :   }
    3649                 : 
    3650               3 :   return NS_OK;
    3651                 : }
    3652                 : 
    3653                 : 
    3654                 : void
    3655            1027 : nsNavHistoryFolderResultNode::ClearChildren(bool unregister)
    3656                 : {
    3657            2144 :   for (PRInt32 i = 0; i < mChildren.Count(); ++i)
    3658            1117 :     mChildren[i]->OnRemoving();
    3659            1027 :   mChildren.Clear();
    3660                 : 
    3661            1027 :   bool needsUnregister = unregister && (mContentsValid || mAsyncPendingStmt);
    3662            1027 :   if (needsUnregister && mResult && mIsRegisteredFolderObserver) {
    3663             745 :     mResult->RemoveBookmarkFolderObserver(this, mItemId);
    3664             745 :     mIsRegisteredFolderObserver = false;
    3665                 :   }
    3666            1027 :   mContentsValid = false;
    3667            1027 : }
    3668                 : 
    3669                 : 
    3670                 : /**
    3671                 :  * This is called to update the result when something has changed that we
    3672                 :  * can not incrementally update.
    3673                 :  */
    3674                 : nsresult
    3675               2 : nsNavHistoryFolderResultNode::Refresh()
    3676                 : {
    3677               2 :   nsNavHistoryResult* result = GetResult();
    3678               2 :   NS_ENSURE_STATE(result);
    3679               2 :   if (result->mBatchInProgress) {
    3680               0 :     result->requestRefresh(this);
    3681               0 :     return NS_OK;
    3682                 :   }
    3683                 : 
    3684               2 :   ClearChildren(true);
    3685                 : 
    3686               2 :   if (!mExpanded) {
    3687                 :     // When we are not expanded, we don't update, just invalidate and unhook.
    3688               2 :     return NS_OK;
    3689                 :   }
    3690                 : 
    3691                 :   // Ignore errors from FillChildren, since we will still want to refresh
    3692                 :   // the tree (there just might not be anything in it on error).  ClearChildren
    3693                 :   // has unregistered us as an observer since FillChildren will try to
    3694                 :   // re-register us.
    3695               0 :   (void)FillChildren();
    3696                 : 
    3697               0 :   NOTIFY_RESULT_OBSERVERS(result, InvalidateContainer(TO_CONTAINER(this)));
    3698               0 :   return NS_OK;
    3699                 : }
    3700                 : 
    3701                 : 
    3702                 : /**
    3703                 :  * Implements the logic described above the constructor.  This sees if we
    3704                 :  * should do an incremental update and returns true if so.  If not, it
    3705                 :  * invalidates our children, unregisters us an observer, and returns false.
    3706                 :  */
    3707                 : bool
    3708             629 : nsNavHistoryFolderResultNode::StartIncrementalUpdate()
    3709                 : {
    3710                 :   // if any items are excluded, we can not do incremental updates since the
    3711                 :   // indices from the bookmark service will not be valid
    3712                 : 
    3713            1887 :   if (!mOptions->ExcludeItems() &&
    3714             629 :       !mOptions->ExcludeQueries() &&
    3715             629 :       !mOptions->ExcludeReadOnlyFolders()) {
    3716                 :     // easy case: we are visible, always do incremental update
    3717             629 :     if (mExpanded || AreChildrenVisible())
    3718             627 :       return true;
    3719                 : 
    3720               2 :     nsNavHistoryResult* result = GetResult();
    3721               2 :     NS_ENSURE_TRUE(result, false);
    3722                 : 
    3723                 :     // When any observers are attached also do incremental updates if our
    3724                 :     // parent is visible, so that twisties are drawn correctly.
    3725               2 :     if (mParent)
    3726               0 :       return result->mObservers.Length() > 0;
    3727                 :   }
    3728                 : 
    3729                 :   // otherwise, we don't do incremental updates, invalidate and unregister
    3730               2 :   (void)Refresh();
    3731               2 :   return false;
    3732                 : }
    3733                 : 
    3734                 : 
    3735                 : /**
    3736                 :  * This function adds aDelta to all bookmark indices between the two endpoints,
    3737                 :  * inclusive.  It is used when items are added or removed from the bookmark
    3738                 :  * folder.
    3739                 :  */
    3740                 : void
    3741             303 : nsNavHistoryFolderResultNode::ReindexRange(PRInt32 aStartIndex,
    3742                 :                                            PRInt32 aEndIndex,
    3743                 :                                            PRInt32 aDelta)
    3744                 : {
    3745             815 :   for (PRInt32 i = 0; i < mChildren.Count(); ++i) {
    3746             512 :     nsNavHistoryResultNode* node = mChildren[i];
    3747             512 :     if (node->mBookmarkIndex >= aStartIndex &&
    3748                 :         node->mBookmarkIndex <= aEndIndex)
    3749              31 :       node->mBookmarkIndex += aDelta;
    3750                 :   }
    3751             303 : }
    3752                 : 
    3753                 : 
    3754                 : /**
    3755                 :  * Searches this folder for a node with the given id.
    3756                 :  *
    3757                 :  * @return the node if found, null otherwise.
    3758                 :  * @note Does not addref the node!
    3759                 :  */
    3760                 : nsNavHistoryResultNode*
    3761             503 : nsNavHistoryFolderResultNode::FindChildById(PRInt64 aItemId,
    3762                 :     PRUint32* aNodeIndex)
    3763                 : {
    3764             942 :   for (PRInt32 i = 0; i < mChildren.Count(); ++i) {
    3765            1513 :     if (mChildren[i]->mItemId == aItemId ||
    3766             441 :         (mChildren[i]->IsFolder() &&
    3767             130 :          mChildren[i]->GetAsFolder()->mQueryItemId == aItemId)) {
    3768             503 :       *aNodeIndex = i;
    3769             503 :       return mChildren[i];
    3770                 :     }
    3771                 :   }
    3772               0 :   return nsnull;
    3773                 : }
    3774                 : 
    3775                 : 
    3776                 : // Used by nsNavHistoryFolderResultNode's nsINavBookmarkObserver methods below.
    3777                 : // If the container is notified of a bookmark event while asynchronous execution
    3778                 : // is pending, this restarts it and returns.
    3779                 : #define RESTART_AND_RETURN_IF_ASYNC_PENDING() \
    3780                 :   if (mAsyncPendingStmt) { \
    3781                 :     CancelAsyncOpen(true); \
    3782                 :     return NS_OK; \
    3783                 :   }
    3784                 : 
    3785                 : 
    3786                 : NS_IMETHODIMP
    3787               0 : nsNavHistoryFolderResultNode::OnBeginUpdateBatch()
    3788                 : {
    3789               0 :   return NS_OK;
    3790                 : }
    3791                 : 
    3792                 : 
    3793                 : NS_IMETHODIMP
    3794               0 : nsNavHistoryFolderResultNode::OnEndUpdateBatch()
    3795                 : {
    3796               0 :   return NS_OK;
    3797                 : }
    3798                 : 
    3799                 : 
    3800                 : NS_IMETHODIMP
    3801             125 : nsNavHistoryFolderResultNode::OnItemAdded(PRInt64 aItemId,
    3802                 :                                           PRInt64 aParentFolder,
    3803                 :                                           PRInt32 aIndex,
    3804                 :                                           PRUint16 aItemType,
    3805                 :                                           nsIURI* aURI,
    3806                 :                                           const nsACString& aTitle,
    3807                 :                                           PRTime aDateAdded,
    3808                 :                                           const nsACString& aGUID,
    3809                 :                                           const nsACString& aParentGUID)
    3810                 : {
    3811             125 :   NS_ASSERTION(aParentFolder == mItemId, "Got wrong bookmark update");
    3812                 : 
    3813             250 :   bool excludeItems = (mResult && mResult->mRootNode->mOptions->ExcludeItems()) ||
    3814             125 :                         (mParent && mParent->mOptions->ExcludeItems()) ||
    3815             375 :                         mOptions->ExcludeItems();
    3816                 : 
    3817                 :   // here, try to do something reasonable if the bookmark service gives us
    3818                 :   // a bogus index.
    3819             125 :   if (aIndex < 0) {
    3820               0 :     NS_NOTREACHED("Invalid index for item adding: <0");
    3821               0 :     aIndex = 0;
    3822                 :   }
    3823             125 :   else if (aIndex > mChildren.Count()) {
    3824               0 :     if (!excludeItems) {
    3825                 :       // Something wrong happened while updating indexes.
    3826               0 :       NS_NOTREACHED("Invalid index for item adding: greater than count");
    3827                 :     }
    3828               0 :     aIndex = mChildren.Count();
    3829                 :   }
    3830                 : 
    3831             125 :   RESTART_AND_RETURN_IF_ASYNC_PENDING();
    3832                 : 
    3833                 :   nsresult rv;
    3834                 : 
    3835                 :   // Check for query URIs, which are bookmarks, but treated as containers
    3836                 :   // in results and views.
    3837             125 :   bool isQuery = false;
    3838             125 :   if (aItemType == nsINavBookmarksService::TYPE_BOOKMARK) {
    3839              68 :     NS_ASSERTION(aURI, "Got a null URI when we are a bookmark?!");
    3840             136 :     nsCAutoString itemURISpec;
    3841              68 :     rv = aURI->GetSpec(itemURISpec);
    3842              68 :     NS_ENSURE_SUCCESS(rv, rv);
    3843             136 :     isQuery = IsQueryURI(itemURISpec);
    3844                 :   }
    3845                 : 
    3846             195 :   if (aItemType != nsINavBookmarksService::TYPE_FOLDER &&
    3847              70 :       !isQuery && excludeItems) {
    3848                 :     // don't update items when we aren't displaying them, but we still need
    3849                 :     // to adjust bookmark indices to account for the insertion
    3850               0 :     ReindexRange(aIndex, PR_INT32_MAX, 1);
    3851               0 :     return NS_OK;
    3852                 :   }
    3853                 : 
    3854             125 :   if (!StartIncrementalUpdate())
    3855               0 :     return NS_OK; // folder was completely refreshed for us
    3856                 : 
    3857                 :   // adjust indices to account for insertion
    3858             125 :   ReindexRange(aIndex, PR_INT32_MAX, 1);
    3859                 : 
    3860             250 :   nsRefPtr<nsNavHistoryResultNode> node;
    3861             125 :   if (aItemType == nsINavBookmarksService::TYPE_BOOKMARK) {
    3862              68 :     nsNavHistory* history = nsNavHistory::GetHistoryService();
    3863              68 :     NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY);
    3864              68 :     rv = history->BookmarkIdToResultNode(aItemId, mOptions, getter_AddRefs(node));
    3865              68 :     NS_ENSURE_SUCCESS(rv, rv);
    3866                 :   }
    3867              57 :   else if (aItemType == nsINavBookmarksService::TYPE_FOLDER) {
    3868              55 :     nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService();
    3869              55 :     NS_ENSURE_TRUE(bookmarks, NS_ERROR_OUT_OF_MEMORY);
    3870              55 :     rv = bookmarks->ResultNodeForContainer(aItemId, mOptions, getter_AddRefs(node));
    3871              55 :     NS_ENSURE_SUCCESS(rv, rv);
    3872                 :   }
    3873               2 :   else if (aItemType == nsINavBookmarksService::TYPE_SEPARATOR) {
    3874               2 :     node = new nsNavHistorySeparatorResultNode();
    3875               2 :     NS_ENSURE_TRUE(node, NS_ERROR_OUT_OF_MEMORY);
    3876               2 :     node->mItemId = aItemId;
    3877                 :   }
    3878                 : 
    3879             125 :   node->mBookmarkIndex = aIndex;
    3880                 : 
    3881             248 :   if (aItemType == nsINavBookmarksService::TYPE_SEPARATOR ||
    3882             123 :       GetSortType() == nsINavHistoryQueryOptions::SORT_BY_NONE) {
    3883                 :     // insert at natural bookmarks position
    3884             125 :     return InsertChildAt(node, aIndex);
    3885                 :   }
    3886                 : 
    3887                 :   // insert at sorted position
    3888               0 :   return InsertSortedChild(node, false);
    3889                 : }
    3890                 : 
    3891                 : 
    3892                 : NS_IMETHODIMP
    3893               0 : nsNavHistoryFolderResultNode::OnBeforeItemRemoved(PRInt64 aItemId,
    3894                 :                                                   PRUint16 aItemType,
    3895                 :                                                   PRInt64 aParentId,
    3896                 :                                                   const nsACString& aGUID,
    3897                 :                                                   const nsACString& aParentGUID)
    3898                 : {
    3899               0 :   return NS_OK;
    3900                 : }
    3901                 : 
    3902                 : 
    3903                 : NS_IMETHODIMP
    3904             178 : nsNavHistoryFolderResultNode::OnItemRemoved(PRInt64 aItemId,
    3905                 :                                             PRInt64 aParentFolder,
    3906                 :                                             PRInt32 aIndex,
    3907                 :                                             PRUint16 aItemType,
    3908                 :                                             nsIURI* aURI,
    3909                 :                                             const nsACString& aGUID,
    3910                 :                                             const nsACString& aParentGUID)
    3911                 : {
    3912                 :   // We only care about notifications when a child changes.  When the deleted
    3913                 :   // item is us, our parent should also be registered and will remove us from
    3914                 :   // its list.
    3915             178 :   if (mItemId == aItemId)
    3916               0 :     return NS_OK;
    3917                 : 
    3918             178 :   NS_ASSERTION(aParentFolder == mItemId, "Got wrong bookmark update");
    3919                 : 
    3920             178 :   RESTART_AND_RETURN_IF_ASYNC_PENDING();
    3921                 : 
    3922             356 :   bool excludeItems = (mResult && mResult->mRootNode->mOptions->ExcludeItems()) ||
    3923             205 :                         (mParent && mParent->mOptions->ExcludeItems()) ||
    3924             561 :                         mOptions->ExcludeItems();
    3925                 : 
    3926                 :   // don't trust the index from the bookmark service, find it ourselves.  The
    3927                 :   // sorting could be different, or the bookmark services indices and ours might
    3928                 :   // be out of sync somehow.
    3929                 :   PRUint32 index;
    3930             178 :   nsNavHistoryResultNode* node = FindChildById(aItemId, &index);
    3931             178 :   if (!node) {
    3932               0 :     if (excludeItems)
    3933               0 :       return NS_OK;
    3934                 : 
    3935               0 :     NS_NOTREACHED("Removing item we don't have");
    3936               0 :     return NS_ERROR_FAILURE;
    3937                 :   }
    3938                 : 
    3939             178 :   if ((node->IsURI() || node->IsSeparator()) && excludeItems) {
    3940                 :     // don't update items when we aren't displaying them, but we do need to
    3941                 :     // adjust everybody's bookmark indices to account for the removal
    3942               0 :     ReindexRange(aIndex, PR_INT32_MAX, -1);
    3943               0 :     return NS_OK;
    3944                 :   }
    3945                 : 
    3946             178 :   if (!StartIncrementalUpdate())
    3947               2 :     return NS_OK; // we are completely refreshed
    3948                 : 
    3949                 :   // shift all following indices down
    3950             176 :   ReindexRange(aIndex + 1, PR_INT32_MAX, -1);
    3951                 : 
    3952             176 :   return RemoveChildAt(index);
    3953                 : }
    3954                 : 
    3955                 : 
    3956                 : NS_IMETHODIMP
    3957             372 : nsNavHistoryResultNode::OnItemChanged(PRInt64 aItemId,
    3958                 :                                       const nsACString& aProperty,
    3959                 :                                       bool aIsAnnotationProperty,
    3960                 :                                       const nsACString& aNewValue,
    3961                 :                                       PRTime aLastModified,
    3962                 :                                       PRUint16 aItemType,
    3963                 :                                       PRInt64 aParentId,
    3964                 :                                       const nsACString& aGUID,
    3965                 :                                       const nsACString& aParentGUID)
    3966                 : {
    3967             372 :   if (aItemId != mItemId)
    3968              50 :     return NS_OK;
    3969                 : 
    3970             322 :   mLastModified = aLastModified;
    3971                 : 
    3972             322 :   nsNavHistoryResult* result = GetResult();
    3973             322 :   NS_ENSURE_STATE(result);
    3974                 : 
    3975             322 :   bool shouldNotify = !mParent || mParent->AreChildrenVisible();
    3976                 : 
    3977             322 :   if (aIsAnnotationProperty) {
    3978              67 :     if (shouldNotify)
    3979              67 :       NOTIFY_RESULT_OBSERVERS(result, NodeAnnotationChanged(this, aProperty));
    3980                 :   }
    3981             255 :   else if (aProperty.EqualsLiteral("title")) {
    3982                 :     // XXX: what should we do if the new title is void?
    3983              49 :     mTitle = aNewValue;
    3984              49 :     if (shouldNotify)
    3985              49 :       NOTIFY_RESULT_OBSERVERS(result, NodeTitleChanged(this, mTitle));
    3986                 :   }
    3987             206 :   else if (aProperty.EqualsLiteral("uri")) {
    3988                 :     // clear the tags string as well
    3989               2 :     mTags.SetIsVoid(true);
    3990               2 :     mURI = aNewValue;
    3991               2 :     if (shouldNotify)
    3992               2 :       NOTIFY_RESULT_OBSERVERS(result, NodeURIChanged(this, mURI));
    3993                 :   }
    3994             204 :   else if (aProperty.EqualsLiteral("favicon")) {
    3995              14 :     mFaviconURI = aNewValue;
    3996              14 :     if (shouldNotify)
    3997              14 :       NOTIFY_RESULT_OBSERVERS(result, NodeIconChanged(this));
    3998                 :   }
    3999             190 :   else if (aProperty.EqualsLiteral("cleartime")) {
    4000               0 :     mTime = 0;
    4001               0 :     if (shouldNotify) {
    4002               0 :       NOTIFY_RESULT_OBSERVERS(result,
    4003                 :                               NodeHistoryDetailsChanged(this, 0, mAccessCount));
    4004                 :     }
    4005                 :   }
    4006             190 :   else if (aProperty.EqualsLiteral("tags")) {
    4007              10 :     mTags.SetIsVoid(true);
    4008              10 :     if (shouldNotify)
    4009              10 :       NOTIFY_RESULT_OBSERVERS(result, NodeTagsChanged(this));
    4010                 :   }
    4011             180 :   else if (aProperty.EqualsLiteral("dateAdded")) {
    4012                 :     // aNewValue has the date as a string, but we can use aLastModified,
    4013                 :     // because it's set to the same value when dateAdded is changed.
    4014              70 :     mDateAdded = aLastModified;
    4015              70 :     if (shouldNotify)
    4016              70 :       NOTIFY_RESULT_OBSERVERS(result, NodeDateAddedChanged(this, mDateAdded));
    4017                 :   }
    4018             110 :   else if (aProperty.EqualsLiteral("lastModified")) {
    4019             108 :     if (shouldNotify) {
    4020             108 :       NOTIFY_RESULT_OBSERVERS(result,
    4021                 :                               NodeLastModifiedChanged(this, aLastModified));
    4022                 :     }
    4023                 :   }
    4024               2 :   else if (aProperty.EqualsLiteral("keyword")) {
    4025               2 :     if (shouldNotify)
    4026               2 :       NOTIFY_RESULT_OBSERVERS(result, NodeKeywordChanged(this, aNewValue));
    4027                 :   }
    4028                 :   else
    4029               0 :     NS_NOTREACHED("Unknown bookmark property changing.");
    4030                 : 
    4031             322 :   if (!mParent)
    4032               0 :     return NS_OK;
    4033                 : 
    4034                 :   // DO NOT OPTIMIZE THIS TO CHECK aProperty
    4035                 :   // The sorting methods fall back to each other so we need to re-sort the
    4036                 :   // result even if it's not set to sort by the given property.
    4037             322 :   PRInt32 ourIndex = mParent->FindChild(this);
    4038             322 :   mParent->EnsureItemPosition(ourIndex);
    4039                 : 
    4040             322 :   return NS_OK;
    4041                 : }
    4042                 : 
    4043                 : 
    4044                 : NS_IMETHODIMP
    4045             143 : nsNavHistoryFolderResultNode::OnItemChanged(PRInt64 aItemId,
    4046                 :                                             const nsACString& aProperty,
    4047                 :                                             bool aIsAnnotationProperty,
    4048                 :                                             const nsACString& aNewValue,
    4049                 :                                             PRTime aLastModified,
    4050                 :                                             PRUint16 aItemType,
    4051                 :                                             PRInt64 aParentId,
    4052                 :                                             const nsACString& aGUID,
    4053                 :                                             const nsACString&aParentGUID)
    4054                 : {
    4055                 :   // The query-item's title is used for simple-query nodes
    4056             143 :   if (mQueryItemId != -1) {
    4057               1 :     bool isTitleChange = aProperty.EqualsLiteral("title");
    4058               1 :     if ((mQueryItemId == aItemId && !isTitleChange) ||
    4059                 :         (mQueryItemId != aItemId && isTitleChange)) {
    4060               1 :       return NS_OK;
    4061                 :     }
    4062                 :   }
    4063                 : 
    4064             142 :   RESTART_AND_RETURN_IF_ASYNC_PENDING();
    4065                 : 
    4066                 :   return nsNavHistoryResultNode::OnItemChanged(aItemId, aProperty,
    4067                 :                                                aIsAnnotationProperty,
    4068                 :                                                aNewValue, aLastModified,
    4069                 :                                                aItemType, aParentId, aGUID,
    4070             142 :                                                aParentGUID);
    4071                 : }
    4072                 : 
    4073                 : /**
    4074                 :  * Updates visit count and last visit time and refreshes.
    4075                 :  */
    4076                 : NS_IMETHODIMP
    4077               1 : nsNavHistoryFolderResultNode::OnItemVisited(PRInt64 aItemId,
    4078                 :                                             PRInt64 aVisitId,
    4079                 :                                             PRTime aTime,
    4080                 :                                             PRUint32 aTransitionType,
    4081                 :                                             nsIURI* aURI,
    4082                 :                                             PRInt64 aParentId,
    4083                 :                                             const nsACString& aGUID,
    4084                 :                                             const nsACString& aParentGUID)
    4085                 : {
    4086               2 :   bool excludeItems = (mResult && mResult->mRootNode->mOptions->ExcludeItems()) ||
    4087               1 :                         (mParent && mParent->mOptions->ExcludeItems()) ||
    4088               3 :                         mOptions->ExcludeItems();
    4089               1 :   if (excludeItems)
    4090               0 :     return NS_OK; // don't update items when we aren't displaying them
    4091                 : 
    4092               1 :   RESTART_AND_RETURN_IF_ASYNC_PENDING();
    4093                 : 
    4094               1 :   if (!StartIncrementalUpdate())
    4095               0 :     return NS_OK;
    4096                 : 
    4097                 :   PRUint32 nodeIndex;
    4098               1 :   nsNavHistoryResultNode* node = FindChildById(aItemId, &nodeIndex);
    4099               1 :   if (!node)
    4100               0 :     return NS_ERROR_FAILURE;
    4101                 : 
    4102                 :   // Update node.
    4103               1 :   node->mTime = aTime;
    4104               1 :   ++node->mAccessCount;
    4105                 : 
    4106                 :   // Update us.
    4107               1 :   PRInt32 oldAccessCount = mAccessCount;
    4108               1 :   ++mAccessCount;
    4109               1 :   if (aTime > mTime)
    4110               1 :     mTime = aTime;
    4111               1 :   nsresult rv = ReverseUpdateStats(mAccessCount - oldAccessCount);
    4112               1 :   NS_ENSURE_SUCCESS(rv, rv);
    4113                 : 
    4114               1 :   if (AreChildrenVisible()) {
    4115                 :     // Sorting has not changed, just redraw the row if it's visible.
    4116               1 :     nsNavHistoryResult* result = GetResult();
    4117               1 :     NOTIFY_RESULT_OBSERVERS(result,
    4118                 :                             NodeHistoryDetailsChanged(node, mTime, mAccessCount));
    4119                 :   }
    4120                 : 
    4121                 :   // Update sorting if necessary.
    4122               1 :   PRUint32 sortType = GetSortType();
    4123               1 :   if (sortType == nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_ASCENDING ||
    4124                 :       sortType == nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_DESCENDING ||
    4125                 :       sortType == nsINavHistoryQueryOptions::SORT_BY_DATE_ASCENDING ||
    4126                 :       sortType == nsINavHistoryQueryOptions::SORT_BY_DATE_DESCENDING) {
    4127               0 :     PRInt32 childIndex = FindChild(node);
    4128               0 :     NS_ASSERTION(childIndex >= 0, "Could not find child we just got a reference to");
    4129               0 :     if (childIndex >= 0) {
    4130               0 :       EnsureItemPosition(childIndex);
    4131                 :     }
    4132                 :   }
    4133                 : 
    4134               1 :   return NS_OK;
    4135                 : }
    4136                 : 
    4137                 : 
    4138                 : NS_IMETHODIMP
    4139               2 : nsNavHistoryFolderResultNode::OnItemMoved(PRInt64 aItemId,
    4140                 :                                           PRInt64 aOldParent,
    4141                 :                                           PRInt32 aOldIndex,
    4142                 :                                           PRInt64 aNewParent,
    4143                 :                                           PRInt32 aNewIndex,
    4144                 :                                           PRUint16 aItemType,
    4145                 :                                           const nsACString& aGUID,
    4146                 :                                           const nsACString& aOldParentGUID,
    4147                 :                                           const nsACString& aNewParentGUID)
    4148                 : {
    4149               2 :   NS_ASSERTION(aOldParent == mItemId || aNewParent == mItemId,
    4150                 :                "Got a bookmark message that doesn't belong to us");
    4151                 : 
    4152               2 :   RESTART_AND_RETURN_IF_ASYNC_PENDING();
    4153                 : 
    4154               2 :   if (!StartIncrementalUpdate())
    4155               0 :     return NS_OK; // entire container was refreshed for us
    4156                 : 
    4157               2 :   if (aOldParent == aNewParent) {
    4158                 :     // getting moved within the same folder, we don't want to do a remove and
    4159                 :     // an add because that will lose your tree state.
    4160                 : 
    4161                 :     // adjust bookmark indices
    4162               1 :     ReindexRange(aOldIndex + 1, PR_INT32_MAX, -1);
    4163               1 :     ReindexRange(aNewIndex, PR_INT32_MAX, 1);
    4164                 : 
    4165                 :     PRUint32 index;
    4166               1 :     nsNavHistoryResultNode* node = FindChildById(aItemId, &index);
    4167               1 :     if (!node) {
    4168               0 :       NS_NOTREACHED("Can't find folder that is moving!");
    4169               0 :       return NS_ERROR_FAILURE;
    4170                 :     }
    4171               1 :     NS_ASSERTION(index >= 0 && index < PRUint32(mChildren.Count()),
    4172                 :                  "Invalid index!");
    4173               1 :     node->mBookmarkIndex = aNewIndex;
    4174                 : 
    4175                 :     // adjust position
    4176               1 :     EnsureItemPosition(index);
    4177               1 :     return NS_OK;
    4178                 :   } else {
    4179                 :     // moving between two different folders, just do a remove and an add
    4180               2 :     nsCOMPtr<nsIURI> itemURI;
    4181               2 :     nsCAutoString itemTitle;
    4182               1 :     if (aItemType == nsINavBookmarksService::TYPE_BOOKMARK) {
    4183               1 :       nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService();
    4184               1 :       NS_ENSURE_TRUE(bookmarks, NS_ERROR_OUT_OF_MEMORY);
    4185               1 :       nsresult rv = bookmarks->GetBookmarkURI(aItemId, getter_AddRefs(itemURI));
    4186               1 :       NS_ENSURE_SUCCESS(rv, rv);
    4187               1 :       rv = bookmarks->GetItemTitle(aItemId, itemTitle);
    4188               1 :       NS_ENSURE_SUCCESS(rv, rv);
    4189                 :     }
    4190               1 :     if (aOldParent == mItemId) {
    4191                 :       OnItemRemoved(aItemId, aOldParent, aOldIndex, aItemType, itemURI,
    4192               1 :                     aGUID, aOldParentGUID);
    4193                 :     }
    4194               1 :     if (aNewParent == mItemId) {
    4195                 :       OnItemAdded(aItemId, aNewParent, aNewIndex, aItemType, itemURI, itemTitle,
    4196                 :                   PR_Now(), // This is a dummy dateAdded, not the real value.
    4197               0 :                   aGUID, aNewParentGUID);
    4198                 :     }
    4199                 :   }
    4200               1 :   return NS_OK;
    4201                 : }
    4202                 : 
    4203                 : 
    4204                 : /**
    4205                 :  * Separator nodes do not hold any data.
    4206                 :  */
    4207              33 : nsNavHistorySeparatorResultNode::nsNavHistorySeparatorResultNode()
    4208              66 :   : nsNavHistoryResultNode(EmptyCString(), EmptyCString(),
    4209              99 :                            0, 0, EmptyCString())
    4210                 : {
    4211              33 : }
    4212                 : 
    4213                 : 
    4214            1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsNavHistoryResult)
    4215                 : 
    4216                 : static PLDHashOperator
    4217             754 : RemoveBookmarkFolderObserversCallback(nsTrimInt64HashKey::KeyType aKey,
    4218                 :                                       nsNavHistoryResult::FolderObserverList*& aData,
    4219                 :                                       void* userArg)
    4220                 : {
    4221             754 :   delete aData;
    4222             754 :   return PL_DHASH_REMOVE;
    4223                 : }
    4224                 : 
    4225             873 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsNavHistoryResult)
    4226             873 :   tmp->StopObserving();
    4227             873 :   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mRootNode)
    4228             873 :   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSTARRAY(mObservers)
    4229             873 :   tmp->mBookmarkFolderObservers.Enumerate(&RemoveBookmarkFolderObserversCallback, nsnull);
    4230             873 :   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSTARRAY(mAllBookmarksObservers)
    4231             873 :   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSTARRAY(mHistoryObservers)
    4232             873 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    4233                 : 
    4234                 : static PLDHashOperator
    4235             863 : TraverseBookmarkFolderObservers(nsTrimInt64HashKey::KeyType aKey,
    4236                 :                                 nsNavHistoryResult::FolderObserverList* &aData,
    4237                 :                                 void *aClosure)
    4238                 : {
    4239                 :   nsCycleCollectionTraversalCallback* cb =
    4240             863 :     static_cast<nsCycleCollectionTraversalCallback*>(aClosure);
    4241             887 :   for (PRUint32 i = 0; i < aData->Length(); ++i) {
    4242              24 :     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
    4243                 :                                        "mBookmarkFolderObservers value[i]");
    4244              24 :     nsNavHistoryResultNode* node = aData->ElementAt(i);
    4245              24 :     cb->NoteXPCOMChild(node);
    4246                 :   }
    4247             863 :   return PL_DHASH_NEXT;
    4248                 : }
    4249                 : 
    4250                 : static void
    4251             935 : traverseResultObservers(nsMaybeWeakPtrArray<nsINavHistoryResultObserver> aObservers,
    4252                 :                         void *aClosure)
    4253                 : {
    4254                 :   nsCycleCollectionTraversalCallback* cb =
    4255             935 :     static_cast<nsCycleCollectionTraversalCallback*>(aClosure);
    4256             935 :   for (PRUint32 i = 0; i < aObservers.Length(); ++i) {
    4257               0 :     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mResultObservers value[i]");
    4258               0 :     const nsCOMPtr<nsINavHistoryResultObserver> &obs = aObservers.ElementAt(i);
    4259               0 :     cb->NoteXPCOMChild(obs);
    4260                 :   }
    4261             935 : }
    4262                 : 
    4263             935 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsNavHistoryResult)
    4264             935 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mRootNode, nsINavHistoryContainerResultNode)
    4265             935 :   traverseResultObservers(tmp->mObservers, &cb);
    4266             935 :   tmp->mBookmarkFolderObservers.Enumerate(&TraverseBookmarkFolderObservers, &cb);
    4267             935 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSTARRAY_MEMBER(mAllBookmarksObservers, nsNavHistoryQueryResultNode)
    4268             935 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSTARRAY_MEMBER(mHistoryObservers, nsNavHistoryQueryResultNode)
    4269             935 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    4270                 : 
    4271           23285 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsNavHistoryResult)
    4272           23285 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsNavHistoryResult)
    4273                 : 
    4274           40305 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsNavHistoryResult)
    4275           21078 :   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsINavHistoryResult)
    4276           20180 :   NS_INTERFACE_MAP_STATIC_AMBIGUOUS(nsNavHistoryResult)
    4277           20180 :   NS_INTERFACE_MAP_ENTRY(nsINavHistoryResult)
    4278           19301 :   NS_INTERFACE_MAP_ENTRY(nsINavBookmarkObserver)
    4279            7861 :   NS_INTERFACE_MAP_ENTRY(nsINavHistoryObserver)
    4280            7339 :   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
    4281            5187 : NS_INTERFACE_MAP_END
    4282                 : 
    4283             873 : nsNavHistoryResult::nsNavHistoryResult(nsNavHistoryContainerResultNode* aRoot)
    4284                 : : mRootNode(aRoot)
    4285                 : , mNeedsToApplySortingMode(false)
    4286                 : , mIsHistoryObserver(false)
    4287                 : , mIsBookmarkFolderObserver(false)
    4288                 : , mIsAllBookmarksObserver(false)
    4289                 : , mBatchInProgress(false)
    4290             873 : , mSuppressNotifications(false)
    4291                 : {
    4292             873 :   mRootNode->mResult = this;
    4293             873 : }
    4294                 : 
    4295            2619 : nsNavHistoryResult::~nsNavHistoryResult()
    4296                 : {
    4297                 :   // delete all bookmark folder observer arrays which are allocated on the heap
    4298             873 :   mBookmarkFolderObservers.Enumerate(&RemoveBookmarkFolderObserversCallback, nsnull);
    4299            3492 : }
    4300                 : 
    4301                 : void
    4302            1879 : nsNavHistoryResult::StopObserving()
    4303                 : {
    4304            1879 :   if (mIsBookmarkFolderObserver || mIsAllBookmarksObserver) {
    4305             693 :     nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService();
    4306             693 :     if (bookmarks) {
    4307             693 :       bookmarks->RemoveObserver(this);
    4308             693 :       mIsBookmarkFolderObserver = false;
    4309             693 :       mIsAllBookmarksObserver = false;
    4310                 :     }
    4311                 :   }
    4312            1879 :   if (mIsHistoryObserver) {
    4313             383 :     nsNavHistory* history = nsNavHistory::GetHistoryService();
    4314             383 :     if (history) {
    4315             383 :       history->RemoveObserver(this);
    4316             383 :       mIsHistoryObserver = false;
    4317                 :     }
    4318                 :   }
    4319            1879 : }
    4320                 : 
    4321                 : /**
    4322                 :  * @note you must call AddRef before this, since we may do things like
    4323                 :  * register ourselves.
    4324                 :  */
    4325                 : nsresult
    4326             873 : nsNavHistoryResult::Init(nsINavHistoryQuery** aQueries,
    4327                 :                          PRUint32 aQueryCount,
    4328                 :                          nsNavHistoryQueryOptions *aOptions)
    4329                 : {
    4330                 :   nsresult rv;
    4331             873 :   NS_ASSERTION(aOptions, "Must have valid options");
    4332             873 :   NS_ASSERTION(aQueries && aQueryCount > 0, "Must have >1 query in result");
    4333                 : 
    4334                 :   // Fill saved source queries with copies of the original (the caller might
    4335                 :   // change their original objects, and we always want to reflect the source
    4336                 :   // parameters).
    4337            1772 :   for (PRUint32 i = 0; i < aQueryCount; ++i) {
    4338            1798 :     nsCOMPtr<nsINavHistoryQuery> queryClone;
    4339             899 :     rv = aQueries[i]->Clone(getter_AddRefs(queryClone));
    4340             899 :     NS_ENSURE_SUCCESS(rv, rv);
    4341             899 :     if (!mQueries.AppendObject(queryClone))
    4342               0 :       return NS_ERROR_OUT_OF_MEMORY;
    4343                 :   }
    4344             873 :   rv = aOptions->Clone(getter_AddRefs(mOptions));
    4345             873 :   NS_ENSURE_SUCCESS(rv, rv);
    4346             873 :   mSortingMode = aOptions->SortingMode();
    4347             873 :   rv = aOptions->GetSortingAnnotation(mSortingAnnotation);
    4348             873 :   NS_ENSURE_SUCCESS(rv, rv);
    4349                 : 
    4350             873 :   if (!mBookmarkFolderObservers.Init(128))
    4351               0 :     return NS_ERROR_OUT_OF_MEMORY;
    4352                 : 
    4353             873 :   NS_ASSERTION(mRootNode->mIndentLevel == -1,
    4354                 :                "Root node's indent level initialized wrong");
    4355             873 :   mRootNode->FillStats();
    4356                 : 
    4357             873 :   return NS_OK;
    4358                 : }
    4359                 : 
    4360                 : 
    4361                 : /**
    4362                 :  * Constructs a new history result object.
    4363                 :  */
    4364                 : nsresult // static
    4365             873 : nsNavHistoryResult::NewHistoryResult(nsINavHistoryQuery** aQueries,
    4366                 :                                      PRUint32 aQueryCount,
    4367                 :                                      nsNavHistoryQueryOptions* aOptions,
    4368                 :                                      nsNavHistoryContainerResultNode* aRoot,
    4369                 :                                      bool aBatchInProgress,
    4370                 :                                      nsNavHistoryResult** result)
    4371                 : {
    4372             873 :   *result = new nsNavHistoryResult(aRoot);
    4373             873 :   if (!*result)
    4374               0 :     return NS_ERROR_OUT_OF_MEMORY;
    4375             873 :   NS_ADDREF(*result); // must happen before Init
    4376                 :   // Correctly set mBatchInProgress for the result based on the root node value.
    4377             873 :   (*result)->mBatchInProgress = aBatchInProgress;
    4378             873 :   nsresult rv = (*result)->Init(aQueries, aQueryCount, aOptions);
    4379             873 :   if (NS_FAILED(rv)) {
    4380               0 :     NS_RELEASE(*result);
    4381               0 :     *result = nsnull;
    4382               0 :     return rv;
    4383                 :   }
    4384                 : 
    4385             873 :   return NS_OK;
    4386                 : }
    4387                 : 
    4388                 : 
    4389                 : void
    4390             543 : nsNavHistoryResult::AddHistoryObserver(nsNavHistoryQueryResultNode* aNode)
    4391                 : {
    4392             543 :   if (!mIsHistoryObserver) {
    4393             383 :       nsNavHistory* history = nsNavHistory::GetHistoryService();
    4394             383 :       NS_ASSERTION(history, "Can't create history service");
    4395             383 :       history->AddObserver(this, true);
    4396             383 :       mIsHistoryObserver = true;
    4397                 :   }
    4398                 :   // Don't add duplicate observers.  In some case we don't unregister when
    4399                 :   // children are cleared (see ClearChildren) and the next FillChildren call
    4400                 :   // will try to add the observer again.
    4401             543 :   if (mHistoryObservers.IndexOf(aNode) == mHistoryObservers.NoIndex) {
    4402             518 :     mHistoryObservers.AppendElement(aNode);
    4403                 :   }
    4404             543 : }
    4405                 : 
    4406                 : 
    4407                 : void
    4408             412 : nsNavHistoryResult::AddAllBookmarksObserver(nsNavHistoryQueryResultNode* aNode)
    4409                 : {
    4410             412 :   if (!mIsAllBookmarksObserver && !mIsBookmarkFolderObserver) {
    4411             292 :     nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService();
    4412             292 :     if (!bookmarks) {
    4413               0 :       NS_NOTREACHED("Can't create bookmark service");
    4414               0 :       return;
    4415                 :     }
    4416             292 :     bookmarks->AddObserver(this, true);
    4417             292 :     mIsAllBookmarksObserver = true;
    4418                 :   }
    4419                 :   // Don't add duplicate observers.  In some case we don't unregister when
    4420                 :   // children are cleared (see ClearChildren) and the next FillChildren call
    4421                 :   // will try to add the observer again.
    4422             412 :   if (mAllBookmarksObservers.IndexOf(aNode) == mAllBookmarksObservers.NoIndex) {
    4423             348 :     mAllBookmarksObservers.AppendElement(aNode);
    4424                 :   }
    4425                 : }
    4426                 : 
    4427                 : 
    4428                 : void
    4429             769 : nsNavHistoryResult::AddBookmarkFolderObserver(nsNavHistoryFolderResultNode* aNode,
    4430                 :                                               PRInt64 aFolder)
    4431                 : {
    4432             769 :   if (!mIsBookmarkFolderObserver && !mIsAllBookmarksObserver) {
    4433             401 :     nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService();
    4434             401 :     if (!bookmarks) {
    4435               0 :       NS_NOTREACHED("Can't create bookmark service");
    4436               0 :       return;
    4437                 :     }
    4438             401 :     bookmarks->AddObserver(this, true);
    4439             401 :     mIsBookmarkFolderObserver = true;
    4440                 :   }
    4441                 :   // Don't add duplicate observers.  In some case we don't unregister when
    4442                 :   // children are cleared (see ClearChildren) and the next FillChildren call
    4443                 :   // will try to add the observer again.
    4444             769 :   FolderObserverList* list = BookmarkFolderObserversForId(aFolder, true);
    4445             769 :   if (list->IndexOf(aNode) == list->NoIndex) {
    4446             769 :     list->AppendElement(aNode);
    4447                 :   }
    4448                 : }
    4449                 : 
    4450                 : 
    4451                 : void
    4452             864 : nsNavHistoryResult::RemoveHistoryObserver(nsNavHistoryQueryResultNode* aNode)
    4453                 : {
    4454             864 :   mHistoryObservers.RemoveElement(aNode);
    4455             864 : }
    4456                 : 
    4457                 : 
    4458                 : void
    4459             864 : nsNavHistoryResult::RemoveAllBookmarksObserver(nsNavHistoryQueryResultNode* aNode)
    4460                 : {
    4461             864 :   mAllBookmarksObservers.RemoveElement(aNode);
    4462             864 : }
    4463                 : 
    4464                 : 
    4465                 : void
    4466             745 : nsNavHistoryResult::RemoveBookmarkFolderObserver(nsNavHistoryFolderResultNode* aNode,
    4467                 :                                                  PRInt64 aFolder)
    4468                 : {
    4469             745 :   FolderObserverList* list = BookmarkFolderObserversForId(aFolder, false);
    4470             745 :   if (!list)
    4471               0 :     return; // we don't even have an entry for that folder
    4472             745 :   list->RemoveElement(aNode);
    4473                 : }
    4474                 : 
    4475                 : 
    4476                 : nsNavHistoryResult::FolderObserverList*
    4477            6015 : nsNavHistoryResult::BookmarkFolderObserversForId(PRInt64 aFolderId, bool aCreate)
    4478                 : {
    4479                 :   FolderObserverList* list;
    4480            6015 :   if (mBookmarkFolderObservers.Get(aFolderId, &list))
    4481            1957 :     return list;
    4482            4058 :   if (!aCreate)
    4483            3304 :     return nsnull;
    4484                 : 
    4485                 :   // need to create a new list
    4486             754 :   list = new FolderObserverList;
    4487             754 :   mBookmarkFolderObservers.Put(aFolderId, list);
    4488             754 :   return list;
    4489                 : }
    4490                 : 
    4491                 : 
    4492                 : NS_IMETHODIMP
    4493               0 : nsNavHistoryResult::GetSortingMode(PRUint16* aSortingMode)
    4494                 : {
    4495               0 :   *aSortingMode = mSortingMode;
    4496               0 :   return NS_OK;
    4497                 : }
    4498                 : 
    4499                 : 
    4500                 : NS_IMETHODIMP
    4501             484 : nsNavHistoryResult::SetSortingMode(PRUint16 aSortingMode)
    4502                 : {
    4503             484 :   NS_ENSURE_STATE(mRootNode);
    4504                 : 
    4505             484 :   if (aSortingMode > nsINavHistoryQueryOptions::SORT_BY_FRECENCY_DESCENDING)
    4506               0 :     return NS_ERROR_INVALID_ARG;
    4507                 : 
    4508                 :   // Keep everything in sync.
    4509             484 :   NS_ASSERTION(mOptions, "Options should always be present for a root query");
    4510                 : 
    4511             484 :   mSortingMode = aSortingMode;
    4512                 : 
    4513             484 :   if (!mRootNode->mExpanded) {
    4514                 :     // Need to do this later when node will be expanded.
    4515             144 :     mNeedsToApplySortingMode = true;
    4516             144 :     return NS_OK;
    4517                 :   }
    4518                 : 
    4519                 :   // Actually do sorting.
    4520                 :   nsNavHistoryContainerResultNode::SortComparator comparator =
    4521             340 :       nsNavHistoryContainerResultNode::GetSortingComparator(aSortingMode);
    4522             340 :   if (comparator) {
    4523             340 :     nsNavHistory* history = nsNavHistory::GetHistoryService();
    4524             340 :     NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY);
    4525             340 :     mRootNode->RecursiveSort(mSortingAnnotation.get(), comparator);
    4526                 :   }
    4527                 : 
    4528             340 :   NOTIFY_RESULT_OBSERVERS(this, SortingChanged(aSortingMode));
    4529             340 :   NOTIFY_RESULT_OBSERVERS(this, InvalidateContainer(mRootNode));
    4530             340 :   return NS_OK;
    4531                 : }
    4532                 : 
    4533                 : 
    4534                 : NS_IMETHODIMP
    4535               0 : nsNavHistoryResult::GetSortingAnnotation(nsACString& _result) {
    4536               0 :   _result.Assign(mSortingAnnotation);
    4537               0 :   return NS_OK;
    4538                 : }
    4539                 : 
    4540                 : 
    4541                 : NS_IMETHODIMP
    4542               1 : nsNavHistoryResult::SetSortingAnnotation(const nsACString& aSortingAnnotation) {
    4543               1 :   mSortingAnnotation.Assign(aSortingAnnotation);
    4544               1 :   return NS_OK;
    4545                 : }
    4546                 : 
    4547                 : 
    4548                 : NS_IMETHODIMP
    4549               7 : nsNavHistoryResult::AddObserver(nsINavHistoryResultObserver* aObserver,
    4550                 :                                 bool aOwnsWeak)
    4551                 : {
    4552               7 :   NS_ENSURE_ARG(aObserver);
    4553               7 :   nsresult rv = mObservers.AppendWeakElement(aObserver, aOwnsWeak);
    4554               7 :   NS_ENSURE_SUCCESS(rv, rv);
    4555                 :   
    4556               7 :   rv = aObserver->SetResult(this);
    4557               7 :   NS_ENSURE_SUCCESS(rv, rv);
    4558                 : 
    4559                 :   // If we are batching, notify a fake batch start to the observers.
    4560                 :   // Not doing so would then notify a not coupled batch end.
    4561               7 :   if (mBatchInProgress) {
    4562               0 :     NOTIFY_RESULT_OBSERVERS(this, Batching(true));
    4563                 :   }
    4564                 : 
    4565               7 :   return NS_OK;
    4566                 : }
    4567                 : 
    4568                 : 
    4569                 : NS_IMETHODIMP
    4570               7 : nsNavHistoryResult::RemoveObserver(nsINavHistoryResultObserver* aObserver)
    4571                 : {
    4572               7 :   NS_ENSURE_ARG(aObserver);
    4573               7 :   return mObservers.RemoveWeakElement(aObserver);
    4574                 : }
    4575                 : 
    4576                 : 
    4577                 : NS_IMETHODIMP
    4578              20 : nsNavHistoryResult::GetSuppressNotifications(bool* _retval)
    4579                 : {
    4580              20 :   *_retval = mSuppressNotifications;
    4581              20 :   return NS_OK;
    4582                 : }
    4583                 : 
    4584                 : 
    4585                 : NS_IMETHODIMP
    4586              40 : nsNavHistoryResult::SetSuppressNotifications(bool aSuppressNotifications)
    4587                 : {
    4588              40 :   mSuppressNotifications = aSuppressNotifications;
    4589              40 :   return NS_OK;
    4590                 : }
    4591                 : 
    4592                 : 
    4593                 : NS_IMETHODIMP
    4594            1043 : nsNavHistoryResult::GetRoot(nsINavHistoryContainerResultNode** aRoot)
    4595                 : {
    4596            1043 :   if (!mRootNode) {
    4597               0 :     NS_NOTREACHED("Root is null");
    4598               0 :     *aRoot = nsnull;
    4599               0 :     return NS_ERROR_FAILURE;
    4600                 :   }
    4601            1043 :   return mRootNode->QueryInterface(NS_GET_IID(nsINavHistoryContainerResultNode),
    4602            1043 :                                    reinterpret_cast<void**>(aRoot));
    4603                 : }
    4604                 : 
    4605                 : 
    4606                 : void
    4607              59 : nsNavHistoryResult::requestRefresh(nsNavHistoryContainerResultNode* aContainer)
    4608                 : {
    4609                 :   // Don't add twice the same container.
    4610              59 :   if (mRefreshParticipants.IndexOf(aContainer) == mRefreshParticipants.NoIndex)
    4611              28 :     mRefreshParticipants.AppendElement(aContainer);
    4612              59 : }
    4613                 : 
    4614                 : // nsINavBookmarkObserver implementation
    4615                 : 
    4616                 : // Here, it is important that we create a COPY of the observer array. Some
    4617                 : // observers will requery themselves, which may cause the observer array to
    4618                 : // be modified or added to.
    4619                 : #define ENUMERATE_BOOKMARK_FOLDER_OBSERVERS(_folderId, _functionCall) \
    4620                 :   PR_BEGIN_MACRO \
    4621                 :     FolderObserverList* _fol = BookmarkFolderObserversForId(_folderId, false); \
    4622                 :     if (_fol) { \
    4623                 :       FolderObserverList _listCopy(*_fol); \
    4624                 :       for (PRUint32 _fol_i = 0; _fol_i < _listCopy.Length(); ++_fol_i) { \
    4625                 :         if (_listCopy[_fol_i]) \
    4626                 :           _listCopy[_fol_i]->_functionCall; \
    4627                 :       } \
    4628                 :     } \
    4629                 :   PR_END_MACRO
    4630                 : #define ENUMERATE_LIST_OBSERVERS(_listType, _functionCall, _observersList, _conditionCall) \
    4631                 :   PR_BEGIN_MACRO \
    4632                 :     _listType _listCopy(_observersList); \
    4633                 :     for (PRUint32 _obs_i = 0; _obs_i < _listCopy.Length(); ++_obs_i) { \
    4634                 :       if (_listCopy[_obs_i] && _listCopy[_obs_i]->_conditionCall) \
    4635                 :         _listCopy[_obs_i]->_functionCall; \
    4636                 :     } \
    4637                 :   PR_END_MACRO
    4638                 : #define ENUMERATE_QUERY_OBSERVERS(_functionCall, _observersList, _conditionCall) \
    4639                 :   ENUMERATE_LIST_OBSERVERS(QueryObserverList, _functionCall, _observersList, _conditionCall)
    4640                 : #define ENUMERATE_ALL_BOOKMARKS_OBSERVERS(_functionCall) \
    4641                 :   ENUMERATE_QUERY_OBSERVERS(_functionCall, mAllBookmarksObservers, IsQuery())
    4642                 : #define ENUMERATE_HISTORY_OBSERVERS(_functionCall) \
    4643                 :   ENUMERATE_QUERY_OBSERVERS(_functionCall, mHistoryObservers, IsQuery())
    4644                 : 
    4645                 : #define NOTIFY_REFRESH_PARTICIPANTS() \
    4646                 :   PR_BEGIN_MACRO \
    4647                 :   ENUMERATE_LIST_OBSERVERS(ContainerObserverList, Refresh(), mRefreshParticipants, IsContainer()); \
    4648                 :   mRefreshParticipants.Clear(); \
    4649                 :   PR_END_MACRO
    4650                 : 
    4651                 : NS_IMETHODIMP
    4652             231 : nsNavHistoryResult::OnBeginUpdateBatch()
    4653                 : {
    4654                 :   // Since we could be observing both history and bookmarks, it's possible both
    4655                 :   // notify the batch.  We can safely ignore nested calls.
    4656             231 :   if (!mBatchInProgress) {
    4657             169 :     mBatchInProgress = true;
    4658             169 :     ENUMERATE_HISTORY_OBSERVERS(OnBeginUpdateBatch());
    4659             169 :     ENUMERATE_ALL_BOOKMARKS_OBSERVERS(OnBeginUpdateBatch());
    4660                 : 
    4661             169 :     NOTIFY_RESULT_OBSERVERS(this, Batching(true));
    4662                 :   }
    4663                 : 
    4664             231 :   return NS_OK;
    4665                 : }
    4666                 : 
    4667                 : 
    4668                 : NS_IMETHODIMP
    4669             231 : nsNavHistoryResult::OnEndUpdateBatch()
    4670                 : {
    4671                 :   // Since we could be observing both history and bookmarks, it's possible both
    4672                 :   // notify the batch.  We can safely ignore nested calls.
    4673                 :   // Notice it's possible we are notified OnEndUpdateBatch more times than
    4674                 :   // onBeginUpdateBatch, since the result could be created in the middle of
    4675                 :   // nested batches.
    4676             231 :   if (mBatchInProgress) {
    4677             169 :     ENUMERATE_HISTORY_OBSERVERS(OnEndUpdateBatch());
    4678             169 :     ENUMERATE_ALL_BOOKMARKS_OBSERVERS(OnEndUpdateBatch());
    4679                 : 
    4680                 :     // Setting mBatchInProgress before notifying the end of the batch to
    4681                 :     // observers would make evantual calls to Refresh() directly handled rather
    4682                 :     // than enqueued.  Thus set it just before handling refreshes.
    4683             169 :     mBatchInProgress = false;
    4684             169 :     NOTIFY_REFRESH_PARTICIPANTS();
    4685             169 :     NOTIFY_RESULT_OBSERVERS(this, Batching(false));
    4686                 :   }
    4687                 : 
    4688             231 :   return NS_OK;
    4689                 : }
    4690                 : 
    4691                 : 
    4692                 : NS_IMETHODIMP
    4693             791 : nsNavHistoryResult::OnItemAdded(PRInt64 aItemId,
    4694                 :                                 PRInt64 aParentId,
    4695                 :                                 PRInt32 aIndex,
    4696                 :                                 PRUint16 aItemType,
    4697                 :                                 nsIURI* aURI,
    4698                 :                                 const nsACString& aTitle,
    4699                 :                                 PRTime aDateAdded,
    4700                 :                                 const nsACString& aGUID,
    4701                 :                                 const nsACString& aParentGUID)
    4702                 : {
    4703             791 :   ENUMERATE_BOOKMARK_FOLDER_OBSERVERS(aParentId,
    4704                 :     OnItemAdded(aItemId, aParentId, aIndex, aItemType, aURI, aTitle, aDateAdded,
    4705                 :                 aGUID, aParentGUID)
    4706                 :   );
    4707             791 :   ENUMERATE_HISTORY_OBSERVERS(
    4708                 :     OnItemAdded(aItemId, aParentId, aIndex, aItemType, aURI, aTitle, aDateAdded,
    4709                 :                 aGUID, aParentGUID)
    4710                 :   );
    4711             791 :   ENUMERATE_ALL_BOOKMARKS_OBSERVERS(
    4712                 :     OnItemAdded(aItemId, aParentId, aIndex, aItemType, aURI, aTitle, aDateAdded,
    4713                 :                 aGUID, aParentGUID)
    4714                 :   );
    4715             791 :   return NS_OK;
    4716                 : }
    4717                 : 
    4718                 : 
    4719                 : NS_IMETHODIMP
    4720             892 : nsNavHistoryResult::OnBeforeItemRemoved(PRInt64 aItemId,
    4721                 :                                         PRUint16 aItemType,
    4722                 :                                         PRInt64 aParentId,
    4723                 :                                         const nsACString& aGUID,
    4724                 :                                         const nsACString& aParentGUID)
    4725                 : {
    4726             892 :   ENUMERATE_ALL_BOOKMARKS_OBSERVERS(
    4727                 :     OnBeforeItemRemoved(aItemId, aItemType, aParentId, aGUID, aParentGUID);
    4728                 :   );
    4729             892 :   return NS_OK;
    4730                 : }
    4731                 : 
    4732                 : 
    4733                 : NS_IMETHODIMP
    4734             892 : nsNavHistoryResult::OnItemRemoved(PRInt64 aItemId,
    4735                 :                                   PRInt64 aParentId,
    4736                 :                                   PRInt32 aIndex,
    4737                 :                                   PRUint16 aItemType,
    4738                 :                                   nsIURI* aURI,
    4739                 :                                   const nsACString& aGUID,
    4740                 :                                   const nsACString& aParentGUID)
    4741                 : {
    4742             892 :   ENUMERATE_BOOKMARK_FOLDER_OBSERVERS(aParentId,
    4743                 :       OnItemRemoved(aItemId, aParentId, aIndex, aItemType, aURI, aGUID,
    4744                 :                     aParentGUID));
    4745             892 :   ENUMERATE_ALL_BOOKMARKS_OBSERVERS(
    4746                 :       OnItemRemoved(aItemId, aParentId, aIndex, aItemType, aURI, aGUID,
    4747                 :                     aParentGUID));
    4748             892 :   ENUMERATE_HISTORY_OBSERVERS(
    4749                 :       OnItemRemoved(aItemId, aParentId, aIndex, aItemType, aURI, aGUID,
    4750                 :                     aParentGUID));
    4751             892 :   return NS_OK;
    4752                 : }
    4753                 : 
    4754                 : 
    4755                 : NS_IMETHODIMP
    4756            2814 : nsNavHistoryResult::OnItemChanged(PRInt64 aItemId,
    4757                 :                                   const nsACString &aProperty,
    4758                 :                                   bool aIsAnnotationProperty,
    4759                 :                                   const nsACString &aNewValue,
    4760                 :                                   PRTime aLastModified,
    4761                 :                                   PRUint16 aItemType,
    4762                 :                                   PRInt64 aParentId,
    4763                 :                                   const nsACString& aGUID,
    4764                 :                                   const nsACString& aParentGUID)
    4765                 : {
    4766            2814 :   ENUMERATE_ALL_BOOKMARKS_OBSERVERS(
    4767                 :     OnItemChanged(aItemId, aProperty, aIsAnnotationProperty, aNewValue,
    4768                 :                   aLastModified, aItemType, aParentId, aGUID, aParentGUID));
    4769                 : 
    4770                 :   // Note: folder-nodes set their own bookmark observer only once they're
    4771                 :   // opened, meaning we cannot optimize this code path for changes done to
    4772                 :   // folder-nodes.
    4773                 : 
    4774            2814 :   FolderObserverList* list = BookmarkFolderObserversForId(aParentId, false);
    4775            2814 :   if (!list)
    4776            2105 :     return NS_OK;
    4777                 : 
    4778            1032 :   for (PRUint32 i = 0; i < list->Length(); ++i) {
    4779             646 :     nsRefPtr<nsNavHistoryFolderResultNode> folder = list->ElementAt(i);
    4780             323 :     if (folder) {
    4781                 :       PRUint32 nodeIndex;
    4782                 :       nsRefPtr<nsNavHistoryResultNode> node =
    4783             646 :         folder->FindChildById(aItemId, &nodeIndex);
    4784                 :       // if ExcludeItems is true we don't update non visible items
    4785             323 :       bool excludeItems = (mRootNode->mOptions->ExcludeItems()) ||
    4786             323 :                              folder->mOptions->ExcludeItems();
    4787             969 :       if (node &&
    4788             646 :           (!excludeItems || !(node->IsURI() || node->IsSeparator())) &&
    4789             323 :           folder->StartIncrementalUpdate()) {
    4790             323 :         node->OnItemChanged(aItemId, aProperty, aIsAnnotationProperty,
    4791                 :                             aNewValue, aLastModified, aItemType, aParentId,
    4792             323 :                             aGUID, aParentGUID);
    4793                 :       }
    4794                 :     }
    4795                 :   }
    4796                 : 
    4797                 :   // Note: we do NOT call history observers in this case.  This notification is
    4798                 :   // the same as other history notification, except that here we know the item
    4799                 :   // is a bookmark.  History observers will handle the history notification
    4800                 :   // instead.
    4801             709 :   return NS_OK;
    4802                 : }
    4803                 : 
    4804                 : 
    4805                 : NS_IMETHODIMP
    4806               1 : nsNavHistoryResult::OnItemVisited(PRInt64 aItemId,
    4807                 :                                   PRInt64 aVisitId,
    4808                 :                                   PRTime aVisitTime,
    4809                 :                                   PRUint32 aTransitionType,
    4810                 :                                   nsIURI* aURI,
    4811                 :                                   PRInt64 aParentId,
    4812                 :                                   const nsACString& aGUID,
    4813                 :                                   const nsACString& aParentGUID)
    4814                 : {
    4815               1 :   ENUMERATE_BOOKMARK_FOLDER_OBSERVERS(aParentId,
    4816                 :       OnItemVisited(aItemId, aVisitId, aVisitTime, aTransitionType, aURI,
    4817                 :                     aParentId, aGUID, aParentGUID));
    4818               1 :   ENUMERATE_ALL_BOOKMARKS_OBSERVERS(
    4819                 :       OnItemVisited(aItemId, aVisitId, aVisitTime, aTransitionType, aURI,
    4820                 :                     aParentId, aGUID, aParentGUID));
    4821                 :   // Note: we do NOT call history observers in this case.  This notification is
    4822                 :   // the same as OnVisit, except that here we know the item is a bookmark.
    4823                 :   // History observers will handle the history notification instead.
    4824               1 :   return NS_OK;
    4825                 : }
    4826                 : 
    4827                 : 
    4828                 : /**
    4829                 :  * Need to notify both the source and the destination folders (if they are
    4830                 :  * different).
    4831                 :  */
    4832                 : NS_IMETHODIMP
    4833               2 : nsNavHistoryResult::OnItemMoved(PRInt64 aItemId,
    4834                 :                                 PRInt64 aOldParent,
    4835                 :                                 PRInt32 aOldIndex,
    4836                 :                                 PRInt64 aNewParent,
    4837                 :                                 PRInt32 aNewIndex,
    4838                 :                                 PRUint16 aItemType,
    4839                 :                                 const nsACString& aGUID,
    4840                 :                                 const nsACString& aOldParentGUID,
    4841                 :                                 const nsACString& aNewParentGUID)
    4842                 : {
    4843               2 :   ENUMERATE_BOOKMARK_FOLDER_OBSERVERS(aOldParent,
    4844                 :       OnItemMoved(aItemId, aOldParent, aOldIndex, aNewParent, aNewIndex,
    4845                 :                   aItemType, aGUID, aOldParentGUID, aNewParentGUID));
    4846               2 :   if (aNewParent != aOldParent) {
    4847               1 :     ENUMERATE_BOOKMARK_FOLDER_OBSERVERS(aNewParent,
    4848                 :         OnItemMoved(aItemId, aOldParent, aOldIndex, aNewParent, aNewIndex,
    4849                 :                     aItemType, aGUID, aOldParentGUID, aNewParentGUID));
    4850                 :   }
    4851               2 :   ENUMERATE_ALL_BOOKMARKS_OBSERVERS(OnItemMoved(aItemId, aOldParent, aOldIndex,
    4852                 :                                                 aNewParent, aNewIndex,
    4853                 :                                                 aItemType, aGUID,
    4854                 :                                                 aOldParentGUID,
    4855                 :                                                 aNewParentGUID));
    4856               2 :   ENUMERATE_HISTORY_OBSERVERS(OnItemMoved(aItemId, aOldParent, aOldIndex,
    4857                 :                                           aNewParent, aNewIndex, aItemType,
    4858                 :                                           aGUID, aOldParentGUID,
    4859                 :                                           aNewParentGUID));
    4860               2 :   return NS_OK;
    4861                 : }
    4862                 : 
    4863                 : 
    4864                 : NS_IMETHODIMP
    4865              42 : nsNavHistoryResult::OnVisit(nsIURI* aURI, PRInt64 aVisitId, PRTime aTime,
    4866                 :                             PRInt64 aSessionId, PRInt64 aReferringId,
    4867                 :                             PRUint32 aTransitionType, const nsACString& aGUID,
    4868                 :                             PRUint32* aAdded)
    4869                 : {
    4870              42 :   PRUint32 added = 0;
    4871                 : 
    4872              42 :   ENUMERATE_HISTORY_OBSERVERS(OnVisit(aURI, aVisitId, aTime, aSessionId,
    4873                 :                                       aReferringId, aTransitionType, aGUID,
    4874                 :                                       &added));
    4875                 : 
    4876              42 :   if (!mRootNode->mExpanded)
    4877               0 :     return NS_OK;
    4878                 : 
    4879                 :   // If this visit is accepted by an overlapped container, and not all
    4880                 :   // overlapped containers are visible, we should still call Refresh if the
    4881                 :   // visit falls into any of them.
    4882              42 :   bool todayIsMissing = false;
    4883              42 :   PRUint32 resultType = mRootNode->mOptions->ResultType();
    4884              42 :   if (resultType == nsINavHistoryQueryOptions::RESULTS_AS_DATE_QUERY ||
    4885                 :       resultType == nsINavHistoryQueryOptions::RESULTS_AS_DATE_SITE_QUERY) {
    4886                 :     PRUint32 childCount;
    4887               2 :     nsresult rv = mRootNode->GetChildCount(&childCount);
    4888               2 :     NS_ENSURE_SUCCESS(rv, rv);
    4889               2 :     if (childCount) {
    4890               4 :       nsCOMPtr<nsINavHistoryResultNode> firstChild;
    4891               2 :       rv = mRootNode->GetChild(0, getter_AddRefs(firstChild));
    4892               2 :       NS_ENSURE_SUCCESS(rv, rv);
    4893               4 :       nsCAutoString title;
    4894               2 :       rv = firstChild->GetTitle(title);
    4895               2 :       NS_ENSURE_SUCCESS(rv, rv);
    4896               2 :       nsNavHistory* history = nsNavHistory::GetHistoryService();
    4897               2 :       NS_ENSURE_TRUE(history, 0);
    4898               6 :       nsCAutoString todayLabel;
    4899                 :       history->GetStringFromName(
    4900               2 :         NS_LITERAL_STRING("finduri-AgeInDays-is-0").get(), todayLabel);
    4901               2 :       todayIsMissing = !todayLabel.Equals(title);
    4902                 :     }
    4903                 :   }
    4904                 : 
    4905              42 :   if (!added || todayIsMissing) {
    4906                 :     // None of registered query observers has accepted our URI.  This means,
    4907                 :     // that a matching query either was not expanded or it does not exist.
    4908              28 :     PRUint32 resultType = mRootNode->mOptions->ResultType();
    4909              28 :     if (resultType == nsINavHistoryQueryOptions::RESULTS_AS_DATE_QUERY ||
    4910                 :         resultType == nsINavHistoryQueryOptions::RESULTS_AS_DATE_SITE_QUERY ||
    4911                 :         resultType == nsINavHistoryQueryOptions::RESULTS_AS_SITE_QUERY)
    4912               2 :       (void)mRootNode->GetAsQuery()->Refresh();
    4913                 :     else {
    4914                 :       // We are result of a folder node, then we should run through history
    4915                 :       // observers that are containers queries and refresh them.
    4916                 :       // We use a copy of the observers array since requerying could potentially
    4917                 :       // cause changes to the array.
    4918              26 :       ENUMERATE_QUERY_OBSERVERS(Refresh(), mHistoryObservers, IsContainersQuery());
    4919                 :     }
    4920                 :   }
    4921                 : 
    4922              42 :   return NS_OK;
    4923                 : }
    4924                 : 
    4925                 : 
    4926                 : NS_IMETHODIMP
    4927              63 : nsNavHistoryResult::OnTitleChanged(nsIURI* aURI,
    4928                 :                                    const nsAString& aPageTitle,
    4929                 :                                    const nsACString& aGUID)
    4930                 : {
    4931              63 :   ENUMERATE_HISTORY_OBSERVERS(OnTitleChanged(aURI, aPageTitle, aGUID));
    4932              63 :   return NS_OK;
    4933                 : }
    4934                 : 
    4935                 : 
    4936                 : NS_IMETHODIMP
    4937              10 : nsNavHistoryResult::OnBeforeDeleteURI(nsIURI *aURI,
    4938                 :                                       const nsACString& aGUID,
    4939                 :                                       PRUint16 aReason)
    4940                 : {
    4941              10 :   return NS_OK;
    4942                 : }
    4943                 : 
    4944                 : 
    4945                 : NS_IMETHODIMP
    4946              10 : nsNavHistoryResult::OnDeleteURI(nsIURI *aURI,
    4947                 :                                 const nsACString& aGUID,
    4948                 :                                 PRUint16 aReason)
    4949                 : {
    4950              10 :   ENUMERATE_HISTORY_OBSERVERS(OnDeleteURI(aURI, aGUID, aReason));
    4951              10 :   return NS_OK;
    4952                 : }
    4953                 : 
    4954                 : 
    4955                 : NS_IMETHODIMP
    4956               1 : nsNavHistoryResult::OnClearHistory()
    4957                 : {
    4958               1 :   ENUMERATE_HISTORY_OBSERVERS(OnClearHistory());
    4959               1 :   return NS_OK;
    4960                 : }
    4961                 : 
    4962                 : 
    4963                 : NS_IMETHODIMP
    4964               0 : nsNavHistoryResult::OnPageChanged(nsIURI* aURI,
    4965                 :                                   PRUint32 aChangedAttribute,
    4966                 :                                   const nsAString& aValue,
    4967                 :                                   const nsACString& aGUID)
    4968                 : {
    4969               0 :   ENUMERATE_HISTORY_OBSERVERS(OnPageChanged(aURI, aChangedAttribute, aValue, aGUID));
    4970               0 :   return NS_OK;
    4971                 : }
    4972                 : 
    4973                 : 
    4974                 : /**
    4975                 :  * Don't do anything when visits expire.
    4976                 :  */
    4977                 : NS_IMETHODIMP
    4978               1 : nsNavHistoryResult::OnDeleteVisits(nsIURI* aURI,
    4979                 :                                    PRTime aVisitTime,
    4980                 :                                    const nsACString& aGUID,
    4981                 :                                    PRUint16 aReason)
    4982                 : {
    4983               1 :   ENUMERATE_HISTORY_OBSERVERS(OnDeleteVisits(aURI, aVisitTime, aGUID, aReason));
    4984               1 :   return NS_OK;
    4985            4392 : }

Generated by: LCOV version 1.7