Bug 1446951 - 6 - Change internal results code to support a single query. r=standard8 draft
authorMarco Bonardo <mbonardo@mozilla.com>
Mon, 19 Mar 2018 11:58:35 +0100
changeset 770869 1de38692f1acc5569745d3b6d8aa02c4fd62c229
parent 770868 4ef80310c588ea6ae6e7fd5ae4b9ef201a3ab4bd
child 770870 726ef80d87cf10119b3ce156d176eeac13af9e32
push id103521
push usermak77@bonardo.net
push dateWed, 21 Mar 2018 22:48:31 +0000
reviewersstandard8
bugs1446951
milestone61.0a1
Bug 1446951 - 6 - Change internal results code to support a single query. r=standard8 MozReview-Commit-ID: 5ceutaSM6Vk
toolkit/components/places/nsNavHistory.cpp
toolkit/components/places/nsNavHistory.h
toolkit/components/places/nsNavHistoryQuery.cpp
toolkit/components/places/nsNavHistoryQuery.h
toolkit/components/places/nsNavHistoryResult.cpp
toolkit/components/places/nsNavHistoryResult.h
toolkit/components/places/tests/unit/test_annotations.js
--- a/toolkit/components/places/nsNavHistory.cpp
+++ b/toolkit/components/places/nsNavHistory.cpp
@@ -188,20 +188,20 @@ NS_INTERFACE_MAP_END
 
 // We don't care about flattening everything
 NS_IMPL_CI_INTERFACE_GETTER(nsNavHistory,
                             nsINavHistoryService,
                             nsIBrowserHistory)
 
 namespace {
 
-static int64_t GetSimpleBookmarksQueryFolder(nsNavHistoryQuery* aQuery,
-                                             nsNavHistoryQueryOptions* aOptions);
-static void ParseSearchTermsFromQueries(const nsCOMArray<nsNavHistoryQuery>& aQueries,
-                                        nsTArray<nsTArray<nsString>*>* aTerms);
+static int64_t GetSimpleBookmarksQueryFolder(const RefPtr<nsNavHistoryQuery>& aQuery,
+                                             const RefPtr<nsNavHistoryQueryOptions>& aOptions);
+static void ParseSearchTermsFromQuery(const RefPtr<nsNavHistoryQuery>& aQuery,
+                                      nsTArray<nsString>* aTerms);
 
 void GetTagsSqlFragment(int64_t aTagsFolder,
                         const nsACString& aRelation,
                         bool aHasSearchTerms,
                         nsACString& _sqlFragment) {
   if (!aHasSearchTerms)
     _sqlFragment.AssignLiteral("null");
   else {
@@ -785,57 +785,46 @@ nsNavHistory::NormalizeTime(uint32_t aRe
 //    QUERYUPDATE_NONE:
 //      A query that never updates, e.g. the left-pane root query.
 //
 //    aHasSearchTerms will be set to true if the query has any dependence on
 //    keywords. When there is no dependence on keywords, we can handle title
 //    change operations as simple instead of complex.
 
 uint32_t
-nsNavHistory::GetUpdateRequirements(const nsCOMArray<nsNavHistoryQuery>& aQueries,
+nsNavHistory::GetUpdateRequirements(const RefPtr<nsNavHistoryQuery>& aQuery,
                                     nsNavHistoryQueryOptions* aOptions,
                                     bool* aHasSearchTerms)
 {
-  NS_ASSERTION(aQueries.Count() > 0, "Must have at least one query");
-
   // first check if there are search terms
   *aHasSearchTerms = false;
-  int32_t i;
-  for (i = 0; i < aQueries.Count(); i ++) {
-    aQueries[i]->GetHasSearchTerms(aHasSearchTerms);
-    if (*aHasSearchTerms)
-      break;
-  }
+  aQuery->GetHasSearchTerms(aHasSearchTerms);
 
   bool nonTimeBasedItems = false;
   bool domainBasedItems = false;
 
-  for (i = 0; i < aQueries.Count(); i ++) {
-    nsNavHistoryQuery* query = aQueries[i];
-
-    bool hasSearchTerms = !query->SearchTerms().IsEmpty();
-    if (query->Folders().Length() > 0 ||
-        query->OnlyBookmarked() ||
-        query->Tags().Length() > 0 ||
-        (aOptions->QueryType() == nsINavHistoryQueryOptions::QUERY_TYPE_BOOKMARKS &&
-         hasSearchTerms)) {
-      return QUERYUPDATE_COMPLEX_WITH_BOOKMARKS;
-    }
-
-    // Note: we don't currently have any complex non-bookmarked items, but these
-    // are expected to be added. Put detection of these items here.
-    if (hasSearchTerms ||
-        !query->Domain().IsVoid() ||
-        query->Uri() != nullptr)
-      nonTimeBasedItems = true;
-
-    if (!query->Domain().IsVoid())
-      domainBasedItems = true;
+  bool hasSearchTerms = !aQuery->SearchTerms().IsEmpty();
+  if (aQuery->Folders().Length() > 0 ||
+      aQuery->OnlyBookmarked() ||
+      aQuery->Tags().Length() > 0 ||
+      (aOptions->QueryType() == nsINavHistoryQueryOptions::QUERY_TYPE_BOOKMARKS &&
+        hasSearchTerms)) {
+    return QUERYUPDATE_COMPLEX_WITH_BOOKMARKS;
   }
 
+  // Note: we don't currently have any complex non-bookmarked items, but these
+  // are expected to be added. Put detection of these items here.
+  if (hasSearchTerms ||
+      !aQuery->Domain().IsVoid() ||
+      aQuery->Uri() != nullptr)
+    nonTimeBasedItems = true;
+
+  if (!aQuery->Domain().IsVoid())
+    domainBasedItems = true;
+
   if (aOptions->ResultType() ==
         nsINavHistoryQueryOptions::RESULTS_AS_TAG_QUERY)
       return QUERYUPDATE_COMPLEX_WITH_BOOKMARKS;
 
   if (aOptions->ResultType() ==
         nsINavHistoryQueryOptions::RESULTS_AS_ROOTS_QUERY)
       return QUERYUPDATE_MOBILEPREF;
 
@@ -845,146 +834,136 @@ nsNavHistory::GetUpdateRequirements(cons
 
   // Whenever there is a maximum number of results,
   // and we are not a bookmark query we must requery. This
   // is because we can't generally know if any given addition/change causes
   // the item to be in the top N items in the database.
   if (aOptions->MaxResults() > 0)
     return QUERYUPDATE_COMPLEX;
 
-  if (aQueries.Count() == 1 && domainBasedItems)
+  if (domainBasedItems)
     return QUERYUPDATE_HOST;
-  if (aQueries.Count() == 1 && !nonTimeBasedItems)
+  if (!nonTimeBasedItems)
     return QUERYUPDATE_TIME;
 
   return QUERYUPDATE_SIMPLE;
 }
 
 
 // nsNavHistory::EvaluateQueryForNode
 //
-//    This runs the node through the given queries to see if satisfies the
+//    This runs the node through the given query to see if satisfies the
 //    query conditions. Not every query parameters are handled by this code,
 //    but we handle the most common ones so that performance is better.
 //
 //    We assume that the time on the node is the time that we want to compare.
 //    This is not necessarily true because URL nodes have the last access time,
 //    which is not necessarily the same. However, since this is being called
 //    to update the list, we assume that the last access time is the current
 //    access time that we are being asked to compare so it works out.
 //
 //    Returns true if node matches the query, false if not.
 
 bool
-nsNavHistory::EvaluateQueryForNode(const nsCOMArray<nsNavHistoryQuery>& aQueries,
+nsNavHistory::EvaluateQueryForNode(const RefPtr<nsNavHistoryQuery>& aQuery,
                                    nsNavHistoryQueryOptions* aOptions,
                                    nsNavHistoryResultNode* aNode)
 {
   // lazily created from the node's string when we need to match URIs
   nsCOMPtr<nsIURI> nodeUri;
 
   // --- hidden ---
   if (aNode->mHidden && !aOptions->IncludeHidden())
     return false;
 
-  for (int32_t i = 0; i < aQueries.Count(); i ++) {
-    bool hasIt;
-    nsCOMPtr<nsNavHistoryQuery> query = aQueries[i];
-
-    // --- begin time ---
-    query->GetHasBeginTime(&hasIt);
-    if (hasIt) {
-      PRTime beginTime = NormalizeTime(query->BeginTimeReference(),
-                                       query->BeginTime());
-      if (aNode->mTime < beginTime)
-        continue; // before our time range
-    }
-
-    // --- end time ---
-    query->GetHasEndTime(&hasIt);
-    if (hasIt) {
-      PRTime endTime = NormalizeTime(query->EndTimeReference(),
-                                     query->EndTime());
-      if (aNode->mTime > endTime)
-        continue; // after our time range
-    }
-
-    // --- search terms ---
-    if (! query->SearchTerms().IsEmpty()) {
-      // we can use the existing filtering code, just give it our one object in
-      // an array.
-      nsCOMArray<nsNavHistoryResultNode> inputSet;
-      inputSet.AppendObject(aNode);
-      nsCOMArray<nsNavHistoryQuery> queries;
-      queries.AppendObject(query);
-      nsCOMArray<nsNavHistoryResultNode> filteredSet;
-      nsresult rv = FilterResultSet(nullptr, inputSet, &filteredSet, queries, aOptions);
-      if (NS_FAILED(rv))
-        continue;
-      if (! filteredSet.Count())
-        continue; // did not make it through the filter, doesn't match
+  bool hasIt;
+  // --- begin time ---
+  aQuery->GetHasBeginTime(&hasIt);
+  if (hasIt) {
+    PRTime beginTime = NormalizeTime(aQuery->BeginTimeReference(),
+                                     aQuery->BeginTime());
+    if (aNode->mTime < beginTime)
+      return false;
+  }
+
+  // --- end time ---
+  aQuery->GetHasEndTime(&hasIt);
+  if (hasIt) {
+    PRTime endTime = NormalizeTime(aQuery->EndTimeReference(),
+                                   aQuery->EndTime());
+    if (aNode->mTime > endTime)
+      return false;
+  }
+
+  // --- search terms ---
+  if (!aQuery->SearchTerms().IsEmpty()) {
+    // we can use the existing filtering code, just give it our one object in
+    // an array.
+    nsCOMArray<nsNavHistoryResultNode> inputSet;
+    inputSet.AppendObject(aNode);
+    nsCOMArray<nsNavHistoryResultNode> filteredSet;
+    nsresult rv = FilterResultSet(nullptr, inputSet, &filteredSet, aQuery, aOptions);
+    if (NS_FAILED(rv))
+      return false;
+    if (!filteredSet.Count())
+      return false;
+  }
+
+  // --- domain/host matching ---
+  aQuery->GetHasDomain(&hasIt);
+  if (hasIt) {
+    if (!nodeUri) {
+      // lazy creation of nodeUri, which might be checked for multiple queries
+      if (NS_FAILED(NS_NewURI(getter_AddRefs(nodeUri), aNode->mURI)))
+        return false;
     }
-
-    // --- domain/host matching ---
-    query->GetHasDomain(&hasIt);
-    if (hasIt) {
-      if (! nodeUri) {
-        // lazy creation of nodeUri, which might be checked for multiple queries
-        if (NS_FAILED(NS_NewURI(getter_AddRefs(nodeUri), aNode->mURI)))
-          continue;
-      }
-      nsAutoCString asciiRequest;
-      if (NS_FAILED(AsciiHostNameFromHostString(query->Domain(), asciiRequest)))
-        continue;
-
-      if (query->DomainIsHost()) {
-        nsAutoCString host;
-        if (NS_FAILED(nodeUri->GetAsciiHost(host)))
-          continue;
-
-        if (! asciiRequest.Equals(host))
-          continue; // host names don't match
-      }
-      // check domain names
-      nsAutoCString domain;
-      DomainNameFromURI(nodeUri, domain);
-      if (! asciiRequest.Equals(domain))
-        continue; // domain names don't match
+    nsAutoCString asciiRequest;
+    if (NS_FAILED(AsciiHostNameFromHostString(aQuery->Domain(), asciiRequest)))
+      return false;
+
+    if (aQuery->DomainIsHost()) {
+      nsAutoCString host;
+      if (NS_FAILED(nodeUri->GetAsciiHost(host)))
+        return false;
+
+      if (!asciiRequest.Equals(host))
+        return false;
     }
-
-    // --- URI matching ---
-    if (query->Uri()) {
-      if (! nodeUri) { // lazy creation of nodeUri
-        if (NS_FAILED(NS_NewURI(getter_AddRefs(nodeUri), aNode->mURI)))
-          continue;
-      }
-
-      bool equals;
-      nsresult rv = query->Uri()->Equals(nodeUri, &equals);
-      NS_ENSURE_SUCCESS(rv, false);
-      if (! equals)
-        continue;
+    // check domain names
+    nsAutoCString domain;
+    DomainNameFromURI(nodeUri, domain);
+    if (!asciiRequest.Equals(domain))
+      return false;
+  }
+
+  // --- URI matching ---
+  if (aQuery->Uri()) {
+    if (!nodeUri) { // lazy creation of nodeUri
+      if (NS_FAILED(NS_NewURI(getter_AddRefs(nodeUri), aNode->mURI)))
+        return false;
     }
 
-    // Transitions matching.
-    const nsTArray<uint32_t>& transitions = query->Transitions();
-    if (aNode->mTransitionType > 0 &&
-        transitions.Length() &&
-        !transitions.Contains(aNode->mTransitionType)) {
-      continue; // transition doesn't match.
-    }
-
-    // If we ever make it to the bottom of this loop, that means it passed all
-    // tests for the given query. Since queries are ORed together, that means
-    // it passed everything and we are done.
-    return true;
+    bool equals;
+    nsresult rv = aQuery->Uri()->Equals(nodeUri, &equals);
+    NS_ENSURE_SUCCESS(rv, false);
+    if (!equals)
+      return false;
   }
 
-  // didn't match any query
-  return false;
+  // Transitions matching.
+  const nsTArray<uint32_t>& transitions = aQuery->Transitions();
+  if (aNode->mTransitionType > 0 &&
+      transitions.Length() &&
+      !transitions.Contains(aNode->mTransitionType)) {
+    return false;
+  }
+
+  // If we ever make it to the bottom, that means it passed all the tests for
+  // the given query.
+  return true;
 }
 
 
 // nsNavHistory::AsciiHostNameFromHostString
 //
 //    We might have interesting encodings and different case in the host name.
 //    This will convert that host name into an ASCII host name by sending it
 //    through the URI canonicalization. The result can be used for comparison
@@ -1239,85 +1218,79 @@ nsNavHistory::ExecuteQuery(nsINavHistory
                            nsINavHistoryQueryOptions *aOptions,
                            nsINavHistoryResult** _retval)
 {
   NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread");
   NS_ENSURE_ARG(aQuery);
   NS_ENSURE_ARG(aOptions);
   NS_ENSURE_ARG_POINTER(_retval);
 
-  nsresult rv;
-  // concrete options
-  nsCOMPtr<nsNavHistoryQueryOptions> options = do_QueryInterface(aOptions);
-  NS_ENSURE_TRUE(options, NS_ERROR_INVALID_ARG);
-
-  // concrete queries array
-  nsCOMArray<nsNavHistoryQuery> queries;
-  RefPtr<nsNavHistoryQuery> query = do_QueryObject(aQuery);
+  // Clone the input query and options, because the caller might change the
+  // objects, but we always want to reflect the original parameters.
+  nsCOMPtr<nsINavHistoryQuery> queryClone;
+  aQuery->Clone(getter_AddRefs(queryClone));
+  NS_ENSURE_STATE(queryClone);
+  RefPtr<nsNavHistoryQuery> query = do_QueryObject(queryClone);
   NS_ENSURE_STATE(query);
-  queries.AppendObject(query);
+  nsCOMPtr<nsINavHistoryQueryOptions> optionsClone;
+  aOptions->Clone(getter_AddRefs(optionsClone));
+  NS_ENSURE_STATE(optionsClone);
+  RefPtr<nsNavHistoryQueryOptions> options = do_QueryObject(optionsClone);
+  NS_ENSURE_STATE(options);
 
   // Create the root node.
   RefPtr<nsNavHistoryContainerResultNode> rootNode;
 
   int64_t folderId = GetSimpleBookmarksQueryFolder(query, options);
   if (folderId) {
     // In the simple case where we're just querying children of a single
     // bookmark folder, we can more efficiently generate results.
     nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService();
     NS_ENSURE_TRUE(bookmarks, NS_ERROR_OUT_OF_MEMORY);
     RefPtr<nsNavHistoryResultNode> tempRootNode;
-    rv = bookmarks->ResultNodeForContainer(folderId, options,
-                                           getter_AddRefs(tempRootNode));
+    nsresult rv = bookmarks->ResultNodeForContainer(folderId, options,
+                                                    getter_AddRefs(tempRootNode));
     if (NS_SUCCEEDED(rv)) {
       rootNode = tempRootNode->GetAsContainer();
     }
     else {
       NS_WARNING("Generating a generic empty node for a broken query!");
       // This is a perf hack to generate an empty query that skips filtering.
       options->SetExcludeItems(true);
     }
   }
 
   if (!rootNode) {
     // Either this is not a folder shortcut, or is a broken one.  In both cases
     // just generate a query node.
     rootNode = new nsNavHistoryQueryResultNode(EmptyCString(),
-                                               queries, options);
+                                               query, options);
   }
 
   // Create the result that will hold nodes.  Inject batching status into it.
-  RefPtr<nsNavHistoryResult> result;
-  rv = nsNavHistoryResult::NewHistoryResult(queries, 1, options,
-                                            rootNode, isBatching(),
-                                            getter_AddRefs(result));
-  NS_ENSURE_SUCCESS(rv, rv);
-
+  RefPtr<nsNavHistoryResult> result = new nsNavHistoryResult(rootNode,
+                                                             query, options,
+                                                             isBatching());
   result.forget(_retval);
   return NS_OK;
 }
 
 // determine from our nsNavHistoryQuery array and nsNavHistoryQueryOptions
 // if this is the place query from the history menu.
 // from browser-menubar.inc, our history menu query is:
 // place:sort=4&maxResults=10
 // note, any maxResult > 0 will still be considered a history menu query
 // or if this is the place query from the "Most Visited" item in the
 // "Smart Bookmarks" folder: place:sort=8&maxResults=10
 // note, any maxResult > 0 will still be considered a Most Visited menu query
 static
-bool IsOptimizableHistoryQuery(const nsCOMArray<nsNavHistoryQuery>& aQueries,
-                                 nsNavHistoryQueryOptions *aOptions,
-                                 uint16_t aSortMode)
+bool IsOptimizableHistoryQuery(const RefPtr<nsNavHistoryQuery>& aQuery,
+                               const RefPtr<nsNavHistoryQueryOptions>& aOptions,
+                               uint16_t aSortMode)
 {
-  if (aQueries.Count() != 1)
-    return false;
-
-  nsNavHistoryQuery *aQuery = aQueries[0];
-
   if (aOptions->QueryType() != nsINavHistoryQueryOptions::QUERY_TYPE_HISTORY)
     return false;
 
   if (aOptions->ResultType() != nsINavHistoryQueryOptions::RESULTS_AS_URI)
     return false;
 
   if (aOptions->SortingMode() != aSortMode)
     return false;
@@ -1360,18 +1333,18 @@ bool IsOptimizableHistoryQuery(const nsC
 
   if (aQuery->Transitions().Length() > 0)
     return false;
 
   return true;
 }
 
 static
-bool NeedToFilterResultSet(const nsCOMArray<nsNavHistoryQuery>& aQueries,
-                             nsNavHistoryQueryOptions *aOptions)
+bool NeedToFilterResultSet(const RefPtr<nsNavHistoryQuery>& aQuery,
+                           nsNavHistoryQueryOptions *aOptions)
 {
   uint16_t resultType = aOptions->ResultType();
   return resultType == nsINavHistoryQueryOptions::RESULTS_AS_TAG_CONTENTS ||
          aOptions->ExcludeQueries();
 }
 
 // ** Helper class for ConstructQueryString **/
 
@@ -2181,18 +2154,18 @@ PlacesSQLQueryBuilder::Limit()
     mQueryString.AppendInt(mMaxResults);
     mQueryString.Append(' ');
   }
   return NS_OK;
 }
 
 nsresult
 nsNavHistory::ConstructQueryString(
-    const nsCOMArray<nsNavHistoryQuery>& aQueries,
-    nsNavHistoryQueryOptions* aOptions,
+    const RefPtr<nsNavHistoryQuery>& aQuery,
+    const RefPtr<nsNavHistoryQueryOptions>& aOptions,
     nsCString& queryString,
     bool& aParamsPresent,
     nsNavHistory::StringHash& aAddParams)
 {
   // For information about visit_type see nsINavHistoryService.idl.
   // visitType == 0 is undefined (see bug #375777 for details).
   // Some sites, especially Javascript-heavy ones, load things in frames to
   // display them, resulting in a lot of these entries. This is the reason
@@ -2201,29 +2174,27 @@ nsNavHistory::ConstructQueryString(
   aParamsPresent = false;
 
   int32_t sortingMode = aOptions->SortingMode();
   NS_ASSERTION(sortingMode >= nsINavHistoryQueryOptions::SORT_BY_NONE &&
                sortingMode <= nsINavHistoryQueryOptions::SORT_BY_FRECENCY_DESCENDING,
                "Invalid sortingMode found while building query!");
 
   bool hasSearchTerms = false;
-  for (int32_t i = 0; i < aQueries.Count() && !hasSearchTerms; i++) {
-    aQueries[i]->GetHasSearchTerms(&hasSearchTerms);
-  }
+  aQuery->GetHasSearchTerms(&hasSearchTerms);
 
   nsAutoCString tagsSqlFragment;
   GetTagsSqlFragment(GetTagsFolder(),
                      NS_LITERAL_CSTRING("h.id"),
                      hasSearchTerms,
                      tagsSqlFragment);
 
-  if (IsOptimizableHistoryQuery(aQueries, aOptions,
+  if (IsOptimizableHistoryQuery(aQuery, aOptions,
         nsINavHistoryQueryOptions::SORT_BY_DATE_DESCENDING) ||
-      IsOptimizableHistoryQuery(aQueries, aOptions,
+      IsOptimizableHistoryQuery(aQuery, aOptions,
         nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_DESCENDING)) {
     // Generate an optimized query for the history menu and most visited
     // smart bookmark.
     queryString = NS_LITERAL_CSTRING(
       "SELECT h.id, h.url, h.title AS page_title, h.rev_host, h.visit_count, h.last_visit_date, "
           "null, null, null, null, null, ") +
           tagsSqlFragment + NS_LITERAL_CSTRING(", h.frecency, h.hidden, h.guid, "
           "null, null, null "
@@ -2250,33 +2221,29 @@ nsNavHistory::ConstructQueryString(
     nsAutoCString additionalQueryOptions;
 
     queryString.ReplaceSubstring("{QUERY_OPTIONS}",
                                   additionalQueryOptions.get());
     return NS_OK;
   }
 
   nsAutoCString conditions;
-  for (int32_t i = 0; i < aQueries.Count(); i++) {
-    nsCString queryClause;
-    rv = QueryToSelectClause(aQueries[i], aOptions, i, &queryClause);
-    NS_ENSURE_SUCCESS(rv, rv);
-    if (! queryClause.IsEmpty()) {
-      aParamsPresent = true;
-      if (! conditions.IsEmpty()) // exists previous clause: multiple ones are ORed
-        conditions += NS_LITERAL_CSTRING(" OR ");
-      conditions += NS_LITERAL_CSTRING("(") + queryClause +
-        NS_LITERAL_CSTRING(")");
-    }
+  nsCString queryClause;
+  rv = QueryToSelectClause(aQuery, aOptions, &queryClause);
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (!queryClause.IsEmpty()) {
+    // TODO: This should be set on a case basis, not blindly.
+    aParamsPresent = true;
+    conditions += queryClause;
   }
 
-  // Determine whether we can push maxResults constraints into the queries
+  // Determine whether we can push maxResults constraints into the query
   // as LIMIT, or if we need to do result count clamping later
   // using FilterResultSet()
-  bool useLimitClause = !NeedToFilterResultSet(aQueries, aOptions);
+  bool useLimitClause = !NeedToFilterResultSet(aQuery, aOptions);
 
   PlacesSQLQueryBuilder queryStringBuilder(conditions, aOptions,
                                            useLimitClause, aAddParams,
                                            hasSearchTerms);
   rv = queryStringBuilder.GetQueryString(queryString);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
@@ -2293,29 +2260,29 @@ nsNavHistory::ConstructQueryString(
 //    statistics that will be built from the perspective of the tree. See
 //    nsNavHistoryQueryResultNode::FillChildren
 //
 //    FIXME: This only does keyword searching for the first query, and does
 //    it ANDed with the all the rest of the queries.
 
 nsresult
 nsNavHistory::GetQueryResults(nsNavHistoryQueryResultNode *aResultNode,
-                              const nsCOMArray<nsNavHistoryQuery>& aQueries,
-                              nsNavHistoryQueryOptions *aOptions,
+                              const RefPtr<nsNavHistoryQuery>& aQuery,
+                              const RefPtr<nsNavHistoryQueryOptions>& aOptions,
                               nsCOMArray<nsNavHistoryResultNode>* aResults)
 {
+  NS_ENSURE_ARG_POINTER(aQuery);
   NS_ENSURE_ARG_POINTER(aOptions);
   NS_ASSERTION(aResults->Count() == 0, "Initial result array must be empty");
-  if (! aQueries.Count())
-    return NS_ERROR_INVALID_ARG;
+
 
   nsCString queryString;
   bool paramsPresent = false;
   nsNavHistory::StringHash addParams(HISTORY_DATE_CONT_LENGTH);
-  nsresult rv = ConstructQueryString(aQueries, aOptions, queryString,
+  nsresult rv = ConstructQueryString(aQuery, aOptions, queryString,
                                      paramsPresent, addParams);
   NS_ENSURE_SUCCESS(rv,rv);
 
   // create statement
   nsCOMPtr<mozIStorageStatement> statement = mDB->GetStatement(queryString);
 #ifdef DEBUG
   if (!statement) {
     nsCOMPtr<mozIStorageConnection> conn = mDB->MainConn();
@@ -2328,39 +2295,35 @@ nsNavHistory::GetQueryResults(nsNavHisto
             queryString.get(), lastError, lastErrorString.get());
     }
   }
 #endif
   NS_ENSURE_STATE(statement);
   mozStorageStatementScoper scoper(statement);
 
   if (paramsPresent) {
-    // bind parameters
-    int32_t i;
-    for (i = 0; i < aQueries.Count(); i++) {
-      rv = BindQueryClauseParameters(statement, i, aQueries[i], aOptions);
-      NS_ENSURE_SUCCESS(rv, rv);
-    }
+    rv = BindQueryClauseParameters(statement, aQuery, aOptions);
+    NS_ENSURE_SUCCESS(rv, rv);
   }
 
   for (auto iter = addParams.Iter(); !iter.Done(); iter.Next()) {
     nsresult rv = statement->BindUTF8StringByName(iter.Key(), iter.Data());
     if (NS_FAILED(rv)) {
       break;
     }
   }
 
   // Optimize the case where there is no need for any post-query filtering.
-  if (NeedToFilterResultSet(aQueries, aOptions)) {
+  if (NeedToFilterResultSet(aQuery, aOptions)) {
     // Generate the top-level results.
     nsCOMArray<nsNavHistoryResultNode> toplevel;
     rv = ResultsAsList(statement, aOptions, &toplevel);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    FilterResultSet(aResultNode, toplevel, aResults, aQueries, aOptions);
+    FilterResultSet(aResultNode, toplevel, aResults, aQuery, aOptions);
   } else {
     rv = ResultsAsList(statement, aOptions, aResults);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return NS_OK;
 }
 
@@ -2911,28 +2874,25 @@ nsNavHistory::AsyncExecuteLegacyQuery(ns
                                       mozIStoragePendingStatement** _stmt)
 {
   NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread");
   NS_ENSURE_ARG(aQuery);
   NS_ENSURE_ARG(aOptions);
   NS_ENSURE_ARG(aCallback);
   NS_ENSURE_ARG_POINTER(_stmt);
 
-  nsCOMPtr<nsNavHistoryQuery> query = do_QueryObject(aQuery);
+  RefPtr<nsNavHistoryQuery> query = do_QueryObject(aQuery);
   NS_ENSURE_STATE(query);
-  nsCOMArray<nsNavHistoryQuery> queries;
-  queries.AppendObject(query);
-
-  nsCOMPtr<nsNavHistoryQueryOptions> options = do_QueryInterface(aOptions);
+  RefPtr<nsNavHistoryQueryOptions> options = do_QueryObject(aOptions);
   NS_ENSURE_ARG(options);
 
   nsCString queryString;
   bool paramsPresent = false;
   nsNavHistory::StringHash addParams(HISTORY_DATE_CONT_LENGTH);
-  nsresult rv = ConstructQueryString(queries, options, queryString,
+  nsresult rv = ConstructQueryString(query, options, queryString,
                                      paramsPresent, addParams);
   NS_ENSURE_SUCCESS(rv,rv);
 
   nsCOMPtr<mozIStorageAsyncStatement> statement =
     mDB->GetAsyncStatement(queryString);
   NS_ENSURE_STATE(statement);
 
 #ifdef DEBUG
@@ -2946,22 +2906,18 @@ nsNavHistory::AsyncExecuteLegacyQuery(ns
       printf("Places failed to create a statement from this query:\n%s\nStorage error (%d): %s\n",
             queryString.get(), lastError, lastErrorString.get());
     }
   }
 #endif
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (paramsPresent) {
-    // bind parameters
-    int32_t i;
-    for (i = 0; i < queries.Count(); i++) {
-      rv = BindQueryClauseParameters(statement, i, queries[i], options);
-      NS_ENSURE_SUCCESS(rv, rv);
-    }
+    rv = BindQueryClauseParameters(statement, query, options);
+    NS_ENSURE_SUCCESS(rv, rv);
   }
 
   for (auto iter = addParams.Iter(); !iter.Done(); iter.Next()) {
     nsresult rv = statement->BindUTF8StringByName(iter.Key(), iter.Data());
     if (NS_FAILED(rv)) {
       break;
     }
   }
@@ -3153,28 +3109,21 @@ nsNavHistory::DecayFrecency()
   return NS_OK;
 }
 
 
 // Query stuff *****************************************************************
 
 // Helper class for QueryToSelectClause
 //
-// This class helps to build part of the WHERE clause. It supports
-// multiple queries by appending the query index to the parameter name.
-// For the query with index 0 the parameter name is not altered what
-// allows using this parameter in other situations (see SelectAsSite).
+// This class helps to build part of the WHERE clause.
 
 class ConditionBuilder
 {
 public:
-
-  explicit ConditionBuilder(int32_t aQueryIndex): mQueryIndex(aQueryIndex)
-  { }
-
   ConditionBuilder& Condition(const char* aStr)
   {
     if (!mClause.IsEmpty())
       mClause.AppendLiteral(" AND ");
     Str(aStr);
     return *this;
   }
 
@@ -3184,56 +3133,50 @@ public:
     mClause.Append(aStr);
     mClause.Append(' ');
     return *this;
   }
 
   ConditionBuilder& Param(const char* aParam)
   {
     mClause.Append(' ');
-    if (!mQueryIndex)
-      mClause.Append(aParam);
-    else
-      mClause += nsPrintfCString("%s%d", aParam, mQueryIndex);
-
+    mClause.Append(aParam);
     mClause.Append(' ');
     return *this;
   }
 
   void GetClauseString(nsCString& aResult)
   {
     aResult = mClause;
   }
 
 private:
 
-  int32_t mQueryIndex;
   nsCString mClause;
 };
 
 
 // nsNavHistory::QueryToSelectClause
 //
 //    THE BEHAVIOR SHOULD BE IN SYNC WITH BindQueryClauseParameters
 //
 //    I don't check return values from the query object getters because there's
 //    no way for those to fail.
 
 nsresult
-nsNavHistory::QueryToSelectClause(nsNavHistoryQuery* aQuery, // const
-                                  nsNavHistoryQueryOptions* aOptions,
-                                  int32_t aQueryIndex,
+nsNavHistory::QueryToSelectClause(const RefPtr<nsNavHistoryQuery>& aQuery,
+                                  const RefPtr<nsNavHistoryQueryOptions>& aOptions,
                                   nsCString* aClause)
 {
   bool hasIt;
   // We don't use the value from options here - we post filter if that
   // is set.
   bool excludeQueries = false;
 
-  ConditionBuilder clause(aQueryIndex);
+  ConditionBuilder clause;
 
   if ((NS_SUCCEEDED(aQuery->GetHasBeginTime(&hasIt)) && hasIt) ||
     (NS_SUCCEEDED(aQuery->GetHasEndTime(&hasIt)) && hasIt)) {
     clause.Condition("EXISTS (SELECT 1 FROM moz_historyvisits "
                               "WHERE place_id = h.id");
     // begin time
     if (NS_SUCCEEDED(aQuery->GetHasBeginTime(&hasIt)) && hasIt)
       clause.Condition("visit_date >=").Param(":begin_time");
@@ -3386,150 +3329,123 @@ nsNavHistory::QueryToSelectClause(nsNavH
 
 
 // nsNavHistory::BindQueryClauseParameters
 //
 //    THE BEHAVIOR SHOULD BE IN SYNC WITH QueryToSelectClause
 
 nsresult
 nsNavHistory::BindQueryClauseParameters(mozIStorageBaseStatement* statement,
-                                        int32_t aQueryIndex,
-                                        nsNavHistoryQuery* aQuery, // const
-                                        nsNavHistoryQueryOptions* aOptions)
+                                        const RefPtr<nsNavHistoryQuery>& aQuery,
+                                        const RefPtr<nsNavHistoryQueryOptions>& aOptions)
 {
   nsresult rv;
 
   bool hasIt;
-  // Append numbered index to param names, to replace them correctly in
-  // case of multiple queries.  If we have just one query we don't change the
-  // param name though.
-  nsAutoCString qIndex;
-  if (aQueryIndex > 0)
-    qIndex.AppendInt(aQueryIndex);
-
   // begin time
   if (NS_SUCCEEDED(aQuery->GetHasBeginTime(&hasIt)) && hasIt) {
     PRTime time = NormalizeTime(aQuery->BeginTimeReference(),
                                 aQuery->BeginTime());
-    rv = statement->BindInt64ByName(
-      NS_LITERAL_CSTRING("begin_time") + qIndex, time);
+    rv = statement->BindInt64ByName(NS_LITERAL_CSTRING("begin_time"), time);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // end time
   if (NS_SUCCEEDED(aQuery->GetHasEndTime(&hasIt)) && hasIt) {
     PRTime time = NormalizeTime(aQuery->EndTimeReference(),
                                 aQuery->EndTime());
-    rv = statement->BindInt64ByName(
-      NS_LITERAL_CSTRING("end_time") + qIndex, time
-    );
+    rv = statement->BindInt64ByName(NS_LITERAL_CSTRING("end_time"), time);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // search terms
   if (NS_SUCCEEDED(aQuery->GetHasSearchTerms(&hasIt)) && hasIt) {
-    rv = statement->BindStringByName(
-      NS_LITERAL_CSTRING("search_string") + qIndex,
-      aQuery->SearchTerms()
-    );
+    rv = statement->BindStringByName(NS_LITERAL_CSTRING("search_string"),
+                                     aQuery->SearchTerms());
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // min and max visit count
   int32_t visits = aQuery->MinVisits();
   if (visits >= 0) {
-    rv = statement->BindInt32ByName(
-      NS_LITERAL_CSTRING("min_visits") + qIndex, visits
-    );
+    rv = statement->BindInt32ByName(NS_LITERAL_CSTRING("min_visits"), visits);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   visits = aQuery->MaxVisits();
   if (visits >= 0) {
-    rv = statement->BindInt32ByName(
-      NS_LITERAL_CSTRING("max_visits") + qIndex, visits
-    );
+    rv = statement->BindInt32ByName(NS_LITERAL_CSTRING("max_visits"), visits);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // domain (see GetReversedHostname for more info on reversed host names)
   if (NS_SUCCEEDED(aQuery->GetHasDomain(&hasIt)) && hasIt) {
     nsString revDomain;
     GetReversedHostname(NS_ConvertUTF8toUTF16(aQuery->Domain()), revDomain);
 
     if (aQuery->DomainIsHost()) {
-      rv = statement->BindStringByName(
-        NS_LITERAL_CSTRING("domain_lower") + qIndex, revDomain
-      );
+      rv = statement->BindStringByName(NS_LITERAL_CSTRING("domain_lower"),
+                                       revDomain);
       NS_ENSURE_SUCCESS(rv, rv);
     } else {
       // for "mozilla.org" do query >= "gro.allizom." AND < "gro.allizom/"
       // which will get everything starting with "gro.allizom." while using the
       // index (using SUBSTRING() causes indexes to be discarded).
       NS_ASSERTION(revDomain[revDomain.Length() - 1] == '.', "Invalid rev. host");
-      rv = statement->BindStringByName(
-        NS_LITERAL_CSTRING("domain_lower") + qIndex, revDomain
-      );
+      rv = statement->BindStringByName(NS_LITERAL_CSTRING("domain_lower"),
+                                       revDomain);
       NS_ENSURE_SUCCESS(rv, rv);
       revDomain.Truncate(revDomain.Length() - 1);
       revDomain.Append(char16_t('/'));
-      rv = statement->BindStringByName(
-        NS_LITERAL_CSTRING("domain_upper") + qIndex, revDomain
-      );
+      rv = statement->BindStringByName(NS_LITERAL_CSTRING("domain_upper"),
+                                       revDomain);
       NS_ENSURE_SUCCESS(rv, rv);
     }
   }
 
   // URI
   if (aQuery->Uri()) {
-    rv = URIBinder::Bind(
-      statement, NS_LITERAL_CSTRING("uri") + qIndex, aQuery->Uri()
-    );
+    rv = URIBinder::Bind(statement, NS_LITERAL_CSTRING("uri"), aQuery->Uri());
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // annotation
   if (!aQuery->Annotation().IsEmpty()) {
-    rv = statement->BindUTF8StringByName(
-      NS_LITERAL_CSTRING("anno") + qIndex, aQuery->Annotation()
-    );
+    rv = statement->BindUTF8StringByName(NS_LITERAL_CSTRING("anno"),
+                                         aQuery->Annotation());
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // tags
   const nsTArray<nsString> &tags = aQuery->Tags();
   if (tags.Length() > 0) {
     for (uint32_t i = 0; i < tags.Length(); ++i) {
       nsPrintfCString paramName("tag%d_", i);
       NS_ConvertUTF16toUTF8 tag(tags[i]);
       ToLowerCase(tag);
-      rv = statement->BindUTF8StringByName(paramName + qIndex, tag);
+      rv = statement->BindUTF8StringByName(paramName, tag);
       NS_ENSURE_SUCCESS(rv, rv);
     }
     int64_t tagsFolder = GetTagsFolder();
-    rv = statement->BindInt64ByName(
-      NS_LITERAL_CSTRING("tags_folder") + qIndex, tagsFolder
-    );
+    rv = statement->BindInt64ByName(NS_LITERAL_CSTRING("tags_folder"),
+                                    tagsFolder);
     NS_ENSURE_SUCCESS(rv, rv);
     if (!aQuery->TagsAreNot()) {
-      rv = statement->BindInt32ByName(
-        NS_LITERAL_CSTRING("tag_count") + qIndex, tags.Length()
-      );
+      rv = statement->BindInt32ByName(NS_LITERAL_CSTRING("tag_count"),
+                                      tags.Length());
       NS_ENSURE_SUCCESS(rv, rv);
     }
   }
 
   // transitions
   const nsTArray<uint32_t>& transitions = aQuery->Transitions();
-  if (transitions.Length() > 0) {
-    for (uint32_t i = 0; i < transitions.Length(); ++i) {
-      nsPrintfCString paramName("transition%d_", i);
-      rv = statement->BindInt64ByName(paramName + qIndex, transitions[i]);
-      NS_ENSURE_SUCCESS(rv, rv);
-    }
+  for (uint32_t i = 0; i < transitions.Length(); ++i) {
+    nsPrintfCString paramName("transition%d_", i);
+    rv = statement->BindInt64ByName(paramName, transitions[i]);
+    NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return NS_OK;
 }
 
 
 // nsNavHistory::ResultsAsList
 //
@@ -3608,26 +3524,26 @@ nsNavHistory::GetTagsFolder()
 //
 // Note:  changes to filtering in FilterResultSet()
 // may require changes to NeedToFilterResultSet()
 
 nsresult
 nsNavHistory::FilterResultSet(nsNavHistoryQueryResultNode* aQueryNode,
                               const nsCOMArray<nsNavHistoryResultNode>& aSet,
                               nsCOMArray<nsNavHistoryResultNode>* aFiltered,
-                              const nsCOMArray<nsNavHistoryQuery>& aQueries,
+                              const RefPtr<nsNavHistoryQuery>& aQuery,
                               nsNavHistoryQueryOptions *aOptions)
 {
   // get the bookmarks service
   nsNavBookmarks *bookmarks = nsNavBookmarks::GetBookmarksService();
   NS_ENSURE_TRUE(bookmarks, NS_ERROR_OUT_OF_MEMORY);
 
   // parse the search terms
-  nsTArray<nsTArray<nsString>*> terms;
-  ParseSearchTermsFromQueries(aQueries, &terms);
+  nsTArray<nsString> terms;
+  ParseSearchTermsFromQuery(aQuery, &terms);
 
   uint16_t resultType = aOptions->ResultType();
   bool excludeQueries = aOptions->ExcludeQueries();
   for (int32_t nodeIndex = 0; nodeIndex < aSet.Count(); nodeIndex++) {
     if (excludeQueries && aSet[nodeIndex]->IsQuery()) {
       continue;
     }
 
@@ -3643,65 +3559,50 @@ nsNavHistory::FilterResultSet(nsNavHisto
     if (aSet[nodeIndex]->mItemId != -1 && aQueryNode &&
         aQueryNode->mItemId == aSet[nodeIndex]->mItemId) {
       continue;
     }
 
     // If there are search terms, we are already getting only uri nodes,
     // thus we don't need to filter node types. Though, we must check for
     // matching terms.
-    bool appendNode = false;
-    for (int32_t queryIndex = 0;
-         queryIndex < aQueries.Count() && !appendNode; queryIndex++) {
-
-      if (terms[queryIndex]->Length()) {
-        // Filter based on search terms.
-        // Convert title and url for the current node to UTF16 strings.
-        NS_ConvertUTF8toUTF16 nodeTitle(aSet[nodeIndex]->mTitle);
-        // Unescape the URL for search terms matching.
-        nsAutoCString cNodeURL(aSet[nodeIndex]->mURI);
-        NS_ConvertUTF8toUTF16 nodeURL(NS_UnescapeURL(cNodeURL));
-
-        // Determine if every search term matches anywhere in the title, url or
-        // tag.
-        bool matchAll = true;
-        for (int32_t termIndex = terms[queryIndex]->Length() - 1;
-             termIndex >= 0 && matchAll;
-             termIndex--) {
-          nsString& term = terms[queryIndex]->ElementAt(termIndex);
-
-          // True if any of them match; false makes us quit the loop
-          matchAll = CaseInsensitiveFindInReadable(term, nodeTitle) ||
-                     CaseInsensitiveFindInReadable(term, nodeURL) ||
-                     CaseInsensitiveFindInReadable(term, aSet[nodeIndex]->mTags);
-        }
-
-        // Skip the node if we don't match all terms in the title, url or tag
-        if (!matchAll)
-          continue;
+    if (terms.Length()) {
+      // Filter based on search terms.
+      // Convert title and url for the current node to UTF16 strings.
+      NS_ConvertUTF8toUTF16 nodeTitle(aSet[nodeIndex]->mTitle);
+      // Unescape the URL for search terms matching.
+      nsAutoCString cNodeURL(aSet[nodeIndex]->mURI);
+      NS_ConvertUTF8toUTF16 nodeURL(NS_UnescapeURL(cNodeURL));
+
+      // Determine if every search term matches anywhere in the title, url or
+      // tag.
+      bool matchAllTerms = true;
+      for (int32_t termIndex = terms.Length() - 1;
+            termIndex >= 0 && matchAllTerms;
+            termIndex--) {
+        nsString& term = terms.ElementAt(termIndex);
+        // True if any of them match; false makes us quit the loop
+        matchAllTerms = CaseInsensitiveFindInReadable(term, nodeTitle) ||
+                        CaseInsensitiveFindInReadable(term, nodeURL) ||
+                        CaseInsensitiveFindInReadable(term, aSet[nodeIndex]->mTags);
       }
-
-      // We passed all filters, so we can append the node to filtered results.
-      appendNode = true;
+      // Skip the node if we don't match all terms in the title, url or tag
+      if (!matchAllTerms) {
+        continue;
+      }
     }
 
-    if (appendNode)
-      aFiltered->AppendObject(aSet[nodeIndex]);
+    aFiltered->AppendObject(aSet[nodeIndex]);
 
     // Stop once we have reached max results.
     if (aOptions->MaxResults() > 0 &&
         (uint32_t)aFiltered->Count() >= aOptions->MaxResults())
       break;
   }
 
-  // De-allocate the temporary matrixes.
-  for (int32_t i = 0; i < aQueries.Count(); i++) {
-    delete terms[i];
-  }
-
   return NS_OK;
 }
 
 void
 nsNavHistory::registerEmbedVisit(nsIURI* aURI,
                                  int64_t aTime)
 {
   NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread");
@@ -4004,19 +3905,17 @@ nsNavHistory::QueryRowToResult(int64_t i
         // concrete folder title).
         if (!aTitle.IsEmpty()) {
           resultNode->mTitle = aTitle;
         }
       }
     }
     else {
       // This is a regular query.
-      nsCOMArray<nsNavHistoryQuery> queries;
-      queries.AppendObject(queryObj);
-      resultNode = new nsNavHistoryQueryResultNode(aTitle, aTime, queries, optionsObj);
+      resultNode = new nsNavHistoryQueryResultNode(aTitle, aTime, queryObj, optionsObj);
       resultNode->mItemId = itemId;
       resultNode->mBookmarkGuid = aBookmarkGuid;
     }
   }
 
   if (NS_FAILED(rv)) {
     NS_WARNING("Generating a generic empty node for a broken query!");
     // This is a broken query, that either did not parse or points to not
@@ -4287,18 +4186,18 @@ namespace {
 //    folder with no other constraints. In these common cases, we can more
 //    efficiently compute the results.
 //
 //    A simple bookmarks query will result in a hierarchical tree of
 //    bookmark items, folders and separators.
 //
 //    Returns the folder ID if it is a simple folder query, 0 if not.
 static int64_t
-GetSimpleBookmarksQueryFolder(nsNavHistoryQuery* aQuery,
-                              nsNavHistoryQueryOptions* aOptions)
+GetSimpleBookmarksQueryFolder(const RefPtr<nsNavHistoryQuery>& aQuery,
+                              const RefPtr<nsNavHistoryQueryOptions>& aOptions)
 {
   if (aQuery->Folders().Length() != 1)
     return 0;
 
   bool hasIt;
   aQuery->GetHasBeginTime(&hasIt);
   if (hasIt)
     return 0;
@@ -4316,73 +4215,67 @@ GetSimpleBookmarksQueryFolder(nsNavHisto
     return 0;
   if (aQuery->Tags().Length() > 0)
     return 0;
   if (aOptions->MaxResults() > 0)
     return 0;
 
   // RESULTS_AS_TAG_CONTENTS is quite similar to a folder shortcut, but it must
   // not be treated like that, since it needs all query options.
-  if(aOptions->ResultType() == nsINavHistoryQueryOptions::RESULTS_AS_TAG_CONTENTS)
+  if (aOptions->ResultType() == nsINavHistoryQueryOptions::RESULTS_AS_TAG_CONTENTS)
     return 0;
 
   // Don't care about onlyBookmarked flag, since specifying a bookmark
   // folder is inferring onlyBookmarked.
 
   return aQuery->Folders()[0];
 }
 
 
-// ParseSearchTermsFromQueries
+// ParseSearchTermsFromQuery
 //
-//    Construct a matrix of search terms from the given queries array.
-//    All of the query objects are ORed together. Within a query, all the terms
-//    are ANDed together. See nsINavHistoryService.idl.
+//    Construct an array of search terms from the given query.
+//    Within a query, all the terms are ANDed together.
 //
 //    This just breaks the query up into words. We don't do anything fancy,
 //    not even quoting. We do, however, strip quotes, because people might
 //    try to input quotes expecting them to do something and get no results
 //    back.
 
 inline bool isQueryWhitespace(char16_t ch)
 {
   return ch == ' ';
 }
 
-void ParseSearchTermsFromQueries(const nsCOMArray<nsNavHistoryQuery>& aQueries,
-                                 nsTArray<nsTArray<nsString>*>* aTerms)
+void ParseSearchTermsFromQuery(const RefPtr<nsNavHistoryQuery>& aQuery,
+                               nsTArray<nsString>* aTerms)
 {
   int32_t lastBegin = -1;
-  for (int32_t i = 0; i < aQueries.Count(); i++) {
-    nsTArray<nsString> *queryTerms = new nsTArray<nsString>();
-    bool hasSearchTerms;
-    if (NS_SUCCEEDED(aQueries[i]->GetHasSearchTerms(&hasSearchTerms)) &&
-        hasSearchTerms) {
-      const nsString& searchTerms = aQueries[i]->SearchTerms();
-      for (uint32_t j = 0; j < searchTerms.Length(); j++) {
-        if (isQueryWhitespace(searchTerms[j]) ||
-            searchTerms[j] == '"') {
-          if (lastBegin >= 0) {
-            // found the end of a word
-            queryTerms->AppendElement(Substring(searchTerms, lastBegin,
-                                               j - lastBegin));
-            lastBegin = -1;
-          }
-        } else {
-          if (lastBegin < 0) {
-            // found the beginning of a word
-            lastBegin = j;
-          }
+  bool hasSearchTerms;
+  if (NS_SUCCEEDED(aQuery->GetHasSearchTerms(&hasSearchTerms)) &&
+      hasSearchTerms) {
+    const nsString& searchTerms = aQuery->SearchTerms();
+    for (uint32_t j = 0; j < searchTerms.Length(); j++) {
+      if (isQueryWhitespace(searchTerms[j]) ||
+          searchTerms[j] == '"') {
+        if (lastBegin >= 0) {
+          // found the end of a word
+          aTerms->AppendElement(Substring(searchTerms, lastBegin, j - lastBegin));
+          lastBegin = -1;
+        }
+      } else {
+        if (lastBegin < 0) {
+          // found the beginning of a word
+          lastBegin = j;
         }
       }
-      // last word
-      if (lastBegin >= 0)
-        queryTerms->AppendElement(Substring(searchTerms, lastBegin));
     }
-    aTerms->AppendElement(queryTerms);
+    // last word
+    if (lastBegin >= 0)
+      aTerms->AppendElement(Substring(searchTerms, lastBegin));
   }
 }
 
 } // namespace
 
 
 nsresult
 nsNavHistory::UpdateFrecency(int64_t aPlaceId)
--- a/toolkit/components/places/nsNavHistory.h
+++ b/toolkit/components/places/nsNavHistory.h
@@ -253,18 +253,18 @@ public:
   static const int32_t kGetInfoIndex_FromVisitId;
   static const int32_t kGetInfoIndex_VisitType;
 
   int64_t GetTagsFolder();
 
   // this actually executes a query and gives you results, it is used by
   // nsNavHistoryQueryResultNode
   nsresult GetQueryResults(nsNavHistoryQueryResultNode *aResultNode,
-                           const nsCOMArray<nsNavHistoryQuery>& aQueries,
-                           nsNavHistoryQueryOptions *aOptions,
+                           const RefPtr<nsNavHistoryQuery>& aQuery,
+                           const RefPtr<nsNavHistoryQueryOptions>& aOptions,
                            nsCOMArray<nsNavHistoryResultNode>* aResults);
 
   // Take a row of kGetInfoIndex_* columns and construct a ResultNode.
   // The row must contain the full set of columns.
   nsresult RowToResult(mozIStorageValueArray* aRow,
                        nsNavHistoryQueryOptions* aOptions,
                        nsNavHistoryResultNode** aResult);
   nsresult QueryRowToResult(int64_t aItemId,
@@ -293,20 +293,20 @@ public:
                                    const nsACString& aGUID);
 
   /**
    * Returns current number of days stored in history.
    */
   int32_t GetDaysOfHistory();
 
   // used by query result nodes to update: see comment on body of CanLiveUpdateQuery
-  static uint32_t GetUpdateRequirements(const nsCOMArray<nsNavHistoryQuery>& aQueries,
+  static uint32_t GetUpdateRequirements(const RefPtr<nsNavHistoryQuery>& aQuery,
                                         nsNavHistoryQueryOptions* aOptions,
                                         bool* aHasSearchTerms);
-  bool EvaluateQueryForNode(const nsCOMArray<nsNavHistoryQuery>& aQueries,
+  bool EvaluateQueryForNode(const RefPtr<nsNavHistoryQuery>& aQuery,
                               nsNavHistoryQueryOptions* aOptions,
                               nsNavHistoryResultNode* aNode);
 
   static nsresult AsciiHostNameFromHostString(const nsACString& aHostName,
                                               nsACString& aAscii);
   void DomainNameFromURI(nsIURI* aURI,
                          nsACString& aDomainName);
   static PRTime NormalizeTime(uint32_t aRelative, PRTime aOffset);
@@ -534,41 +534,39 @@ protected:
   PRTime GetNow();
   PRTime mCachedNow;
   nsCOMPtr<nsITimer> mExpireNowTimer;
   /**
    * Called when the cached now value is expired and needs renewal.
    */
   static void expireNowTimerCallback(nsITimer* aTimer, void* aClosure);
 
-  nsresult ConstructQueryString(const nsCOMArray<nsNavHistoryQuery>& aQueries,
-                                nsNavHistoryQueryOptions* aOptions,
+  nsresult ConstructQueryString(const RefPtr<nsNavHistoryQuery>& aQuery,
+                                const RefPtr<nsNavHistoryQueryOptions>& aOptions,
                                 nsCString& queryString,
                                 bool& aParamsPresent,
                                 StringHash& aAddParams);
 
-  nsresult QueryToSelectClause(nsNavHistoryQuery* aQuery,
-                               nsNavHistoryQueryOptions* aOptions,
-                               int32_t aQueryIndex,
+  nsresult QueryToSelectClause(const RefPtr<nsNavHistoryQuery>& aQuery,
+                               const RefPtr<nsNavHistoryQueryOptions>& aOptions,
                                nsCString* aClause);
   nsresult BindQueryClauseParameters(mozIStorageBaseStatement* statement,
-                                     int32_t aQueryIndex,
-                                     nsNavHistoryQuery* aQuery,
-                                     nsNavHistoryQueryOptions* aOptions);
+                                     const RefPtr<nsNavHistoryQuery>& aQuery,
+                                     const RefPtr<nsNavHistoryQueryOptions>& aOptions);
 
   nsresult ResultsAsList(mozIStorageStatement* statement,
                          nsNavHistoryQueryOptions* aOptions,
                          nsCOMArray<nsNavHistoryResultNode>* aResults);
 
   void TitleForDomain(const nsCString& domain, nsACString& aTitle);
 
   nsresult FilterResultSet(nsNavHistoryQueryResultNode *aParentNode,
                            const nsCOMArray<nsNavHistoryResultNode>& aSet,
                            nsCOMArray<nsNavHistoryResultNode>* aFiltered,
-                           const nsCOMArray<nsNavHistoryQuery>& aQueries,
+                           const RefPtr<nsNavHistoryQuery>& aQuery,
                            nsNavHistoryQueryOptions* aOptions);
 
   // observers
   nsMaybeWeakPtrArray<nsINavHistoryObserver> mObservers;
 
   // effective tld service
   nsCOMPtr<nsIEffectiveTLDService> mTLDService;
   nsCOMPtr<nsIIDNService>          mIDNService;
--- a/toolkit/components/places/nsNavHistoryQuery.cpp
+++ b/toolkit/components/places/nsNavHistoryQuery.cpp
@@ -802,26 +802,27 @@ nsNavHistoryQuery::nsNavHistoryQuery()
     mTagsAreNot(false)
 {
   // differentiate not set (IsVoid) from empty string (local files)
   mDomain.SetIsVoid(true);
 }
 
 nsNavHistoryQuery::nsNavHistoryQuery(const nsNavHistoryQuery& aOther)
   : mMinVisits(aOther.mMinVisits), mMaxVisits(aOther.mMaxVisits),
-    mBeginTime(aOther.mBeginTime),
-    mBeginTimeReference(aOther.mBeginTimeReference),
+    mBeginTime(aOther.mBeginTime), mBeginTimeReference(aOther.mBeginTimeReference),
     mEndTime(aOther.mEndTime), mEndTimeReference(aOther.mEndTimeReference),
     mSearchTerms(aOther.mSearchTerms), mOnlyBookmarked(aOther.mOnlyBookmarked),
     mDomainIsHost(aOther.mDomainIsHost), mDomain(aOther.mDomain),
-    mUri(aOther.mUri),
-    mAnnotationIsNot(aOther.mAnnotationIsNot),
-    mAnnotation(aOther.mAnnotation), mTags(aOther.mTags),
-    mTagsAreNot(aOther.mTagsAreNot), mTransitions(aOther.mTransitions)
-{}
+    mUri(aOther.mUri), mAnnotationIsNot(aOther.mAnnotationIsNot),
+    mAnnotation(aOther.mAnnotation),
+    mFolders(aOther.mFolders),
+    mTags(aOther.mTags), mTagsAreNot(aOther.mTagsAreNot),
+    mTransitions(aOther.mTransitions)
+{
+}
 
 NS_IMETHODIMP nsNavHistoryQuery::GetBeginTime(PRTime *aBeginTime)
 {
   *aBeginTime = mBeginTime;
   return NS_OK;
 }
 NS_IMETHODIMP nsNavHistoryQuery::SetBeginTime(PRTime aBeginTime)
 {
@@ -1209,31 +1210,66 @@ NS_IMETHODIMP nsNavHistoryQuery::SetTran
 {
   if (!mTransitions.ReplaceElementsAt(0, mTransitions.Length(), aTransitions,
                                       aCount))
     return NS_ERROR_OUT_OF_MEMORY;
 
   return NS_OK;
 }
 
-NS_IMETHODIMP nsNavHistoryQuery::Clone(nsINavHistoryQuery** _retval)
+NS_IMETHODIMP
+nsNavHistoryQuery::Clone(nsINavHistoryQuery** _clone)
 {
-  *_retval = nullptr;
+  nsNavHistoryQuery *clone = nullptr;
+  Unused << Clone(&clone);
+  *_clone = clone;
+  return NS_OK;
+}
 
+nsresult
+nsNavHistoryQuery::Clone(nsNavHistoryQuery** _clone)
+{
+  *_clone = nullptr;
   RefPtr<nsNavHistoryQuery> clone = new nsNavHistoryQuery(*this);
-  NS_ENSURE_TRUE(clone, NS_ERROR_OUT_OF_MEMORY);
-
-  clone.forget(_retval);
+  clone.forget(_clone);
   return NS_OK;
 }
 
-
 // nsNavHistoryQueryOptions
 NS_IMPL_ISUPPORTS(nsNavHistoryQueryOptions, nsNavHistoryQueryOptions, nsINavHistoryQueryOptions)
 
+nsNavHistoryQueryOptions::nsNavHistoryQueryOptions()
+: mSort(0)
+, mResultType(0)
+, mExcludeItems(false)
+, mExcludeQueries(false)
+, mExcludeReadOnlyFolders(false)
+, mExpandQueries(true)
+, mIncludeHidden(false)
+, mMaxResults(0)
+, mQueryType(nsINavHistoryQueryOptions::QUERY_TYPE_HISTORY)
+, mAsyncEnabled(false)
+{
+}
+
+nsNavHistoryQueryOptions::nsNavHistoryQueryOptions(const nsNavHistoryQueryOptions& other)
+: mSort(other.mSort)
+, mSortingAnnotation(other.mSortingAnnotation)
+, mResultType(other.mResultType)
+, mExcludeItems(other.mExcludeItems)
+, mExcludeQueries(other.mExcludeQueries)
+, mExcludeReadOnlyFolders(other.mExcludeReadOnlyFolders)
+, mExpandQueries(other.mExpandQueries)
+, mIncludeHidden(other.mIncludeHidden)
+, mMaxResults(other.mMaxResults)
+, mQueryType(other.mQueryType)
+, mAsyncEnabled(other.mAsyncEnabled)
+{
+}
+
 // sortingMode
 NS_IMETHODIMP
 nsNavHistoryQueryOptions::GetSortingMode(uint16_t* aMode)
 {
   *aMode = mSort;
   return NS_OK;
 }
 NS_IMETHODIMP
@@ -1393,48 +1429,34 @@ nsNavHistoryQueryOptions::GetAsyncEnable
 }
 NS_IMETHODIMP
 nsNavHistoryQueryOptions::SetAsyncEnabled(bool aAsyncEnabled)
 {
   mAsyncEnabled = aAsyncEnabled;
   return NS_OK;
 }
 
-
 NS_IMETHODIMP
-nsNavHistoryQueryOptions::Clone(nsINavHistoryQueryOptions** aResult)
+nsNavHistoryQueryOptions::Clone(nsINavHistoryQueryOptions** _clone)
 {
   nsNavHistoryQueryOptions *clone = nullptr;
-  nsresult rv = Clone(&clone);
-  *aResult = clone;
-  return rv;
+  Unused << Clone(&clone);
+  *_clone = clone;
+  return NS_OK;
 }
 
 nsresult
-nsNavHistoryQueryOptions::Clone(nsNavHistoryQueryOptions **aResult)
+nsNavHistoryQueryOptions::Clone(nsNavHistoryQueryOptions** _clone)
 {
-  *aResult = nullptr;
-  nsNavHistoryQueryOptions *result = new nsNavHistoryQueryOptions();
-
-  RefPtr<nsNavHistoryQueryOptions> resultHolder(result);
-  result->mSort = mSort;
-  result->mResultType = mResultType;
-  result->mExcludeItems = mExcludeItems;
-  result->mExcludeQueries = mExcludeQueries;
-  result->mExpandQueries = mExpandQueries;
-  result->mMaxResults = mMaxResults;
-  result->mQueryType = mQueryType;
-  result->mParentAnnotationToExclude = mParentAnnotationToExclude;
-  result->mAsyncEnabled = mAsyncEnabled;
-
-  resultHolder.forget(aResult);
+  *_clone = nullptr;
+  RefPtr<nsNavHistoryQueryOptions> clone = new nsNavHistoryQueryOptions(*this);
+  clone.forget(_clone);
   return NS_OK;
 }
 
-
 // AppendBoolKeyValueIfTrue
 
 void // static
 AppendBoolKeyValueIfTrue(nsACString& aString, const nsCString& aName,
                          nsINavHistoryQuery* aQuery,
                          BoolQueryGetter getter)
 {
   bool value;
--- a/toolkit/components/places/nsNavHistoryQuery.h
+++ b/toolkit/components/places/nsNavHistoryQuery.h
@@ -61,21 +61,25 @@ public:
   {
     if (!mTransitions.ReplaceElementsAt(0, mTransitions.Length(),
                                         aTransitions))
       return NS_ERROR_OUT_OF_MEMORY;
 
     return NS_OK;
   }
 
+  nsresult Clone(nsNavHistoryQuery **_clone);
+
 private:
   ~nsNavHistoryQuery() {}
 
 protected:
 
+  // IF YOU ADD MORE ITEMS:
+  //  * Add to the copy constructor
   int32_t mMinVisits;
   int32_t mMaxVisits;
   PRTime mBeginTime;
   uint32_t mBeginTimeReference;
   PRTime mEndTime;
   uint32_t mEndTimeReference;
   nsString mSearchTerms;
   bool mOnlyBookmarked;
@@ -95,28 +99,18 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsNavHisto
 // nsNavHistoryQueryOptions
 
 #define NS_NAVHISTORYQUERYOPTIONS_IID \
 {0x95f8ba3b, 0xd681, 0x4d89, {0xab, 0xd1, 0xfd, 0xae, 0xf2, 0xa3, 0xde, 0x18}}
 
 class nsNavHistoryQueryOptions final : public nsINavHistoryQueryOptions
 {
 public:
-  nsNavHistoryQueryOptions()
-  : mSort(0)
-  , mResultType(0)
-  , mExcludeItems(false)
-  , mExcludeQueries(false)
-  , mExcludeReadOnlyFolders(false)
-  , mExpandQueries(true)
-  , mIncludeHidden(false)
-  , mMaxResults(0)
-  , mQueryType(nsINavHistoryQueryOptions::QUERY_TYPE_HISTORY)
-  , mAsyncEnabled(false)
-  { }
+  nsNavHistoryQueryOptions();
+  nsNavHistoryQueryOptions(const nsNavHistoryQueryOptions& other);
 
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_NAVHISTORYQUERYOPTIONS_IID)
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSINAVHISTORYQUERYOPTIONS
 
   uint16_t SortingMode() const { return mSort; }
   uint16_t ResultType() const { return mResultType; }
@@ -124,31 +118,29 @@ public:
   bool ExcludeQueries() const { return mExcludeQueries; }
   bool ExcludeReadOnlyFolders() const { return mExcludeReadOnlyFolders; }
   bool ExpandQueries() const { return mExpandQueries; }
   bool IncludeHidden() const { return mIncludeHidden; }
   uint32_t MaxResults() const { return mMaxResults; }
   uint16_t QueryType() const { return mQueryType; }
   bool AsyncEnabled() const { return mAsyncEnabled; }
 
-  nsresult Clone(nsNavHistoryQueryOptions **aResult);
+  nsresult Clone(nsNavHistoryQueryOptions **_clone);
 
 private:
   ~nsNavHistoryQueryOptions() {}
-  nsNavHistoryQueryOptions(const nsNavHistoryQueryOptions& other) {} // no copy
 
   // IF YOU ADD MORE ITEMS:
+  //  * Add to the copy constructor
   //  * Add a new getter for C++ above if it makes sense
   //  * Add to the serialization code (see nsNavHistory::QueriesToQueryString())
   //  * Add to the deserialization code (see nsNavHistory::QueryStringToQueries)
-  //  * Add to the nsNavHistoryQueryOptions::Clone() function
   //  * Add to the nsNavHistory.cpp::GetSimpleBookmarksQueryFolder function if applicable
   uint16_t mSort;
   nsCString mSortingAnnotation;
-  nsCString mParentAnnotationToExclude;
   uint16_t mResultType;
   bool mExcludeItems;
   bool mExcludeQueries;
   bool mExcludeReadOnlyFolders;
   bool mExpandQueries;
   bool mIncludeHidden;
   uint32_t mMaxResults;
   uint16_t mQueryType;
--- a/toolkit/components/places/nsNavHistoryResult.cpp
+++ b/toolkit/components/places/nsNavHistoryResult.cpp
@@ -1635,23 +1635,24 @@ nsNavHistoryContainerResultNode::GetChil
     return NS_ERROR_NOT_AVAILABLE;
   *aChildCount = mChildren.Count();
   return NS_OK;
 }
 
 
 NS_IMETHODIMP
 nsNavHistoryContainerResultNode::GetChild(uint32_t aIndex,
-                                          nsINavHistoryResultNode** _retval)
+                                          nsINavHistoryResultNode** _child)
 {
   if (!mExpanded)
     return NS_ERROR_NOT_AVAILABLE;
   if (aIndex >= uint32_t(mChildren.Count()))
     return NS_ERROR_INVALID_ARG;
-  NS_ADDREF(*_retval = mChildren[aIndex]);
+  nsCOMPtr<nsINavHistoryResultNode> child = mChildren[aIndex];
+  child.forget(_child);
   return NS_OK;
 }
 
 
 NS_IMETHODIMP
 nsNavHistoryContainerResultNode::GetChildIndex(nsINavHistoryResultNode* aNode,
                                                uint32_t* _retval)
 {
@@ -1696,77 +1697,53 @@ nsNavHistoryQueryResultNode::nsNavHistor
   mLiveUpdate(QUERYUPDATE_COMPLEX_WITH_BOOKMARKS),
   mHasSearchTerms(false),
   mContentsValid(false),
   mBatchChanges(0)
 {
 }
 
 nsNavHistoryQueryResultNode::nsNavHistoryQueryResultNode(
-    const nsACString& aTitle, const nsCOMArray<nsNavHistoryQuery>& aQueries,
+    const nsACString& aTitle, const RefPtr<nsNavHistoryQuery>& aQuery,
     nsNavHistoryQueryOptions* aOptions) :
   nsNavHistoryContainerResultNode(EmptyCString(), aTitle,
                                   nsNavHistoryResultNode::RESULT_TYPE_QUERY,
                                   aOptions),
-  mQueries(aQueries),
+  mQuery(aQuery),
   mContentsValid(false),
   mBatchChanges(0),
-  mTransitions(mQueries[0]->Transitions())
+  mTransitions(aQuery->Transitions())
 {
-  NS_ASSERTION(aQueries.Count() > 0, "Must have at least one query");
-
   nsNavHistory* history = nsNavHistory::GetHistoryService();
   NS_ASSERTION(history, "History service missing");
   if (history) {
-    mLiveUpdate = history->GetUpdateRequirements(mQueries, mOptions,
+    mLiveUpdate = history->GetUpdateRequirements(mQuery, mOptions,
                                                  &mHasSearchTerms);
   }
-
-  // Collect transitions shared by all queries.
-  for (int32_t i = 1; i < mQueries.Count(); ++i) {
-    const nsTArray<uint32_t>& queryTransitions = mQueries[i]->Transitions();
-    for (uint32_t j = 0; j < mTransitions.Length() ; ++j) {
-      uint32_t transition = mTransitions.SafeElementAt(j, 0);
-      if (transition && !queryTransitions.Contains(transition))
-        mTransitions.RemoveElement(transition);
-    }
-  }
 }
 
 nsNavHistoryQueryResultNode::nsNavHistoryQueryResultNode(
     const nsACString& aTitle,
     PRTime aTime,
-    const nsCOMArray<nsNavHistoryQuery>& aQueries,
+    const RefPtr<nsNavHistoryQuery>& aQuery,
     nsNavHistoryQueryOptions* aOptions) :
   nsNavHistoryContainerResultNode(EmptyCString(), aTitle, aTime,
                                   nsNavHistoryResultNode::RESULT_TYPE_QUERY,
                                   aOptions),
-  mQueries(aQueries),
+  mQuery(aQuery),
   mContentsValid(false),
   mBatchChanges(0),
-  mTransitions(mQueries[0]->Transitions())
+  mTransitions(aQuery->Transitions())
 {
-  NS_ASSERTION(aQueries.Count() > 0, "Must have at least one query");
-
   nsNavHistory* history = nsNavHistory::GetHistoryService();
   NS_ASSERTION(history, "History service missing");
   if (history) {
-    mLiveUpdate = history->GetUpdateRequirements(mQueries, mOptions,
+    mLiveUpdate = history->GetUpdateRequirements(mQuery, mOptions,
                                                  &mHasSearchTerms);
   }
-
-  // Collect transitions shared by all queries.
-  for (int32_t i = 1; i < mQueries.Count(); ++i) {
-    const nsTArray<uint32_t>& queryTransitions = mQueries[i]->Transitions();
-    for (uint32_t j = 0; j < mTransitions.Length() ; ++j) {
-      uint32_t transition = mTransitions.SafeElementAt(j, 0);
-      if (transition && !queryTransitions.Contains(transition))
-        mTransitions.RemoveElement(transition);
-    }
-  }
 }
 
 nsNavHistoryQueryResultNode::~nsNavHistoryQueryResultNode() {
   // Remove this node from result's observers.  We don't need to be notified
   // anymore.
   if (mResult && mResult->mAllBookmarksObservers.Contains(this))
     mResult->RemoveAllBookmarksObserver(this);
   if (mResult && mResult->mHistoryObservers.Contains(this))
@@ -1929,17 +1906,17 @@ nsNavHistoryQueryResultNode::GetHasChild
 
 /**
  * This doesn't just return mURI because in the case of queries that may
  * be lazily constructed from the query objects.
  */
 NS_IMETHODIMP
 nsNavHistoryQueryResultNode::GetUri(nsACString& aURI)
 {
-  nsresult rv = VerifyQueriesSerialized();
+  nsresult rv = VerifyQuerySerialized();
   NS_ENSURE_SUCCESS(rv, rv);
   aURI = mURI;
   return NS_OK;
 }
 
 
 NS_IMETHODIMP
 nsNavHistoryQueryResultNode::GetFolderItemId(int64_t* aItemId)
@@ -1952,91 +1929,89 @@ NS_IMETHODIMP
 nsNavHistoryQueryResultNode::GetTargetFolderGuid(nsACString& aGuid) {
   aGuid = EmptyCString();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNavHistoryQueryResultNode::GetQuery(nsINavHistoryQuery** _query)
 {
-  nsresult rv = VerifyQueriesParsed();
+  nsresult rv = VerifyQueryParsed();
   NS_ENSURE_SUCCESS(rv, rv);
-  NS_ASSERTION(mQueries.Count() > 0, "Must have >= 1 query");
-
-  nsCOMPtr<nsINavHistoryQuery> query = mQueries[0];
+
+  nsCOMPtr<nsINavHistoryQuery> query = do_QueryInterface(mQuery);
   query.forget(_query);
   return NS_OK;
 }
 
 
 NS_IMETHODIMP
-nsNavHistoryQueryResultNode::GetQueryOptions(
-                                      nsINavHistoryQueryOptions** aQueryOptions)
+nsNavHistoryQueryResultNode::GetQueryOptions(nsINavHistoryQueryOptions** _options)
 {
-  *aQueryOptions = Options();
-  NS_ADDREF(*aQueryOptions);
+  MOZ_ASSERT(mOptions, "Options should be valid");
+  nsCOMPtr<nsINavHistoryQueryOptions> options = do_QueryInterface(mOptions);
+  options.forget(_options);
   return NS_OK;
 }
 
 /**
- * Safe options getter, ensures queries are parsed first.
+ * Safe options getter, ensures query is parsed first.
  */
 nsNavHistoryQueryOptions*
 nsNavHistoryQueryResultNode::Options()
 {
-  nsresult rv = VerifyQueriesParsed();
+  nsresult rv = VerifyQueryParsed();
   if (NS_FAILED(rv))
     return nullptr;
   MOZ_ASSERT(mOptions, "Options invalid, cannot generate from URI");
   return mOptions;
 }
 
 
 nsresult
-nsNavHistoryQueryResultNode::VerifyQueriesParsed()
+nsNavHistoryQueryResultNode::VerifyQueryParsed()
 {
-  if (mQueries.Count() > 0) {
+  if (mQuery) {
     MOZ_ASSERT(mOriginalOptions && mOptions,
-               "If a result has queries, it also needs options");
+               "A result with a parsed query must have options");
     return NS_OK;
   }
   NS_ASSERTION(!mURI.IsEmpty(),
                "Query nodes must have either a URI or query/options");
 
   nsNavHistory* history = nsNavHistory::GetHistoryService();
   NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY);
 
   nsCOMPtr<nsINavHistoryQuery> query;
   nsCOMPtr<nsINavHistoryQueryOptions> options;
   nsresult rv = history->QueryStringToQuery(mURI, getter_AddRefs(query),
                                             getter_AddRefs(options));
   NS_ENSURE_SUCCESS(rv, rv);
   mOptions = do_QueryObject(options);
   NS_ENSURE_STATE(mOptions);
-  RefPtr<nsNavHistoryQuery> queryObj = do_QueryObject(query);
-  NS_ENSURE_STATE(queryObj);
-  mQueries.AppendObject(queryObj);
-  mLiveUpdate = history->GetUpdateRequirements(mQueries, mOptions,
+  mQuery = do_QueryObject(query);
+  NS_ENSURE_STATE(mQuery);
+  mLiveUpdate = history->GetUpdateRequirements(mQuery, mOptions,
                                                &mHasSearchTerms);
   return NS_OK;
 }
 
 
 nsresult
-nsNavHistoryQueryResultNode::VerifyQueriesSerialized()
+nsNavHistoryQueryResultNode::VerifyQuerySerialized()
 {
   if (!mURI.IsEmpty()) {
     return NS_OK;
   }
-  MOZ_ASSERT(mQueries.Count() > 0 && mOptions,
+  MOZ_ASSERT(mQuery && mOptions,
              "Query nodes must have either a URI or query/options");
 
   nsNavHistory* history = nsNavHistory::GetHistoryService();
   NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY);
-  nsCOMPtr<nsINavHistoryQuery> query = do_QueryInterface(mQueries[0]);
+  nsCOMPtr<nsINavHistoryQuery> query = do_QueryInterface(mQuery);
   nsresult rv = history->QueryToQueryString(query, mOriginalOptions, mURI);
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ENSURE_STATE(!mURI.IsEmpty());
   return NS_OK;
 }
 
 
 nsresult
@@ -2046,19 +2021,19 @@ nsNavHistoryQueryResultNode::FillChildre
                "Don't call FillChildren when contents are valid");
   NS_ASSERTION(mChildren.Count() == 0,
                "We are trying to fill children when there already are some");
 
   nsNavHistory* history = nsNavHistory::GetHistoryService();
   NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY);
 
   // get the results from the history service
-  nsresult rv = VerifyQueriesParsed();
+  nsresult rv = VerifyQueryParsed();
   NS_ENSURE_SUCCESS(rv, rv);
-  rv = history->GetQueryResults(this, mQueries, mOptions, &mChildren);
+  rv = history->GetQueryResults(this, mQuery, mOptions, &mChildren);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // it is important to call FillStats to fill in the parents on all
   // nodes and the result node pointers on the containers
   FillStats();
 
   uint16_t sortType = GetSortType();
 
@@ -2367,82 +2342,74 @@ nsNavHistoryQueryResultNode::OnVisit(nsI
   switch(mLiveUpdate) {
     case QUERYUPDATE_MOBILEPREF: {
       return NS_OK;
     }
 
     case QUERYUPDATE_HOST: {
       // For these simple yet common cases we can check the host ourselves
       // before doing the overhead of creating a new result node.
-      MOZ_ASSERT(mQueries.Count() == 1,
-                 "Host updated queries can have only one object");
-      RefPtr<nsNavHistoryQuery> query = do_QueryObject(mQueries[0]);
-
       bool hasDomain;
-      query->GetHasDomain(&hasDomain);
+      mQuery->GetHasDomain(&hasDomain);
       if (!hasDomain)
         return NS_OK;
 
       nsAutoCString host;
       if (NS_FAILED(aURI->GetAsciiHost(host)))
         return NS_OK;
 
-      if (!query->Domain().Equals(host))
+      if (!mQuery->Domain().Equals(host))
         return NS_OK;
 
       // Fall through to check the time, if the time is not present it will
       // still match.
       MOZ_FALLTHROUGH;
     }
 
     case QUERYUPDATE_TIME: {
       // For these simple yet common cases we can check the time ourselves
       // before doing the overhead of creating a new result node.
-      MOZ_ASSERT(mQueries.Count() == 1,
-                 "Time updated queries can have only one object");
-      RefPtr<nsNavHistoryQuery> query = do_QueryObject(mQueries[0]);
-
       bool hasIt;
-      query->GetHasBeginTime(&hasIt);
+      mQuery->GetHasBeginTime(&hasIt);
       if (hasIt) {
-        PRTime beginTime = history->NormalizeTime(query->BeginTimeReference(),
-                                                  query->BeginTime());
+        PRTime beginTime = history->NormalizeTime(mQuery->BeginTimeReference(),
+                                                  mQuery->BeginTime());
         if (aTime < beginTime)
           return NS_OK; // before our time range
       }
-      query->GetHasEndTime(&hasIt);
+      mQuery->GetHasEndTime(&hasIt);
       if (hasIt) {
-        PRTime endTime = history->NormalizeTime(query->EndTimeReference(),
-                                                query->EndTime());
+        PRTime endTime = history->NormalizeTime(mQuery->EndTimeReference(),
+                                                mQuery->EndTime());
         if (aTime > endTime)
           return NS_OK; // after our time range
       }
       // Now we know that our visit satisfies the time range, fall through to
       // the QUERYUPDATE_SIMPLE case below.
       MOZ_FALLTHROUGH;
     }
 
     case QUERYUPDATE_SIMPLE: {
-      // If all of the queries are filtered by some transitions, skip the
+      // If the query is filtered by some transitions, skip the
       // update if aTransitionType doesn't match any of them.
       if (mTransitions.Length() > 0 && !mTransitions.Contains(aTransitionType))
         return NS_OK;
 
       // The history service can tell us whether the new item should appear
       // in the result.  We first have to construct a node for it to check.
       RefPtr<nsNavHistoryResultNode> addition;
       nsresult rv = history->VisitIdToResultNode(aVisitId, mOptions,
                                                  getter_AddRefs(addition));
       NS_ENSURE_SUCCESS(rv, rv);
       if (!addition) {
         // Certain result types manage the nodes by themselves.
         return NS_OK;
       }
       addition->mTransitionType = aTransitionType;
-      if (!history->EvaluateQueryForNode(mQueries, mOptions, addition))
+      if (!history->EvaluateQueryForNode(mQuery, mOptions, addition))
         return NS_OK; // don't need to include in our query
 
       if (mOptions->ResultType() == nsNavHistoryQueryOptions::RESULTS_AS_VISIT) {
         // If this is a visit type query, just insert the new visit.  We never
         // update visits, only add or remove them.
         rv = InsertSortedChild(addition);
         NS_ENSURE_SUCCESS(rv, rv);
       } else {
@@ -2518,48 +2485,48 @@ nsNavHistoryQueryResultNode::OnTitleChan
 
   // compute what the new title should be
   NS_ConvertUTF16toUTF8 newTitle(aPageTitle);
 
   bool onlyOneEntry =
     mOptions->ResultType() == nsINavHistoryQueryOptions::RESULTS_AS_URI ||
     mOptions->ResultType() == nsINavHistoryQueryOptions::RESULTS_AS_TAG_CONTENTS;
 
-  // See if our queries have any search term matching.
+  // See if our query has any search term matching.
   if (mHasSearchTerms) {
     // Find all matching URI nodes.
     nsCOMArray<nsNavHistoryResultNode> matches;
     nsAutoCString spec;
     nsresult rv = aURI->GetSpec(spec);
     NS_ENSURE_SUCCESS(rv, rv);
     RecursiveFindURIs(onlyOneEntry, this, spec, &matches);
     if (matches.Count() == 0) {
       // This could be a new node matching the query, thus we could need
       // to add it to the result.
       RefPtr<nsNavHistoryResultNode> node;
       nsNavHistory* history = nsNavHistory::GetHistoryService();
       NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY);
       rv = history->URIToResultNode(aURI, mOptions, getter_AddRefs(node));
       NS_ENSURE_SUCCESS(rv, rv);
-      if (history->EvaluateQueryForNode(mQueries, mOptions, node)) {
+      if (history->EvaluateQueryForNode(mQuery, mOptions, node)) {
         rv = InsertSortedChild(node);
         NS_ENSURE_SUCCESS(rv, rv);
       }
     }
     for (int32_t i = 0; i < matches.Count(); ++i) {
       // For each matched node we check if it passes the query filter, if not
       // we remove the node from the result, otherwise we'll update the title
       // later.
       nsNavHistoryResultNode* node = matches[i];
       // We must check the node with the new title.
       node->mTitle = newTitle;
 
       nsNavHistory* history = nsNavHistory::GetHistoryService();
       NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY);
-      if (!history->EvaluateQueryForNode(mQueries, mOptions, node)) {
+      if (!history->EvaluateQueryForNode(mQuery, mOptions, node)) {
         nsNavHistoryContainerResultNode* parent = node->mParent;
         // URI nodes should always have parents
         NS_ENSURE_TRUE(parent, NS_ERROR_UNEXPECTED);
         int32_t childIndex = parent->FindChild(node);
         NS_ASSERTION(childIndex >= 0, "Child not found in parent");
         parent->RemoveChildAt(childIndex);
       }
     }
@@ -2743,33 +2710,33 @@ nsNavHistoryQueryResultNode::NotifyIfTag
   nsCOMArray<nsNavHistoryResultNode> matches;
   RecursiveFindURIs(onlyOneEntry, this, spec, &matches);
 
   if (matches.Count() == 0 && mHasSearchTerms) {
     // A new tag has been added, it's possible it matches our query.
     NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY);
     rv = history->URIToResultNode(aURI, mOptions, getter_AddRefs(node));
     NS_ENSURE_SUCCESS(rv, rv);
-    if (history->EvaluateQueryForNode(mQueries, mOptions, node)) {
+    if (history->EvaluateQueryForNode(mQuery, mOptions, node)) {
       rv = InsertSortedChild(node);
       NS_ENSURE_SUCCESS(rv, rv);
     }
   }
 
   for (int32_t i = 0; i < matches.Count(); ++i) {
     nsNavHistoryResultNode* node = matches[i];
     // Force a tags update before checking the node.
     node->mTags.SetIsVoid(true);
     nsAutoString tags;
     rv = node->GetTags(tags);
     NS_ENSURE_SUCCESS(rv, rv);
     // It's possible now this node does not respect anymore the conditions.
     // In such a case it should be removed.
     if (mHasSearchTerms &&
-        !history->EvaluateQueryForNode(mQueries, mOptions, node)) {
+        !history->EvaluateQueryForNode(mQuery, mOptions, node)) {
       nsNavHistoryContainerResultNode* parent = node->mParent;
       // URI nodes should always have parents
       NS_ENSURE_TRUE(parent, NS_ERROR_UNEXPECTED);
       int32_t childIndex = parent->FindChild(node);
       NS_ASSERTION(childIndex >= 0, "Child not found in parent");
       parent->RemoveChildAt(childIndex);
     }
     else {
@@ -3125,23 +3092,21 @@ nsNavHistoryFolderResultNode::GetQuery(n
 }
 
 
 /**
  * Options for the query that gives you this bookmarks folder.  This is just
  * the options for the folder with the current folder ID set.
  */
 NS_IMETHODIMP
-nsNavHistoryFolderResultNode::GetQueryOptions(
-                                      nsINavHistoryQueryOptions** aQueryOptions)
+nsNavHistoryFolderResultNode::GetQueryOptions(nsINavHistoryQueryOptions** _options)
 {
-  NS_ASSERTION(mOptions, "Options invalid");
-
-  *aQueryOptions = mOptions;
-  NS_ADDREF(*aQueryOptions);
+  MOZ_ASSERT(mOptions, "Options should be valid");
+  nsCOMPtr<nsINavHistoryQueryOptions> options = do_QueryInterface(mOptions);
+  options.forget(_options);
   return NS_OK;
 }
 
 
 nsresult
 nsNavHistoryFolderResultNode::FillChildren()
 {
   NS_ASSERTION(!mContentsValid,
@@ -4004,28 +3969,39 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsINavHistoryResult)
   NS_INTERFACE_MAP_STATIC_AMBIGUOUS(nsNavHistoryResult)
   NS_INTERFACE_MAP_ENTRY(nsINavHistoryResult)
   NS_INTERFACE_MAP_ENTRY(nsINavBookmarkObserver)
   NS_INTERFACE_MAP_ENTRY(nsINavHistoryObserver)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
 NS_INTERFACE_MAP_END
 
-nsNavHistoryResult::nsNavHistoryResult(nsNavHistoryContainerResultNode* aRoot)
-  : mRootNode(aRoot)
+nsNavHistoryResult::nsNavHistoryResult(nsNavHistoryContainerResultNode* aRoot,
+                                       const RefPtr<nsNavHistoryQuery>& aQuery,
+                                       const RefPtr<nsNavHistoryQueryOptions>& aOptions,
+                                       bool aBatchInProgress
+) : mRootNode(aRoot)
+  , mQuery(aQuery)
+  , mOptions(aOptions)
   , mNeedsToApplySortingMode(false)
   , mIsHistoryObserver(false)
   , mIsBookmarkFolderObserver(false)
   , mIsAllBookmarksObserver(false)
   , mIsMobilePrefObserver(false)
   , mBookmarkFolderObservers(64)
-  , mBatchInProgress(false)
+  , mBatchInProgress(aBatchInProgress)
   , mSuppressNotifications(false)
 {
+  mSortingMode = aOptions->SortingMode();
+  MOZ_ALWAYS_SUCCEEDS(aOptions->GetSortingAnnotation(mSortingAnnotation));
+
   mRootNode->mResult = this;
+  MOZ_ASSERT(mRootNode->mIndentLevel == -1,
+             "Root node's indent level initialized wrong");
+  mRootNode->FillStats();
 }
 
 nsNavHistoryResult::~nsNavHistoryResult()
 {
   // Delete all heap-allocated bookmark folder observer arrays.
   for (auto it = mBookmarkFolderObservers.Iter(); !it.Done(); it.Next()) {
     delete it.Data();
     it.Remove();
@@ -4053,81 +4029,16 @@ nsNavHistoryResult::StopObserving()
     nsNavHistory* history = nsNavHistory::GetHistoryService();
     if (history) {
       history->RemoveObserver(this);
       mIsHistoryObserver = false;
     }
   }
 }
 
-/**
- * @note you must call AddRef before this, since we may do things like
- * register ourselves.
- */
-nsresult
-nsNavHistoryResult::Init(nsCOMArray<nsNavHistoryQuery>& aQueries,
-                         uint32_t aQueryCount,
-                         nsNavHistoryQueryOptions *aOptions)
-{
-  nsresult rv;
-  NS_ASSERTION(aOptions, "Must have valid options");
-  NS_ASSERTION(aQueries.Count() == aQueryCount && aQueryCount > 0, "Must have >1 query in result");
-
-  // Fill saved source queries with copies of the original (the caller might
-  // change their original objects, and we always want to reflect the source
-  // parameters).
-  for (uint32_t i = 0; i < aQueryCount; ++i) {
-    nsCOMPtr<nsINavHistoryQuery> queryClone;
-    rv = aQueries[i]->Clone(getter_AddRefs(queryClone));
-    NS_ENSURE_SUCCESS(rv, rv);
-    if (!mQueries.AppendObject(queryClone))
-      return NS_ERROR_OUT_OF_MEMORY;
-  }
-  rv = aOptions->Clone(getter_AddRefs(mOptions));
-  NS_ENSURE_SUCCESS(rv, rv);
-  mSortingMode = aOptions->SortingMode();
-  rv = aOptions->GetSortingAnnotation(mSortingAnnotation);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  NS_ASSERTION(mRootNode->mIndentLevel == -1,
-               "Root node's indent level initialized wrong");
-  mRootNode->FillStats();
-
-  return NS_OK;
-}
-
-
-/**
- * Constructs a new history result object.
- */
-nsresult // static
-nsNavHistoryResult::NewHistoryResult(nsCOMArray<nsNavHistoryQuery>& aQueries,
-                                     uint32_t aQueryCount,
-                                     nsNavHistoryQueryOptions* aOptions,
-                                     nsNavHistoryContainerResultNode* aRoot,
-                                     bool aBatchInProgress,
-                                     nsNavHistoryResult** result)
-{
-  *result = new nsNavHistoryResult(aRoot);
-  if (!*result)
-    return NS_ERROR_OUT_OF_MEMORY;
-  NS_ADDREF(*result); // must happen before Init
-  // Correctly set mBatchInProgress for the result based on the root node value.
-  (*result)->mBatchInProgress = aBatchInProgress;
-  nsresult rv = (*result)->Init(aQueries, aQueryCount, aOptions);
-  if (NS_FAILED(rv)) {
-    NS_RELEASE(*result);
-    *result = nullptr;
-    return rv;
-  }
-
-  return NS_OK;
-}
-
-
 void
 nsNavHistoryResult::AddHistoryObserver(nsNavHistoryQueryResultNode* aNode)
 {
   if (!mIsHistoryObserver) {
       nsNavHistory* history = nsNavHistory::GetHistoryService();
       NS_ASSERTION(history, "Can't create history service");
       history->AddObserver(this, true);
       mIsHistoryObserver = true;
--- a/toolkit/components/places/nsNavHistoryResult.h
+++ b/toolkit/components/places/nsNavHistoryResult.h
@@ -96,23 +96,16 @@ private:
   { 0x455d1d40, 0x1b9b, 0x40e6, { 0xa6, 0x41, 0x8b, 0xb7, 0xe8, 0x82, 0x23, 0x87 } }
 
 class nsNavHistoryResult final : public nsSupportsWeakReference,
                                  public nsINavHistoryResult,
                                  public nsINavBookmarkObserver,
                                  public nsINavHistoryObserver
 {
 public:
-  static nsresult NewHistoryResult(nsCOMArray<nsNavHistoryQuery>& aQueries,
-                                   uint32_t aQueryCount,
-                                   nsNavHistoryQueryOptions* aOptions,
-                                   nsNavHistoryContainerResultNode* aRoot,
-                                   bool aBatchInProgress,
-                                   nsNavHistoryResult** result);
-
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_NAVHISTORYRESULT_IID)
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_NSINAVHISTORYRESULT
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsNavHistoryResult, nsINavHistoryResult)
   NS_DECL_BOOKMARK_HISTORY_OBSERVER_EXTERNAL(override)
   NS_IMETHOD OnVisits(nsIVisitData** aVisits,
                       uint32_t aVisitsCount) override;
@@ -128,26 +121,25 @@ public:
   void StopObserving();
 
   nsresult OnVisit(nsIURI* aURI, int64_t aVisitId, PRTime aTime,
                    uint32_t aTransitionType, const nsACString& aGUID,
                    bool aHidden, uint32_t aVisitCount,
                    const nsAString& aLastKnownTitle);
 
 public:
-  // two-stage init, use NewHistoryResult to construct
-  explicit nsNavHistoryResult(nsNavHistoryContainerResultNode* mRoot);
-  nsresult Init(nsCOMArray<nsNavHistoryQuery>& aQueries,
-                uint32_t aQueryCount,
-                nsNavHistoryQueryOptions *aOptions);
+  explicit nsNavHistoryResult(nsNavHistoryContainerResultNode* mRoot,
+                              const RefPtr<nsNavHistoryQuery>& aQuery,
+                              const RefPtr<nsNavHistoryQueryOptions>& aOptions,
+                              bool aBatchInProgress);
 
   RefPtr<nsNavHistoryContainerResultNode> mRootNode;
 
-  nsCOMArray<nsINavHistoryQuery> mQueries;
-  nsCOMPtr<nsNavHistoryQueryOptions> mOptions;
+  RefPtr<nsNavHistoryQuery> mQuery;
+  RefPtr<nsNavHistoryQueryOptions> mOptions;
 
   // One of nsNavHistoryQueryOptions.SORY_BY_* This is initialized to mOptions.sortingMode,
   // but may be overridden if the user clicks on one of the columns.
   uint16_t mSortingMode;
   // If root node is closed and we try to apply a sortingMode, it would not
   // work.  So we will apply it when the node will be reopened and populated.
   // This var states the fact we need to apply sortingMode in such a situation.
   bool mNeedsToApplySortingMode;
@@ -228,17 +220,17 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsNavHisto
 
 // This is used by the base classes instead of
 // NS_FORWARD_NSINAVHISTORYRESULTNODE(nsNavHistoryResultNode) because they
 // need to redefine GetType and GetUri rather than forwarding them. This
 // implements all the simple getters instead of forwarding because they are so
 // short and we can save a virtual function call.
 //
 // (GetUri is redefined only by QueryResultNode and FolderResultNode because
-// the queries might not necessarily be parsed. The rest just return the node's
+// the query might not necessarily be parsed. The rest just return the node's
 // buffer.)
 #define NS_FORWARD_COMMON_RESULTNODE_TO_BASE \
   NS_IMPLEMENT_SIMPLE_RESULTNODE \
   NS_IMETHOD GetIcon(nsACString& aIcon) override \
     { return nsNavHistoryResultNode::GetIcon(aIcon); } \
   NS_IMETHOD GetParent(nsINavHistoryContainerResultNode** aParent) override \
     { return nsNavHistoryResultNode::GetParent(aParent); } \
   NS_IMETHOD GetParentResult(nsINavHistoryResult** aResult) override \
@@ -483,18 +475,18 @@ public:
   nsCOMArray<nsNavHistoryResultNode> mChildren;
 
   // mOriginalOptions is the options object used to _define_ this specific
   // container node. It may differ from mOptions, that is the options used
   // to _fill_ this container node, because mOptions may be modified by
   // the direct parent of this container node, see SetAsParentOfNode. For
   // example, if the parent has excludeItems, options will have it too, even if
   // originally this object was not defined with that option.
-  nsCOMPtr<nsNavHistoryQueryOptions> mOriginalOptions;
-  nsCOMPtr<nsNavHistoryQueryOptions> mOptions;
+  RefPtr<nsNavHistoryQueryOptions> mOriginalOptions;
+  RefPtr<nsNavHistoryQueryOptions> mOptions;
 
   void FillStats();
   // Sets this container as parent of aNode, propagating the appropriate options.
   void SetAsParentOfNode(nsNavHistoryResultNode* aNode);
   nsresult ReverseUpdateStats(int32_t aAccessCountChange);
 
   // Sorting methods.
   typedef nsCOMArray<nsNavHistoryResultNode>::nsCOMArrayComparatorFunc SortComparator;
@@ -624,21 +616,21 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsNavHisto
 class nsNavHistoryQueryResultNode final : public nsNavHistoryContainerResultNode,
                                           public nsINavHistoryQueryResultNode,
                                           public nsINavBookmarkObserver
 {
 public:
   nsNavHistoryQueryResultNode(const nsACString& aTitle,
                               const nsACString& aQueryURI);
   nsNavHistoryQueryResultNode(const nsACString& aTitle,
-                              const nsCOMArray<nsNavHistoryQuery>& aQueries,
+                              const RefPtr<nsNavHistoryQuery>& aQuery,
                               nsNavHistoryQueryOptions* aOptions);
   nsNavHistoryQueryResultNode(const nsACString& aTitle,
                               PRTime aTime,
-                              const nsCOMArray<nsNavHistoryQuery>& aQueries,
+                              const RefPtr<nsNavHistoryQuery>& aQuery,
                               nsNavHistoryQueryOptions* aOptions);
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_FORWARD_COMMON_RESULTNODE_TO_BASE
   NS_IMETHOD GetType(uint32_t* type) override
     { *type = nsNavHistoryResultNode::RESULT_TYPE_QUERY; return NS_OK; }
   NS_IMETHOD GetUri(nsACString& aURI) override; // does special lazy creation
   NS_FORWARD_CONTAINERNODE_EXCEPT_HASCHILDREN
@@ -658,28 +650,28 @@ public:
   // query nodes when the visited uri belongs to them. If no such query exists,
   // the history result creates a new query node dynamically.
   nsresult OnVisit(nsIURI* aURI, int64_t aVisitId, PRTime aTime,
                    uint32_t aTransitionType, bool aHidden,
                    uint32_t* aAdded);
   virtual void OnRemoving() override;
 
 public:
-  // this constructs lazily mURI from mQueries and mOptions, call
-  // VerifyQueriesSerialized either this or mQueries/mOptions should be valid
-  nsresult VerifyQueriesSerialized();
+  // This constructs lazily mURI from mQuery and mOptions.
+  // Either this or mQuery/mOptions should be valid.
+  nsresult VerifyQuerySerialized();
 
-  // these may be constructed lazily from mURI, call VerifyQueriesParsed
-  // either this or mURI should be valid
-  nsCOMArray<nsNavHistoryQuery> mQueries;
+  // This may be constructed lazily from mURI, call VerifyQueryParsed.
+  // Either this or mURI should be valid.
+  RefPtr<nsNavHistoryQuery> mQuery;
   uint32_t mLiveUpdate; // one of QUERYUPDATE_* in nsNavHistory.h
   bool mHasSearchTerms;
-  nsresult VerifyQueriesParsed();
+  nsresult VerifyQueryParsed();
 
-  // safe options getter, ensures queries are parsed
+  // safe options getter, ensures query is parsed
   nsNavHistoryQueryOptions* Options();
 
   // this indicates whether the query contents are valid, they don't go away
   // after the container is closed until a notification comes in
   bool mContentsValid;
 
   nsresult FillChildren();
   void ClearChildren(bool unregister);
@@ -689,17 +681,17 @@ public:
   virtual void GetSortingAnnotation(nsACString& aSortingAnnotation) override;
   virtual void RecursiveSort(const char* aData,
                              SortComparator aComparator) override;
 
   nsresult NotifyIfTagsChanged(nsIURI* aURI);
 
   uint32_t mBatchChanges;
 
-  // Tracks transition type filters shared by all mQueries.
+  // Tracks transition type filters.
   nsTArray<uint32_t> mTransitions;
 
 protected:
   virtual ~nsNavHistoryQueryResultNode();
 };
 
 
 // nsNavHistoryFolderResultNode
--- a/toolkit/components/places/tests/unit/test_annotations.js
+++ b/toolkit/components/places/tests/unit/test_annotations.js
@@ -292,17 +292,17 @@ add_task(async function test_execute() {
   });
   annosvc.removeItemAnnotation(testItemId, int32Key);
 
   testItem = await PlacesUtils.bookmarks.fetch(testItem.guid);
   var lastModified4 = testItem.lastModified;
   info("verify that removing an annotation updates the last modified date");
   info("lastModified3 = " + lastModified3);
   info("lastModified4 = " + lastModified4);
-  Assert.ok(lastModified4 > lastModified3);
+  Assert.ok(is_time_ordered(lastModified3, lastModified4));
 
   Assert.equal(annoObserver.PAGE_lastRemoved_URI, testURI.spec);
   Assert.equal(annoObserver.PAGE_lastRemoved_AnnoName, int32Key);
   Assert.equal(annoObserver.ITEM_lastRemoved_Id, testItemId);
   Assert.equal(annoObserver.ITEM_lastRemoved_AnnoName, int32Key);
 
   // test that getItems/PagesWithAnnotation returns an empty array after
   // removing all items/pages which had the annotation set, see bug 380317.