LCOV - code coverage report
Current view: top level - toolkit/components/places - nsNavHistoryResult.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 1961 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 214 0.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13