LCOV - code coverage report
Current view: top level - toolkit/components/places - nsNavHistory.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 141 2017 7.0 %
Date: 2017-07-14 16:53:18 Functions: 23 140 16.4 %
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             : 
       9             : #include "mozilla/DebugOnly.h"
      10             : #include "mozilla/IntegerPrintfMacros.h"
      11             : #include "mozilla/SizePrintfMacros.h"
      12             : 
      13             : #include "nsNavHistory.h"
      14             : 
      15             : #include "mozIPlacesAutoComplete.h"
      16             : #include "nsNavBookmarks.h"
      17             : #include "nsAnnotationService.h"
      18             : #include "nsFaviconService.h"
      19             : #include "nsPlacesMacros.h"
      20             : #include "DateTimeFormat.h"
      21             : #include "History.h"
      22             : #include "Helpers.h"
      23             : 
      24             : #include "nsTArray.h"
      25             : #include "nsCollationCID.h"
      26             : #include "nsNetUtil.h"
      27             : #include "nsPrintfCString.h"
      28             : #include "nsPromiseFlatString.h"
      29             : #include "nsString.h"
      30             : #include "nsUnicharUtils.h"
      31             : #include "prsystem.h"
      32             : #include "prtime.h"
      33             : #include "nsEscape.h"
      34             : #include "nsIEffectiveTLDService.h"
      35             : #include "nsIClassInfoImpl.h"
      36             : #include "nsIIDNService.h"
      37             : #include "nsThreadUtils.h"
      38             : #include "nsAppDirectoryServiceDefs.h"
      39             : #include "nsMathUtils.h"
      40             : #include "mozilla/storage.h"
      41             : #include "mozilla/Preferences.h"
      42             : #include <algorithm>
      43             : 
      44             : #ifdef MOZ_XUL
      45             : #include "nsIAutoCompleteInput.h"
      46             : #include "nsIAutoCompletePopup.h"
      47             : #endif
      48             : 
      49             : using namespace mozilla;
      50             : using namespace mozilla::places;
      51             : 
      52             : // The maximum number of things that we will store in the recent events list
      53             : // before calling ExpireNonrecentEvents. This number should be big enough so it
      54             : // is very difficult to get that many unconsumed events (for example, typed but
      55             : // never visited) in the RECENT_EVENT_THRESHOLD. Otherwise, we'll start
      56             : // checking each one for every page visit, which will be somewhat slower.
      57             : #define RECENT_EVENT_QUEUE_MAX_LENGTH 128
      58             : 
      59             : // preference ID strings
      60             : #define PREF_HISTORY_ENABLED                    "places.history.enabled"
      61             : 
      62             : #define PREF_FREC_NUM_VISITS                    "places.frecency.numVisits"
      63             : #define PREF_FREC_NUM_VISITS_DEF                10
      64             : #define PREF_FREC_FIRST_BUCKET_CUTOFF           "places.frecency.firstBucketCutoff"
      65             : #define PREF_FREC_FIRST_BUCKET_CUTOFF_DEF       4
      66             : #define PREF_FREC_SECOND_BUCKET_CUTOFF          "places.frecency.secondBucketCutoff"
      67             : #define PREF_FREC_SECOND_BUCKET_CUTOFF_DEF      14
      68             : #define PREF_FREC_THIRD_BUCKET_CUTOFF           "places.frecency.thirdBucketCutoff"
      69             : #define PREF_FREC_THIRD_BUCKET_CUTOFF_DEF       31
      70             : #define PREF_FREC_FOURTH_BUCKET_CUTOFF          "places.frecency.fourthBucketCutoff"
      71             : #define PREF_FREC_FOURTH_BUCKET_CUTOFF_DEF      90
      72             : #define PREF_FREC_FIRST_BUCKET_WEIGHT           "places.frecency.firstBucketWeight"
      73             : #define PREF_FREC_FIRST_BUCKET_WEIGHT_DEF       100
      74             : #define PREF_FREC_SECOND_BUCKET_WEIGHT          "places.frecency.secondBucketWeight"
      75             : #define PREF_FREC_SECOND_BUCKET_WEIGHT_DEF      70
      76             : #define PREF_FREC_THIRD_BUCKET_WEIGHT           "places.frecency.thirdBucketWeight"
      77             : #define PREF_FREC_THIRD_BUCKET_WEIGHT_DEF       50
      78             : #define PREF_FREC_FOURTH_BUCKET_WEIGHT          "places.frecency.fourthBucketWeight"
      79             : #define PREF_FREC_FOURTH_BUCKET_WEIGHT_DEF      30
      80             : #define PREF_FREC_DEFAULT_BUCKET_WEIGHT         "places.frecency.defaultBucketWeight"
      81             : #define PREF_FREC_DEFAULT_BUCKET_WEIGHT_DEF     10
      82             : #define PREF_FREC_EMBED_VISIT_BONUS             "places.frecency.embedVisitBonus"
      83             : #define PREF_FREC_EMBED_VISIT_BONUS_DEF         0
      84             : #define PREF_FREC_FRAMED_LINK_VISIT_BONUS       "places.frecency.framedLinkVisitBonus"
      85             : #define PREF_FREC_FRAMED_LINK_VISIT_BONUS_DEF   0
      86             : #define PREF_FREC_LINK_VISIT_BONUS              "places.frecency.linkVisitBonus"
      87             : #define PREF_FREC_LINK_VISIT_BONUS_DEF          100
      88             : #define PREF_FREC_TYPED_VISIT_BONUS             "places.frecency.typedVisitBonus"
      89             : #define PREF_FREC_TYPED_VISIT_BONUS_DEF         2000
      90             : #define PREF_FREC_BOOKMARK_VISIT_BONUS          "places.frecency.bookmarkVisitBonus"
      91             : #define PREF_FREC_BOOKMARK_VISIT_BONUS_DEF      75
      92             : #define PREF_FREC_DOWNLOAD_VISIT_BONUS          "places.frecency.downloadVisitBonus"
      93             : #define PREF_FREC_DOWNLOAD_VISIT_BONUS_DEF      0
      94             : #define PREF_FREC_PERM_REDIRECT_VISIT_BONUS     "places.frecency.permRedirectVisitBonus"
      95             : #define PREF_FREC_PERM_REDIRECT_VISIT_BONUS_DEF 0
      96             : #define PREF_FREC_TEMP_REDIRECT_VISIT_BONUS     "places.frecency.tempRedirectVisitBonus"
      97             : #define PREF_FREC_TEMP_REDIRECT_VISIT_BONUS_DEF 0
      98             : #define PREF_FREC_REDIR_SOURCE_VISIT_BONUS      "places.frecency.redirectSourceVisitBonus"
      99             : #define PREF_FREC_REDIR_SOURCE_VISIT_BONUS_DEF  25
     100             : #define PREF_FREC_DEFAULT_VISIT_BONUS           "places.frecency.defaultVisitBonus"
     101             : #define PREF_FREC_DEFAULT_VISIT_BONUS_DEF       0
     102             : #define PREF_FREC_UNVISITED_BOOKMARK_BONUS      "places.frecency.unvisitedBookmarkBonus"
     103             : #define PREF_FREC_UNVISITED_BOOKMARK_BONUS_DEF  140
     104             : #define PREF_FREC_UNVISITED_TYPED_BONUS         "places.frecency.unvisitedTypedBonus"
     105             : #define PREF_FREC_UNVISITED_TYPED_BONUS_DEF     200
     106             : #define PREF_FREC_RELOAD_VISIT_BONUS            "places.frecency.reloadVisitBonus"
     107             : #define PREF_FREC_RELOAD_VISIT_BONUS_DEF        0
     108             : 
     109             : // This is a 'hidden' pref for the purposes of unit tests.
     110             : #define PREF_FREC_DECAY_RATE     "places.frecency.decayRate"
     111             : #define PREF_FREC_DECAY_RATE_DEF 0.975f
     112             : 
     113             : // In order to avoid calling PR_now() too often we use a cached "now" value
     114             : // for repeating stuff.  These are milliseconds between "now" cache refreshes.
     115             : #define RENEW_CACHED_NOW_TIMEOUT ((int32_t)3 * PR_MSEC_PER_SEC)
     116             : 
     117             : // character-set annotation
     118             : #define CHARSET_ANNO NS_LITERAL_CSTRING("URIProperties/characterSet")
     119             : 
     120             : // These macros are used when splitting history by date.
     121             : // These are the day containers and catch-all final container.
     122             : #define HISTORY_ADDITIONAL_DATE_CONT_NUM 3
     123             : // We use a guess of the number of months considering all of them 30 days
     124             : // long, but we split only the last 6 months.
     125             : #define HISTORY_DATE_CONT_NUM(_daysFromOldestVisit) \
     126             :   (HISTORY_ADDITIONAL_DATE_CONT_NUM + \
     127             :    std::min(6, (int32_t)ceilf((float)_daysFromOldestVisit/30)))
     128             : // Max number of containers, used to initialize the params hash.
     129             : #define HISTORY_DATE_CONT_LENGTH 8
     130             : 
     131             : // Initial length of the embed visits cache.
     132             : #define EMBED_VISITS_INITIAL_CACHE_LENGTH 64
     133             : 
     134             : // Initial length of the recent events cache.
     135             : #define RECENT_EVENTS_INITIAL_CACHE_LENGTH 64
     136             : 
     137             : // Observed topics.
     138             : #ifdef MOZ_XUL
     139             : #define TOPIC_AUTOCOMPLETE_FEEDBACK_INCOMING "autocomplete-will-enter-text"
     140             : #endif
     141             : #define TOPIC_IDLE_DAILY "idle-daily"
     142             : #define TOPIC_PREF_CHANGED "nsPref:changed"
     143             : #define TOPIC_PROFILE_TEARDOWN "profile-change-teardown"
     144             : #define TOPIC_PROFILE_CHANGE "profile-before-change"
     145             : 
     146             : static const char* kObservedPrefs[] = {
     147             :   PREF_HISTORY_ENABLED
     148             : , PREF_FREC_NUM_VISITS
     149             : , PREF_FREC_FIRST_BUCKET_CUTOFF
     150             : , PREF_FREC_SECOND_BUCKET_CUTOFF
     151             : , PREF_FREC_THIRD_BUCKET_CUTOFF
     152             : , PREF_FREC_FOURTH_BUCKET_CUTOFF
     153             : , PREF_FREC_FIRST_BUCKET_WEIGHT
     154             : , PREF_FREC_SECOND_BUCKET_WEIGHT
     155             : , PREF_FREC_THIRD_BUCKET_WEIGHT
     156             : , PREF_FREC_FOURTH_BUCKET_WEIGHT
     157             : , PREF_FREC_DEFAULT_BUCKET_WEIGHT
     158             : , PREF_FREC_EMBED_VISIT_BONUS
     159             : , PREF_FREC_FRAMED_LINK_VISIT_BONUS
     160             : , PREF_FREC_LINK_VISIT_BONUS
     161             : , PREF_FREC_TYPED_VISIT_BONUS
     162             : , PREF_FREC_BOOKMARK_VISIT_BONUS
     163             : , PREF_FREC_DOWNLOAD_VISIT_BONUS
     164             : , PREF_FREC_PERM_REDIRECT_VISIT_BONUS
     165             : , PREF_FREC_TEMP_REDIRECT_VISIT_BONUS
     166             : , PREF_FREC_REDIR_SOURCE_VISIT_BONUS
     167             : , PREF_FREC_DEFAULT_VISIT_BONUS
     168             : , PREF_FREC_UNVISITED_BOOKMARK_BONUS
     169             : , PREF_FREC_UNVISITED_TYPED_BONUS
     170             : , nullptr
     171             : };
     172             : 
     173          92 : NS_IMPL_ADDREF(nsNavHistory)
     174          84 : NS_IMPL_RELEASE(nsNavHistory)
     175             : 
     176           3 : NS_IMPL_CLASSINFO(nsNavHistory, nullptr, nsIClassInfo::SINGLETON,
     177             :                   NS_NAVHISTORYSERVICE_CID)
     178         102 : NS_INTERFACE_MAP_BEGIN(nsNavHistory)
     179         102 :   NS_INTERFACE_MAP_ENTRY(nsINavHistoryService)
     180         100 :   NS_INTERFACE_MAP_ENTRY(nsIBrowserHistory)
     181          98 :   NS_INTERFACE_MAP_ENTRY(nsIObserver)
     182          98 :   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
     183          49 :   NS_INTERFACE_MAP_ENTRY(nsPIPlacesDatabase)
     184          45 :   NS_INTERFACE_MAP_ENTRY(mozIStorageVacuumParticipant)
     185          45 :   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsINavHistoryService)
     186          17 :   NS_IMPL_QUERY_CLASSINFO(nsNavHistory)
     187          15 : NS_INTERFACE_MAP_END
     188             : 
     189             : // We don't care about flattening everything
     190           1 : NS_IMPL_CI_INTERFACE_GETTER(nsNavHistory,
     191             :                             nsINavHistoryService,
     192             :                             nsIBrowserHistory)
     193             : 
     194             : namespace {
     195             : 
     196             : static int64_t GetSimpleBookmarksQueryFolder(
     197             :     const nsCOMArray<nsNavHistoryQuery>& aQueries,
     198             :     nsNavHistoryQueryOptions* aOptions);
     199             : static void ParseSearchTermsFromQueries(const nsCOMArray<nsNavHistoryQuery>& aQueries,
     200             :                                         nsTArray<nsTArray<nsString>*>* aTerms);
     201             : 
     202           0 : void GetTagsSqlFragment(int64_t aTagsFolder,
     203             :                         const nsACString& aRelation,
     204             :                         bool aHasSearchTerms,
     205             :                         nsACString& _sqlFragment) {
     206           0 :   if (!aHasSearchTerms)
     207           0 :     _sqlFragment.AssignLiteral("null");
     208             :   else {
     209             :     // This subquery DOES NOT order tags for performance reasons.
     210           0 :     _sqlFragment.Assign(NS_LITERAL_CSTRING(
     211             :          "(SELECT GROUP_CONCAT(t_t.title, ',') "
     212             :            "FROM moz_bookmarks b_t "
     213             :            "JOIN moz_bookmarks t_t ON t_t.id = +b_t.parent  "
     214           0 :            "WHERE b_t.fk = ") + aRelation + NS_LITERAL_CSTRING(" "
     215           0 :            "AND t_t.parent = ") +
     216           0 :            nsPrintfCString("%" PRId64, aTagsFolder) + NS_LITERAL_CSTRING(" "
     217           0 :          ")"));
     218             :   }
     219             : 
     220           0 :   _sqlFragment.AppendLiteral(" AS tags ");
     221           0 : }
     222             : 
     223             : /**
     224             :  * This class sets begin/end of batch updates to correspond to C++ scopes so
     225             :  * we can be sure end always gets called.
     226             :  */
     227             : class UpdateBatchScoper
     228             : {
     229             : public:
     230           0 :   explicit UpdateBatchScoper(nsNavHistory& aNavHistory) : mNavHistory(aNavHistory)
     231             :   {
     232           0 :     mNavHistory.BeginUpdateBatch();
     233           0 :   }
     234           0 :   ~UpdateBatchScoper()
     235           0 :   {
     236           0 :     mNavHistory.EndUpdateBatch();
     237           0 :   }
     238             : protected:
     239             :   nsNavHistory& mNavHistory;
     240             : };
     241             : 
     242             : } // namespace
     243             : 
     244             : 
     245             : // Queries rows indexes to bind or get values, if adding a new one, be sure to
     246             : // update nsNavBookmarks statements and its kGetChildrenIndex_* constants
     247             : const int32_t nsNavHistory::kGetInfoIndex_PageID = 0;
     248             : const int32_t nsNavHistory::kGetInfoIndex_URL = 1;
     249             : const int32_t nsNavHistory::kGetInfoIndex_Title = 2;
     250             : const int32_t nsNavHistory::kGetInfoIndex_RevHost = 3;
     251             : const int32_t nsNavHistory::kGetInfoIndex_VisitCount = 4;
     252             : const int32_t nsNavHistory::kGetInfoIndex_VisitDate = 5;
     253             : const int32_t nsNavHistory::kGetInfoIndex_FaviconURL = 6;
     254             : const int32_t nsNavHistory::kGetInfoIndex_ItemId = 7;
     255             : const int32_t nsNavHistory::kGetInfoIndex_ItemDateAdded = 8;
     256             : const int32_t nsNavHistory::kGetInfoIndex_ItemLastModified = 9;
     257             : const int32_t nsNavHistory::kGetInfoIndex_ItemParentId = 10;
     258             : const int32_t nsNavHistory::kGetInfoIndex_ItemTags = 11;
     259             : const int32_t nsNavHistory::kGetInfoIndex_Frecency = 12;
     260             : const int32_t nsNavHistory::kGetInfoIndex_Hidden = 13;
     261             : const int32_t nsNavHistory::kGetInfoIndex_Guid = 14;
     262             : const int32_t nsNavHistory::kGetInfoIndex_VisitId = 15;
     263             : const int32_t nsNavHistory::kGetInfoIndex_FromVisitId = 16;
     264             : const int32_t nsNavHistory::kGetInfoIndex_VisitType = 17;
     265             : // These columns are followed by corresponding constants in nsNavBookmarks.cpp,
     266             : // which must be kept in sync:
     267             : // nsNavBookmarks::kGetChildrenIndex_Guid = 18;
     268             : // nsNavBookmarks::kGetChildrenIndex_Position = 19;
     269             : // nsNavBookmarks::kGetChildrenIndex_Type = 20;
     270             : // nsNavBookmarks::kGetChildrenIndex_PlaceID = 21;
     271             : 
     272           2 : PLACES_FACTORY_SINGLETON_IMPLEMENTATION(nsNavHistory, gHistoryService)
     273             : 
     274             : 
     275           1 : nsNavHistory::nsNavHistory()
     276             :   : mBatchLevel(0)
     277             :   , mBatchDBTransaction(nullptr)
     278             :   , mCachedNow(0)
     279             :   , mRecentTyped(RECENT_EVENTS_INITIAL_CACHE_LENGTH)
     280             :   , mRecentLink(RECENT_EVENTS_INITIAL_CACHE_LENGTH)
     281             :   , mRecentBookmark(RECENT_EVENTS_INITIAL_CACHE_LENGTH)
     282             :   , mEmbedVisits(EMBED_VISITS_INITIAL_CACHE_LENGTH)
     283             :   , mHistoryEnabled(true)
     284             :   , mNumVisitsForFrecency(10)
     285             :   , mTagsFolder(-1)
     286             :   , mDaysOfHistory(-1)
     287             :   , mLastCachedStartOfDay(INT64_MAX)
     288             :   , mLastCachedEndOfDay(0)
     289             :   , mCanNotify(true)
     290           1 :   , mCacheObservers("history-observers")
     291             : {
     292           1 :   NS_ASSERTION(!gHistoryService,
     293             :                "Attempting to create two instances of the service!");
     294           1 :   gHistoryService = this;
     295           1 : }
     296             : 
     297             : 
     298           0 : nsNavHistory::~nsNavHistory()
     299             : {
     300             :   // remove the static reference to the service. Check to make sure its us
     301             :   // in case somebody creates an extra instance of the service.
     302           0 :   NS_ASSERTION(gHistoryService == this,
     303             :                "Deleting a non-singleton instance of the service");
     304           0 :   if (gHistoryService == this)
     305           0 :     gHistoryService = nullptr;
     306           0 : }
     307             : 
     308             : 
     309             : nsresult
     310           1 : nsNavHistory::Init()
     311             : {
     312           1 :   LoadPrefs();
     313             : 
     314           1 :   mDB = Database::GetDatabase();
     315           1 :   NS_ENSURE_STATE(mDB);
     316             : 
     317             :   /*****************************************************************************
     318             :    *** IMPORTANT NOTICE!
     319             :    ***
     320             :    *** Nothing after these add observer calls should return anything but NS_OK.
     321             :    *** If a failure code is returned, this nsNavHistory object will be held onto
     322             :    *** by the observer service and the preference service.
     323             :    ****************************************************************************/
     324             : 
     325             :   // Observe preferences changes.
     326           1 :   Preferences::AddWeakObservers(this, kObservedPrefs);
     327             : 
     328           2 :   nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
     329           1 :   if (obsSvc) {
     330           1 :     (void)obsSvc->AddObserver(this, TOPIC_PLACES_CONNECTION_CLOSED, true);
     331           1 :     (void)obsSvc->AddObserver(this, TOPIC_IDLE_DAILY, true);
     332             : #ifdef MOZ_XUL
     333           1 :     (void)obsSvc->AddObserver(this, TOPIC_AUTOCOMPLETE_FEEDBACK_INCOMING, true);
     334             : #endif
     335             :   }
     336             : 
     337             :   // Don't add code that can fail here! Do it up above, before we add our
     338             :   // observers.
     339             : 
     340           1 :   return NS_OK;
     341             : }
     342             : 
     343             : NS_IMETHODIMP
     344           1 : nsNavHistory::GetDatabaseStatus(uint16_t *aDatabaseStatus)
     345             : {
     346           1 :   NS_ENSURE_ARG_POINTER(aDatabaseStatus);
     347           1 :   *aDatabaseStatus = mDB->GetDatabaseStatus();
     348           1 :   return NS_OK;
     349             : }
     350             : 
     351             : uint32_t
     352           1 : nsNavHistory::GetRecentFlags(nsIURI *aURI)
     353             : {
     354           1 :   uint32_t result = 0;
     355           2 :   nsAutoCString spec;
     356           1 :   nsresult rv = aURI->GetSpec(spec);
     357           1 :   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Unable to get aURI's spec");
     358             : 
     359           1 :   if (NS_SUCCEEDED(rv)) {
     360           1 :     if (CheckIsRecentEvent(&mRecentTyped, spec))
     361           0 :       result |= RECENT_TYPED;
     362           1 :     if (CheckIsRecentEvent(&mRecentLink, spec))
     363           0 :       result |= RECENT_ACTIVATED;
     364           1 :     if (CheckIsRecentEvent(&mRecentBookmark, spec))
     365           0 :       result |= RECENT_BOOKMARKED;
     366             :   }
     367             : 
     368           2 :   return result;
     369             : }
     370             : 
     371             : nsresult
     372           0 : nsNavHistory::GetIdForPage(nsIURI* aURI,
     373             :                            int64_t* _pageId,
     374             :                            nsCString& _GUID)
     375             : {
     376           0 :   *_pageId = 0;
     377             : 
     378           0 :   nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
     379             :     "SELECT id, url, title, rev_host, visit_count, guid "
     380             :     "FROM moz_places "
     381             :     "WHERE url_hash = hash(:page_url) AND url = :page_url "
     382           0 :   );
     383           0 :   NS_ENSURE_STATE(stmt);
     384           0 :   mozStorageStatementScoper scoper(stmt);
     385             : 
     386           0 :   nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), aURI);
     387           0 :   NS_ENSURE_SUCCESS(rv, rv);
     388             : 
     389           0 :   bool hasEntry = false;
     390           0 :   rv = stmt->ExecuteStep(&hasEntry);
     391           0 :   NS_ENSURE_SUCCESS(rv, rv);
     392             : 
     393           0 :   if (hasEntry) {
     394           0 :     rv = stmt->GetInt64(0, _pageId);
     395           0 :     NS_ENSURE_SUCCESS(rv, rv);
     396           0 :     rv = stmt->GetUTF8String(5, _GUID);
     397           0 :     NS_ENSURE_SUCCESS(rv, rv);
     398             :   }
     399             : 
     400           0 :   return NS_OK;
     401             : }
     402             : 
     403             : nsresult
     404           0 : nsNavHistory::GetOrCreateIdForPage(nsIURI* aURI,
     405             :                                    int64_t* _pageId,
     406             :                                    nsCString& _GUID)
     407             : {
     408           0 :   nsresult rv = GetIdForPage(aURI, _pageId, _GUID);
     409           0 :   NS_ENSURE_SUCCESS(rv, rv);
     410             : 
     411           0 :   if (*_pageId != 0) {
     412           0 :     return NS_OK;
     413             :   }
     414             : 
     415             :   // Create a new hidden, untyped and unvisited entry.
     416           0 :   nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
     417             :     "INSERT INTO moz_places (url, url_hash, rev_host, hidden, frecency, guid) "
     418             :     "VALUES (:page_url, hash(:page_url), :rev_host, :hidden, :frecency, :guid) "
     419           0 :   );
     420           0 :   NS_ENSURE_STATE(stmt);
     421           0 :   mozStorageStatementScoper scoper(stmt);
     422             : 
     423           0 :   rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), aURI);
     424           0 :   NS_ENSURE_SUCCESS(rv, rv);
     425             :   // host (reversed with trailing period)
     426           0 :   nsAutoString revHost;
     427           0 :   rv = GetReversedHostname(aURI, revHost);
     428             :   // Not all URI types have hostnames, so this is optional.
     429           0 :   if (NS_SUCCEEDED(rv)) {
     430           0 :     rv = stmt->BindStringByName(NS_LITERAL_CSTRING("rev_host"), revHost);
     431             :   } else {
     432           0 :     rv = stmt->BindNullByName(NS_LITERAL_CSTRING("rev_host"));
     433             :   }
     434           0 :   NS_ENSURE_SUCCESS(rv, rv);
     435           0 :   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("hidden"), 1);
     436           0 :   NS_ENSURE_SUCCESS(rv, rv);
     437           0 :   nsAutoCString spec;
     438           0 :   rv = aURI->GetSpec(spec);
     439           0 :   NS_ENSURE_SUCCESS(rv, rv);
     440           0 :   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("frecency"),
     441           0 :                              IsQueryURI(spec) ? 0 : -1);
     442           0 :   NS_ENSURE_SUCCESS(rv, rv);
     443           0 :   rv = GenerateGUID(_GUID);
     444           0 :   NS_ENSURE_SUCCESS(rv, rv);
     445           0 :   rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"), _GUID);
     446           0 :   NS_ENSURE_SUCCESS(rv, rv);
     447             : 
     448           0 :   rv = stmt->Execute();
     449           0 :   NS_ENSURE_SUCCESS(rv, rv);
     450             : 
     451           0 :   *_pageId = sLastInsertedPlaceId;
     452             : 
     453           0 :   return NS_OK;
     454             : }
     455             : 
     456             : 
     457             : void
     458           1 : nsNavHistory::LoadPrefs()
     459             : {
     460             :   // History preferences.
     461           1 :   mHistoryEnabled = Preferences::GetBool(PREF_HISTORY_ENABLED, true);
     462             : 
     463             :   // Frecency preferences.
     464             : #define FRECENCY_PREF(_prop, _pref) \
     465             :   _prop = Preferences::GetInt(_pref, _pref##_DEF)
     466             : 
     467           1 :   FRECENCY_PREF(mNumVisitsForFrecency,     PREF_FREC_NUM_VISITS);
     468           1 :   FRECENCY_PREF(mFirstBucketCutoffInDays,  PREF_FREC_FIRST_BUCKET_CUTOFF);
     469           1 :   FRECENCY_PREF(mSecondBucketCutoffInDays, PREF_FREC_SECOND_BUCKET_CUTOFF);
     470           1 :   FRECENCY_PREF(mThirdBucketCutoffInDays,  PREF_FREC_THIRD_BUCKET_CUTOFF);
     471           1 :   FRECENCY_PREF(mFourthBucketCutoffInDays, PREF_FREC_FOURTH_BUCKET_CUTOFF);
     472           1 :   FRECENCY_PREF(mEmbedVisitBonus,          PREF_FREC_EMBED_VISIT_BONUS);
     473           1 :   FRECENCY_PREF(mFramedLinkVisitBonus,     PREF_FREC_FRAMED_LINK_VISIT_BONUS);
     474           1 :   FRECENCY_PREF(mLinkVisitBonus,           PREF_FREC_LINK_VISIT_BONUS);
     475           1 :   FRECENCY_PREF(mTypedVisitBonus,          PREF_FREC_TYPED_VISIT_BONUS);
     476           1 :   FRECENCY_PREF(mBookmarkVisitBonus,       PREF_FREC_BOOKMARK_VISIT_BONUS);
     477           1 :   FRECENCY_PREF(mDownloadVisitBonus,       PREF_FREC_DOWNLOAD_VISIT_BONUS);
     478           1 :   FRECENCY_PREF(mPermRedirectVisitBonus,   PREF_FREC_PERM_REDIRECT_VISIT_BONUS);
     479           1 :   FRECENCY_PREF(mTempRedirectVisitBonus,   PREF_FREC_TEMP_REDIRECT_VISIT_BONUS);
     480           1 :   FRECENCY_PREF(mRedirectSourceVisitBonus, PREF_FREC_REDIR_SOURCE_VISIT_BONUS);
     481           1 :   FRECENCY_PREF(mDefaultVisitBonus,        PREF_FREC_DEFAULT_VISIT_BONUS);
     482           1 :   FRECENCY_PREF(mUnvisitedBookmarkBonus,   PREF_FREC_UNVISITED_BOOKMARK_BONUS);
     483           1 :   FRECENCY_PREF(mUnvisitedTypedBonus,      PREF_FREC_UNVISITED_TYPED_BONUS);
     484           1 :   FRECENCY_PREF(mReloadVisitBonus,         PREF_FREC_RELOAD_VISIT_BONUS);
     485           1 :   FRECENCY_PREF(mFirstBucketWeight,        PREF_FREC_FIRST_BUCKET_WEIGHT);
     486           1 :   FRECENCY_PREF(mSecondBucketWeight,       PREF_FREC_SECOND_BUCKET_WEIGHT);
     487           1 :   FRECENCY_PREF(mThirdBucketWeight,        PREF_FREC_THIRD_BUCKET_WEIGHT);
     488           1 :   FRECENCY_PREF(mFourthBucketWeight,       PREF_FREC_FOURTH_BUCKET_WEIGHT);
     489           1 :   FRECENCY_PREF(mDefaultWeight,            PREF_FREC_DEFAULT_BUCKET_WEIGHT);
     490             : 
     491             : #undef FRECENCY_PREF
     492           1 : }
     493             : 
     494             : 
     495             : void
     496           1 : nsNavHistory::NotifyOnVisit(nsIURI* aURI,
     497             :                             int64_t aVisitId,
     498             :                             PRTime aTime,
     499             :                             int64_t aReferrerVisitId,
     500             :                             int32_t aTransitionType,
     501             :                             const nsACString& aGuid,
     502             :                             bool aHidden,
     503             :                             uint32_t aVisitCount,
     504             :                             uint32_t aTyped,
     505             :                             const nsAString& aLastKnownTitle)
     506             : {
     507           1 :   MOZ_ASSERT(!aGuid.IsEmpty());
     508           1 :   MOZ_ASSERT(aVisitCount, "Should have at least 1 visit when notifying");
     509             :   // If there's no history, this visit will surely add a day.  If the visit is
     510             :   // added before or after the last cached day, the day count may have changed.
     511             :   // Otherwise adding multiple visits in the same day should not invalidate
     512             :   // the cache.
     513           1 :   if (mDaysOfHistory == 0) {
     514           0 :     mDaysOfHistory = 1;
     515           1 :   } else if (aTime > mLastCachedEndOfDay || aTime < mLastCachedStartOfDay) {
     516           1 :     mDaysOfHistory = -1;
     517             :   }
     518             : 
     519           1 :   NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
     520             :                    nsINavHistoryObserver,
     521             :                    OnVisit(aURI, aVisitId, aTime, 0, aReferrerVisitId,
     522             :                            aTransitionType, aGuid, aHidden, aVisitCount, aTyped, aLastKnownTitle));
     523           1 : }
     524             : 
     525             : void
     526           0 : nsNavHistory::NotifyTitleChange(nsIURI* aURI,
     527             :                                 const nsString& aTitle,
     528             :                                 const nsACString& aGUID)
     529             : {
     530           0 :   MOZ_ASSERT(!aGUID.IsEmpty());
     531           0 :   NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
     532             :                    nsINavHistoryObserver, OnTitleChanged(aURI, aTitle, aGUID));
     533           0 : }
     534             : 
     535             : void
     536           2 : nsNavHistory::NotifyFrecencyChanged(nsIURI* aURI,
     537             :                                     int32_t aNewFrecency,
     538             :                                     const nsACString& aGUID,
     539             :                                     bool aHidden,
     540             :                                     PRTime aLastVisitDate)
     541             : {
     542           2 :   MOZ_ASSERT(!aGUID.IsEmpty());
     543           2 :   NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
     544             :                    nsINavHistoryObserver,
     545             :                    OnFrecencyChanged(aURI, aNewFrecency, aGUID, aHidden,
     546             :                                      aLastVisitDate));
     547           2 : }
     548             : 
     549             : void
     550           0 : nsNavHistory::NotifyManyFrecenciesChanged()
     551             : {
     552           0 :   NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
     553             :                    nsINavHistoryObserver,
     554             :                    OnManyFrecenciesChanged());
     555           0 : }
     556             : 
     557             : namespace {
     558             : 
     559           6 : class FrecencyNotification : public Runnable
     560             : {
     561             : public:
     562           2 :   FrecencyNotification(const nsACString& aSpec,
     563             :                        int32_t aNewFrecency,
     564             :                        const nsACString& aGUID,
     565             :                        bool aHidden,
     566             :                        PRTime aLastVisitDate)
     567           2 :     : mozilla::Runnable("FrecencyNotification")
     568             :     , mSpec(aSpec)
     569             :     , mNewFrecency(aNewFrecency)
     570             :     , mGUID(aGUID)
     571             :     , mHidden(aHidden)
     572           2 :     , mLastVisitDate(aLastVisitDate)
     573             :   {
     574           2 :   }
     575             : 
     576           2 :   NS_IMETHOD Run() override
     577             :   {
     578           2 :     MOZ_ASSERT(NS_IsMainThread(), "Must be called on the main thread");
     579           2 :     nsNavHistory* navHistory = nsNavHistory::GetHistoryService();
     580           2 :     if (navHistory) {
     581           4 :       nsCOMPtr<nsIURI> uri;
     582           2 :       (void)NS_NewURI(getter_AddRefs(uri), mSpec);
     583             :       // We cannot assert since some automated tests are checking this path.
     584           2 :       NS_WARNING_ASSERTION(uri, "Invalid URI in FrecencyNotification");
     585             :       // Notify a frecency change only if we have a valid uri, otherwise
     586             :       // the observer couldn't gather any useful data from the notification.
     587           2 :       if (uri) {
     588           2 :         navHistory->NotifyFrecencyChanged(uri, mNewFrecency, mGUID, mHidden,
     589           2 :                                           mLastVisitDate);
     590             :       }
     591             :     }
     592           2 :     return NS_OK;
     593             :   }
     594             : 
     595             : private:
     596             :   nsCString mSpec;
     597             :   int32_t mNewFrecency;
     598             :   nsCString mGUID;
     599             :   bool mHidden;
     600             :   PRTime mLastVisitDate;
     601             : };
     602             : 
     603             : } // namespace
     604             : 
     605             : void
     606           2 : nsNavHistory::DispatchFrecencyChangedNotification(const nsACString& aSpec,
     607             :                                                   int32_t aNewFrecency,
     608             :                                                   const nsACString& aGUID,
     609             :                                                   bool aHidden,
     610             :                                                   PRTime aLastVisitDate) const
     611             : {
     612             :   nsCOMPtr<nsIRunnable> notif = new FrecencyNotification(aSpec, aNewFrecency,
     613             :                                                          aGUID, aHidden,
     614           4 :                                                          aLastVisitDate);
     615           2 :   (void)NS_DispatchToMainThread(notif);
     616           2 : }
     617             : 
     618             : Atomic<int64_t> nsNavHistory::sLastInsertedPlaceId(0);
     619             : Atomic<int64_t> nsNavHistory::sLastInsertedVisitId(0);
     620             : 
     621             : void // static
     622           2 : nsNavHistory::StoreLastInsertedId(const nsACString& aTable,
     623             :                                   const int64_t aLastInsertedId) {
     624           2 :   if (aTable.Equals(NS_LITERAL_CSTRING("moz_places"))) {
     625           1 :     nsNavHistory::sLastInsertedPlaceId = aLastInsertedId;
     626           1 :   } else if (aTable.Equals(NS_LITERAL_CSTRING("moz_historyvisits"))) {
     627           1 :     nsNavHistory::sLastInsertedVisitId = aLastInsertedId;
     628             :   } else {
     629           0 :     MOZ_ASSERT(false, "Trying to store the insert id for an unknown table?");
     630             :   }
     631           2 : }
     632             : 
     633             : int32_t
     634           0 : nsNavHistory::GetDaysOfHistory() {
     635           0 :   MOZ_ASSERT(NS_IsMainThread(), "This can only be called on the main thread");
     636             : 
     637           0 :   if (mDaysOfHistory != -1)
     638           0 :     return mDaysOfHistory;
     639             : 
     640             :   // SQLite doesn't have a CEIL() function, so we must do that later.
     641             :   // We should also take into account timers resolution, that may be as bad as
     642             :   // 16ms on Windows, so in some cases the difference may be 0, if the
     643             :   // check is done near the visit.  Thus remember to check for NULL separately.
     644           0 :   nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
     645             :     "SELECT CAST(( "
     646             :         "strftime('%s','now','localtime','utc') - "
     647             :         "(SELECT MIN(visit_date)/1000000 FROM moz_historyvisits) "
     648             :       ") AS DOUBLE) "
     649             :     "/86400, "
     650             :     "strftime('%s','now','localtime','+1 day','start of day','utc') * 1000000"
     651           0 :   );
     652           0 :   NS_ENSURE_TRUE(stmt, 0);
     653           0 :   mozStorageStatementScoper scoper(stmt);
     654             : 
     655             :   bool hasResult;
     656           0 :   if (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
     657             :     // If we get NULL, then there are no visits, otherwise there must always be
     658             :     // at least 1 day of history.
     659             :     bool hasNoVisits;
     660           0 :     (void)stmt->GetIsNull(0, &hasNoVisits);
     661           0 :     mDaysOfHistory = hasNoVisits ?
     662           0 :       0 : std::max(1, static_cast<int32_t>(ceil(stmt->AsDouble(0))));
     663           0 :     mLastCachedStartOfDay =
     664           0 :       NormalizeTime(nsINavHistoryQuery::TIME_RELATIVE_TODAY, 0);
     665           0 :     mLastCachedEndOfDay = stmt->AsInt64(1) - 1; // Start of tomorrow - 1.
     666             :   }
     667             : 
     668           0 :   return mDaysOfHistory;
     669             : }
     670             : 
     671             : PRTime
     672           0 : nsNavHistory::GetNow()
     673             : {
     674           0 :   if (!mCachedNow) {
     675           0 :     mCachedNow = PR_Now();
     676           0 :     if (!mExpireNowTimer)
     677           0 :       mExpireNowTimer = do_CreateInstance("@mozilla.org/timer;1");
     678           0 :     if (mExpireNowTimer)
     679           0 :       mExpireNowTimer->InitWithNamedFuncCallback(expireNowTimerCallback,
     680             :                                                  this,
     681             :                                                  RENEW_CACHED_NOW_TIMEOUT,
     682             :                                                  nsITimer::TYPE_ONE_SHOT,
     683           0 :                                                  "nsNavHistory::GetNow");
     684             :   }
     685           0 :   return mCachedNow;
     686             : }
     687             : 
     688             : 
     689           0 : void nsNavHistory::expireNowTimerCallback(nsITimer* aTimer, void* aClosure)
     690             : {
     691           0 :   nsNavHistory *history = static_cast<nsNavHistory *>(aClosure);
     692           0 :   if (history) {
     693           0 :     history->mCachedNow = 0;
     694           0 :     history->mExpireNowTimer = nullptr;
     695             :   }
     696           0 : }
     697             : 
     698             : 
     699             : /**
     700             :  * Code borrowed from mozilla/xpfe/components/history/src/nsGlobalHistory.cpp
     701             :  * Pass in a pre-normalized now and a date, and we'll find the difference since
     702             :  * midnight on each of the days.
     703             :  */
     704             : static PRTime
     705           0 : NormalizeTimeRelativeToday(PRTime aTime)
     706             : {
     707             :   // round to midnight this morning
     708             :   PRExplodedTime explodedTime;
     709           0 :   PR_ExplodeTime(aTime, PR_LocalTimeParameters, &explodedTime);
     710             : 
     711             :   // set to midnight (0:00)
     712           0 :   explodedTime.tm_min =
     713           0 :     explodedTime.tm_hour =
     714           0 :     explodedTime.tm_sec =
     715           0 :     explodedTime.tm_usec = 0;
     716             : 
     717           0 :   return PR_ImplodeTime(&explodedTime);
     718             : }
     719             : 
     720             : // nsNavHistory::NormalizeTime
     721             : //
     722             : //    Converts a nsINavHistoryQuery reference+offset time into a PRTime
     723             : //    relative to the epoch.
     724             : //
     725             : //    It is important that this function NOT use the current time optimization.
     726             : //    It is called to update queries, and we really need to know what right
     727             : //    now is because those incoming values will also have current times that
     728             : //    we will have to compare against.
     729             : 
     730             : PRTime // static
     731           0 : nsNavHistory::NormalizeTime(uint32_t aRelative, PRTime aOffset)
     732             : {
     733             :   PRTime ref;
     734           0 :   switch (aRelative)
     735             :   {
     736             :     case nsINavHistoryQuery::TIME_RELATIVE_EPOCH:
     737           0 :       return aOffset;
     738             :     case nsINavHistoryQuery::TIME_RELATIVE_TODAY:
     739           0 :       ref = NormalizeTimeRelativeToday(PR_Now());
     740           0 :       break;
     741             :     case nsINavHistoryQuery::TIME_RELATIVE_NOW:
     742           0 :       ref = PR_Now();
     743           0 :       break;
     744             :     default:
     745           0 :       NS_NOTREACHED("Invalid relative time");
     746           0 :       return 0;
     747             :   }
     748           0 :   return ref + aOffset;
     749             : }
     750             : 
     751             : // nsNavHistory::GetUpdateRequirements
     752             : //
     753             : //    Returns conditions for query update.
     754             : //
     755             : //    QUERYUPDATE_TIME:
     756             : //      This query is only limited by an inclusive time range on the first
     757             : //      query object. The caller can quickly evaluate the time itself if it
     758             : //      chooses. This is even simpler than "simple" below.
     759             : //    QUERYUPDATE_SIMPLE:
     760             : //      This query is evaluatable using EvaluateQueryForNode to do live
     761             : //      updating.
     762             : //    QUERYUPDATE_COMPLEX:
     763             : //      This query is not evaluatable using EvaluateQueryForNode. When something
     764             : //      happens that this query updates, you will need to re-run the query.
     765             : //    QUERYUPDATE_COMPLEX_WITH_BOOKMARKS:
     766             : //      A complex query that additionally has dependence on bookmarks. All
     767             : //      bookmark-dependent queries fall under this category.
     768             : //
     769             : //    aHasSearchTerms will be set to true if the query has any dependence on
     770             : //    keywords. When there is no dependence on keywords, we can handle title
     771             : //    change operations as simple instead of complex.
     772             : 
     773             : uint32_t
     774           0 : nsNavHistory::GetUpdateRequirements(const nsCOMArray<nsNavHistoryQuery>& aQueries,
     775             :                                     nsNavHistoryQueryOptions* aOptions,
     776             :                                     bool* aHasSearchTerms)
     777             : {
     778           0 :   NS_ASSERTION(aQueries.Count() > 0, "Must have at least one query");
     779             : 
     780             :   // first check if there are search terms
     781           0 :   *aHasSearchTerms = false;
     782             :   int32_t i;
     783           0 :   for (i = 0; i < aQueries.Count(); i ++) {
     784           0 :     aQueries[i]->GetHasSearchTerms(aHasSearchTerms);
     785           0 :     if (*aHasSearchTerms)
     786           0 :       break;
     787             :   }
     788             : 
     789           0 :   bool nonTimeBasedItems = false;
     790           0 :   bool domainBasedItems = false;
     791             : 
     792           0 :   for (i = 0; i < aQueries.Count(); i ++) {
     793           0 :     nsNavHistoryQuery* query = aQueries[i];
     794             : 
     795           0 :     if (query->Folders().Length() > 0 ||
     796           0 :         query->OnlyBookmarked() ||
     797           0 :         query->Tags().Length() > 0) {
     798           0 :       return QUERYUPDATE_COMPLEX_WITH_BOOKMARKS;
     799             :     }
     800             : 
     801             :     // Note: we don't currently have any complex non-bookmarked items, but these
     802             :     // are expected to be added. Put detection of these items here.
     803           0 :     if (!query->SearchTerms().IsEmpty() ||
     804           0 :         !query->Domain().IsVoid() ||
     805           0 :         query->Uri() != nullptr)
     806           0 :       nonTimeBasedItems = true;
     807             : 
     808           0 :     if (! query->Domain().IsVoid())
     809           0 :       domainBasedItems = true;
     810             :   }
     811             : 
     812           0 :   if (aOptions->ResultType() ==
     813             :       nsINavHistoryQueryOptions::RESULTS_AS_TAG_QUERY)
     814           0 :     return QUERYUPDATE_COMPLEX_WITH_BOOKMARKS;
     815             : 
     816             :   // Whenever there is a maximum number of results,
     817             :   // and we are not a bookmark query we must requery. This
     818             :   // is because we can't generally know if any given addition/change causes
     819             :   // the item to be in the top N items in the database.
     820           0 :   if (aOptions->MaxResults() > 0)
     821           0 :     return QUERYUPDATE_COMPLEX;
     822             : 
     823           0 :   if (aQueries.Count() == 1 && domainBasedItems)
     824           0 :     return QUERYUPDATE_HOST;
     825           0 :   if (aQueries.Count() == 1 && !nonTimeBasedItems)
     826           0 :     return QUERYUPDATE_TIME;
     827             : 
     828           0 :   return QUERYUPDATE_SIMPLE;
     829             : }
     830             : 
     831             : 
     832             : // nsNavHistory::EvaluateQueryForNode
     833             : //
     834             : //    This runs the node through the given queries to see if satisfies the
     835             : //    query conditions. Not every query parameters are handled by this code,
     836             : //    but we handle the most common ones so that performance is better.
     837             : //
     838             : //    We assume that the time on the node is the time that we want to compare.
     839             : //    This is not necessarily true because URL nodes have the last access time,
     840             : //    which is not necessarily the same. However, since this is being called
     841             : //    to update the list, we assume that the last access time is the current
     842             : //    access time that we are being asked to compare so it works out.
     843             : //
     844             : //    Returns true if node matches the query, false if not.
     845             : 
     846             : bool
     847           0 : nsNavHistory::EvaluateQueryForNode(const nsCOMArray<nsNavHistoryQuery>& aQueries,
     848             :                                    nsNavHistoryQueryOptions* aOptions,
     849             :                                    nsNavHistoryResultNode* aNode)
     850             : {
     851             :   // lazily created from the node's string when we need to match URIs
     852           0 :   nsCOMPtr<nsIURI> nodeUri;
     853             : 
     854             :   // --- hidden ---
     855           0 :   if (aNode->mHidden && !aOptions->IncludeHidden())
     856           0 :     return false;
     857             : 
     858           0 :   for (int32_t i = 0; i < aQueries.Count(); i ++) {
     859             :     bool hasIt;
     860           0 :     nsCOMPtr<nsNavHistoryQuery> query = aQueries[i];
     861             : 
     862             :     // --- begin time ---
     863           0 :     query->GetHasBeginTime(&hasIt);
     864           0 :     if (hasIt) {
     865           0 :       PRTime beginTime = NormalizeTime(query->BeginTimeReference(),
     866           0 :                                        query->BeginTime());
     867           0 :       if (aNode->mTime < beginTime)
     868           0 :         continue; // before our time range
     869             :     }
     870             : 
     871             :     // --- end time ---
     872           0 :     query->GetHasEndTime(&hasIt);
     873           0 :     if (hasIt) {
     874           0 :       PRTime endTime = NormalizeTime(query->EndTimeReference(),
     875           0 :                                      query->EndTime());
     876           0 :       if (aNode->mTime > endTime)
     877           0 :         continue; // after our time range
     878             :     }
     879             : 
     880             :     // --- search terms ---
     881           0 :     if (! query->SearchTerms().IsEmpty()) {
     882             :       // we can use the existing filtering code, just give it our one object in
     883             :       // an array.
     884           0 :       nsCOMArray<nsNavHistoryResultNode> inputSet;
     885           0 :       inputSet.AppendObject(aNode);
     886           0 :       nsCOMArray<nsNavHistoryQuery> queries;
     887           0 :       queries.AppendObject(query);
     888           0 :       nsCOMArray<nsNavHistoryResultNode> filteredSet;
     889           0 :       nsresult rv = FilterResultSet(nullptr, inputSet, &filteredSet, queries, aOptions);
     890           0 :       if (NS_FAILED(rv))
     891           0 :         continue;
     892           0 :       if (! filteredSet.Count())
     893           0 :         continue; // did not make it through the filter, doesn't match
     894             :     }
     895             : 
     896             :     // --- domain/host matching ---
     897           0 :     query->GetHasDomain(&hasIt);
     898           0 :     if (hasIt) {
     899           0 :       if (! nodeUri) {
     900             :         // lazy creation of nodeUri, which might be checked for multiple queries
     901           0 :         if (NS_FAILED(NS_NewURI(getter_AddRefs(nodeUri), aNode->mURI)))
     902           0 :           continue;
     903             :       }
     904           0 :       nsAutoCString asciiRequest;
     905           0 :       if (NS_FAILED(AsciiHostNameFromHostString(query->Domain(), asciiRequest)))
     906           0 :         continue;
     907             : 
     908           0 :       if (query->DomainIsHost()) {
     909           0 :         nsAutoCString host;
     910           0 :         if (NS_FAILED(nodeUri->GetAsciiHost(host)))
     911           0 :           continue;
     912             : 
     913           0 :         if (! asciiRequest.Equals(host))
     914           0 :           continue; // host names don't match
     915             :       }
     916             :       // check domain names
     917           0 :       nsAutoCString domain;
     918           0 :       DomainNameFromURI(nodeUri, domain);
     919           0 :       if (! asciiRequest.Equals(domain))
     920           0 :         continue; // domain names don't match
     921             :     }
     922             : 
     923             :     // --- URI matching ---
     924           0 :     if (query->Uri()) {
     925           0 :       if (! nodeUri) { // lazy creation of nodeUri
     926           0 :         if (NS_FAILED(NS_NewURI(getter_AddRefs(nodeUri), aNode->mURI)))
     927           0 :           continue;
     928             :       }
     929             : 
     930             :       bool equals;
     931           0 :       nsresult rv = query->Uri()->Equals(nodeUri, &equals);
     932           0 :       NS_ENSURE_SUCCESS(rv, false);
     933           0 :       if (! equals)
     934           0 :         continue;
     935             :     }
     936             : 
     937             :     // Transitions matching.
     938           0 :     const nsTArray<uint32_t>& transitions = query->Transitions();
     939           0 :     if (aNode->mTransitionType > 0 &&
     940           0 :         transitions.Length() &&
     941           0 :         !transitions.Contains(aNode->mTransitionType)) {
     942           0 :       continue; // transition doesn't match.
     943             :     }
     944             : 
     945             :     // If we ever make it to the bottom of this loop, that means it passed all
     946             :     // tests for the given query. Since queries are ORed together, that means
     947             :     // it passed everything and we are done.
     948           0 :     return true;
     949             :   }
     950             : 
     951             :   // didn't match any query
     952           0 :   return false;
     953             : }
     954             : 
     955             : 
     956             : // nsNavHistory::AsciiHostNameFromHostString
     957             : //
     958             : //    We might have interesting encodings and different case in the host name.
     959             : //    This will convert that host name into an ASCII host name by sending it
     960             : //    through the URI canonicalization. The result can be used for comparison
     961             : //    with other ASCII host name strings.
     962             : nsresult // static
     963           0 : nsNavHistory::AsciiHostNameFromHostString(const nsACString& aHostName,
     964             :                                           nsACString& aAscii)
     965             : {
     966           0 :   aAscii.Truncate();
     967           0 :   if (aHostName.IsEmpty()) {
     968           0 :     return NS_OK;
     969             :   }
     970             :   // To properly generate a uri we must provide a protocol.
     971           0 :   nsAutoCString fakeURL("http://");
     972           0 :   fakeURL.Append(aHostName);
     973           0 :   nsCOMPtr<nsIURI> uri;
     974           0 :   nsresult rv = NS_NewURI(getter_AddRefs(uri), fakeURL);
     975           0 :   NS_ENSURE_SUCCESS(rv, rv);
     976           0 :   rv = uri->GetAsciiHost(aAscii);
     977           0 :   NS_ENSURE_SUCCESS(rv, rv);
     978           0 :   return NS_OK;
     979             : }
     980             : 
     981             : 
     982             : // nsNavHistory::DomainNameFromURI
     983             : //
     984             : //    This does the www.mozilla.org -> mozilla.org and
     985             : //    foo.theregister.co.uk -> theregister.co.uk conversion
     986             : void
     987           0 : nsNavHistory::DomainNameFromURI(nsIURI *aURI,
     988             :                                 nsACString& aDomainName)
     989             : {
     990             :   // lazily get the effective tld service
     991           0 :   if (!mTLDService)
     992           0 :     mTLDService = do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
     993             : 
     994           0 :   if (mTLDService) {
     995             :     // get the base domain for a given hostname.
     996             :     // e.g. for "images.bbc.co.uk", this would be "bbc.co.uk".
     997           0 :     nsresult rv = mTLDService->GetBaseDomain(aURI, 0, aDomainName);
     998           0 :     if (NS_SUCCEEDED(rv))
     999           0 :       return;
    1000             :   }
    1001             : 
    1002             :   // just return the original hostname
    1003             :   // (it's also possible the host is an IP address)
    1004           0 :   aURI->GetAsciiHost(aDomainName);
    1005             : }
    1006             : 
    1007             : 
    1008             : NS_IMETHODIMP
    1009           0 : nsNavHistory::GetHasHistoryEntries(bool* aHasEntries)
    1010             : {
    1011           0 :   NS_ENSURE_ARG_POINTER(aHasEntries);
    1012           0 :   *aHasEntries = GetDaysOfHistory() > 0;
    1013           0 :   return NS_OK;
    1014             : }
    1015             : 
    1016             : 
    1017             : namespace {
    1018             : 
    1019           0 : class InvalidateAllFrecenciesCallback : public AsyncStatementCallback
    1020             : {
    1021             : public:
    1022           0 :   InvalidateAllFrecenciesCallback()
    1023           0 :   {
    1024           0 :   }
    1025             : 
    1026           0 :   NS_IMETHOD HandleCompletion(uint16_t aReason)
    1027             :   {
    1028           0 :     if (aReason == REASON_FINISHED) {
    1029           0 :       nsNavHistory *navHistory = nsNavHistory::GetHistoryService();
    1030           0 :       NS_ENSURE_STATE(navHistory);
    1031           0 :       navHistory->NotifyManyFrecenciesChanged();
    1032             :     }
    1033           0 :     return NS_OK;
    1034             :   }
    1035             : };
    1036             : 
    1037             : } // namespace
    1038             : 
    1039             : nsresult
    1040           0 : nsNavHistory::invalidateFrecencies(const nsCString& aPlaceIdsQueryString)
    1041             : {
    1042             :   // Exclude place: queries by setting their frecency to zero.
    1043             :   nsCString invalidFrecenciesSQLFragment(
    1044             :     "UPDATE moz_places SET frecency = "
    1045           0 :   );
    1046           0 :   if (!aPlaceIdsQueryString.IsEmpty())
    1047           0 :     invalidFrecenciesSQLFragment.AppendLiteral("NOTIFY_FRECENCY(");
    1048             :   invalidFrecenciesSQLFragment.AppendLiteral(
    1049             :       "(CASE "
    1050             :        "WHEN url_hash BETWEEN hash('place', 'prefix_lo') AND "
    1051             :                              "hash('place', 'prefix_hi') "
    1052             :        "THEN 0 "
    1053             :        "ELSE -1 "
    1054             :        "END) "
    1055           0 :   );
    1056           0 :   if (!aPlaceIdsQueryString.IsEmpty()) {
    1057             :     invalidFrecenciesSQLFragment.AppendLiteral(
    1058             :       ", url, guid, hidden, last_visit_date) "
    1059           0 :     );
    1060             :   }
    1061             :   invalidFrecenciesSQLFragment.AppendLiteral(
    1062             :     "WHERE frecency > 0 "
    1063           0 :   );
    1064           0 :   if (!aPlaceIdsQueryString.IsEmpty()) {
    1065           0 :     invalidFrecenciesSQLFragment.AppendLiteral("AND id IN(");
    1066           0 :     invalidFrecenciesSQLFragment.Append(aPlaceIdsQueryString);
    1067           0 :     invalidFrecenciesSQLFragment.Append(')');
    1068             :   }
    1069             :   RefPtr<InvalidateAllFrecenciesCallback> cb =
    1070           0 :     aPlaceIdsQueryString.IsEmpty() ? new InvalidateAllFrecenciesCallback()
    1071           0 :                                    : nullptr;
    1072             : 
    1073           0 :   nsCOMPtr<mozIStorageAsyncStatement> stmt = mDB->GetAsyncStatement(
    1074             :     invalidFrecenciesSQLFragment
    1075           0 :   );
    1076           0 :   NS_ENSURE_STATE(stmt);
    1077             : 
    1078           0 :   nsCOMPtr<mozIStoragePendingStatement> ps;
    1079           0 :   nsresult rv = stmt->ExecuteAsync(cb, getter_AddRefs(ps));
    1080           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1081             : 
    1082           0 :   return NS_OK;
    1083             : }
    1084             : 
    1085             : 
    1086             : // Call this method before visiting a URL in order to help determine the
    1087             : // transition type of the visit.
    1088             : //
    1089             : // @see MarkPageAsTyped
    1090             : 
    1091             : NS_IMETHODIMP
    1092           0 : nsNavHistory::MarkPageAsFollowedBookmark(nsIURI* aURI)
    1093             : {
    1094           0 :   NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread");
    1095           0 :   NS_ENSURE_ARG(aURI);
    1096             : 
    1097             :   // don't add when history is disabled
    1098           0 :   if (IsHistoryDisabled())
    1099           0 :     return NS_OK;
    1100             : 
    1101           0 :   nsAutoCString uriString;
    1102           0 :   nsresult rv = aURI->GetSpec(uriString);
    1103           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1104             : 
    1105           0 :   mRecentBookmark.Put(uriString, GetNow());
    1106             : 
    1107           0 :   if (mRecentBookmark.Count() > RECENT_EVENT_QUEUE_MAX_LENGTH)
    1108           0 :     ExpireNonrecentEvents(&mRecentBookmark);
    1109             : 
    1110           0 :   return NS_OK;
    1111             : }
    1112             : 
    1113             : 
    1114             : // nsNavHistory::CanAddURI
    1115             : //
    1116             : //    Filter out unwanted URIs such as "chrome:", "mailbox:", etc.
    1117             : //
    1118             : //    The model is if we don't know differently then add which basically means
    1119             : //    we are suppose to try all the things we know not to allow in and then if
    1120             : //    we don't bail go on and allow it in.
    1121             : 
    1122             : NS_IMETHODIMP
    1123           4 : nsNavHistory::CanAddURI(nsIURI* aURI, bool* canAdd)
    1124             : {
    1125           4 :   NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread");
    1126           4 :   NS_ENSURE_ARG(aURI);
    1127           4 :   NS_ENSURE_ARG_POINTER(canAdd);
    1128             : 
    1129             :   // Default to false.
    1130           4 :   *canAdd = false;
    1131             : 
    1132             :   // If history is disabled, don't add any entry.
    1133           4 :   if (IsHistoryDisabled()) {
    1134           0 :     return NS_OK;
    1135             :   }
    1136             : 
    1137             :   // If the url length is over a threshold, don't add it.
    1138           8 :   nsCString spec;
    1139           4 :   nsresult rv = aURI->GetSpec(spec);
    1140           4 :   NS_ENSURE_SUCCESS(rv, rv);
    1141           4 :   if (!mDB || spec.Length() > mDB->MaxUrlLength()) {
    1142           0 :     return NS_OK;
    1143             :   }
    1144             : 
    1145           8 :   nsAutoCString scheme;
    1146           4 :   rv = aURI->GetScheme(scheme);
    1147           4 :   NS_ENSURE_SUCCESS(rv, rv);
    1148             : 
    1149             :   // first check the most common cases (HTTP, HTTPS) to allow in to avoid most
    1150             :   // of the work
    1151           4 :   if (scheme.EqualsLiteral("http")) {
    1152           4 :     *canAdd = true;
    1153           4 :     return NS_OK;
    1154             :   }
    1155           0 :   if (scheme.EqualsLiteral("https")) {
    1156           0 :     *canAdd = true;
    1157           0 :     return NS_OK;
    1158             :   }
    1159             : 
    1160             :   // now check for all bad things
    1161           0 :   if (scheme.EqualsLiteral("about") ||
    1162           0 :       scheme.EqualsLiteral("blob") ||
    1163           0 :       scheme.EqualsLiteral("chrome") ||
    1164           0 :       scheme.EqualsLiteral("data") ||
    1165           0 :       scheme.EqualsLiteral("imap") ||
    1166           0 :       scheme.EqualsLiteral("javascript") ||
    1167           0 :       scheme.EqualsLiteral("mailbox") ||
    1168           0 :       scheme.EqualsLiteral("moz-anno") ||
    1169           0 :       scheme.EqualsLiteral("news") ||
    1170           0 :       scheme.EqualsLiteral("page-icon") ||
    1171           0 :       scheme.EqualsLiteral("resource") ||
    1172           0 :       scheme.EqualsLiteral("view-source") ||
    1173           0 :       scheme.EqualsLiteral("wyciwyg")) {
    1174           0 :     return NS_OK;
    1175             :   }
    1176           0 :   *canAdd = true;
    1177           0 :   return NS_OK;
    1178             : }
    1179             : 
    1180             : // nsNavHistory::GetNewQuery
    1181             : 
    1182             : NS_IMETHODIMP
    1183           0 : nsNavHistory::GetNewQuery(nsINavHistoryQuery **_retval)
    1184             : {
    1185           0 :   NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread");
    1186           0 :   NS_ENSURE_ARG_POINTER(_retval);
    1187             : 
    1188           0 :   RefPtr<nsNavHistoryQuery> query = new nsNavHistoryQuery();
    1189           0 :   query.forget(_retval);
    1190           0 :   return NS_OK;
    1191             : }
    1192             : 
    1193             : // nsNavHistory::GetNewQueryOptions
    1194             : 
    1195             : NS_IMETHODIMP
    1196           0 : nsNavHistory::GetNewQueryOptions(nsINavHistoryQueryOptions **_retval)
    1197             : {
    1198           0 :   NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread");
    1199           0 :   NS_ENSURE_ARG_POINTER(_retval);
    1200             : 
    1201           0 :   RefPtr<nsNavHistoryQueryOptions> queryOptions = new nsNavHistoryQueryOptions();
    1202           0 :   queryOptions.forget(_retval);
    1203           0 :   return NS_OK;
    1204             : }
    1205             : 
    1206             : // nsNavHistory::ExecuteQuery
    1207             : //
    1208             : 
    1209             : NS_IMETHODIMP
    1210           0 : nsNavHistory::ExecuteQuery(nsINavHistoryQuery *aQuery, nsINavHistoryQueryOptions *aOptions,
    1211             :                            nsINavHistoryResult** _retval)
    1212             : {
    1213           0 :   NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread");
    1214           0 :   NS_ENSURE_ARG(aQuery);
    1215           0 :   NS_ENSURE_ARG(aOptions);
    1216           0 :   NS_ENSURE_ARG_POINTER(_retval);
    1217             : 
    1218           0 :   return ExecuteQueries(&aQuery, 1, aOptions, _retval);
    1219             : }
    1220             : 
    1221             : 
    1222             : // nsNavHistory::ExecuteQueries
    1223             : //
    1224             : //    This function is actually very simple, we just create the proper root node (either
    1225             : //    a bookmark folder or a complex query node) and assign it to the result. The node
    1226             : //    will then populate itself accordingly.
    1227             : //
    1228             : //    Quick overview of query operation: When you call this function, we will construct
    1229             : //    the correct container node and set the options you give it. This node will then
    1230             : //    fill itself. Folder nodes will call nsNavBookmarks::QueryFolderChildren, and
    1231             : //    all other queries will call GetQueryResults. If these results contain other
    1232             : //    queries, those will be populated when the container is opened.
    1233             : 
    1234             : NS_IMETHODIMP
    1235           0 : nsNavHistory::ExecuteQueries(nsINavHistoryQuery** aQueries, uint32_t aQueryCount,
    1236             :                              nsINavHistoryQueryOptions *aOptions,
    1237             :                              nsINavHistoryResult** _retval)
    1238             : {
    1239           0 :   NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread");
    1240           0 :   NS_ENSURE_ARG(aQueries);
    1241           0 :   NS_ENSURE_ARG(aOptions);
    1242           0 :   NS_ENSURE_ARG(aQueryCount);
    1243           0 :   NS_ENSURE_ARG_POINTER(_retval);
    1244             : 
    1245             :   nsresult rv;
    1246             :   // concrete options
    1247           0 :   nsCOMPtr<nsNavHistoryQueryOptions> options = do_QueryInterface(aOptions);
    1248           0 :   NS_ENSURE_TRUE(options, NS_ERROR_INVALID_ARG);
    1249             : 
    1250             :   // concrete queries array
    1251           0 :   nsCOMArray<nsNavHistoryQuery> queries;
    1252           0 :   for (uint32_t i = 0; i < aQueryCount; i ++) {
    1253           0 :     nsCOMPtr<nsNavHistoryQuery> query = do_QueryInterface(aQueries[i], &rv);
    1254           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1255           0 :     queries.AppendElement(query.forget());
    1256             :   }
    1257             : 
    1258             :   // Create the root node.
    1259           0 :   RefPtr<nsNavHistoryContainerResultNode> rootNode;
    1260           0 :   int64_t folderId = GetSimpleBookmarksQueryFolder(queries, options);
    1261           0 :   if (folderId) {
    1262             :     // In the simple case where we're just querying children of a single
    1263             :     // bookmark folder, we can more efficiently generate results.
    1264           0 :     nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService();
    1265           0 :     NS_ENSURE_TRUE(bookmarks, NS_ERROR_OUT_OF_MEMORY);
    1266           0 :     RefPtr<nsNavHistoryResultNode> tempRootNode;
    1267           0 :     rv = bookmarks->ResultNodeForContainer(folderId, options,
    1268           0 :                                            getter_AddRefs(tempRootNode));
    1269           0 :     if (NS_SUCCEEDED(rv)) {
    1270           0 :       rootNode = tempRootNode->GetAsContainer();
    1271             :     }
    1272             :     else {
    1273           0 :       NS_WARNING("Generating a generic empty node for a broken query!");
    1274             :       // This is a perf hack to generate an empty query that skips filtering.
    1275           0 :       options->SetExcludeItems(true);
    1276             :     }
    1277             :   }
    1278             : 
    1279           0 :   if (!rootNode) {
    1280             :     // Either this is not a folder shortcut, or is a broken one.  In both cases
    1281             :     // just generate a query node.
    1282           0 :     rootNode = new nsNavHistoryQueryResultNode(EmptyCString(),
    1283           0 :                                                queries, options);
    1284             :   }
    1285             : 
    1286             :   // Create the result that will hold nodes.  Inject batching status into it.
    1287           0 :   RefPtr<nsNavHistoryResult> result;
    1288           0 :   rv = nsNavHistoryResult::NewHistoryResult(aQueries, aQueryCount, options,
    1289           0 :                                             rootNode, isBatching(),
    1290           0 :                                             getter_AddRefs(result));
    1291           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1292             : 
    1293           0 :   result.forget(_retval);
    1294           0 :   return NS_OK;
    1295             : }
    1296             : 
    1297             : // determine from our nsNavHistoryQuery array and nsNavHistoryQueryOptions
    1298             : // if this is the place query from the history menu.
    1299             : // from browser-menubar.inc, our history menu query is:
    1300             : // place:sort=4&maxResults=10
    1301             : // note, any maxResult > 0 will still be considered a history menu query
    1302             : // or if this is the place query from the "Most Visited" item in the
    1303             : // "Smart Bookmarks" folder: place:sort=8&maxResults=10
    1304             : // note, any maxResult > 0 will still be considered a Most Visited menu query
    1305             : static
    1306           0 : bool IsOptimizableHistoryQuery(const nsCOMArray<nsNavHistoryQuery>& aQueries,
    1307             :                                  nsNavHistoryQueryOptions *aOptions,
    1308             :                                  uint16_t aSortMode)
    1309             : {
    1310           0 :   if (aQueries.Count() != 1)
    1311           0 :     return false;
    1312             : 
    1313           0 :   nsNavHistoryQuery *aQuery = aQueries[0];
    1314             : 
    1315           0 :   if (aOptions->QueryType() != nsINavHistoryQueryOptions::QUERY_TYPE_HISTORY)
    1316           0 :     return false;
    1317             : 
    1318           0 :   if (aOptions->ResultType() != nsINavHistoryQueryOptions::RESULTS_AS_URI)
    1319           0 :     return false;
    1320             : 
    1321           0 :   if (aOptions->SortingMode() != aSortMode)
    1322           0 :     return false;
    1323             : 
    1324           0 :   if (aOptions->MaxResults() <= 0)
    1325           0 :     return false;
    1326             : 
    1327           0 :   if (aOptions->ExcludeItems())
    1328           0 :     return false;
    1329             : 
    1330           0 :   if (aOptions->IncludeHidden())
    1331           0 :     return false;
    1332             : 
    1333           0 :   if (aQuery->MinVisits() != -1 || aQuery->MaxVisits() != -1)
    1334           0 :     return false;
    1335             : 
    1336           0 :   if (aQuery->BeginTime() || aQuery->BeginTimeReference())
    1337           0 :     return false;
    1338             : 
    1339           0 :   if (aQuery->EndTime() || aQuery->EndTimeReference())
    1340           0 :     return false;
    1341             : 
    1342           0 :   if (!aQuery->SearchTerms().IsEmpty())
    1343           0 :     return false;
    1344             : 
    1345           0 :   if (aQuery->OnlyBookmarked())
    1346           0 :     return false;
    1347             : 
    1348           0 :   if (aQuery->DomainIsHost() || !aQuery->Domain().IsEmpty())
    1349           0 :     return false;
    1350             : 
    1351           0 :   if (aQuery->AnnotationIsNot() || !aQuery->Annotation().IsEmpty())
    1352           0 :     return false;
    1353             : 
    1354           0 :   if (aQuery->Folders().Length() > 0)
    1355           0 :     return false;
    1356             : 
    1357           0 :   if (aQuery->Tags().Length() > 0)
    1358           0 :     return false;
    1359             : 
    1360           0 :   if (aQuery->Transitions().Length() > 0)
    1361           0 :     return false;
    1362             : 
    1363           0 :   return true;
    1364             : }
    1365             : 
    1366             : static
    1367           0 : bool NeedToFilterResultSet(const nsCOMArray<nsNavHistoryQuery>& aQueries,
    1368             :                              nsNavHistoryQueryOptions *aOptions)
    1369             : {
    1370           0 :   uint16_t resultType = aOptions->ResultType();
    1371           0 :   return resultType == nsINavHistoryQueryOptions::RESULTS_AS_TAG_CONTENTS;
    1372             : }
    1373             : 
    1374             : // ** Helper class for ConstructQueryString **/
    1375             : 
    1376           0 : class PlacesSQLQueryBuilder
    1377             : {
    1378             : public:
    1379             :   PlacesSQLQueryBuilder(const nsCString& aConditions,
    1380             :                         nsNavHistoryQueryOptions* aOptions,
    1381             :                         bool aUseLimit,
    1382             :                         nsNavHistory::StringHash& aAddParams,
    1383             :                         bool aHasSearchTerms);
    1384             : 
    1385             :   nsresult GetQueryString(nsCString& aQueryString);
    1386             : 
    1387             : private:
    1388             :   nsresult Select();
    1389             : 
    1390             :   nsresult SelectAsURI();
    1391             :   nsresult SelectAsVisit();
    1392             :   nsresult SelectAsDay();
    1393             :   nsresult SelectAsSite();
    1394             :   nsresult SelectAsTag();
    1395             : 
    1396             :   nsresult Where();
    1397             :   nsresult GroupBy();
    1398             :   nsresult OrderBy();
    1399             :   nsresult Limit();
    1400             : 
    1401             :   void OrderByColumnIndexAsc(int32_t aIndex);
    1402             :   void OrderByColumnIndexDesc(int32_t aIndex);
    1403             :   // Use these if you want a case insensitive sorting.
    1404             :   void OrderByTextColumnIndexAsc(int32_t aIndex);
    1405             :   void OrderByTextColumnIndexDesc(int32_t aIndex);
    1406             : 
    1407             :   const nsCString& mConditions;
    1408             :   bool mUseLimit;
    1409             :   bool mHasSearchTerms;
    1410             : 
    1411             :   uint16_t mResultType;
    1412             :   uint16_t mQueryType;
    1413             :   bool mIncludeHidden;
    1414             :   uint16_t mSortingMode;
    1415             :   uint32_t mMaxResults;
    1416             : 
    1417             :   nsCString mQueryString;
    1418             :   nsCString mGroupBy;
    1419             :   bool mHasDateColumns;
    1420             :   bool mSkipOrderBy;
    1421             :   nsNavHistory::StringHash& mAddParams;
    1422             : };
    1423             : 
    1424           0 : PlacesSQLQueryBuilder::PlacesSQLQueryBuilder(
    1425             :     const nsCString& aConditions,
    1426             :     nsNavHistoryQueryOptions* aOptions,
    1427             :     bool aUseLimit,
    1428             :     nsNavHistory::StringHash& aAddParams,
    1429           0 :     bool aHasSearchTerms)
    1430             : : mConditions(aConditions)
    1431             : , mUseLimit(aUseLimit)
    1432             : , mHasSearchTerms(aHasSearchTerms)
    1433           0 : , mResultType(aOptions->ResultType())
    1434           0 : , mQueryType(aOptions->QueryType())
    1435           0 : , mIncludeHidden(aOptions->IncludeHidden())
    1436           0 : , mSortingMode(aOptions->SortingMode())
    1437           0 : , mMaxResults(aOptions->MaxResults())
    1438             : , mSkipOrderBy(false)
    1439           0 : , mAddParams(aAddParams)
    1440             : {
    1441           0 :   mHasDateColumns = (mQueryType == nsINavHistoryQueryOptions::QUERY_TYPE_BOOKMARKS);
    1442           0 : }
    1443             : 
    1444             : nsresult
    1445           0 : PlacesSQLQueryBuilder::GetQueryString(nsCString& aQueryString)
    1446             : {
    1447           0 :   nsresult rv = Select();
    1448           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1449           0 :   rv = Where();
    1450           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1451           0 :   rv = GroupBy();
    1452           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1453           0 :   rv = OrderBy();
    1454           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1455           0 :   rv = Limit();
    1456           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1457             : 
    1458           0 :   aQueryString = mQueryString;
    1459           0 :   return NS_OK;
    1460             : }
    1461             : 
    1462             : nsresult
    1463           0 : PlacesSQLQueryBuilder::Select()
    1464             : {
    1465             :   nsresult rv;
    1466             : 
    1467           0 :   switch (mResultType)
    1468             :   {
    1469             :     case nsINavHistoryQueryOptions::RESULTS_AS_URI:
    1470             :     case nsINavHistoryQueryOptions::RESULTS_AS_TAG_CONTENTS:
    1471           0 :       rv = SelectAsURI();
    1472           0 :       NS_ENSURE_SUCCESS(rv, rv);
    1473           0 :       break;
    1474             : 
    1475             :     case nsINavHistoryQueryOptions::RESULTS_AS_VISIT:
    1476             :     case nsINavHistoryQueryOptions::RESULTS_AS_FULL_VISIT:
    1477           0 :       rv = SelectAsVisit();
    1478           0 :       NS_ENSURE_SUCCESS(rv, rv);
    1479           0 :       break;
    1480             : 
    1481             :     case nsINavHistoryQueryOptions::RESULTS_AS_DATE_QUERY:
    1482             :     case nsINavHistoryQueryOptions::RESULTS_AS_DATE_SITE_QUERY:
    1483           0 :       rv = SelectAsDay();
    1484           0 :       NS_ENSURE_SUCCESS(rv, rv);
    1485           0 :       break;
    1486             : 
    1487             :     case nsINavHistoryQueryOptions::RESULTS_AS_SITE_QUERY:
    1488           0 :       rv = SelectAsSite();
    1489           0 :       NS_ENSURE_SUCCESS(rv, rv);
    1490           0 :       break;
    1491             : 
    1492             :     case nsINavHistoryQueryOptions::RESULTS_AS_TAG_QUERY:
    1493           0 :       rv = SelectAsTag();
    1494           0 :       NS_ENSURE_SUCCESS(rv, rv);
    1495           0 :       break;
    1496             : 
    1497             :     default:
    1498           0 :       NS_NOTREACHED("Invalid result type");
    1499             :   }
    1500           0 :   return NS_OK;
    1501             : }
    1502             : 
    1503             : nsresult
    1504           0 : PlacesSQLQueryBuilder::SelectAsURI()
    1505             : {
    1506           0 :   nsNavHistory *history = nsNavHistory::GetHistoryService();
    1507           0 :   NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY);
    1508           0 :   nsAutoCString tagsSqlFragment;
    1509             : 
    1510           0 :   switch (mQueryType) {
    1511             :     case nsINavHistoryQueryOptions::QUERY_TYPE_HISTORY:
    1512           0 :       GetTagsSqlFragment(history->GetTagsFolder(),
    1513           0 :                          NS_LITERAL_CSTRING("h.id"),
    1514           0 :                          mHasSearchTerms,
    1515           0 :                          tagsSqlFragment);
    1516             : 
    1517           0 :       mQueryString = NS_LITERAL_CSTRING(
    1518             :         "SELECT h.id, h.url, h.title AS page_title, h.rev_host, h.visit_count, "
    1519           0 :         "h.last_visit_date, null, null, null, null, null, ") +
    1520           0 :         tagsSqlFragment + NS_LITERAL_CSTRING(", h.frecency, h.hidden, h.guid, "
    1521             :         "null, null, null "
    1522             :         "FROM moz_places h "
    1523             :         // WHERE 1 is a no-op since additonal conditions will start with AND.
    1524             :         "WHERE 1 "
    1525             :           "{QUERY_OPTIONS_VISITS} {QUERY_OPTIONS_PLACES} "
    1526           0 :           "{ADDITIONAL_CONDITIONS} ");
    1527           0 :       break;
    1528             : 
    1529             :     case nsINavHistoryQueryOptions::QUERY_TYPE_BOOKMARKS:
    1530           0 :       if (mResultType == nsINavHistoryQueryOptions::RESULTS_AS_TAG_CONTENTS) {
    1531             :         // Order-by clause is hardcoded because we need to discard duplicates
    1532             :         // in FilterResultSet. We will retain only the last modified item,
    1533             :         // so we are ordering by place id and last modified to do a faster
    1534             :         // filtering.
    1535           0 :         mSkipOrderBy = true;
    1536             : 
    1537           0 :         GetTagsSqlFragment(history->GetTagsFolder(),
    1538           0 :                            NS_LITERAL_CSTRING("b2.fk"),
    1539           0 :                            mHasSearchTerms,
    1540           0 :                            tagsSqlFragment);
    1541             : 
    1542           0 :         mQueryString = NS_LITERAL_CSTRING(
    1543             :           "SELECT b2.fk, h.url, COALESCE(b2.title, h.title) AS page_title, "
    1544             :             "h.rev_host, h.visit_count, h.last_visit_date, null, b2.id, "
    1545           0 :             "b2.dateAdded, b2.lastModified, b2.parent, ") +
    1546           0 :             tagsSqlFragment + NS_LITERAL_CSTRING(", h.frecency, h.hidden, h.guid, "
    1547             :             "null, null, null, b2.guid, b2.position, b2.type, b2.fk "
    1548             :           "FROM moz_bookmarks b2 "
    1549             :           "JOIN (SELECT b.fk "
    1550             :                 "FROM moz_bookmarks b "
    1551             :                 // ADDITIONAL_CONDITIONS will filter on parent.
    1552             :                 "WHERE b.type = 1 {ADDITIONAL_CONDITIONS} "
    1553             :                 ") AS seed ON b2.fk = seed.fk "
    1554             :           "JOIN moz_places h ON h.id = b2.fk "
    1555             :           "WHERE NOT EXISTS ( "
    1556           0 :             "SELECT id FROM moz_bookmarks WHERE id = b2.parent AND parent = ") +
    1557           0 :                 nsPrintfCString("%" PRId64, history->GetTagsFolder()) +
    1558           0 :           NS_LITERAL_CSTRING(") "
    1559           0 :           "ORDER BY b2.fk DESC, b2.lastModified DESC");
    1560             :       }
    1561             :       else {
    1562           0 :         GetTagsSqlFragment(history->GetTagsFolder(),
    1563           0 :                            NS_LITERAL_CSTRING("b.fk"),
    1564           0 :                            mHasSearchTerms,
    1565           0 :                            tagsSqlFragment);
    1566           0 :         mQueryString = NS_LITERAL_CSTRING(
    1567             :           "SELECT b.fk, h.url, COALESCE(b.title, h.title) AS page_title, "
    1568             :             "h.rev_host, h.visit_count, h.last_visit_date, null, b.id, "
    1569           0 :             "b.dateAdded, b.lastModified, b.parent, ") +
    1570           0 :             tagsSqlFragment + NS_LITERAL_CSTRING(", h.frecency, h.hidden, h.guid,"
    1571             :             "null, null, null, b.guid, b.position, b.type, b.fk "
    1572             :           "FROM moz_bookmarks b "
    1573             :           "JOIN moz_places h ON b.fk = h.id "
    1574             :           "WHERE NOT EXISTS "
    1575             :               "(SELECT id FROM moz_bookmarks "
    1576           0 :                 "WHERE id = b.parent AND parent = ") +
    1577           0 :                   nsPrintfCString("%" PRId64, history->GetTagsFolder()) +
    1578           0 :               NS_LITERAL_CSTRING(") "
    1579           0 :             "{ADDITIONAL_CONDITIONS}");
    1580             :       }
    1581           0 :       break;
    1582             : 
    1583             :     default:
    1584           0 :       return NS_ERROR_NOT_IMPLEMENTED;
    1585             :   }
    1586           0 :   return NS_OK;
    1587             : }
    1588             : 
    1589             : nsresult
    1590           0 : PlacesSQLQueryBuilder::SelectAsVisit()
    1591             : {
    1592           0 :   nsNavHistory *history = nsNavHistory::GetHistoryService();
    1593           0 :   NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY);
    1594           0 :   nsAutoCString tagsSqlFragment;
    1595           0 :   GetTagsSqlFragment(history->GetTagsFolder(),
    1596           0 :                      NS_LITERAL_CSTRING("h.id"),
    1597           0 :                      mHasSearchTerms,
    1598           0 :                      tagsSqlFragment);
    1599           0 :   mQueryString = NS_LITERAL_CSTRING(
    1600             :     "SELECT h.id, h.url, h.title AS page_title, h.rev_host, h.visit_count, "
    1601           0 :       "v.visit_date, null, null, null, null, null, ") +
    1602           0 :       tagsSqlFragment + NS_LITERAL_CSTRING(", h.frecency, h.hidden, h.guid, "
    1603             :       "v.id, v.from_visit, v.visit_type "
    1604             :     "FROM moz_places h "
    1605             :     "JOIN moz_historyvisits v ON h.id = v.place_id "
    1606             :     // WHERE 1 is a no-op since additonal conditions will start with AND.
    1607             :     "WHERE 1 "
    1608             :       "{QUERY_OPTIONS_VISITS} {QUERY_OPTIONS_PLACES} "
    1609           0 :       "{ADDITIONAL_CONDITIONS} ");
    1610             : 
    1611           0 :   return NS_OK;
    1612             : }
    1613             : 
    1614             : nsresult
    1615           0 : PlacesSQLQueryBuilder::SelectAsDay()
    1616             : {
    1617           0 :   mSkipOrderBy = true;
    1618             : 
    1619             :   // Sort child queries based on sorting mode if it's provided, otherwise
    1620             :   // fallback to default sort by title ascending.
    1621           0 :   uint16_t sortingMode = nsINavHistoryQueryOptions::SORT_BY_TITLE_ASCENDING;
    1622           0 :   if (mSortingMode != nsINavHistoryQueryOptions::SORT_BY_NONE &&
    1623           0 :       mResultType == nsINavHistoryQueryOptions::RESULTS_AS_DATE_QUERY)
    1624           0 :     sortingMode = mSortingMode;
    1625             : 
    1626             :   uint16_t resultType =
    1627           0 :     mResultType == nsINavHistoryQueryOptions::RESULTS_AS_DATE_QUERY ?
    1628             :       (uint16_t)nsINavHistoryQueryOptions::RESULTS_AS_URI :
    1629           0 :       (uint16_t)nsINavHistoryQueryOptions::RESULTS_AS_SITE_QUERY;
    1630             : 
    1631             :   // beginTime will become the node's time property, we don't use endTime
    1632             :   // because it could overlap, and we use time to sort containers and find
    1633             :   // insert position in a result.
    1634           0 :   mQueryString = nsPrintfCString(
    1635             :      "SELECT null, "
    1636             :        "'place:type=%d&sort=%d&beginTime='||beginTime||'&endTime='||endTime, "
    1637             :       "dayTitle, null, null, beginTime, null, null, null, null, null, null, "
    1638             :       "null, null, null "
    1639             :      "FROM (", // TOUTER BEGIN
    1640             :      resultType,
    1641           0 :      sortingMode);
    1642             : 
    1643           0 :   nsNavHistory *history = nsNavHistory::GetHistoryService();
    1644           0 :   NS_ENSURE_STATE(history);
    1645             : 
    1646           0 :   int32_t daysOfHistory = history->GetDaysOfHistory();
    1647           0 :   for (int32_t i = 0; i <= HISTORY_DATE_CONT_NUM(daysOfHistory); i++) {
    1648           0 :     nsAutoCString dateName;
    1649             :     // Timeframes are calculated as BeginTime <= container < EndTime.
    1650             :     // Notice times can't be relative to now, since to recognize a query we
    1651             :     // must ensure it won't change based on the time it is built.
    1652             :     // So, to select till now, we really select till start of tomorrow, that is
    1653             :     // a fixed timestamp.
    1654             :     // These are used as limits for the inside containers.
    1655           0 :     nsAutoCString sqlFragmentContainerBeginTime, sqlFragmentContainerEndTime;
    1656             :     // These are used to query if the container should be visible.
    1657           0 :     nsAutoCString sqlFragmentSearchBeginTime, sqlFragmentSearchEndTime;
    1658           0 :     switch(i) {
    1659             :        case 0:
    1660             :         // Today
    1661             :          history->GetStringFromName(
    1662           0 :           u"finduri-AgeInDays-is-0", dateName);
    1663             :         // From start of today
    1664           0 :         sqlFragmentContainerBeginTime = NS_LITERAL_CSTRING(
    1665           0 :           "(strftime('%s','now','localtime','start of day','utc')*1000000)");
    1666             :         // To now (tomorrow)
    1667           0 :         sqlFragmentContainerEndTime = NS_LITERAL_CSTRING(
    1668           0 :           "(strftime('%s','now','localtime','start of day','+1 day','utc')*1000000)");
    1669             :         // Search for the same timeframe.
    1670           0 :         sqlFragmentSearchBeginTime = sqlFragmentContainerBeginTime;
    1671           0 :         sqlFragmentSearchEndTime = sqlFragmentContainerEndTime;
    1672           0 :          break;
    1673             :        case 1:
    1674             :         // Yesterday
    1675             :          history->GetStringFromName(
    1676           0 :           u"finduri-AgeInDays-is-1", dateName);
    1677             :         // From start of yesterday
    1678           0 :         sqlFragmentContainerBeginTime = NS_LITERAL_CSTRING(
    1679           0 :           "(strftime('%s','now','localtime','start of day','-1 day','utc')*1000000)");
    1680             :         // To start of today
    1681           0 :         sqlFragmentContainerEndTime = NS_LITERAL_CSTRING(
    1682           0 :           "(strftime('%s','now','localtime','start of day','utc')*1000000)");
    1683             :         // Search for the same timeframe.
    1684           0 :         sqlFragmentSearchBeginTime = sqlFragmentContainerBeginTime;
    1685           0 :         sqlFragmentSearchEndTime = sqlFragmentContainerEndTime;
    1686           0 :         break;
    1687             :       case 2:
    1688             :         // Last 7 days
    1689             :         history->GetAgeInDaysString(7,
    1690           0 :           u"finduri-AgeInDays-last-is", dateName);
    1691             :         // From start of 7 days ago
    1692           0 :         sqlFragmentContainerBeginTime = NS_LITERAL_CSTRING(
    1693           0 :           "(strftime('%s','now','localtime','start of day','-7 days','utc')*1000000)");
    1694             :         // To now (tomorrow)
    1695           0 :         sqlFragmentContainerEndTime = NS_LITERAL_CSTRING(
    1696           0 :           "(strftime('%s','now','localtime','start of day','+1 day','utc')*1000000)");
    1697             :         // This is an overlapped container, but we show it only if there are
    1698             :         // visits older than yesterday.
    1699           0 :         sqlFragmentSearchBeginTime = sqlFragmentContainerBeginTime;
    1700           0 :         sqlFragmentSearchEndTime = NS_LITERAL_CSTRING(
    1701           0 :           "(strftime('%s','now','localtime','start of day','-2 days','utc')*1000000)");
    1702           0 :         break;
    1703             :       case 3:
    1704             :         // This month
    1705             :         history->GetStringFromName(
    1706           0 :           u"finduri-AgeInMonths-is-0", dateName);
    1707             :         // From start of this month
    1708           0 :         sqlFragmentContainerBeginTime = NS_LITERAL_CSTRING(
    1709           0 :           "(strftime('%s','now','localtime','start of month','utc')*1000000)");
    1710             :         // To now (tomorrow)
    1711           0 :         sqlFragmentContainerEndTime = NS_LITERAL_CSTRING(
    1712           0 :           "(strftime('%s','now','localtime','start of day','+1 day','utc')*1000000)");
    1713             :         // This is an overlapped container, but we show it only if there are
    1714             :         // visits older than 7 days ago.
    1715           0 :         sqlFragmentSearchBeginTime = sqlFragmentContainerBeginTime;
    1716           0 :         sqlFragmentSearchEndTime = NS_LITERAL_CSTRING(
    1717           0 :           "(strftime('%s','now','localtime','start of day','-7 days','utc')*1000000)");
    1718           0 :          break;
    1719             :        default:
    1720           0 :         if (i == HISTORY_ADDITIONAL_DATE_CONT_NUM + 6) {
    1721             :           // Older than 6 months
    1722             :           history->GetAgeInDaysString(6,
    1723           0 :             u"finduri-AgeInMonths-isgreater", dateName);
    1724             :           // From start of epoch
    1725           0 :           sqlFragmentContainerBeginTime = NS_LITERAL_CSTRING(
    1726           0 :             "(datetime(0, 'unixepoch')*1000000)");
    1727             :           // To start of 6 months ago ( 5 months + this month).
    1728           0 :           sqlFragmentContainerEndTime = NS_LITERAL_CSTRING(
    1729           0 :             "(strftime('%s','now','localtime','start of month','-5 months','utc')*1000000)");
    1730             :           // Search for the same timeframe.
    1731           0 :           sqlFragmentSearchBeginTime = sqlFragmentContainerBeginTime;
    1732           0 :           sqlFragmentSearchEndTime = sqlFragmentContainerEndTime;
    1733           0 :           break;
    1734             :         }
    1735           0 :         int32_t MonthIndex = i - HISTORY_ADDITIONAL_DATE_CONT_NUM;
    1736             :         // Previous months' titles are month's name if inside this year,
    1737             :         // month's name and year for previous years.
    1738             :         PRExplodedTime tm;
    1739           0 :         PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &tm);
    1740           0 :         uint16_t currentYear = tm.tm_year;
    1741             :         // Set day before month, setting month without day could cause issues.
    1742             :         // For example setting month to February when today is 30, since
    1743             :         // February has not 30 days, will return March instead.
    1744             :         // Also, we use day 2 instead of day 1, so that the GMT month is always
    1745             :         // the same as the local month. (Bug 603002)
    1746           0 :         tm.tm_mday = 2;
    1747           0 :         tm.tm_month -= MonthIndex;
    1748             :         // Notice we use GMTParameters because we just want to get the first
    1749             :         // day of each month.  Using LocalTimeParameters would instead force us
    1750             :         // to apply a DST correction that we don't really need here.
    1751           0 :         PR_NormalizeTime(&tm, PR_GMTParameters);
    1752             :         // If the container is for a past year, add the year to its title,
    1753             :         // otherwise just show the month name.
    1754           0 :         if (tm.tm_year < currentYear) {
    1755           0 :           nsNavHistory::GetMonthYear(tm, dateName);
    1756             :         }
    1757             :         else {
    1758           0 :           nsNavHistory::GetMonthName(tm, dateName);
    1759             :         }
    1760             : 
    1761             :         // From start of MonthIndex + 1 months ago
    1762           0 :         sqlFragmentContainerBeginTime = NS_LITERAL_CSTRING(
    1763           0 :           "(strftime('%s','now','localtime','start of month','-");
    1764           0 :         sqlFragmentContainerBeginTime.AppendInt(MonthIndex);
    1765           0 :         sqlFragmentContainerBeginTime.Append(NS_LITERAL_CSTRING(
    1766           0 :             " months','utc')*1000000)"));
    1767             :         // To start of MonthIndex months ago
    1768           0 :         sqlFragmentContainerEndTime = NS_LITERAL_CSTRING(
    1769           0 :           "(strftime('%s','now','localtime','start of month','-");
    1770           0 :         sqlFragmentContainerEndTime.AppendInt(MonthIndex - 1);
    1771           0 :         sqlFragmentContainerEndTime.Append(NS_LITERAL_CSTRING(
    1772           0 :             " months','utc')*1000000)"));
    1773             :         // Search for the same timeframe.
    1774           0 :         sqlFragmentSearchBeginTime = sqlFragmentContainerBeginTime;
    1775           0 :         sqlFragmentSearchEndTime = sqlFragmentContainerEndTime;
    1776           0 :         break;
    1777             :     }
    1778             : 
    1779           0 :     nsPrintfCString dateParam("dayTitle%d", i);
    1780           0 :     mAddParams.Put(dateParam, dateName);
    1781             : 
    1782             :     nsPrintfCString dayRange(
    1783             :       "SELECT :%s AS dayTitle, "
    1784             :              "%s AS beginTime, "
    1785             :              "%s AS endTime "
    1786             :        "WHERE EXISTS ( "
    1787             :         "SELECT id FROM moz_historyvisits "
    1788             :         "WHERE visit_date >= %s "
    1789             :           "AND visit_date < %s "
    1790             :            "AND visit_type NOT IN (0,%d,%d) "
    1791             :            "{QUERY_OPTIONS_VISITS} "
    1792             :          "LIMIT 1 "
    1793             :       ") ",
    1794             :       dateParam.get(),
    1795             :       sqlFragmentContainerBeginTime.get(),
    1796             :       sqlFragmentContainerEndTime.get(),
    1797             :       sqlFragmentSearchBeginTime.get(),
    1798             :       sqlFragmentSearchEndTime.get(),
    1799             :       nsINavHistoryService::TRANSITION_EMBED,
    1800             :       nsINavHistoryService::TRANSITION_FRAMED_LINK
    1801           0 :     );
    1802             : 
    1803           0 :     mQueryString.Append(dayRange);
    1804             : 
    1805           0 :     if (i < HISTORY_DATE_CONT_NUM(daysOfHistory))
    1806           0 :       mQueryString.AppendLiteral(" UNION ALL ");
    1807             :   }
    1808             : 
    1809           0 :   mQueryString.AppendLiteral(") "); // TOUTER END
    1810             : 
    1811           0 :   return NS_OK;
    1812             : }
    1813             : 
    1814             : nsresult
    1815           0 : PlacesSQLQueryBuilder::SelectAsSite()
    1816             : {
    1817           0 :   nsAutoCString localFiles;
    1818             : 
    1819           0 :   nsNavHistory *history = nsNavHistory::GetHistoryService();
    1820           0 :   NS_ENSURE_STATE(history);
    1821             : 
    1822           0 :   history->GetStringFromName(u"localhost", localFiles);
    1823           0 :   mAddParams.Put(NS_LITERAL_CSTRING("localhost"), localFiles);
    1824             : 
    1825             :   // If there are additional conditions the query has to join on visits too.
    1826           0 :   nsAutoCString visitsJoin;
    1827           0 :   nsAutoCString additionalConditions;
    1828           0 :   nsAutoCString timeConstraints;
    1829           0 :   if (!mConditions.IsEmpty()) {
    1830           0 :     visitsJoin.AssignLiteral("JOIN moz_historyvisits v ON v.place_id = h.id ");
    1831             :     additionalConditions.AssignLiteral("{QUERY_OPTIONS_VISITS} "
    1832             :                                        "{QUERY_OPTIONS_PLACES} "
    1833           0 :                                        "{ADDITIONAL_CONDITIONS} ");
    1834             :     timeConstraints.AssignLiteral("||'&beginTime='||:begin_time||"
    1835           0 :                                     "'&endTime='||:end_time");
    1836             :   }
    1837             : 
    1838           0 :   mQueryString = nsPrintfCString(
    1839             :     "SELECT null, 'place:type=%d&sort=%d&domain=&domainIsHost=true'%s, "
    1840             :            ":localhost, :localhost, null, null, null, null, null, null, null, "
    1841             :            "null, null, null "
    1842             :     "WHERE EXISTS ( "
    1843             :       "SELECT h.id FROM moz_places h "
    1844             :       "%s "
    1845             :       "WHERE h.hidden = 0 "
    1846             :         "AND h.visit_count > 0 "
    1847             :         "AND h.url_hash BETWEEN hash('file', 'prefix_lo') AND "
    1848             :                                "hash('file', 'prefix_hi') "
    1849             :       "%s "
    1850             :       "LIMIT 1 "
    1851             :     ") "
    1852             :     "UNION ALL "
    1853             :     "SELECT null, "
    1854             :            "'place:type=%d&sort=%d&domain='||host||'&domainIsHost=true'%s, "
    1855             :            "host, host, null, null, null, null, null, null, null, "
    1856             :            "null, null, null "
    1857             :     "FROM ( "
    1858             :       "SELECT get_unreversed_host(h.rev_host) AS host "
    1859             :       "FROM moz_places h "
    1860             :       "%s "
    1861             :       "WHERE h.hidden = 0 "
    1862             :         "AND h.rev_host <> '.' "
    1863             :         "AND h.visit_count > 0 "
    1864             :         "%s "
    1865             :       "GROUP BY h.rev_host "
    1866             :       "ORDER BY host ASC "
    1867             :     ") ",
    1868             :     nsINavHistoryQueryOptions::RESULTS_AS_URI,
    1869           0 :     mSortingMode,
    1870             :     timeConstraints.get(),
    1871             :     visitsJoin.get(),
    1872             :     additionalConditions.get(),
    1873             :     nsINavHistoryQueryOptions::RESULTS_AS_URI,
    1874           0 :     mSortingMode,
    1875             :     timeConstraints.get(),
    1876             :     visitsJoin.get(),
    1877             :     additionalConditions.get()
    1878           0 :   );
    1879             : 
    1880           0 :   return NS_OK;
    1881             : }
    1882             : 
    1883             : nsresult
    1884           0 : PlacesSQLQueryBuilder::SelectAsTag()
    1885             : {
    1886           0 :   nsNavHistory *history = nsNavHistory::GetHistoryService();
    1887           0 :   NS_ENSURE_STATE(history);
    1888             : 
    1889             :   // This allows sorting by date fields what is not possible with
    1890             :   // other history queries.
    1891           0 :   mHasDateColumns = true;
    1892             : 
    1893           0 :   mQueryString = nsPrintfCString(
    1894             :     "SELECT null, 'place:folder=' || id || '&queryType=%d&type=%d', "
    1895             :            "title, null, null, null, null, null, dateAdded, "
    1896             :            "lastModified, null, null, null, null, null, null "
    1897             :     "FROM moz_bookmarks "
    1898             :     "WHERE parent = %" PRId64,
    1899             :     nsINavHistoryQueryOptions::QUERY_TYPE_BOOKMARKS,
    1900             :     nsINavHistoryQueryOptions::RESULTS_AS_TAG_CONTENTS,
    1901             :     history->GetTagsFolder()
    1902           0 :   );
    1903             : 
    1904           0 :   return NS_OK;
    1905             : }
    1906             : 
    1907             : nsresult
    1908           0 : PlacesSQLQueryBuilder::Where()
    1909             : {
    1910             : 
    1911             :   // Set query options
    1912           0 :   nsAutoCString additionalVisitsConditions;
    1913           0 :   nsAutoCString additionalPlacesConditions;
    1914             : 
    1915           0 :   if (!mIncludeHidden) {
    1916           0 :     additionalPlacesConditions += NS_LITERAL_CSTRING("AND hidden = 0 ");
    1917             :   }
    1918             : 
    1919           0 :   if (mQueryType == nsINavHistoryQueryOptions::QUERY_TYPE_HISTORY) {
    1920             :     // last_visit_date is updated for any kind of visit, so it's a good
    1921             :     // indicator whether the page has visits.
    1922           0 :     additionalPlacesConditions += NS_LITERAL_CSTRING(
    1923             :       "AND last_visit_date NOTNULL "
    1924           0 :     );
    1925             :   }
    1926             : 
    1927           0 :   if (mResultType == nsINavHistoryQueryOptions::RESULTS_AS_URI &&
    1928           0 :       !additionalVisitsConditions.IsEmpty()) {
    1929             :     // URI results don't join on visits.
    1930           0 :     nsAutoCString tmp = additionalVisitsConditions;
    1931           0 :     additionalVisitsConditions = "AND EXISTS (SELECT 1 FROM moz_historyvisits WHERE place_id = h.id ";
    1932           0 :     additionalVisitsConditions.Append(tmp);
    1933           0 :     additionalVisitsConditions.AppendLiteral("LIMIT 1)");
    1934             :   }
    1935             : 
    1936           0 :   mQueryString.ReplaceSubstring("{QUERY_OPTIONS_VISITS}",
    1937           0 :                                 additionalVisitsConditions.get());
    1938           0 :   mQueryString.ReplaceSubstring("{QUERY_OPTIONS_PLACES}",
    1939           0 :                                 additionalPlacesConditions.get());
    1940             : 
    1941             :   // If we used WHERE already, we inject the conditions
    1942             :   // in place of {ADDITIONAL_CONDITIONS}
    1943           0 :   if (mQueryString.Find("{ADDITIONAL_CONDITIONS}", 0) != kNotFound) {
    1944           0 :     nsAutoCString innerCondition;
    1945             :     // If we have condition AND it
    1946           0 :     if (!mConditions.IsEmpty()) {
    1947           0 :       innerCondition = " AND (";
    1948           0 :       innerCondition += mConditions;
    1949           0 :       innerCondition += ")";
    1950             :     }
    1951           0 :     mQueryString.ReplaceSubstring("{ADDITIONAL_CONDITIONS}",
    1952           0 :                                   innerCondition.get());
    1953             : 
    1954           0 :   } else if (!mConditions.IsEmpty()) {
    1955             : 
    1956           0 :     mQueryString += "WHERE ";
    1957           0 :     mQueryString += mConditions;
    1958             : 
    1959             :   }
    1960           0 :   return NS_OK;
    1961             : }
    1962             : 
    1963             : nsresult
    1964           0 : PlacesSQLQueryBuilder::GroupBy()
    1965             : {
    1966           0 :   mQueryString += mGroupBy;
    1967           0 :   return NS_OK;
    1968             : }
    1969             : 
    1970             : nsresult
    1971           0 : PlacesSQLQueryBuilder::OrderBy()
    1972             : {
    1973           0 :   if (mSkipOrderBy)
    1974           0 :     return NS_OK;
    1975             : 
    1976             :   // Sort clause: we will sort later, but if it comes out of the DB sorted,
    1977             :   // our later sort will be basically free. The DB can sort these for free
    1978             :   // most of the time anyway, because it has indices over these items.
    1979           0 :   switch(mSortingMode)
    1980             :   {
    1981             :     case nsINavHistoryQueryOptions::SORT_BY_NONE:
    1982             :       // Ensure sorting does not change based on tables status.
    1983           0 :       if (mResultType == nsINavHistoryQueryOptions::RESULTS_AS_URI) {
    1984           0 :         if (mQueryType == nsINavHistoryQueryOptions::QUERY_TYPE_BOOKMARKS)
    1985           0 :           mQueryString += NS_LITERAL_CSTRING(" ORDER BY b.id ASC ");
    1986           0 :         else if (mQueryType == nsINavHistoryQueryOptions::QUERY_TYPE_HISTORY)
    1987           0 :           mQueryString += NS_LITERAL_CSTRING(" ORDER BY h.id ASC ");
    1988             :       }
    1989           0 :       break;
    1990             :     case nsINavHistoryQueryOptions::SORT_BY_TITLE_ASCENDING:
    1991             :     case nsINavHistoryQueryOptions::SORT_BY_TITLE_DESCENDING:
    1992             :       // If the user wants few results, we limit them by date, necessitating
    1993             :       // a sort by date here (see the IDL definition for maxResults).
    1994             :       // Otherwise we will do actual sorting by title, but since we could need
    1995             :       // to special sort for some locale we will repeat a second sorting at the
    1996             :       // end in nsNavHistoryResult, that should be faster since the list will be
    1997             :       // almost ordered.
    1998           0 :       if (mMaxResults > 0)
    1999           0 :         OrderByColumnIndexDesc(nsNavHistory::kGetInfoIndex_VisitDate);
    2000           0 :       else if (mSortingMode == nsINavHistoryQueryOptions::SORT_BY_TITLE_ASCENDING)
    2001           0 :         OrderByTextColumnIndexAsc(nsNavHistory::kGetInfoIndex_Title);
    2002             :       else
    2003           0 :         OrderByTextColumnIndexDesc(nsNavHistory::kGetInfoIndex_Title);
    2004           0 :       break;
    2005             :     case nsINavHistoryQueryOptions::SORT_BY_DATE_ASCENDING:
    2006           0 :       OrderByColumnIndexAsc(nsNavHistory::kGetInfoIndex_VisitDate);
    2007           0 :       break;
    2008             :     case nsINavHistoryQueryOptions::SORT_BY_DATE_DESCENDING:
    2009           0 :       OrderByColumnIndexDesc(nsNavHistory::kGetInfoIndex_VisitDate);
    2010           0 :       break;
    2011             :     case nsINavHistoryQueryOptions::SORT_BY_URI_ASCENDING:
    2012           0 :       OrderByColumnIndexAsc(nsNavHistory::kGetInfoIndex_URL);
    2013           0 :       break;
    2014             :     case nsINavHistoryQueryOptions::SORT_BY_URI_DESCENDING:
    2015           0 :       OrderByColumnIndexDesc(nsNavHistory::kGetInfoIndex_URL);
    2016           0 :       break;
    2017             :     case nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_ASCENDING:
    2018           0 :       OrderByColumnIndexAsc(nsNavHistory::kGetInfoIndex_VisitCount);
    2019           0 :       break;
    2020             :     case nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_DESCENDING:
    2021           0 :       OrderByColumnIndexDesc(nsNavHistory::kGetInfoIndex_VisitCount);
    2022           0 :       break;
    2023             :     case nsINavHistoryQueryOptions::SORT_BY_DATEADDED_ASCENDING:
    2024           0 :       if (mHasDateColumns)
    2025           0 :         OrderByColumnIndexAsc(nsNavHistory::kGetInfoIndex_ItemDateAdded);
    2026           0 :       break;
    2027             :     case nsINavHistoryQueryOptions::SORT_BY_DATEADDED_DESCENDING:
    2028           0 :       if (mHasDateColumns)
    2029           0 :         OrderByColumnIndexDesc(nsNavHistory::kGetInfoIndex_ItemDateAdded);
    2030           0 :       break;
    2031             :     case nsINavHistoryQueryOptions::SORT_BY_LASTMODIFIED_ASCENDING:
    2032           0 :       if (mHasDateColumns)
    2033           0 :         OrderByColumnIndexAsc(nsNavHistory::kGetInfoIndex_ItemLastModified);
    2034           0 :       break;
    2035             :     case nsINavHistoryQueryOptions::SORT_BY_LASTMODIFIED_DESCENDING:
    2036           0 :       if (mHasDateColumns)
    2037           0 :         OrderByColumnIndexDesc(nsNavHistory::kGetInfoIndex_ItemLastModified);
    2038           0 :       break;
    2039             :     case nsINavHistoryQueryOptions::SORT_BY_TAGS_ASCENDING:
    2040             :     case nsINavHistoryQueryOptions::SORT_BY_TAGS_DESCENDING:
    2041             :     case nsINavHistoryQueryOptions::SORT_BY_ANNOTATION_ASCENDING:
    2042             :     case nsINavHistoryQueryOptions::SORT_BY_ANNOTATION_DESCENDING:
    2043           0 :       break; // Sort later in nsNavHistoryQueryResultNode::FillChildren()
    2044             :     case nsINavHistoryQueryOptions::SORT_BY_FRECENCY_ASCENDING:
    2045           0 :         OrderByColumnIndexAsc(nsNavHistory::kGetInfoIndex_Frecency);
    2046           0 :       break;
    2047             :     case nsINavHistoryQueryOptions::SORT_BY_FRECENCY_DESCENDING:
    2048           0 :         OrderByColumnIndexDesc(nsNavHistory::kGetInfoIndex_Frecency);
    2049           0 :       break;
    2050             :     default:
    2051           0 :       NS_NOTREACHED("Invalid sorting mode");
    2052             :   }
    2053           0 :   return NS_OK;
    2054             : }
    2055             : 
    2056           0 : void PlacesSQLQueryBuilder::OrderByColumnIndexAsc(int32_t aIndex)
    2057             : {
    2058           0 :   mQueryString += nsPrintfCString(" ORDER BY %d ASC", aIndex+1);
    2059           0 : }
    2060             : 
    2061           0 : void PlacesSQLQueryBuilder::OrderByColumnIndexDesc(int32_t aIndex)
    2062             : {
    2063           0 :   mQueryString += nsPrintfCString(" ORDER BY %d DESC", aIndex+1);
    2064           0 : }
    2065             : 
    2066           0 : void PlacesSQLQueryBuilder::OrderByTextColumnIndexAsc(int32_t aIndex)
    2067             : {
    2068           0 :   mQueryString += nsPrintfCString(" ORDER BY %d COLLATE NOCASE ASC",
    2069           0 :                                   aIndex+1);
    2070           0 : }
    2071             : 
    2072           0 : void PlacesSQLQueryBuilder::OrderByTextColumnIndexDesc(int32_t aIndex)
    2073             : {
    2074           0 :   mQueryString += nsPrintfCString(" ORDER BY %d COLLATE NOCASE DESC",
    2075           0 :                                   aIndex+1);
    2076           0 : }
    2077             : 
    2078             : nsresult
    2079           0 : PlacesSQLQueryBuilder::Limit()
    2080             : {
    2081           0 :   if (mUseLimit && mMaxResults > 0) {
    2082           0 :     mQueryString += NS_LITERAL_CSTRING(" LIMIT ");
    2083           0 :     mQueryString.AppendInt(mMaxResults);
    2084           0 :     mQueryString.Append(' ');
    2085             :   }
    2086           0 :   return NS_OK;
    2087             : }
    2088             : 
    2089             : nsresult
    2090           0 : nsNavHistory::ConstructQueryString(
    2091             :     const nsCOMArray<nsNavHistoryQuery>& aQueries,
    2092             :     nsNavHistoryQueryOptions* aOptions,
    2093             :     nsCString& queryString,
    2094             :     bool& aParamsPresent,
    2095             :     nsNavHistory::StringHash& aAddParams)
    2096             : {
    2097             :   // For information about visit_type see nsINavHistoryService.idl.
    2098             :   // visitType == 0 is undefined (see bug #375777 for details).
    2099             :   // Some sites, especially Javascript-heavy ones, load things in frames to
    2100             :   // display them, resulting in a lot of these entries. This is the reason
    2101             :   // why such visits are filtered out.
    2102             :   nsresult rv;
    2103           0 :   aParamsPresent = false;
    2104             : 
    2105           0 :   int32_t sortingMode = aOptions->SortingMode();
    2106           0 :   NS_ASSERTION(sortingMode >= nsINavHistoryQueryOptions::SORT_BY_NONE &&
    2107             :                sortingMode <= nsINavHistoryQueryOptions::SORT_BY_FRECENCY_DESCENDING,
    2108             :                "Invalid sortingMode found while building query!");
    2109             : 
    2110           0 :   bool hasSearchTerms = false;
    2111           0 :   for (int32_t i = 0; i < aQueries.Count() && !hasSearchTerms; i++) {
    2112           0 :     aQueries[i]->GetHasSearchTerms(&hasSearchTerms);
    2113             :   }
    2114             : 
    2115           0 :   nsAutoCString tagsSqlFragment;
    2116           0 :   GetTagsSqlFragment(GetTagsFolder(),
    2117           0 :                      NS_LITERAL_CSTRING("h.id"),
    2118             :                      hasSearchTerms,
    2119           0 :                      tagsSqlFragment);
    2120             : 
    2121           0 :   if (IsOptimizableHistoryQuery(aQueries, aOptions,
    2122           0 :         nsINavHistoryQueryOptions::SORT_BY_DATE_DESCENDING) ||
    2123           0 :       IsOptimizableHistoryQuery(aQueries, aOptions,
    2124             :         nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_DESCENDING)) {
    2125             :     // Generate an optimized query for the history menu and most visited
    2126             :     // smart bookmark.
    2127           0 :     queryString = NS_LITERAL_CSTRING(
    2128             :       "SELECT h.id, h.url, h.title AS page_title, h.rev_host, h.visit_count, h.last_visit_date, "
    2129           0 :           "null, null, null, null, null, ") +
    2130           0 :           tagsSqlFragment + NS_LITERAL_CSTRING(", h.frecency, h.hidden, h.guid, "
    2131             :           "null, null, null "
    2132             :         "FROM moz_places h "
    2133             :         "WHERE h.hidden = 0 "
    2134             :           "AND EXISTS (SELECT id FROM moz_historyvisits WHERE place_id = h.id "
    2135           0 :                        "AND visit_type NOT IN ") +
    2136           0 :                        nsPrintfCString("(0,%d,%d) ",
    2137             :                                        nsINavHistoryService::TRANSITION_EMBED,
    2138           0 :                                        nsINavHistoryService::TRANSITION_FRAMED_LINK) +
    2139           0 :                        NS_LITERAL_CSTRING("LIMIT 1) "
    2140             :           "{QUERY_OPTIONS} "
    2141           0 :         );
    2142             : 
    2143           0 :     queryString.AppendLiteral("ORDER BY ");
    2144           0 :     if (sortingMode == nsINavHistoryQueryOptions::SORT_BY_DATE_DESCENDING)
    2145           0 :       queryString.AppendLiteral("last_visit_date DESC ");
    2146             :     else
    2147           0 :       queryString.AppendLiteral("visit_count DESC ");
    2148             : 
    2149           0 :     queryString.AppendLiteral("LIMIT ");
    2150           0 :     queryString.AppendInt(aOptions->MaxResults());
    2151             : 
    2152           0 :     nsAutoCString additionalQueryOptions;
    2153             : 
    2154           0 :     queryString.ReplaceSubstring("{QUERY_OPTIONS}",
    2155           0 :                                   additionalQueryOptions.get());
    2156           0 :     return NS_OK;
    2157             :   }
    2158             : 
    2159           0 :   nsAutoCString conditions;
    2160           0 :   for (int32_t i = 0; i < aQueries.Count(); i++) {
    2161           0 :     nsCString queryClause;
    2162           0 :     rv = QueryToSelectClause(aQueries[i], aOptions, i, &queryClause);
    2163           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2164           0 :     if (! queryClause.IsEmpty()) {
    2165           0 :       aParamsPresent = true;
    2166           0 :       if (! conditions.IsEmpty()) // exists previous clause: multiple ones are ORed
    2167           0 :         conditions += NS_LITERAL_CSTRING(" OR ");
    2168           0 :       conditions += NS_LITERAL_CSTRING("(") + queryClause +
    2169           0 :         NS_LITERAL_CSTRING(")");
    2170             :     }
    2171             :   }
    2172             : 
    2173             :   // Determine whether we can push maxResults constraints into the queries
    2174             :   // as LIMIT, or if we need to do result count clamping later
    2175             :   // using FilterResultSet()
    2176           0 :   bool useLimitClause = !NeedToFilterResultSet(aQueries, aOptions);
    2177             : 
    2178             :   PlacesSQLQueryBuilder queryStringBuilder(conditions, aOptions,
    2179             :                                            useLimitClause, aAddParams,
    2180           0 :                                            hasSearchTerms);
    2181           0 :   rv = queryStringBuilder.GetQueryString(queryString);
    2182           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2183             : 
    2184           0 :   return NS_OK;
    2185             : }
    2186             : 
    2187             : // nsNavHistory::GetQueryResults
    2188             : //
    2189             : //    Call this to get the results from a complex query. This is used by
    2190             : //    nsNavHistoryQueryResultNode to populate its children. For simple bookmark
    2191             : //    queries, use nsNavBookmarks::QueryFolderChildren.
    2192             : //
    2193             : //    THIS DOES NOT DO SORTING. You will need to sort the container yourself
    2194             : //    when you get the results. This is because sorting depends on tree
    2195             : //    statistics that will be built from the perspective of the tree. See
    2196             : //    nsNavHistoryQueryResultNode::FillChildren
    2197             : //
    2198             : //    FIXME: This only does keyword searching for the first query, and does
    2199             : //    it ANDed with the all the rest of the queries.
    2200             : 
    2201             : nsresult
    2202           0 : nsNavHistory::GetQueryResults(nsNavHistoryQueryResultNode *aResultNode,
    2203             :                               const nsCOMArray<nsNavHistoryQuery>& aQueries,
    2204             :                               nsNavHistoryQueryOptions *aOptions,
    2205             :                               nsCOMArray<nsNavHistoryResultNode>* aResults)
    2206             : {
    2207           0 :   NS_ENSURE_ARG_POINTER(aOptions);
    2208           0 :   NS_ASSERTION(aResults->Count() == 0, "Initial result array must be empty");
    2209           0 :   if (! aQueries.Count())
    2210           0 :     return NS_ERROR_INVALID_ARG;
    2211             : 
    2212           0 :   nsCString queryString;
    2213           0 :   bool paramsPresent = false;
    2214           0 :   nsNavHistory::StringHash addParams(HISTORY_DATE_CONT_LENGTH);
    2215             :   nsresult rv = ConstructQueryString(aQueries, aOptions, queryString,
    2216           0 :                                      paramsPresent, addParams);
    2217           0 :   NS_ENSURE_SUCCESS(rv,rv);
    2218             : 
    2219             :   // create statement
    2220           0 :   nsCOMPtr<mozIStorageStatement> statement = mDB->GetStatement(queryString);
    2221             : #ifdef DEBUG
    2222           0 :   if (!statement) {
    2223           0 :     nsCOMPtr<mozIStorageConnection> conn = mDB->MainConn();
    2224           0 :     if (conn) {
    2225           0 :       nsAutoCString lastErrorString;
    2226           0 :       (void)conn->GetLastErrorString(lastErrorString);
    2227           0 :       int32_t lastError = 0;
    2228           0 :       (void)conn->GetLastError(&lastError);
    2229           0 :       printf("Places failed to create a statement from this query:\n%s\nStorage error (%d): %s\n",
    2230           0 :             queryString.get(), lastError, lastErrorString.get());
    2231             :     }
    2232             :   }
    2233             : #endif
    2234           0 :   NS_ENSURE_STATE(statement);
    2235           0 :   mozStorageStatementScoper scoper(statement);
    2236             : 
    2237           0 :   if (paramsPresent) {
    2238             :     // bind parameters
    2239             :     int32_t i;
    2240           0 :     for (i = 0; i < aQueries.Count(); i++) {
    2241           0 :       rv = BindQueryClauseParameters(statement, i, aQueries[i], aOptions);
    2242           0 :       NS_ENSURE_SUCCESS(rv, rv);
    2243             :     }
    2244             :   }
    2245             : 
    2246           0 :   for (auto iter = addParams.Iter(); !iter.Done(); iter.Next()) {
    2247           0 :     nsresult rv = statement->BindUTF8StringByName(iter.Key(), iter.Data());
    2248           0 :     if (NS_FAILED(rv)) {
    2249           0 :       break;
    2250             :     }
    2251             :   }
    2252             : 
    2253             :   // Optimize the case where there is no need for any post-query filtering.
    2254           0 :   if (NeedToFilterResultSet(aQueries, aOptions)) {
    2255             :     // Generate the top-level results.
    2256           0 :     nsCOMArray<nsNavHistoryResultNode> toplevel;
    2257           0 :     rv = ResultsAsList(statement, aOptions, &toplevel);
    2258           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2259             : 
    2260           0 :     FilterResultSet(aResultNode, toplevel, aResults, aQueries, aOptions);
    2261             :   } else {
    2262           0 :     rv = ResultsAsList(statement, aOptions, aResults);
    2263           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2264             :   }
    2265             : 
    2266           0 :   return NS_OK;
    2267             : }
    2268             : 
    2269             : NS_IMETHODIMP
    2270           3 : nsNavHistory::AddObserver(nsINavHistoryObserver* aObserver, bool aOwnsWeak)
    2271             : {
    2272           3 :   NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread");
    2273           3 :   NS_ENSURE_ARG(aObserver);
    2274             : 
    2275           3 :   if (NS_WARN_IF(!mCanNotify))
    2276           0 :     return NS_ERROR_UNEXPECTED;
    2277             : 
    2278           3 :   return mObservers.AppendWeakElement(aObserver, aOwnsWeak);
    2279             : }
    2280             : 
    2281             : NS_IMETHODIMP
    2282           0 : nsNavHistory::RemoveObserver(nsINavHistoryObserver* aObserver)
    2283             : {
    2284           0 :   NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread");
    2285           0 :   NS_ENSURE_ARG(aObserver);
    2286             : 
    2287           0 :   return mObservers.RemoveWeakElement(aObserver);
    2288             : }
    2289             : 
    2290             : NS_IMETHODIMP
    2291           0 : nsNavHistory::GetObservers(uint32_t* _count,
    2292             :                            nsINavHistoryObserver*** _observers)
    2293             : {
    2294           0 :   NS_ENSURE_ARG_POINTER(_count);
    2295           0 :   NS_ENSURE_ARG_POINTER(_observers);
    2296             : 
    2297           0 :   *_count = 0;
    2298           0 :   *_observers = nullptr;
    2299             : 
    2300             :   // Clear any cached value, cause it's very likely the consumer has made
    2301             :   // changes to history and is now trying to notify them.
    2302           0 :   mDaysOfHistory = -1;
    2303             : 
    2304           0 :   if (!mCanNotify)
    2305           0 :     return NS_OK;
    2306             : 
    2307           0 :   nsCOMArray<nsINavHistoryObserver> observers;
    2308             : 
    2309             :   // First add the category cache observers.
    2310           0 :   mCacheObservers.GetEntries(observers);
    2311             : 
    2312             :   // Then add the other observers.
    2313           0 :   for (uint32_t i = 0; i < mObservers.Length(); ++i) {
    2314           0 :     const nsCOMPtr<nsINavHistoryObserver> &observer = mObservers.ElementAt(i).GetValue();
    2315             :     // Skip nullified weak observers.
    2316           0 :     if (observer)
    2317           0 :       observers.AppendElement(observer);
    2318             :   }
    2319             : 
    2320           0 :   if (observers.Count() == 0)
    2321           0 :     return NS_OK;
    2322             : 
    2323           0 :   *_count = observers.Count();
    2324           0 :   observers.Forget(_observers);
    2325             : 
    2326           0 :   return NS_OK;
    2327             : }
    2328             : 
    2329             : // See RunInBatchMode
    2330             : nsresult
    2331           0 : nsNavHistory::BeginUpdateBatch()
    2332             : {
    2333           0 :   if (mBatchLevel++ == 0) {
    2334           0 :     mBatchDBTransaction = new mozStorageTransaction(mDB->MainConn(), false,
    2335             :                                                     mozIStorageConnection::TRANSACTION_DEFERRED,
    2336           0 :                                                     true);
    2337             : 
    2338           0 :     NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
    2339             :                      nsINavHistoryObserver, OnBeginUpdateBatch());
    2340             :   }
    2341           0 :   return NS_OK;
    2342             : }
    2343             : 
    2344             : // nsNavHistory::EndUpdateBatch
    2345             : nsresult
    2346           0 : nsNavHistory::EndUpdateBatch()
    2347             : {
    2348           0 :   if (--mBatchLevel == 0) {
    2349           0 :     if (mBatchDBTransaction) {
    2350           0 :       DebugOnly<nsresult> rv = mBatchDBTransaction->Commit();
    2351           0 :       NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    2352             :                            "Batch failed to commit transaction");
    2353           0 :       delete mBatchDBTransaction;
    2354           0 :       mBatchDBTransaction = nullptr;
    2355             :     }
    2356             : 
    2357           0 :     NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
    2358             :                      nsINavHistoryObserver, OnEndUpdateBatch());
    2359             :   }
    2360           0 :   return NS_OK;
    2361             : }
    2362             : 
    2363             : NS_IMETHODIMP
    2364           0 : nsNavHistory::RunInBatchMode(nsINavHistoryBatchCallback* aCallback,
    2365             :                              nsISupports* aUserData)
    2366             : {
    2367           0 :   NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread");
    2368           0 :   NS_ENSURE_ARG(aCallback);
    2369             : 
    2370           0 :   UpdateBatchScoper batch(*this);
    2371           0 :   return aCallback->RunBatched(aUserData);
    2372             : }
    2373             : 
    2374             : NS_IMETHODIMP
    2375           0 : nsNavHistory::GetHistoryDisabled(bool *_retval)
    2376             : {
    2377           0 :   NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread");
    2378           0 :   NS_ENSURE_ARG_POINTER(_retval);
    2379             : 
    2380           0 :   *_retval = IsHistoryDisabled();
    2381           0 :   return NS_OK;
    2382             : }
    2383             : 
    2384             : // Browser history *************************************************************
    2385             : 
    2386             : 
    2387             : // nsNavHistory::RemovePagesInternal
    2388             : //
    2389             : //    Deletes a list of placeIds from history.
    2390             : //    This is an internal method used by RemovePages, RemovePagesFromHost and
    2391             : //    RemovePagesByTimeframe.
    2392             : //    Takes a comma separated list of place ids.
    2393             : //    This method does not do any observer notification.
    2394             : 
    2395             : nsresult
    2396           0 : nsNavHistory::RemovePagesInternal(const nsCString& aPlaceIdsQueryString)
    2397             : {
    2398             :   // Return early if there is nothing to delete.
    2399           0 :   if (aPlaceIdsQueryString.IsEmpty())
    2400           0 :     return NS_OK;
    2401             : 
    2402             :   mozStorageTransaction transaction(mDB->MainConn(), false,
    2403             :                                     mozIStorageConnection::TRANSACTION_DEFERRED,
    2404           0 :                                     true);
    2405             : 
    2406             :   // Delete all visits for the specified place ids.
    2407           0 :   nsCOMPtr<mozIStorageConnection> conn = mDB->MainConn();
    2408           0 :   if (!conn) {
    2409           0 :     return NS_ERROR_UNEXPECTED;
    2410             :   }
    2411           0 :   nsresult rv = conn->ExecuteSimpleSQL(
    2412           0 :     NS_LITERAL_CSTRING(
    2413           0 :       "DELETE FROM moz_historyvisits WHERE place_id IN (") +
    2414           0 :         aPlaceIdsQueryString +
    2415           0 :         NS_LITERAL_CSTRING(")")
    2416           0 :   );
    2417           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2418             : 
    2419           0 :   rv = CleanupPlacesOnVisitsDelete(aPlaceIdsQueryString);
    2420           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2421             : 
    2422             :   // Invalidate the cached value for whether there's history or not.
    2423           0 :   mDaysOfHistory = -1;
    2424             : 
    2425           0 :   return transaction.Commit();
    2426             : }
    2427             : 
    2428             : 
    2429             : /**
    2430             :  * Performs cleanup on places that just had all their visits removed, including
    2431             :  * deletion of those places.  This is an internal method used by
    2432             :  * RemovePagesInternal.  This method does not execute in a transaction, so
    2433             :  * callers should make sure they begin one if needed.
    2434             :  *
    2435             :  * @param aPlaceIdsQueryString
    2436             :  *        A comma-separated list of place IDs, each of which just had all its
    2437             :  *        visits removed
    2438             :  */
    2439             : nsresult
    2440           0 : nsNavHistory::CleanupPlacesOnVisitsDelete(const nsCString& aPlaceIdsQueryString)
    2441             : {
    2442             :   // Return early if there is nothing to delete.
    2443           0 :   if (aPlaceIdsQueryString.IsEmpty())
    2444           0 :     return NS_OK;
    2445             : 
    2446             :   // Collect about-to-be-deleted URIs to notify onDeleteURI.
    2447           0 :   nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(NS_LITERAL_CSTRING(
    2448             :     "SELECT h.id, h.url, h.guid, "
    2449             :            "(SUBSTR(h.url, 1, 6) <> 'place:' "
    2450             :            " AND NOT EXISTS (SELECT b.id FROM moz_bookmarks b "
    2451             :                             "WHERE b.fk = h.id LIMIT 1)) as whole_entry "
    2452             :     "FROM moz_places h "
    2453           0 :     "WHERE h.id IN ( ") + aPlaceIdsQueryString + NS_LITERAL_CSTRING(")")
    2454           0 :   );
    2455           0 :   NS_ENSURE_STATE(stmt);
    2456           0 :   mozStorageStatementScoper scoper(stmt);
    2457             : 
    2458           0 :   nsCString filteredPlaceIds;
    2459           0 :   nsCOMArray<nsIURI> URIs;
    2460           0 :   nsTArray<nsCString> GUIDs;
    2461             :   bool hasMore;
    2462           0 :   while (NS_SUCCEEDED(stmt->ExecuteStep(&hasMore)) && hasMore) {
    2463             :     int64_t placeId;
    2464           0 :     nsresult rv = stmt->GetInt64(0, &placeId);
    2465           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2466           0 :     nsAutoCString URLString;
    2467           0 :     rv = stmt->GetUTF8String(1, URLString);
    2468           0 :     nsCString guid;
    2469           0 :     rv = stmt->GetUTF8String(2, guid);
    2470             :     int32_t wholeEntry;
    2471           0 :     rv = stmt->GetInt32(3, &wholeEntry);
    2472           0 :     nsCOMPtr<nsIURI> uri;
    2473           0 :     rv = NS_NewURI(getter_AddRefs(uri), URLString);
    2474           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2475           0 :     if (wholeEntry) {
    2476           0 :       if (!filteredPlaceIds.IsEmpty()) {
    2477           0 :         filteredPlaceIds.Append(',');
    2478             :       }
    2479           0 :       filteredPlaceIds.AppendInt(placeId);
    2480           0 :       URIs.AppendElement(uri.forget());
    2481           0 :       GUIDs.AppendElement(guid);
    2482             :     }
    2483             :     else {
    2484             :       // Notify that we will delete all visits for this page, but not the page
    2485             :       // itself, since it's bookmarked or a place: query.
    2486           0 :       NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
    2487             :                        nsINavHistoryObserver,
    2488             :                        OnDeleteVisits(uri, 0, guid, nsINavHistoryObserver::REASON_DELETED, 0));
    2489             :     }
    2490             :   }
    2491             : 
    2492             :   // if the entry is not bookmarked and is not a place: uri
    2493             :   // then we can remove it from moz_places.
    2494             :   // Note that we do NOT delete favicons. Any unreferenced favicons will be
    2495             :   // deleted next time the browser is shut down.
    2496           0 :   nsCOMPtr<mozIStorageConnection> conn = mDB->MainConn();
    2497           0 :   if (!conn) {
    2498           0 :     return NS_ERROR_UNEXPECTED;
    2499             :   }
    2500           0 :   nsresult rv = conn->ExecuteSimpleSQL(
    2501           0 :     NS_LITERAL_CSTRING(
    2502             :       "DELETE FROM moz_places WHERE id IN ( "
    2503           0 :         ) + filteredPlaceIds + NS_LITERAL_CSTRING(
    2504             :       ") "
    2505             :     )
    2506           0 :   );
    2507           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2508             : 
    2509             :   // Expire orphan icons.
    2510           0 :   rv = conn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2511             :     "DELETE FROM moz_pages_w_icons "
    2512             :     "WHERE page_url_hash NOT IN (SELECT url_hash FROM moz_places) "
    2513           0 :   ));
    2514           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2515           0 :   rv = conn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2516             :     "DELETE FROM moz_icons "
    2517             :     "WHERE root = 0 AND id NOT IN (SELECT icon_id FROM moz_icons_to_pages) "
    2518           0 :   ));
    2519           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2520             : 
    2521             :   // Hosts accumulated during the places delete are updated through a trigger
    2522             :   // (see nsPlacesTriggers.h).
    2523           0 :   rv = conn->ExecuteSimpleSQL(
    2524           0 :     NS_LITERAL_CSTRING("DELETE FROM moz_updatehosts_temp")
    2525           0 :   );
    2526           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2527             : 
    2528             :   // Invalidate frecencies of touched places, since they need recalculation.
    2529           0 :   rv = invalidateFrecencies(aPlaceIdsQueryString);
    2530           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2531             : 
    2532             :   // Finally notify about the removed URIs.
    2533           0 :   for (int32_t i = 0; i < URIs.Count(); ++i) {
    2534           0 :     NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
    2535             :                      nsINavHistoryObserver,
    2536             :                      OnDeleteURI(URIs[i], GUIDs[i], nsINavHistoryObserver::REASON_DELETED));
    2537             :   }
    2538             : 
    2539           0 :   return NS_OK;
    2540             : }
    2541             : 
    2542             : 
    2543             : // nsNavHistory::RemovePages
    2544             : //
    2545             : //    Removes a bunch of uris from history.
    2546             : //    Has better performance than RemovePage when deleting a lot of history.
    2547             : //    We don't do duplicates removal, URIs array should be cleaned-up before.
    2548             : 
    2549             : NS_IMETHODIMP
    2550           0 : nsNavHistory::RemovePages(nsIURI **aURIs, uint32_t aLength)
    2551             : {
    2552           0 :   PLACES_WARN_DEPRECATED();
    2553           0 :   NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread");
    2554           0 :   NS_ENSURE_ARG(aURIs);
    2555             : 
    2556             :   nsresult rv;
    2557             :   // build a list of place ids to delete
    2558           0 :   nsCString deletePlaceIdsQueryString;
    2559           0 :   for (uint32_t i = 0; i < aLength; i++) {
    2560             :     int64_t placeId;
    2561           0 :     nsAutoCString guid;
    2562           0 :     if (!aURIs[i])
    2563           0 :       continue;
    2564           0 :     rv = GetIdForPage(aURIs[i], &placeId, guid);
    2565           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2566           0 :     if (placeId != 0) {
    2567           0 :       if (!deletePlaceIdsQueryString.IsEmpty())
    2568           0 :         deletePlaceIdsQueryString.Append(',');
    2569           0 :       deletePlaceIdsQueryString.AppendInt(placeId);
    2570             :     }
    2571             :   }
    2572             : 
    2573           0 :   UpdateBatchScoper batch(*this); // sends Begin/EndUpdateBatch to observers
    2574             : 
    2575           0 :   rv = RemovePagesInternal(deletePlaceIdsQueryString);
    2576           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2577             : 
    2578             :   // Clear the registered embed visits.
    2579           0 :   clearEmbedVisits();
    2580             : 
    2581           0 :   return NS_OK;
    2582             : }
    2583             : 
    2584             : 
    2585             : // nsNavHistory::RemovePage
    2586             : //
    2587             : //    Removes all visits and the main history entry for the given URI.
    2588             : //    Silently fails if we have no knowledge of the page.
    2589             : 
    2590             : NS_IMETHODIMP
    2591           0 : nsNavHistory::RemovePage(nsIURI *aURI)
    2592             : {
    2593           0 :   PLACES_WARN_DEPRECATED();
    2594           0 :   NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread");
    2595           0 :   NS_ENSURE_ARG(aURI);
    2596             : 
    2597             :   // Build a list of place ids to delete.
    2598             :   int64_t placeId;
    2599           0 :   nsAutoCString guid;
    2600           0 :   nsresult rv = GetIdForPage(aURI, &placeId, guid);
    2601           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2602           0 :   if (placeId == 0) {
    2603           0 :     return NS_OK;
    2604             :   }
    2605           0 :   nsAutoCString deletePlaceIdQueryString;
    2606           0 :   deletePlaceIdQueryString.AppendInt(placeId);
    2607             : 
    2608           0 :   rv = RemovePagesInternal(deletePlaceIdQueryString);
    2609           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2610             : 
    2611             :   // Clear the registered embed visits.
    2612           0 :   clearEmbedVisits();
    2613             : 
    2614           0 :   return NS_OK;
    2615             : }
    2616             : 
    2617             : 
    2618             : // nsNavHistory::RemovePagesFromHost
    2619             : //
    2620             : //    This function will delete all history information about pages from a
    2621             : //    given host. If aEntireDomain is set, we will also delete pages from
    2622             : //    sub hosts (so if we are passed in "microsoft.com" we delete
    2623             : //    "www.microsoft.com", "msdn.microsoft.com", etc.). An empty host name
    2624             : //    means local files and anything else with no host name. You can also pass
    2625             : //    in the localized "(local files)" title given to you from a history query.
    2626             : //
    2627             : //    Silently fails if we have no knowledge of the host.
    2628             : //
    2629             : //    This sends onBeginUpdateBatch/onEndUpdateBatch to observers
    2630             : 
    2631             : NS_IMETHODIMP
    2632           0 : nsNavHistory::RemovePagesFromHost(const nsACString& aHost, bool aEntireDomain)
    2633             : {
    2634           0 :   NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread");
    2635             : 
    2636             :   nsresult rv;
    2637             :   // Local files don't have any host name. We don't want to delete all files in
    2638             :   // history when we get passed an empty string, so force to exact match
    2639           0 :   if (aHost.IsEmpty())
    2640           0 :     aEntireDomain = false;
    2641             : 
    2642             :   // translate "(local files)" to an empty host name
    2643             :   // be sure to use the TitleForDomain to get the localized name
    2644           0 :   nsCString localFiles;
    2645           0 :   TitleForDomain(EmptyCString(), localFiles);
    2646           0 :   nsAutoString host16;
    2647           0 :   if (!aHost.Equals(localFiles))
    2648           0 :     CopyUTF8toUTF16(aHost, host16);
    2649             : 
    2650             :   // see BindQueryClauseParameters for how this host selection works
    2651           0 :   nsAutoString revHostDot;
    2652           0 :   GetReversedHostname(host16, revHostDot);
    2653           0 :   NS_ASSERTION(revHostDot[revHostDot.Length() - 1] == '.', "Invalid rev. host");
    2654           0 :   nsAutoString revHostSlash(revHostDot);
    2655           0 :   revHostSlash.Truncate(revHostSlash.Length() - 1);
    2656           0 :   revHostSlash.Append('/');
    2657             : 
    2658             :   // build condition string based on host selection type
    2659           0 :   nsAutoCString conditionString;
    2660           0 :   if (aEntireDomain)
    2661           0 :     conditionString.AssignLiteral("rev_host >= ?1 AND rev_host < ?2 ");
    2662             :   else
    2663           0 :     conditionString.AssignLiteral("rev_host = ?1 ");
    2664             : 
    2665             :   // create statement depending on delete type
    2666           0 :   nsCOMPtr<mozIStorageStatement> statement = mDB->GetStatement(
    2667           0 :     NS_LITERAL_CSTRING("SELECT id FROM moz_places WHERE ") + conditionString
    2668           0 :   );
    2669           0 :   NS_ENSURE_STATE(statement);
    2670           0 :   mozStorageStatementScoper scoper(statement);
    2671             : 
    2672           0 :   rv = statement->BindStringByIndex(0, revHostDot);
    2673           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2674           0 :   if (aEntireDomain) {
    2675           0 :     rv = statement->BindStringByIndex(1, revHostSlash);
    2676           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2677             :   }
    2678             : 
    2679           0 :   nsCString hostPlaceIds;
    2680           0 :   bool hasMore = false;
    2681           0 :   while (NS_SUCCEEDED(statement->ExecuteStep(&hasMore)) && hasMore) {
    2682           0 :     if (!hostPlaceIds.IsEmpty())
    2683           0 :       hostPlaceIds.Append(',');
    2684             :     int64_t placeId;
    2685           0 :     rv = statement->GetInt64(0, &placeId);
    2686           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2687           0 :     hostPlaceIds.AppendInt(placeId);
    2688             :   }
    2689             : 
    2690             :   // force a full refresh calling onEndUpdateBatch (will call Refresh())
    2691           0 :   UpdateBatchScoper batch(*this); // sends Begin/EndUpdateBatch to observers
    2692             : 
    2693           0 :   rv = RemovePagesInternal(hostPlaceIds);
    2694           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2695             : 
    2696             :   // Clear the registered embed visits.
    2697           0 :   clearEmbedVisits();
    2698             : 
    2699           0 :   return NS_OK;
    2700             : }
    2701             : 
    2702             : 
    2703             : // nsNavHistory::RemovePagesByTimeframe
    2704             : //
    2705             : //    This function will delete all history information about
    2706             : //    pages for a given timeframe.
    2707             : //    Limits are included: aBeginTime <= timeframe <= aEndTime
    2708             : //
    2709             : //    This method sends onBeginUpdateBatch/onEndUpdateBatch to observers
    2710             : 
    2711             : NS_IMETHODIMP
    2712           0 : nsNavHistory::RemovePagesByTimeframe(PRTime aBeginTime, PRTime aEndTime)
    2713             : {
    2714           0 :   NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread");
    2715             : 
    2716             :   nsresult rv;
    2717             :   // build a list of place ids to delete
    2718           0 :   nsCString deletePlaceIdsQueryString;
    2719             : 
    2720             :   // we only need to know if a place has a visit into the given timeframe
    2721             :   // this query is faster than actually selecting in moz_historyvisits
    2722           0 :   nsCOMPtr<mozIStorageStatement> selectByTime = mDB->GetStatement(
    2723             :     "SELECT h.id FROM moz_places h WHERE "
    2724             :       "EXISTS "
    2725             :         "(SELECT id FROM moz_historyvisits v WHERE v.place_id = h.id "
    2726             :           "AND v.visit_date >= :from_date AND v.visit_date <= :to_date LIMIT 1)"
    2727           0 :   );
    2728           0 :   NS_ENSURE_STATE(selectByTime);
    2729           0 :   mozStorageStatementScoper selectByTimeScoper(selectByTime);
    2730             : 
    2731           0 :   rv = selectByTime->BindInt64ByName(NS_LITERAL_CSTRING("from_date"), aBeginTime);
    2732           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2733           0 :   rv = selectByTime->BindInt64ByName(NS_LITERAL_CSTRING("to_date"), aEndTime);
    2734           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2735             : 
    2736           0 :   bool hasMore = false;
    2737           0 :   while (NS_SUCCEEDED(selectByTime->ExecuteStep(&hasMore)) && hasMore) {
    2738             :     int64_t placeId;
    2739           0 :     rv = selectByTime->GetInt64(0, &placeId);
    2740           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2741           0 :     if (placeId != 0) {
    2742           0 :       if (!deletePlaceIdsQueryString.IsEmpty())
    2743           0 :         deletePlaceIdsQueryString.Append(',');
    2744           0 :       deletePlaceIdsQueryString.AppendInt(placeId);
    2745             :     }
    2746             :   }
    2747             : 
    2748             :   // force a full refresh calling onEndUpdateBatch (will call Refresh())
    2749           0 :   UpdateBatchScoper batch(*this); // sends Begin/EndUpdateBatch to observers
    2750             : 
    2751           0 :   rv = RemovePagesInternal(deletePlaceIdsQueryString);
    2752           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2753             : 
    2754             :   // Clear the registered embed visits.
    2755           0 :   clearEmbedVisits();
    2756             : 
    2757           0 :   return NS_OK;
    2758             : }
    2759             : 
    2760             : 
    2761             : // Call this method before visiting a URL in order to help determine the
    2762             : // transition type of the visit.
    2763             : //
    2764             : // @see MarkPageAsFollowedBookmark
    2765             : 
    2766             : NS_IMETHODIMP
    2767           0 : nsNavHistory::MarkPageAsTyped(nsIURI *aURI)
    2768             : {
    2769           0 :   NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread");
    2770           0 :   NS_ENSURE_ARG(aURI);
    2771             : 
    2772             :   // don't add when history is disabled
    2773           0 :   if (IsHistoryDisabled())
    2774           0 :     return NS_OK;
    2775             : 
    2776           0 :   nsAutoCString uriString;
    2777           0 :   nsresult rv = aURI->GetSpec(uriString);
    2778           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2779             : 
    2780           0 :   mRecentTyped.Put(uriString, GetNow());
    2781             : 
    2782           0 :   if (mRecentTyped.Count() > RECENT_EVENT_QUEUE_MAX_LENGTH)
    2783           0 :     ExpireNonrecentEvents(&mRecentTyped);
    2784             : 
    2785           0 :   return NS_OK;
    2786             : }
    2787             : 
    2788             : 
    2789             : // Call this method before visiting a URL in order to help determine the
    2790             : // transition type of the visit.
    2791             : //
    2792             : // @see MarkPageAsTyped
    2793             : 
    2794             : NS_IMETHODIMP
    2795           0 : nsNavHistory::MarkPageAsFollowedLink(nsIURI *aURI)
    2796             : {
    2797           0 :   NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread");
    2798           0 :   NS_ENSURE_ARG(aURI);
    2799             : 
    2800             :   // don't add when history is disabled
    2801           0 :   if (IsHistoryDisabled())
    2802           0 :     return NS_OK;
    2803             : 
    2804           0 :   nsAutoCString uriString;
    2805           0 :   nsresult rv = aURI->GetSpec(uriString);
    2806           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2807             : 
    2808           0 :   mRecentLink.Put(uriString, GetNow());
    2809             : 
    2810           0 :   if (mRecentLink.Count() > RECENT_EVENT_QUEUE_MAX_LENGTH)
    2811           0 :     ExpireNonrecentEvents(&mRecentLink);
    2812             : 
    2813           0 :   return NS_OK;
    2814             : }
    2815             : 
    2816             : 
    2817             : NS_IMETHODIMP
    2818           0 : nsNavHistory::GetPageTitle(nsIURI* aURI, nsAString& aTitle)
    2819             : {
    2820           0 :   PLACES_WARN_DEPRECATED();
    2821             : 
    2822           0 :   NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread");
    2823           0 :   NS_ENSURE_ARG(aURI);
    2824             : 
    2825           0 :   aTitle.Truncate(0);
    2826             : 
    2827           0 :   nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
    2828             :     "SELECT id, url, title, rev_host, visit_count, guid "
    2829             :     "FROM moz_places "
    2830             :     "WHERE url_hash = hash(:page_url) AND url = :page_url "
    2831           0 :   );
    2832           0 :   NS_ENSURE_STATE(stmt);
    2833           0 :   mozStorageStatementScoper scoper(stmt);
    2834             : 
    2835           0 :   nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), aURI);
    2836           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2837             : 
    2838           0 :   bool hasResults = false;
    2839           0 :   rv = stmt->ExecuteStep(&hasResults);
    2840           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2841             : 
    2842           0 :   if (!hasResults) {
    2843           0 :     aTitle.SetIsVoid(true);
    2844           0 :     return NS_OK; // Not found, return a void string.
    2845             :   }
    2846             : 
    2847           0 :   rv = stmt->GetString(2, aTitle);
    2848           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2849             : 
    2850           0 :   return NS_OK;
    2851             : }
    2852             : 
    2853             : 
    2854             : ////////////////////////////////////////////////////////////////////////////////
    2855             : //// mozIStorageVacuumParticipant
    2856             : 
    2857             : NS_IMETHODIMP
    2858           0 : nsNavHistory::GetDatabaseConnection(mozIStorageConnection** _DBConnection)
    2859             : {
    2860           0 :   return GetDBConnection(_DBConnection);
    2861             : }
    2862             : 
    2863             : 
    2864             : NS_IMETHODIMP
    2865           0 : nsNavHistory::GetExpectedDatabasePageSize(int32_t* _expectedPageSize)
    2866             : {
    2867           0 :   NS_ENSURE_STATE(mDB);
    2868           0 :   NS_ENSURE_STATE(mDB->MainConn());
    2869           0 :   return mDB->MainConn()->GetDefaultPageSize(_expectedPageSize);
    2870             : }
    2871             : 
    2872             : 
    2873             : NS_IMETHODIMP
    2874           0 : nsNavHistory::OnBeginVacuum(bool* _vacuumGranted)
    2875             : {
    2876             :   // TODO: Check if we have to deny the vacuum in some heavy-load case.
    2877             :   // We could maybe want to do that during batches?
    2878           0 :   *_vacuumGranted = true;
    2879           0 :   return NS_OK;
    2880             : }
    2881             : 
    2882             : 
    2883             : NS_IMETHODIMP
    2884           0 : nsNavHistory::OnEndVacuum(bool aSucceeded)
    2885             : {
    2886           0 :   NS_WARNING_ASSERTION(aSucceeded, "Places.sqlite vacuum failed.");
    2887           0 :   return NS_OK;
    2888             : }
    2889             : 
    2890             : 
    2891             : ////////////////////////////////////////////////////////////////////////////////
    2892             : //// nsPIPlacesDatabase
    2893             : 
    2894             : NS_IMETHODIMP
    2895           3 : nsNavHistory::GetDBConnection(mozIStorageConnection **_DBConnection)
    2896             : {
    2897           3 :   NS_ENSURE_ARG_POINTER(_DBConnection);
    2898           6 :   RefPtr<mozIStorageConnection> connection = mDB->MainConn();
    2899           3 :   connection.forget(_DBConnection);
    2900             : 
    2901           3 :   return NS_OK;
    2902             : }
    2903             : 
    2904             : NS_IMETHODIMP
    2905           0 : nsNavHistory::GetShutdownClient(nsIAsyncShutdownClient **_shutdownClient)
    2906             : {
    2907           0 :   NS_ENSURE_ARG_POINTER(_shutdownClient);
    2908           0 :   RefPtr<nsIAsyncShutdownClient> client = mDB->GetClientsShutdown();
    2909           0 :   MOZ_ASSERT(client);
    2910           0 :   client.forget(_shutdownClient);
    2911             : 
    2912           0 :   return NS_OK;
    2913             : }
    2914             : 
    2915             : NS_IMETHODIMP
    2916           0 : nsNavHistory::AsyncExecuteLegacyQueries(nsINavHistoryQuery** aQueries,
    2917             :                                         uint32_t aQueryCount,
    2918             :                                         nsINavHistoryQueryOptions* aOptions,
    2919             :                                         mozIStorageStatementCallback* aCallback,
    2920             :                                         mozIStoragePendingStatement** _stmt)
    2921             : {
    2922           0 :   NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread");
    2923           0 :   NS_ENSURE_ARG(aQueries);
    2924           0 :   NS_ENSURE_ARG(aOptions);
    2925           0 :   NS_ENSURE_ARG(aCallback);
    2926           0 :   NS_ENSURE_ARG_POINTER(_stmt);
    2927             : 
    2928           0 :   nsCOMArray<nsNavHistoryQuery> queries;
    2929           0 :   for (uint32_t i = 0; i < aQueryCount; i ++) {
    2930           0 :     nsCOMPtr<nsNavHistoryQuery> query = do_QueryInterface(aQueries[i]);
    2931           0 :     NS_ENSURE_STATE(query);
    2932           0 :     queries.AppendElement(query.forget());
    2933             :   }
    2934           0 :   NS_ENSURE_ARG_MIN(queries.Count(), 1);
    2935             : 
    2936           0 :   nsCOMPtr<nsNavHistoryQueryOptions> options = do_QueryInterface(aOptions);
    2937           0 :   NS_ENSURE_ARG(options);
    2938             : 
    2939           0 :   nsCString queryString;
    2940           0 :   bool paramsPresent = false;
    2941           0 :   nsNavHistory::StringHash addParams(HISTORY_DATE_CONT_LENGTH);
    2942           0 :   nsresult rv = ConstructQueryString(queries, options, queryString,
    2943           0 :                                      paramsPresent, addParams);
    2944           0 :   NS_ENSURE_SUCCESS(rv,rv);
    2945             : 
    2946             :   nsCOMPtr<mozIStorageAsyncStatement> statement =
    2947           0 :     mDB->GetAsyncStatement(queryString);
    2948           0 :   NS_ENSURE_STATE(statement);
    2949             : 
    2950             : #ifdef DEBUG
    2951           0 :   if (NS_FAILED(rv)) {
    2952           0 :     nsCOMPtr<mozIStorageConnection> conn = mDB->MainConn();
    2953           0 :     if (conn) {
    2954           0 :       nsAutoCString lastErrorString;
    2955           0 :       (void)mDB->MainConn()->GetLastErrorString(lastErrorString);
    2956           0 :       int32_t lastError = 0;
    2957           0 :       (void)mDB->MainConn()->GetLastError(&lastError);
    2958           0 :       printf("Places failed to create a statement from this query:\n%s\nStorage error (%d): %s\n",
    2959           0 :             queryString.get(), lastError, lastErrorString.get());
    2960             :     }
    2961             :   }
    2962             : #endif
    2963           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2964             : 
    2965           0 :   if (paramsPresent) {
    2966             :     // bind parameters
    2967             :     int32_t i;
    2968           0 :     for (i = 0; i < queries.Count(); i++) {
    2969           0 :       rv = BindQueryClauseParameters(statement, i, queries[i], options);
    2970           0 :       NS_ENSURE_SUCCESS(rv, rv);
    2971             :     }
    2972             :   }
    2973             : 
    2974           0 :   for (auto iter = addParams.Iter(); !iter.Done(); iter.Next()) {
    2975           0 :     nsresult rv = statement->BindUTF8StringByName(iter.Key(), iter.Data());
    2976           0 :     if (NS_FAILED(rv)) {
    2977           0 :       break;
    2978             :     }
    2979             :   }
    2980             : 
    2981           0 :   rv = statement->ExecuteAsync(aCallback, _stmt);
    2982           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2983             : 
    2984           0 :   return NS_OK;
    2985             : }
    2986             : 
    2987             : 
    2988             : nsresult
    2989           0 : nsNavHistory::NotifyOnPageExpired(nsIURI *aURI, PRTime aVisitTime,
    2990             :                                   bool aWholeEntry, const nsACString& aGUID,
    2991             :                                   uint16_t aReason, uint32_t aTransitionType)
    2992             : {
    2993             :   // Invalidate the cached value for whether there's history or not.
    2994           0 :   mDaysOfHistory = -1;
    2995             : 
    2996           0 :   MOZ_ASSERT(!aGUID.IsEmpty());
    2997           0 :   if (aWholeEntry) {
    2998             :     // Notify our observers that the page has been removed.
    2999           0 :     NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
    3000             :                      nsINavHistoryObserver, OnDeleteURI(aURI, aGUID, aReason));
    3001             :   }
    3002             :   else {
    3003             :     // Notify our observers that some visits for the page have been removed.
    3004           0 :     NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
    3005             :                      nsINavHistoryObserver,
    3006             :                      OnDeleteVisits(aURI, aVisitTime, aGUID, aReason,
    3007             :                                     aTransitionType));
    3008             :   }
    3009             : 
    3010           0 :   return NS_OK;
    3011             : }
    3012             : 
    3013             : ////////////////////////////////////////////////////////////////////////////////
    3014             : //// nsIObserver
    3015             : 
    3016             : NS_IMETHODIMP
    3017           0 : nsNavHistory::Observe(nsISupports *aSubject, const char *aTopic,
    3018             :                     const char16_t *aData)
    3019             : {
    3020           0 :   NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread");
    3021           0 :   if (strcmp(aTopic, TOPIC_PROFILE_TEARDOWN) == 0 ||
    3022           0 :       strcmp(aTopic, TOPIC_PROFILE_CHANGE) == 0 ||
    3023           0 :       strcmp(aTopic, TOPIC_SIMULATE_PLACES_SHUTDOWN) == 0) {
    3024             :     // These notifications are used by tests to simulate a Places shutdown.
    3025             :     // They should just be forwarded to the Database handle.
    3026           0 :     mDB->Observe(aSubject, aTopic, aData);
    3027             :   }
    3028             : 
    3029           0 :   else if (strcmp(aTopic, TOPIC_PLACES_CONNECTION_CLOSED) == 0) {
    3030             :     // Don't even try to notify observers from this point on, the category
    3031             :     // cache would init services that could try to use our APIs.
    3032           0 :     mCanNotify = false;
    3033           0 :     mObservers.Clear();
    3034             :   }
    3035             : 
    3036             : #ifdef MOZ_XUL
    3037           0 :   else if (strcmp(aTopic, TOPIC_AUTOCOMPLETE_FEEDBACK_INCOMING) == 0) {
    3038           0 :     nsCOMPtr<nsIAutoCompleteInput> input = do_QueryInterface(aSubject);
    3039           0 :     if (!input)
    3040           0 :       return NS_OK;
    3041             : 
    3042             :     // If the source is a private window, don't add any input history.
    3043             :     bool isPrivate;
    3044           0 :     nsresult rv = input->GetInPrivateContext(&isPrivate);
    3045           0 :     NS_ENSURE_SUCCESS(rv, rv);
    3046           0 :     if (isPrivate)
    3047           0 :       return NS_OK;
    3048             : 
    3049           0 :     nsCOMPtr<nsIAutoCompletePopup> popup;
    3050           0 :     input->GetPopup(getter_AddRefs(popup));
    3051           0 :     if (!popup)
    3052           0 :       return NS_OK;
    3053             : 
    3054           0 :     nsCOMPtr<nsIAutoCompleteController> controller;
    3055           0 :     input->GetController(getter_AddRefs(controller));
    3056           0 :     if (!controller)
    3057           0 :       return NS_OK;
    3058             : 
    3059             :     // Don't bother if the popup is closed
    3060             :     bool open;
    3061           0 :     rv = popup->GetPopupOpen(&open);
    3062           0 :     NS_ENSURE_SUCCESS(rv, rv);
    3063           0 :     if (!open)
    3064           0 :       return NS_OK;
    3065             : 
    3066             :     // Ignore if nothing selected from the popup
    3067             :     int32_t selectedIndex;
    3068           0 :     rv = popup->GetSelectedIndex(&selectedIndex);
    3069           0 :     NS_ENSURE_SUCCESS(rv, rv);
    3070           0 :     if (selectedIndex == -1)
    3071           0 :       return NS_OK;
    3072             : 
    3073           0 :     rv = AutoCompleteFeedback(selectedIndex, controller);
    3074           0 :     NS_ENSURE_SUCCESS(rv, rv);
    3075             :   }
    3076             : 
    3077             : #endif
    3078           0 :   else if (strcmp(aTopic, TOPIC_PREF_CHANGED) == 0) {
    3079           0 :     LoadPrefs();
    3080             :   }
    3081             : 
    3082           0 :   else if (strcmp(aTopic, TOPIC_IDLE_DAILY) == 0) {
    3083           0 :     (void)DecayFrecency();
    3084             :   }
    3085             : 
    3086           0 :   return NS_OK;
    3087             : }
    3088             : 
    3089             : 
    3090             : namespace {
    3091             : 
    3092           0 : class DecayFrecencyCallback : public AsyncStatementTelemetryTimer
    3093             : {
    3094             : public:
    3095           0 :   DecayFrecencyCallback()
    3096           0 :     : AsyncStatementTelemetryTimer(Telemetry::PLACES_IDLE_FRECENCY_DECAY_TIME_MS)
    3097             :   {
    3098           0 :   }
    3099             : 
    3100           0 :   NS_IMETHOD HandleCompletion(uint16_t aReason)
    3101             :   {
    3102           0 :     (void)AsyncStatementTelemetryTimer::HandleCompletion(aReason);
    3103           0 :     if (aReason == REASON_FINISHED) {
    3104           0 :       nsNavHistory *navHistory = nsNavHistory::GetHistoryService();
    3105           0 :       NS_ENSURE_STATE(navHistory);
    3106           0 :       navHistory->NotifyManyFrecenciesChanged();
    3107             :     }
    3108           0 :     return NS_OK;
    3109             :   }
    3110             : };
    3111             : 
    3112             : } // namespace
    3113             : 
    3114             : nsresult
    3115           0 : nsNavHistory::DecayFrecency()
    3116             : {
    3117           0 :   nsresult rv = FixInvalidFrecencies();
    3118           0 :   NS_ENSURE_SUCCESS(rv, rv);
    3119             : 
    3120           0 :   float decayRate = Preferences::GetFloat(PREF_FREC_DECAY_RATE, PREF_FREC_DECAY_RATE_DEF);
    3121             : 
    3122             :   // Globally decay places frecency rankings to estimate reduced frecency
    3123             :   // values of pages that haven't been visited for a while, i.e., they do
    3124             :   // not get an updated frecency.  A scaling factor of .975 results in .5 the
    3125             :   // original value after 28 days.
    3126             :   // When changing the scaling factor, ensure that the barrier in
    3127             :   // moz_places_afterupdate_frecency_trigger still ignores these changes.
    3128           0 :   nsCOMPtr<mozIStorageAsyncStatement> decayFrecency = mDB->GetAsyncStatement(
    3129             :     "UPDATE moz_places SET frecency = ROUND(frecency * :decay_rate) "
    3130             :     "WHERE frecency > 0"
    3131           0 :   );
    3132           0 :   NS_ENSURE_STATE(decayFrecency);
    3133             : 
    3134           0 :   rv = decayFrecency->BindDoubleByName(NS_LITERAL_CSTRING("decay_rate"),
    3135           0 :                                        static_cast<double>(decayRate));
    3136           0 :   NS_ENSURE_SUCCESS(rv, rv);
    3137             : 
    3138             :   // Decay potentially unused adaptive entries (e.g. those that are at 1)
    3139             :   // to allow better chances for new entries that will start at 1.
    3140           0 :   nsCOMPtr<mozIStorageAsyncStatement> decayAdaptive = mDB->GetAsyncStatement(
    3141             :     "UPDATE moz_inputhistory SET use_count = use_count * .975"
    3142           0 :   );
    3143           0 :   NS_ENSURE_STATE(decayAdaptive);
    3144             : 
    3145             :   // Delete any adaptive entries that won't help in ordering anymore.
    3146           0 :   nsCOMPtr<mozIStorageAsyncStatement> deleteAdaptive = mDB->GetAsyncStatement(
    3147             :     "DELETE FROM moz_inputhistory WHERE use_count < .01"
    3148           0 :   );
    3149           0 :   NS_ENSURE_STATE(deleteAdaptive);
    3150             : 
    3151           0 :   nsCOMPtr<mozIStorageConnection> conn = mDB->MainConn();
    3152           0 :   if (!conn) {
    3153           0 :     return NS_ERROR_UNEXPECTED;
    3154             :   }
    3155             :   mozIStorageBaseStatement *stmts[] = {
    3156           0 :     decayFrecency.get(),
    3157           0 :     decayAdaptive.get(),
    3158           0 :     deleteAdaptive.get()
    3159           0 :   };
    3160           0 :   nsCOMPtr<mozIStoragePendingStatement> ps;
    3161           0 :   RefPtr<DecayFrecencyCallback> cb = new DecayFrecencyCallback();
    3162           0 :   rv = conn->ExecuteAsync(stmts, ArrayLength(stmts), cb,
    3163           0 :                                      getter_AddRefs(ps));
    3164           0 :   NS_ENSURE_SUCCESS(rv, rv);
    3165             : 
    3166           0 :   return NS_OK;
    3167             : }
    3168             : 
    3169             : 
    3170             : // Query stuff *****************************************************************
    3171             : 
    3172             : // Helper class for QueryToSelectClause
    3173             : //
    3174             : // This class helps to build part of the WHERE clause. It supports
    3175             : // multiple queries by appending the query index to the parameter name.
    3176             : // For the query with index 0 the parameter name is not altered what
    3177             : // allows using this parameter in other situations (see SelectAsSite).
    3178             : 
    3179           0 : class ConditionBuilder
    3180             : {
    3181             : public:
    3182             : 
    3183           0 :   explicit ConditionBuilder(int32_t aQueryIndex): mQueryIndex(aQueryIndex)
    3184           0 :   { }
    3185             : 
    3186           0 :   ConditionBuilder& Condition(const char* aStr)
    3187             :   {
    3188           0 :     if (!mClause.IsEmpty())
    3189           0 :       mClause.AppendLiteral(" AND ");
    3190           0 :     Str(aStr);
    3191           0 :     return *this;
    3192             :   }
    3193             : 
    3194           0 :   ConditionBuilder& Str(const char* aStr)
    3195             :   {
    3196           0 :     mClause.Append(' ');
    3197           0 :     mClause.Append(aStr);
    3198           0 :     mClause.Append(' ');
    3199           0 :     return *this;
    3200             :   }
    3201             : 
    3202           0 :   ConditionBuilder& Param(const char* aParam)
    3203             :   {
    3204           0 :     mClause.Append(' ');
    3205           0 :     if (!mQueryIndex)
    3206           0 :       mClause.Append(aParam);
    3207             :     else
    3208           0 :       mClause += nsPrintfCString("%s%d", aParam, mQueryIndex);
    3209             : 
    3210           0 :     mClause.Append(' ');
    3211           0 :     return *this;
    3212             :   }
    3213             : 
    3214           0 :   void GetClauseString(nsCString& aResult)
    3215             :   {
    3216           0 :     aResult = mClause;
    3217           0 :   }
    3218             : 
    3219             : private:
    3220             : 
    3221             :   int32_t mQueryIndex;
    3222             :   nsCString mClause;
    3223             : };
    3224             : 
    3225             : 
    3226             : // nsNavHistory::QueryToSelectClause
    3227             : //
    3228             : //    THE BEHAVIOR SHOULD BE IN SYNC WITH BindQueryClauseParameters
    3229             : //
    3230             : //    I don't check return values from the query object getters because there's
    3231             : //    no way for those to fail.
    3232             : 
    3233             : nsresult
    3234           0 : nsNavHistory::QueryToSelectClause(nsNavHistoryQuery* aQuery, // const
    3235             :                                   nsNavHistoryQueryOptions* aOptions,
    3236             :                                   int32_t aQueryIndex,
    3237             :                                   nsCString* aClause)
    3238             : {
    3239             :   bool hasIt;
    3240           0 :   bool excludeQueries = aOptions->ExcludeQueries();
    3241             : 
    3242           0 :   ConditionBuilder clause(aQueryIndex);
    3243             : 
    3244           0 :   if ((NS_SUCCEEDED(aQuery->GetHasBeginTime(&hasIt)) && hasIt) ||
    3245           0 :     (NS_SUCCEEDED(aQuery->GetHasEndTime(&hasIt)) && hasIt)) {
    3246             :     clause.Condition("EXISTS (SELECT 1 FROM moz_historyvisits "
    3247           0 :                               "WHERE place_id = h.id");
    3248             :     // begin time
    3249           0 :     if (NS_SUCCEEDED(aQuery->GetHasBeginTime(&hasIt)) && hasIt)
    3250           0 :       clause.Condition("visit_date >=").Param(":begin_time");
    3251             :     // end time
    3252           0 :     if (NS_SUCCEEDED(aQuery->GetHasEndTime(&hasIt)) && hasIt)
    3253           0 :       clause.Condition("visit_date <=").Param(":end_time");
    3254           0 :     clause.Str(" LIMIT 1)");
    3255             :   }
    3256             : 
    3257             :   // search terms
    3258             :   bool hasSearchTerms;
    3259             :   int32_t searchBehavior = mozIPlacesAutoComplete::BEHAVIOR_HISTORY |
    3260           0 :                            mozIPlacesAutoComplete::BEHAVIOR_BOOKMARK;
    3261           0 :   if (NS_SUCCEEDED(aQuery->GetHasSearchTerms(&hasSearchTerms)) && hasSearchTerms) {
    3262             :     // Re-use the autocomplete_match function.  Setting the behavior to match
    3263             :     // history or typed history or bookmarks or open pages will match almost
    3264             :     // everything.
    3265           0 :     clause.Condition("AUTOCOMPLETE_MATCH(").Param(":search_string")
    3266           0 :           .Str(", h.url, page_title, tags, ")
    3267           0 :           .Str(nsPrintfCString("1, 1, 1, 1, %d, %d)",
    3268             :                                mozIPlacesAutoComplete::MATCH_ANYWHERE_UNMODIFIED,
    3269           0 :                                searchBehavior).get());
    3270             :     // Serching by terms implicitly exclude queries.
    3271           0 :     excludeQueries = true;
    3272             :   }
    3273             : 
    3274             :   // min and max visit count
    3275           0 :   if (aQuery->MinVisits() >= 0)
    3276           0 :     clause.Condition("h.visit_count >=").Param(":min_visits");
    3277             : 
    3278           0 :   if (aQuery->MaxVisits() >= 0)
    3279           0 :     clause.Condition("h.visit_count <=").Param(":max_visits");
    3280             : 
    3281             :   // only bookmarked, has no affect on bookmarks-only queries
    3282           0 :   if (aOptions->QueryType() != nsINavHistoryQueryOptions::QUERY_TYPE_BOOKMARKS &&
    3283           0 :       aQuery->OnlyBookmarked())
    3284           0 :     clause.Condition("EXISTS (SELECT b.fk FROM moz_bookmarks b WHERE b.type = ")
    3285           0 :           .Str(nsPrintfCString("%d", nsNavBookmarks::TYPE_BOOKMARK).get())
    3286           0 :           .Str("AND b.fk = h.id)");
    3287             : 
    3288             :   // domain
    3289           0 :   if (NS_SUCCEEDED(aQuery->GetHasDomain(&hasIt)) && hasIt) {
    3290           0 :     bool domainIsHost = false;
    3291           0 :     aQuery->GetDomainIsHost(&domainIsHost);
    3292           0 :     if (domainIsHost)
    3293           0 :       clause.Condition("h.rev_host =").Param(":domain_lower");
    3294             :     else
    3295             :       // see domain setting in BindQueryClauseParameters for why we do this
    3296           0 :       clause.Condition("h.rev_host >=").Param(":domain_lower")
    3297           0 :             .Condition("h.rev_host <").Param(":domain_upper");
    3298             :   }
    3299             : 
    3300             :   // URI
    3301           0 :   if (NS_SUCCEEDED(aQuery->GetHasUri(&hasIt)) && hasIt) {
    3302           0 :     clause.Condition("h.url_hash = hash(").Param(":uri").Str(")")
    3303           0 :           .Condition("h.url =").Param(":uri");
    3304             :   }
    3305             : 
    3306             :   // annotation
    3307           0 :   aQuery->GetHasAnnotation(&hasIt);
    3308           0 :   if (hasIt) {
    3309           0 :     clause.Condition("");
    3310           0 :     if (aQuery->AnnotationIsNot())
    3311           0 :       clause.Str("NOT");
    3312             :     clause.Str(
    3313             :       "EXISTS "
    3314             :         "(SELECT h.id "
    3315             :          "FROM moz_annos anno "
    3316             :          "JOIN moz_anno_attributes annoname "
    3317             :            "ON anno.anno_attribute_id = annoname.id "
    3318             :          "WHERE anno.place_id = h.id "
    3319           0 :            "AND annoname.name = ").Param(":anno").Str(")");
    3320             :     // annotation-based queries don't get the common conditions, so you get
    3321             :     // all URLs with that annotation
    3322             :   }
    3323             : 
    3324             :   // tags
    3325           0 :   const nsTArray<nsString> &tags = aQuery->Tags();
    3326           0 :   if (tags.Length() > 0) {
    3327           0 :     clause.Condition("h.id");
    3328           0 :     if (aQuery->TagsAreNot())
    3329           0 :       clause.Str("NOT");
    3330             :     clause.Str(
    3331             :       "IN "
    3332             :         "(SELECT bms.fk "
    3333             :          "FROM moz_bookmarks bms "
    3334             :          "JOIN moz_bookmarks tags ON bms.parent = tags.id "
    3335           0 :          "WHERE tags.parent =").
    3336           0 :            Param(":tags_folder").
    3337           0 :            Str("AND tags.title IN (");
    3338           0 :     for (uint32_t i = 0; i < tags.Length(); ++i) {
    3339           0 :       nsPrintfCString param(":tag%d_", i);
    3340           0 :       clause.Param(param.get());
    3341           0 :       if (i < tags.Length() - 1)
    3342           0 :         clause.Str(",");
    3343             :     }
    3344           0 :     clause.Str(")");
    3345           0 :     if (!aQuery->TagsAreNot())
    3346           0 :       clause.Str("GROUP BY bms.fk HAVING count(*) >=").Param(":tag_count");
    3347           0 :     clause.Str(")");
    3348             :   }
    3349             : 
    3350             :   // transitions
    3351           0 :   const nsTArray<uint32_t>& transitions = aQuery->Transitions();
    3352           0 :   for (uint32_t i = 0; i < transitions.Length(); ++i) {
    3353           0 :     nsPrintfCString param(":transition%d_", i);
    3354             :     clause.Condition("h.id IN (SELECT place_id FROM moz_historyvisits "
    3355           0 :                              "WHERE visit_type = ")
    3356           0 :           .Param(param.get())
    3357           0 :           .Str(")");
    3358             :   }
    3359             : 
    3360             :   // folders
    3361           0 :   const nsTArray<int64_t>& folders = aQuery->Folders();
    3362           0 :   if (folders.Length() > 0) {
    3363           0 :     aOptions->SetQueryType(nsNavHistoryQueryOptions::QUERY_TYPE_BOOKMARKS);
    3364             : 
    3365           0 :     nsTArray<int64_t> includeFolders;
    3366           0 :     includeFolders.AppendElements(folders);
    3367             : 
    3368           0 :     nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService();
    3369           0 :     NS_ENSURE_STATE(bookmarks);
    3370             : 
    3371           0 :     for (nsTArray<int64_t>::size_type i = 0; i < folders.Length(); ++i) {
    3372           0 :       nsTArray<int64_t> subFolders;
    3373           0 :       if (NS_FAILED(bookmarks->GetDescendantFolders(folders[i], subFolders)))
    3374           0 :         continue;
    3375           0 :       includeFolders.AppendElements(subFolders);
    3376             :     }
    3377             : 
    3378           0 :     clause.Condition("b.parent IN(");
    3379           0 :     for (nsTArray<int64_t>::size_type i = 0; i < includeFolders.Length(); ++i) {
    3380           0 :       clause.Str(nsPrintfCString("%" PRId64, includeFolders[i]).get());
    3381           0 :       if (i < includeFolders.Length() - 1) {
    3382           0 :         clause.Str(",");
    3383             :       }
    3384             :     }
    3385           0 :     clause.Str(")");
    3386             :   }
    3387             : 
    3388           0 :   if (excludeQueries) {
    3389             :     // Serching by terms implicitly exclude queries.
    3390             :     clause.Condition("NOT h.url_hash BETWEEN hash('place', 'prefix_lo') AND "
    3391           0 :                                             "hash('place', 'prefix_hi')");
    3392             :   }
    3393             : 
    3394           0 :   clause.GetClauseString(*aClause);
    3395           0 :   return NS_OK;
    3396             : }
    3397             : 
    3398             : 
    3399             : // nsNavHistory::BindQueryClauseParameters
    3400             : //
    3401             : //    THE BEHAVIOR SHOULD BE IN SYNC WITH QueryToSelectClause
    3402             : 
    3403             : nsresult
    3404           0 : nsNavHistory::BindQueryClauseParameters(mozIStorageBaseStatement* statement,
    3405             :                                         int32_t aQueryIndex,
    3406             :                                         nsNavHistoryQuery* aQuery, // const
    3407             :                                         nsNavHistoryQueryOptions* aOptions)
    3408             : {
    3409             :   nsresult rv;
    3410             : 
    3411             :   bool hasIt;
    3412             :   // Append numbered index to param names, to replace them correctly in
    3413             :   // case of multiple queries.  If we have just one query we don't change the
    3414             :   // param name though.
    3415           0 :   nsAutoCString qIndex;
    3416           0 :   if (aQueryIndex > 0)
    3417           0 :     qIndex.AppendInt(aQueryIndex);
    3418             : 
    3419             :   // begin time
    3420           0 :   if (NS_SUCCEEDED(aQuery->GetHasBeginTime(&hasIt)) && hasIt) {
    3421           0 :     PRTime time = NormalizeTime(aQuery->BeginTimeReference(),
    3422           0 :                                 aQuery->BeginTime());
    3423           0 :     rv = statement->BindInt64ByName(
    3424           0 :       NS_LITERAL_CSTRING("begin_time") + qIndex, time);
    3425           0 :     NS_ENSURE_SUCCESS(rv, rv);
    3426             :   }
    3427             : 
    3428             :   // end time
    3429           0 :   if (NS_SUCCEEDED(aQuery->GetHasEndTime(&hasIt)) && hasIt) {
    3430           0 :     PRTime time = NormalizeTime(aQuery->EndTimeReference(),
    3431           0 :                                 aQuery->EndTime());
    3432           0 :     rv = statement->BindInt64ByName(
    3433           0 :       NS_LITERAL_CSTRING("end_time") + qIndex, time
    3434           0 :     );
    3435           0 :     NS_ENSURE_SUCCESS(rv, rv);
    3436             :   }
    3437             : 
    3438             :   // search terms
    3439           0 :   if (NS_SUCCEEDED(aQuery->GetHasSearchTerms(&hasIt)) && hasIt) {
    3440           0 :     rv = statement->BindStringByName(
    3441           0 :       NS_LITERAL_CSTRING("search_string") + qIndex,
    3442           0 :       aQuery->SearchTerms()
    3443           0 :     );
    3444           0 :     NS_ENSURE_SUCCESS(rv, rv);
    3445             :   }
    3446             : 
    3447             :   // min and max visit count
    3448           0 :   int32_t visits = aQuery->MinVisits();
    3449           0 :   if (visits >= 0) {
    3450           0 :     rv = statement->BindInt32ByName(
    3451           0 :       NS_LITERAL_CSTRING("min_visits") + qIndex, visits
    3452           0 :     );
    3453           0 :     NS_ENSURE_SUCCESS(rv, rv);
    3454             :   }
    3455             : 
    3456           0 :   visits = aQuery->MaxVisits();
    3457           0 :   if (visits >= 0) {
    3458           0 :     rv = statement->BindInt32ByName(
    3459           0 :       NS_LITERAL_CSTRING("max_visits") + qIndex, visits
    3460           0 :     );
    3461           0 :     NS_ENSURE_SUCCESS(rv, rv);
    3462             :   }
    3463             : 
    3464             :   // domain (see GetReversedHostname for more info on reversed host names)
    3465           0 :   if (NS_SUCCEEDED(aQuery->GetHasDomain(&hasIt)) && hasIt) {
    3466           0 :     nsString revDomain;
    3467           0 :     GetReversedHostname(NS_ConvertUTF8toUTF16(aQuery->Domain()), revDomain);
    3468             : 
    3469           0 :     if (aQuery->DomainIsHost()) {
    3470           0 :       rv = statement->BindStringByName(
    3471           0 :         NS_LITERAL_CSTRING("domain_lower") + qIndex, revDomain
    3472           0 :       );
    3473           0 :       NS_ENSURE_SUCCESS(rv, rv);
    3474             :     } else {
    3475             :       // for "mozilla.org" do query >= "gro.allizom." AND < "gro.allizom/"
    3476             :       // which will get everything starting with "gro.allizom." while using the
    3477             :       // index (using SUBSTRING() causes indexes to be discarded).
    3478           0 :       NS_ASSERTION(revDomain[revDomain.Length() - 1] == '.', "Invalid rev. host");
    3479           0 :       rv = statement->BindStringByName(
    3480           0 :         NS_LITERAL_CSTRING("domain_lower") + qIndex, revDomain
    3481           0 :       );
    3482           0 :       NS_ENSURE_SUCCESS(rv, rv);
    3483           0 :       revDomain.Truncate(revDomain.Length() - 1);
    3484           0 :       revDomain.Append(char16_t('/'));
    3485           0 :       rv = statement->BindStringByName(
    3486           0 :         NS_LITERAL_CSTRING("domain_upper") + qIndex, revDomain
    3487           0 :       );
    3488           0 :       NS_ENSURE_SUCCESS(rv, rv);
    3489             :     }
    3490             :   }
    3491             : 
    3492             :   // URI
    3493           0 :   if (aQuery->Uri()) {
    3494           0 :     rv = URIBinder::Bind(
    3495           0 :       statement, NS_LITERAL_CSTRING("uri") + qIndex, aQuery->Uri()
    3496           0 :     );
    3497           0 :     NS_ENSURE_SUCCESS(rv, rv);
    3498             :   }
    3499             : 
    3500             :   // annotation
    3501           0 :   if (!aQuery->Annotation().IsEmpty()) {
    3502           0 :     rv = statement->BindUTF8StringByName(
    3503           0 :       NS_LITERAL_CSTRING("anno") + qIndex, aQuery->Annotation()
    3504           0 :     );
    3505           0 :     NS_ENSURE_SUCCESS(rv, rv);
    3506             :   }
    3507             : 
    3508             :   // tags
    3509           0 :   const nsTArray<nsString> &tags = aQuery->Tags();
    3510           0 :   if (tags.Length() > 0) {
    3511           0 :     for (uint32_t i = 0; i < tags.Length(); ++i) {
    3512           0 :       nsPrintfCString paramName("tag%d_", i);
    3513           0 :       NS_ConvertUTF16toUTF8 tag(tags[i]);
    3514           0 :       rv = statement->BindUTF8StringByName(paramName + qIndex, tag);
    3515           0 :       NS_ENSURE_SUCCESS(rv, rv);
    3516             :     }
    3517           0 :     int64_t tagsFolder = GetTagsFolder();
    3518           0 :     rv = statement->BindInt64ByName(
    3519           0 :       NS_LITERAL_CSTRING("tags_folder") + qIndex, tagsFolder
    3520           0 :     );
    3521           0 :     NS_ENSURE_SUCCESS(rv, rv);
    3522           0 :     if (!aQuery->TagsAreNot()) {
    3523           0 :       rv = statement->BindInt32ByName(
    3524           0 :         NS_LITERAL_CSTRING("tag_count") + qIndex, tags.Length()
    3525           0 :       );
    3526           0 :       NS_ENSURE_SUCCESS(rv, rv);
    3527             :     }
    3528             :   }
    3529             : 
    3530             :   // transitions
    3531           0 :   const nsTArray<uint32_t>& transitions = aQuery->Transitions();
    3532           0 :   if (transitions.Length() > 0) {
    3533           0 :     for (uint32_t i = 0; i < transitions.Length(); ++i) {
    3534           0 :       nsPrintfCString paramName("transition%d_", i);
    3535           0 :       rv = statement->BindInt64ByName(paramName + qIndex, transitions[i]);
    3536           0 :       NS_ENSURE_SUCCESS(rv, rv);
    3537             :     }
    3538             :   }
    3539             : 
    3540           0 :   return NS_OK;
    3541             : }
    3542             : 
    3543             : 
    3544             : // nsNavHistory::ResultsAsList
    3545             : //
    3546             : 
    3547             : nsresult
    3548           0 : nsNavHistory::ResultsAsList(mozIStorageStatement* statement,
    3549             :                             nsNavHistoryQueryOptions* aOptions,
    3550             :                             nsCOMArray<nsNavHistoryResultNode>* aResults)
    3551             : {
    3552             :   nsresult rv;
    3553           0 :   nsCOMPtr<mozIStorageValueArray> row = do_QueryInterface(statement, &rv);
    3554           0 :   NS_ENSURE_SUCCESS(rv, rv);
    3555             : 
    3556           0 :   bool hasMore = false;
    3557           0 :   while (NS_SUCCEEDED(statement->ExecuteStep(&hasMore)) && hasMore) {
    3558           0 :     RefPtr<nsNavHistoryResultNode> result;
    3559           0 :     rv = RowToResult(row, aOptions, getter_AddRefs(result));
    3560           0 :     NS_ENSURE_SUCCESS(rv, rv);
    3561           0 :     aResults->AppendElement(result.forget());
    3562             :   }
    3563           0 :   return NS_OK;
    3564             : }
    3565             : 
    3566             : const int64_t UNDEFINED_URN_VALUE = -1;
    3567             : 
    3568             : // Create a urn (like
    3569             : // urn:places-persist:place:group=0&group=1&sort=1&type=1,,%28local%20files%29)
    3570             : // to be used to persist the open state of this container
    3571             : nsresult
    3572           0 : CreatePlacesPersistURN(nsNavHistoryQueryResultNode *aResultNode,
    3573             :                        int64_t aValue, const nsCString& aTitle, nsCString& aURN)
    3574             : {
    3575           0 :   nsAutoCString uri;
    3576           0 :   nsresult rv = aResultNode->GetUri(uri);
    3577           0 :   NS_ENSURE_SUCCESS(rv, rv);
    3578             : 
    3579           0 :   aURN.AssignLiteral("urn:places-persist:");
    3580           0 :   aURN.Append(uri);
    3581             : 
    3582           0 :   aURN.Append(',');
    3583           0 :   if (aValue != UNDEFINED_URN_VALUE)
    3584           0 :     aURN.AppendInt(aValue);
    3585             : 
    3586           0 :   aURN.Append(',');
    3587           0 :   if (!aTitle.IsEmpty()) {
    3588           0 :     nsAutoCString escapedTitle;
    3589           0 :     bool success = NS_Escape(aTitle, escapedTitle, url_XAlphas);
    3590           0 :     NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
    3591           0 :     aURN.Append(escapedTitle);
    3592             :   }
    3593             : 
    3594           0 :   return NS_OK;
    3595             : }
    3596             : 
    3597             : int64_t
    3598           0 : nsNavHistory::GetTagsFolder()
    3599             : {
    3600             :   // cache our tags folder
    3601             :   // note, we can't do this in nsNavHistory::Init(),
    3602             :   // as getting the bookmarks service would initialize it.
    3603           0 :   if (mTagsFolder == -1) {
    3604           0 :     nsNavBookmarks *bookmarks = nsNavBookmarks::GetBookmarksService();
    3605           0 :     NS_ENSURE_TRUE(bookmarks, -1);
    3606             : 
    3607           0 :     nsresult rv = bookmarks->GetTagsFolder(&mTagsFolder);
    3608           0 :     NS_ENSURE_SUCCESS(rv, -1);
    3609             :   }
    3610           0 :   return mTagsFolder;
    3611             : }
    3612             : 
    3613             : // nsNavHistory::FilterResultSet
    3614             : //
    3615             : // This does some post-query-execution filtering:
    3616             : //   - searching on title, url and tags
    3617             : //   - limit count
    3618             : //
    3619             : // Note:  changes to filtering in FilterResultSet()
    3620             : // may require changes to NeedToFilterResultSet()
    3621             : 
    3622             : nsresult
    3623           0 : nsNavHistory::FilterResultSet(nsNavHistoryQueryResultNode* aQueryNode,
    3624             :                               const nsCOMArray<nsNavHistoryResultNode>& aSet,
    3625             :                               nsCOMArray<nsNavHistoryResultNode>* aFiltered,
    3626             :                               const nsCOMArray<nsNavHistoryQuery>& aQueries,
    3627             :                               nsNavHistoryQueryOptions *aOptions)
    3628             : {
    3629             :   // get the bookmarks service
    3630           0 :   nsNavBookmarks *bookmarks = nsNavBookmarks::GetBookmarksService();
    3631           0 :   NS_ENSURE_TRUE(bookmarks, NS_ERROR_OUT_OF_MEMORY);
    3632             : 
    3633             :   // parse the search terms
    3634           0 :   nsTArray<nsTArray<nsString>*> terms;
    3635           0 :   ParseSearchTermsFromQueries(aQueries, &terms);
    3636             : 
    3637           0 :   uint16_t resultType = aOptions->ResultType();
    3638           0 :   for (int32_t nodeIndex = 0; nodeIndex < aSet.Count(); nodeIndex++) {
    3639             :     // exclude-queries is implicit when searching, we're only looking at
    3640             :     // plan URI nodes
    3641           0 :     if (!aSet[nodeIndex]->IsURI())
    3642           0 :       continue;
    3643             : 
    3644             :     // RESULTS_AS_TAG_CONTENTS returns a set ordered by place_id and
    3645             :     // lastModified. So, to remove duplicates, we can retain the first result
    3646             :     // for each uri.
    3647           0 :     if (resultType == nsINavHistoryQueryOptions::RESULTS_AS_TAG_CONTENTS &&
    3648           0 :         nodeIndex > 0 && aSet[nodeIndex]->mURI == aSet[nodeIndex-1]->mURI)
    3649           0 :       continue;
    3650             : 
    3651           0 :     if (aSet[nodeIndex]->mItemId != -1 && aQueryNode &&
    3652           0 :         aQueryNode->mItemId == aSet[nodeIndex]->mItemId) {
    3653           0 :       continue;
    3654             :     }
    3655             : 
    3656             :     // Append the node only if it matches one of the queries.
    3657           0 :     bool appendNode = false;
    3658           0 :     for (int32_t queryIndex = 0;
    3659           0 :          queryIndex < aQueries.Count() && !appendNode; queryIndex++) {
    3660             : 
    3661           0 :       if (terms[queryIndex]->Length()) {
    3662             :         // Filter based on search terms.
    3663             :         // Convert title and url for the current node to UTF16 strings.
    3664           0 :         NS_ConvertUTF8toUTF16 nodeTitle(aSet[nodeIndex]->mTitle);
    3665             :         // Unescape the URL for search terms matching.
    3666           0 :         nsAutoCString cNodeURL(aSet[nodeIndex]->mURI);
    3667           0 :         NS_ConvertUTF8toUTF16 nodeURL(NS_UnescapeURL(cNodeURL));
    3668             : 
    3669             :         // Determine if every search term matches anywhere in the title, url or
    3670             :         // tag.
    3671           0 :         bool matchAll = true;
    3672           0 :         for (int32_t termIndex = terms[queryIndex]->Length() - 1;
    3673           0 :              termIndex >= 0 && matchAll;
    3674             :              termIndex--) {
    3675           0 :           nsString& term = terms[queryIndex]->ElementAt(termIndex);
    3676             : 
    3677             :           // True if any of them match; false makes us quit the loop
    3678           0 :           matchAll = CaseInsensitiveFindInReadable(term, nodeTitle) ||
    3679           0 :                      CaseInsensitiveFindInReadable(term, nodeURL) ||
    3680           0 :                      CaseInsensitiveFindInReadable(term, aSet[nodeIndex]->mTags);
    3681             :         }
    3682             : 
    3683             :         // Skip the node if we don't match all terms in the title, url or tag
    3684           0 :         if (!matchAll)
    3685           0 :           continue;
    3686             :       }
    3687             : 
    3688             :       // We passed all filters, so we can append the node to filtered results.
    3689           0 :       appendNode = true;
    3690             :     }
    3691             : 
    3692           0 :     if (appendNode)
    3693           0 :       aFiltered->AppendObject(aSet[nodeIndex]);
    3694             : 
    3695             :     // Stop once we have reached max results.
    3696           0 :     if (aOptions->MaxResults() > 0 &&
    3697           0 :         (uint32_t)aFiltered->Count() >= aOptions->MaxResults())
    3698           0 :       break;
    3699             :   }
    3700             : 
    3701             :   // De-allocate the temporary matrixes.
    3702           0 :   for (int32_t i = 0; i < aQueries.Count(); i++) {
    3703           0 :     delete terms[i];
    3704             :   }
    3705             : 
    3706           0 :   return NS_OK;
    3707             : }
    3708             : 
    3709             : void
    3710           0 : nsNavHistory::registerEmbedVisit(nsIURI* aURI,
    3711             :                                  int64_t aTime)
    3712             : {
    3713           0 :   NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread");
    3714             : 
    3715           0 :   VisitHashKey* visit = mEmbedVisits.PutEntry(aURI);
    3716           0 :   if (!visit) {
    3717           0 :     NS_WARNING("Unable to register a EMBED visit.");
    3718           0 :     return;
    3719             :   }
    3720           0 :   visit->visitTime = aTime;
    3721             : }
    3722             : 
    3723             : bool
    3724           1 : nsNavHistory::hasEmbedVisit(nsIURI* aURI) {
    3725           1 :   NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread");
    3726             : 
    3727           1 :   return !!mEmbedVisits.GetEntry(aURI);
    3728             : }
    3729             : 
    3730             : void
    3731           0 : nsNavHistory::clearEmbedVisits() {
    3732           0 :   NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread");
    3733             : 
    3734           0 :   mEmbedVisits.Clear();
    3735           0 : }
    3736             : 
    3737             : NS_IMETHODIMP
    3738           0 : nsNavHistory::ClearEmbedVisits() {
    3739           0 :   clearEmbedVisits();
    3740           0 :   return NS_OK;
    3741             : }
    3742             : 
    3743             : NS_IMETHODIMP
    3744           0 : nsNavHistory::MakeGuid(nsACString& aGuid) {
    3745           0 :   if (NS_FAILED(GenerateGUID(aGuid))) {
    3746           0 :     MOZ_ASSERT(false, "Shouldn't fail to create a guid!");
    3747             :     aGuid.SetIsVoid(true);
    3748             :   }
    3749           0 :   return NS_OK;
    3750             : }
    3751             : 
    3752             : // nsNavHistory::CheckIsRecentEvent
    3753             : //
    3754             : //    Sees if this URL happened "recently."
    3755             : //
    3756             : //    It is always removed from our recent list no matter what. It only counts
    3757             : //    as "recent" if the event happened more recently than our event
    3758             : //    threshold ago.
    3759             : 
    3760             : bool
    3761           3 : nsNavHistory::CheckIsRecentEvent(RecentEventHash* hashTable,
    3762             :                                  const nsACString& url)
    3763             : {
    3764             :   PRTime eventTime;
    3765           3 :   if (hashTable->Get(url, reinterpret_cast<int64_t*>(&eventTime))) {
    3766           0 :     hashTable->Remove(url);
    3767           0 :     if (eventTime > GetNow() - RECENT_EVENT_THRESHOLD)
    3768           0 :       return true;
    3769           0 :     return false;
    3770             :   }
    3771           3 :   return false;
    3772             : }
    3773             : 
    3774             : 
    3775             : // nsNavHistory::ExpireNonrecentEvents
    3776             : //
    3777             : //    This goes through our
    3778             : 
    3779             : void
    3780           0 : nsNavHistory::ExpireNonrecentEvents(RecentEventHash* hashTable)
    3781             : {
    3782           0 :   int64_t threshold = GetNow() - RECENT_EVENT_THRESHOLD;
    3783           0 :   for (auto iter = hashTable->Iter(); !iter.Done(); iter.Next()) {
    3784           0 :     if (iter.Data() < threshold) {
    3785           0 :       iter.Remove();
    3786             :     }
    3787             :   }
    3788           0 : }
    3789             : 
    3790             : 
    3791             : // nsNavHistory::RowToResult
    3792             : //
    3793             : //    Here, we just have a generic row. It could be a query, URL, visit,
    3794             : //    or full visit.
    3795             : 
    3796             : nsresult
    3797           0 : nsNavHistory::RowToResult(mozIStorageValueArray* aRow,
    3798             :                           nsNavHistoryQueryOptions* aOptions,
    3799             :                           nsNavHistoryResultNode** aResult)
    3800             : {
    3801           0 :   NS_ASSERTION(aRow && aOptions && aResult, "Null pointer in RowToResult");
    3802             : 
    3803             :   // URL
    3804           0 :   nsAutoCString url;
    3805           0 :   nsresult rv = aRow->GetUTF8String(kGetInfoIndex_URL, url);
    3806           0 :   NS_ENSURE_SUCCESS(rv, rv);
    3807             : 
    3808             :   // title
    3809           0 :   nsAutoCString title;
    3810             :   bool isNull;
    3811           0 :   rv = aRow->GetIsNull(kGetInfoIndex_Title, &isNull);
    3812           0 :   NS_ENSURE_SUCCESS(rv, rv);
    3813           0 :   if (!isNull) {
    3814           0 :     rv = aRow->GetUTF8String(kGetInfoIndex_Title, title);
    3815           0 :     NS_ENSURE_SUCCESS(rv, rv);
    3816             :   }
    3817             : 
    3818           0 :   uint32_t accessCount = aRow->AsInt32(kGetInfoIndex_VisitCount);
    3819           0 :   PRTime time = aRow->AsInt64(kGetInfoIndex_VisitDate);
    3820             : 
    3821             :   // itemId
    3822           0 :   int64_t itemId = aRow->AsInt64(kGetInfoIndex_ItemId);
    3823           0 :   int64_t parentId = -1;
    3824           0 :   if (itemId == 0) {
    3825             :     // This is not a bookmark.  For non-bookmarks we use a -1 itemId value.
    3826             :     // Notice ids in sqlite tables start from 1, so itemId cannot ever be 0.
    3827           0 :     itemId = -1;
    3828             :   }
    3829             :   else {
    3830             :     // This is a bookmark, so it has a parent.
    3831           0 :     int64_t itemParentId = aRow->AsInt64(kGetInfoIndex_ItemParentId);
    3832           0 :     if (itemParentId > 0) {
    3833             :       // The Places root has parent == 0, but that item id does not really
    3834             :       // exist. We want to set the parent only if it's a real one.
    3835           0 :       parentId = itemParentId;
    3836             :     }
    3837             :   }
    3838             : 
    3839           0 :   if (IsQueryURI(url)) {
    3840             :     // Special case "place:" URIs: turn them into containers.
    3841           0 :     if (itemId != -1) {
    3842             :       // We should never expose the history title for query nodes if the
    3843             :       // bookmark-item's title is set to null (the history title may be the
    3844             :       // query string without the place: prefix). Thus we call getItemTitle
    3845             :       // explicitly. Doing this in the SQL query would be less performant since
    3846             :       // it should be done for all results rather than only for queries.
    3847           0 :       nsNavBookmarks *bookmarks = nsNavBookmarks::GetBookmarksService();
    3848           0 :       NS_ENSURE_TRUE(bookmarks, NS_ERROR_OUT_OF_MEMORY);
    3849             : 
    3850           0 :       rv = bookmarks->GetItemTitle(itemId, title);
    3851           0 :       NS_ENSURE_SUCCESS(rv, rv);
    3852             :     }
    3853             : 
    3854           0 :     nsAutoCString guid;
    3855           0 :     if (itemId != -1) {
    3856           0 :       rv = aRow->GetUTF8String(nsNavBookmarks::kGetChildrenIndex_Guid, guid);
    3857           0 :       NS_ENSURE_SUCCESS(rv, rv);
    3858             :     }
    3859             : 
    3860           0 :     RefPtr<nsNavHistoryResultNode> resultNode;
    3861           0 :     rv = QueryRowToResult(itemId, guid, url, title, accessCount, time,
    3862           0 :                           getter_AddRefs(resultNode));
    3863           0 :     NS_ENSURE_SUCCESS(rv, rv);
    3864             : 
    3865           0 :     if (itemId != -1 ||
    3866           0 :         aOptions->ResultType() == nsNavHistoryQueryOptions::RESULTS_AS_TAG_QUERY) {
    3867             :       // RESULTS_AS_TAG_QUERY has date columns
    3868           0 :       resultNode->mDateAdded = aRow->AsInt64(kGetInfoIndex_ItemDateAdded);
    3869           0 :       resultNode->mLastModified = aRow->AsInt64(kGetInfoIndex_ItemLastModified);
    3870           0 :       if (resultNode->IsFolder()) {
    3871             :         // If it's a simple folder node (i.e. a shortcut to another folder), apply
    3872             :         // our options for it. However, if the parent type was tag query, we do not
    3873             :         // apply them, because it would not yield any results.
    3874           0 :         resultNode->GetAsContainer()->mOptions = aOptions;
    3875             :       }
    3876             :     }
    3877             : 
    3878           0 :     resultNode.forget(aResult);
    3879           0 :     return rv;
    3880           0 :   } else if (aOptions->ResultType() == nsNavHistoryQueryOptions::RESULTS_AS_URI ||
    3881           0 :              aOptions->ResultType() == nsNavHistoryQueryOptions::RESULTS_AS_TAG_CONTENTS) {
    3882             :     RefPtr<nsNavHistoryResultNode> resultNode =
    3883           0 :       new nsNavHistoryResultNode(url, title, accessCount, time);
    3884             : 
    3885           0 :     if (itemId != -1) {
    3886           0 :       resultNode->mItemId = itemId;
    3887           0 :       resultNode->mFolderId = parentId;
    3888           0 :       resultNode->mDateAdded = aRow->AsInt64(kGetInfoIndex_ItemDateAdded);
    3889           0 :       resultNode->mLastModified = aRow->AsInt64(kGetInfoIndex_ItemLastModified);
    3890             : 
    3891           0 :       rv = aRow->GetUTF8String(nsNavBookmarks::kGetChildrenIndex_Guid,
    3892           0 :                                resultNode->mBookmarkGuid);
    3893           0 :       NS_ENSURE_SUCCESS(rv, rv);
    3894             :     }
    3895             : 
    3896           0 :     resultNode->mFrecency = aRow->AsInt32(kGetInfoIndex_Frecency);
    3897           0 :     resultNode->mHidden = !!aRow->AsInt32(kGetInfoIndex_Hidden);
    3898             : 
    3899           0 :     nsAutoString tags;
    3900           0 :     rv = aRow->GetString(kGetInfoIndex_ItemTags, tags);
    3901           0 :     NS_ENSURE_SUCCESS(rv, rv);
    3902           0 :     if (!tags.IsVoid()) {
    3903           0 :       resultNode->mTags.Assign(tags);
    3904             :     }
    3905             : 
    3906           0 :     rv = aRow->GetUTF8String(kGetInfoIndex_Guid, resultNode->mPageGuid);
    3907           0 :     NS_ENSURE_SUCCESS(rv, rv);
    3908             : 
    3909           0 :     resultNode.forget(aResult);
    3910           0 :     return NS_OK;
    3911             :   }
    3912             : 
    3913           0 :   if (aOptions->ResultType() == nsNavHistoryQueryOptions::RESULTS_AS_VISIT) {
    3914             :     RefPtr<nsNavHistoryResultNode> resultNode =
    3915           0 :       new nsNavHistoryResultNode(url, title, accessCount, time);
    3916             : 
    3917           0 :     nsAutoString tags;
    3918           0 :     rv = aRow->GetString(kGetInfoIndex_ItemTags, tags);
    3919           0 :     if (!tags.IsVoid())
    3920           0 :       resultNode->mTags.Assign(tags);
    3921             : 
    3922           0 :     rv = aRow->GetUTF8String(kGetInfoIndex_Guid, resultNode->mPageGuid);
    3923           0 :     NS_ENSURE_SUCCESS(rv, rv);
    3924             : 
    3925           0 :     rv = aRow->GetInt64(kGetInfoIndex_VisitId, &resultNode->mVisitId);
    3926           0 :     NS_ENSURE_SUCCESS(rv, rv);
    3927             : 
    3928             :     int64_t fromVisitId;
    3929           0 :     rv = aRow->GetInt64(kGetInfoIndex_FromVisitId, &fromVisitId);
    3930           0 :     NS_ENSURE_SUCCESS(rv, rv);
    3931             : 
    3932           0 :     if (fromVisitId > 0) {
    3933           0 :       resultNode->mFromVisitId = fromVisitId;
    3934             :     }
    3935             : 
    3936           0 :     resultNode->mTransitionType = aRow->AsInt32(kGetInfoIndex_VisitType);
    3937             : 
    3938           0 :     resultNode.forget(aResult);
    3939           0 :     return NS_OK;
    3940             :   }
    3941             : 
    3942           0 :   return NS_ERROR_FAILURE;
    3943             : }
    3944             : 
    3945             : 
    3946             : // nsNavHistory::QueryRowToResult
    3947             : //
    3948             : //    Called by RowToResult when the URI is a place: URI to generate the proper
    3949             : //    folder or query node.
    3950             : 
    3951             : nsresult
    3952           0 : nsNavHistory::QueryRowToResult(int64_t itemId,
    3953             :                                const nsACString& aBookmarkGuid,
    3954             :                                const nsACString& aURI,
    3955             :                                const nsACString& aTitle,
    3956             :                                uint32_t aAccessCount,
    3957             :                                PRTime aTime,
    3958             :                                nsNavHistoryResultNode** aNode)
    3959             : {
    3960           0 :   MOZ_ASSERT((itemId != -1 && !aBookmarkGuid.IsEmpty()) ||
    3961             :              (itemId == -1 && aBookmarkGuid.IsEmpty()));
    3962             : 
    3963           0 :   nsCOMArray<nsNavHistoryQuery> queries;
    3964           0 :   nsCOMPtr<nsNavHistoryQueryOptions> options;
    3965           0 :   nsresult rv = QueryStringToQueryArray(aURI, &queries,
    3966           0 :                                         getter_AddRefs(options));
    3967             : 
    3968           0 :   RefPtr<nsNavHistoryResultNode> resultNode;
    3969             :   // If this failed the query does not parse correctly, let the error pass and
    3970             :   // handle it later.
    3971           0 :   if (NS_SUCCEEDED(rv)) {
    3972             :     // Check if this is a folder shortcut, so we can take a faster path.
    3973           0 :     int64_t targetFolderId = GetSimpleBookmarksQueryFolder(queries, options);
    3974           0 :     if (targetFolderId) {
    3975           0 :       nsNavBookmarks *bookmarks = nsNavBookmarks::GetBookmarksService();
    3976           0 :       NS_ENSURE_TRUE(bookmarks, NS_ERROR_OUT_OF_MEMORY);
    3977             : 
    3978           0 :       rv = bookmarks->ResultNodeForContainer(targetFolderId, options,
    3979           0 :                                              getter_AddRefs(resultNode));
    3980             :       // If this failed the shortcut is pointing to nowhere, let the error pass
    3981             :       // and handle it later.
    3982           0 :       if (NS_SUCCEEDED(rv)) {
    3983             :         // At this point the node is set up like a regular folder node. Here
    3984             :         // we make the necessary change to make it a folder shortcut.
    3985           0 :         resultNode->GetAsFolder()->mTargetFolderItemId = targetFolderId;
    3986           0 :         resultNode->mItemId = itemId;
    3987           0 :         nsAutoCString targetFolderGuid(resultNode->GetAsFolder()->mBookmarkGuid);
    3988           0 :         resultNode->mBookmarkGuid = aBookmarkGuid;
    3989           0 :         resultNode->GetAsFolder()->mTargetFolderGuid = targetFolderGuid;
    3990             : 
    3991             :         // Use the query item title, unless it's empty (in that case use the
    3992             :         // concrete folder title).
    3993           0 :         if (!aTitle.IsEmpty()) {
    3994           0 :           resultNode->mTitle = aTitle;
    3995             :         }
    3996             :       }
    3997             :     }
    3998             :     else {
    3999             :       // This is a regular query.
    4000           0 :       resultNode = new nsNavHistoryQueryResultNode(aTitle, aTime, queries, options);
    4001           0 :       resultNode->mItemId = itemId;
    4002           0 :       resultNode->mBookmarkGuid = aBookmarkGuid;
    4003             :     }
    4004             :   }
    4005             : 
    4006           0 :   if (NS_FAILED(rv)) {
    4007           0 :     NS_WARNING("Generating a generic empty node for a broken query!");
    4008             :     // This is a broken query, that either did not parse or points to not
    4009             :     // existing data.  We don't want to return failure since that will kill the
    4010             :     // whole result.  Instead make a generic empty query node.
    4011           0 :     resultNode = new nsNavHistoryQueryResultNode(aTitle, aURI);
    4012           0 :     resultNode->mItemId = itemId;
    4013           0 :     resultNode->mBookmarkGuid = aBookmarkGuid;
    4014             :     // This is a perf hack to generate an empty query that skips filtering.
    4015           0 :     resultNode->GetAsQuery()->Options()->SetExcludeItems(true);
    4016             :   }
    4017             : 
    4018           0 :   resultNode.forget(aNode);
    4019           0 :   return NS_OK;
    4020             : }
    4021             : 
    4022             : 
    4023             : // nsNavHistory::VisitIdToResultNode
    4024             : //
    4025             : //    Used by the query results to create new nodes on the fly when
    4026             : //    notifications come in. This just creates a node for the given visit ID.
    4027             : 
    4028             : nsresult
    4029           0 : nsNavHistory::VisitIdToResultNode(int64_t visitId,
    4030             :                                   nsNavHistoryQueryOptions* aOptions,
    4031             :                                   nsNavHistoryResultNode** aResult)
    4032             : {
    4033           0 :   nsAutoCString tagsFragment;
    4034           0 :   GetTagsSqlFragment(GetTagsFolder(), NS_LITERAL_CSTRING("h.id"),
    4035           0 :                      true, tagsFragment);
    4036             : 
    4037           0 :   nsCOMPtr<mozIStorageStatement> statement;
    4038           0 :   switch (aOptions->ResultType())
    4039             :   {
    4040             :     case nsNavHistoryQueryOptions::RESULTS_AS_VISIT:
    4041             :     case nsNavHistoryQueryOptions::RESULTS_AS_FULL_VISIT:
    4042             :       // visit query - want exact visit time
    4043             :       // Should match kGetInfoIndex_* (see GetQueryResults)
    4044           0 :       statement = mDB->GetStatement(NS_LITERAL_CSTRING(
    4045             :         "SELECT h.id, h.url, h.title, h.rev_host, h.visit_count, "
    4046             :                "v.visit_date, null, null, null, null, null, "
    4047           0 :                ) + tagsFragment + NS_LITERAL_CSTRING(", h.frecency, h.hidden, h.guid, "
    4048             :               "v.id, v.from_visit, v.visit_type "
    4049             :         "FROM moz_places h "
    4050             :         "JOIN moz_historyvisits v ON h.id = v.place_id "
    4051             :         "WHERE v.id = :visit_id ")
    4052           0 :       );
    4053           0 :       break;
    4054             : 
    4055             :     case nsNavHistoryQueryOptions::RESULTS_AS_URI:
    4056             :       // URL results - want last visit time
    4057             :       // Should match kGetInfoIndex_* (see GetQueryResults)
    4058           0 :       statement = mDB->GetStatement(NS_LITERAL_CSTRING(
    4059             :         "SELECT h.id, h.url, h.title, h.rev_host, h.visit_count, "
    4060             :                "h.last_visit_date, null, null, null, null, null, "
    4061           0 :                ) + tagsFragment + NS_LITERAL_CSTRING(", h.frecency, h.hidden, h.guid, "
    4062             :               "null, null, null "
    4063             :         "FROM moz_places h "
    4064             :         "JOIN moz_historyvisits v ON h.id = v.place_id "
    4065             :         "WHERE v.id = :visit_id ")
    4066           0 :       );
    4067           0 :       break;
    4068             : 
    4069             :     default:
    4070             :       // Query base types like RESULTS_AS_*_QUERY handle additions
    4071             :       // by registering their own observers when they are expanded.
    4072           0 :       return NS_OK;
    4073             :   }
    4074           0 :   NS_ENSURE_STATE(statement);
    4075           0 :   mozStorageStatementScoper scoper(statement);
    4076             : 
    4077           0 :   nsresult rv = statement->BindInt64ByName(NS_LITERAL_CSTRING("visit_id"),
    4078           0 :                                            visitId);
    4079           0 :   NS_ENSURE_SUCCESS(rv, rv);
    4080             : 
    4081           0 :   bool hasMore = false;
    4082           0 :   rv = statement->ExecuteStep(&hasMore);
    4083           0 :   NS_ENSURE_SUCCESS(rv, rv);
    4084           0 :   if (! hasMore) {
    4085           0 :     NS_NOTREACHED("Trying to get a result node for an invalid visit");
    4086           0 :     return NS_ERROR_INVALID_ARG;
    4087             :   }
    4088             : 
    4089           0 :   nsCOMPtr<mozIStorageValueArray> row = do_QueryInterface(statement, &rv);
    4090           0 :   NS_ENSURE_SUCCESS(rv, rv);
    4091             : 
    4092           0 :   return RowToResult(row, aOptions, aResult);
    4093             : }
    4094             : 
    4095             : nsresult
    4096           0 : nsNavHistory::BookmarkIdToResultNode(int64_t aBookmarkId, nsNavHistoryQueryOptions* aOptions,
    4097             :                                      nsNavHistoryResultNode** aResult)
    4098             : {
    4099           0 :   nsAutoCString tagsFragment;
    4100           0 :   GetTagsSqlFragment(GetTagsFolder(), NS_LITERAL_CSTRING("h.id"),
    4101           0 :                      true, tagsFragment);
    4102             :   // Should match kGetInfoIndex_*
    4103           0 :   nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(NS_LITERAL_CSTRING(
    4104             :       "SELECT b.fk, h.url, COALESCE(b.title, h.title), "
    4105             :              "h.rev_host, h.visit_count, h.last_visit_date, null, b.id, "
    4106             :              "b.dateAdded, b.lastModified, b.parent, "
    4107           0 :              ) + tagsFragment + NS_LITERAL_CSTRING(", h.frecency, h.hidden, h.guid, "
    4108             :              "null, null, null, b.guid, b.position, b.type, b.fk "
    4109             :       "FROM moz_bookmarks b "
    4110             :       "JOIN moz_places h ON b.fk = h.id "
    4111             :       "WHERE b.id = :item_id ")
    4112           0 :   );
    4113           0 :   NS_ENSURE_STATE(stmt);
    4114           0 :   mozStorageStatementScoper scoper(stmt);
    4115             : 
    4116           0 :   nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("item_id"),
    4117           0 :                                       aBookmarkId);
    4118           0 :   NS_ENSURE_SUCCESS(rv, rv);
    4119             : 
    4120           0 :   bool hasMore = false;
    4121           0 :   rv = stmt->ExecuteStep(&hasMore);
    4122           0 :   NS_ENSURE_SUCCESS(rv, rv);
    4123           0 :   if (!hasMore) {
    4124           0 :     NS_NOTREACHED("Trying to get a result node for an invalid bookmark identifier");
    4125           0 :     return NS_ERROR_INVALID_ARG;
    4126             :   }
    4127             : 
    4128           0 :   nsCOMPtr<mozIStorageValueArray> row = do_QueryInterface(stmt, &rv);
    4129           0 :   NS_ENSURE_SUCCESS(rv, rv);
    4130             : 
    4131           0 :   return RowToResult(row, aOptions, aResult);
    4132             : }
    4133             : 
    4134             : nsresult
    4135           0 : nsNavHistory::URIToResultNode(nsIURI* aURI,
    4136             :                               nsNavHistoryQueryOptions* aOptions,
    4137             :                               nsNavHistoryResultNode** aResult)
    4138             : {
    4139           0 :   nsAutoCString tagsFragment;
    4140           0 :   GetTagsSqlFragment(GetTagsFolder(), NS_LITERAL_CSTRING("h.id"),
    4141           0 :                      true, tagsFragment);
    4142             :   // Should match kGetInfoIndex_*
    4143           0 :   nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(NS_LITERAL_CSTRING(
    4144             :     "SELECT h.id, :page_url, COALESCE(b.title, h.title), "
    4145             :            "h.rev_host, h.visit_count, h.last_visit_date, null, "
    4146             :            "b.id, b.dateAdded, b.lastModified, b.parent, "
    4147           0 :            ) + tagsFragment + NS_LITERAL_CSTRING(", h.frecency, h.hidden, h.guid, "
    4148             :            "null, null, null, b.guid, b.position, b.type, b.fk "
    4149             :     "FROM moz_places h "
    4150             :     "LEFT JOIN moz_bookmarks b ON b.fk = h.id "
    4151             :     "WHERE h.url_hash = hash(:page_url) AND h.url = :page_url ")
    4152           0 :   );
    4153           0 :   NS_ENSURE_STATE(stmt);
    4154           0 :   mozStorageStatementScoper scoper(stmt);
    4155             : 
    4156           0 :   nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), aURI);
    4157           0 :   NS_ENSURE_SUCCESS(rv, rv);
    4158             : 
    4159           0 :   bool hasMore = false;
    4160           0 :   rv = stmt->ExecuteStep(&hasMore);
    4161           0 :   NS_ENSURE_SUCCESS(rv, rv);
    4162           0 :   if (!hasMore) {
    4163           0 :     NS_NOTREACHED("Trying to get a result node for an invalid url");
    4164           0 :     return NS_ERROR_INVALID_ARG;
    4165             :   }
    4166             : 
    4167           0 :   nsCOMPtr<mozIStorageValueArray> row = do_QueryInterface(stmt, &rv);
    4168           0 :   NS_ENSURE_SUCCESS(rv, rv);
    4169             : 
    4170           0 :   return RowToResult(row, aOptions, aResult);
    4171             : }
    4172             : 
    4173             : void
    4174           0 : nsNavHistory::SendPageChangedNotification(nsIURI* aURI,
    4175             :                                           uint32_t aChangedAttribute,
    4176             :                                           const nsAString& aNewValue,
    4177             :                                           const nsACString& aGUID)
    4178             : {
    4179           0 :   MOZ_ASSERT(!aGUID.IsEmpty());
    4180           0 :   NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
    4181             :                    nsINavHistoryObserver,
    4182             :                    OnPageChanged(aURI, aChangedAttribute, aNewValue, aGUID));
    4183           0 : }
    4184             : 
    4185             : // nsNavHistory::TitleForDomain
    4186             : //
    4187             : //    This computes the title for a given domain. Normally, this is just the
    4188             : //    domain name, but we specially handle empty cases to give you a nice
    4189             : //    localized string.
    4190             : 
    4191             : void
    4192           0 : nsNavHistory::TitleForDomain(const nsCString& domain, nsACString& aTitle)
    4193             : {
    4194           0 :   if (! domain.IsEmpty()) {
    4195           0 :     aTitle = domain;
    4196           0 :     return;
    4197             :   }
    4198             : 
    4199             :   // use the localized one instead
    4200           0 :   GetStringFromName(u"localhost", aTitle);
    4201             : }
    4202             : 
    4203             : void
    4204           0 : nsNavHistory::GetAgeInDaysString(int32_t aInt, const char16_t *aName,
    4205             :                                  nsACString& aResult)
    4206             : {
    4207           0 :   nsIStringBundle *bundle = GetBundle();
    4208           0 :   if (bundle) {
    4209           0 :     nsAutoString intString;
    4210           0 :     intString.AppendInt(aInt);
    4211           0 :     const char16_t* strings[1] = { intString.get() };
    4212           0 :     nsXPIDLString value;
    4213           0 :     nsresult rv = bundle->FormatStringFromName(aName, strings,
    4214           0 :                                                1, getter_Copies(value));
    4215           0 :     if (NS_SUCCEEDED(rv)) {
    4216           0 :       CopyUTF16toUTF8(value, aResult);
    4217           0 :       return;
    4218             :     }
    4219             :   }
    4220           0 :   CopyUTF16toUTF8(nsDependentString(aName), aResult);
    4221             : }
    4222             : 
    4223             : void
    4224           0 : nsNavHistory::GetStringFromName(const char16_t *aName, nsACString& aResult)
    4225             : {
    4226           0 :   nsIStringBundle *bundle = GetBundle();
    4227           0 :   if (bundle) {
    4228           0 :     nsXPIDLString value;
    4229           0 :     nsresult rv = bundle->GetStringFromName(aName, getter_Copies(value));
    4230           0 :     if (NS_SUCCEEDED(rv)) {
    4231           0 :       CopyUTF16toUTF8(value, aResult);
    4232           0 :       return;
    4233             :     }
    4234             :   }
    4235           0 :   CopyUTF16toUTF8(nsDependentString(aName), aResult);
    4236             : }
    4237             : 
    4238             : // static
    4239             : void
    4240           0 : nsNavHistory::GetMonthName(const PRExplodedTime& aTime, nsACString& aResult)
    4241             : {
    4242           0 :   nsAutoString month;
    4243             :   nsresult rv = DateTimeFormat::FormatPRExplodedTime(kDateFormatMonthLong,
    4244             :                                                      kTimeFormatNone,
    4245             :                                                      &aTime,
    4246           0 :                                                      month);
    4247           0 :   if (NS_FAILED(rv)) {
    4248           0 :     aResult = nsPrintfCString("[%d]", aTime.tm_month + 1);
    4249           0 :     return;
    4250             :   }
    4251           0 :   CopyUTF16toUTF8(month, aResult);
    4252             : }
    4253             : 
    4254             : // static
    4255             : void
    4256           0 : nsNavHistory::GetMonthYear(const PRExplodedTime& aTime, nsACString& aResult)
    4257             : {
    4258           0 :   nsAutoString monthYear;
    4259             :   nsresult rv = DateTimeFormat::FormatPRExplodedTime(kDateFormatYearMonthLong,
    4260             :                                                      kTimeFormatNone,
    4261             :                                                      &aTime,
    4262           0 :                                                      monthYear);
    4263           0 :   if (NS_FAILED(rv)) {
    4264           0 :     aResult = nsPrintfCString("[%d-%d]", aTime.tm_month + 1, aTime.tm_year);
    4265           0 :     return;
    4266             :   }
    4267           0 :   CopyUTF16toUTF8(monthYear, aResult);
    4268             : }
    4269             : 
    4270             : 
    4271             : namespace {
    4272             : 
    4273             : // GetSimpleBookmarksQueryFolder
    4274             : //
    4275             : //    Determines if this set of queries is a simple bookmarks query for a
    4276             : //    folder with no other constraints. In these common cases, we can more
    4277             : //    efficiently compute the results.
    4278             : //
    4279             : //    A simple bookmarks query will result in a hierarchical tree of
    4280             : //    bookmark items, folders and separators.
    4281             : //
    4282             : //    Returns the folder ID if it is a simple folder query, 0 if not.
    4283             : static int64_t
    4284           0 : GetSimpleBookmarksQueryFolder(const nsCOMArray<nsNavHistoryQuery>& aQueries,
    4285             :                               nsNavHistoryQueryOptions* aOptions)
    4286             : {
    4287           0 :   if (aQueries.Count() != 1)
    4288           0 :     return 0;
    4289             : 
    4290           0 :   nsNavHistoryQuery* query = aQueries[0];
    4291           0 :   if (query->Folders().Length() != 1)
    4292           0 :     return 0;
    4293             : 
    4294             :   bool hasIt;
    4295           0 :   query->GetHasBeginTime(&hasIt);
    4296           0 :   if (hasIt)
    4297           0 :     return 0;
    4298           0 :   query->GetHasEndTime(&hasIt);
    4299           0 :   if (hasIt)
    4300           0 :     return 0;
    4301           0 :   query->GetHasDomain(&hasIt);
    4302           0 :   if (hasIt)
    4303           0 :     return 0;
    4304           0 :   query->GetHasUri(&hasIt);
    4305           0 :   if (hasIt)
    4306           0 :     return 0;
    4307           0 :   (void)query->GetHasSearchTerms(&hasIt);
    4308           0 :   if (hasIt)
    4309           0 :     return 0;
    4310           0 :   if (query->Tags().Length() > 0)
    4311           0 :     return 0;
    4312           0 :   if (aOptions->MaxResults() > 0)
    4313           0 :     return 0;
    4314             : 
    4315             :   // RESULTS_AS_TAG_CONTENTS is quite similar to a folder shortcut, but it must
    4316             :   // not be treated like that, since it needs all query options.
    4317           0 :   if(aOptions->ResultType() == nsINavHistoryQueryOptions::RESULTS_AS_TAG_CONTENTS)
    4318           0 :     return 0;
    4319             : 
    4320             :   // Don't care about onlyBookmarked flag, since specifying a bookmark
    4321             :   // folder is inferring onlyBookmarked.
    4322             : 
    4323           0 :   return query->Folders()[0];
    4324             : }
    4325             : 
    4326             : 
    4327             : // ParseSearchTermsFromQueries
    4328             : //
    4329             : //    Construct a matrix of search terms from the given queries array.
    4330             : //    All of the query objects are ORed together. Within a query, all the terms
    4331             : //    are ANDed together. See nsINavHistoryService.idl.
    4332             : //
    4333             : //    This just breaks the query up into words. We don't do anything fancy,
    4334             : //    not even quoting. We do, however, strip quotes, because people might
    4335             : //    try to input quotes expecting them to do something and get no results
    4336             : //    back.
    4337             : 
    4338           0 : inline bool isQueryWhitespace(char16_t ch)
    4339             : {
    4340           0 :   return ch == ' ';
    4341             : }
    4342             : 
    4343           0 : void ParseSearchTermsFromQueries(const nsCOMArray<nsNavHistoryQuery>& aQueries,
    4344             :                                  nsTArray<nsTArray<nsString>*>* aTerms)
    4345             : {
    4346           0 :   int32_t lastBegin = -1;
    4347           0 :   for (int32_t i = 0; i < aQueries.Count(); i++) {
    4348           0 :     nsTArray<nsString> *queryTerms = new nsTArray<nsString>();
    4349             :     bool hasSearchTerms;
    4350           0 :     if (NS_SUCCEEDED(aQueries[i]->GetHasSearchTerms(&hasSearchTerms)) &&
    4351             :         hasSearchTerms) {
    4352           0 :       const nsString& searchTerms = aQueries[i]->SearchTerms();
    4353           0 :       for (uint32_t j = 0; j < searchTerms.Length(); j++) {
    4354           0 :         if (isQueryWhitespace(searchTerms[j]) ||
    4355           0 :             searchTerms[j] == '"') {
    4356           0 :           if (lastBegin >= 0) {
    4357             :             // found the end of a word
    4358           0 :             queryTerms->AppendElement(Substring(searchTerms, lastBegin,
    4359           0 :                                                j - lastBegin));
    4360           0 :             lastBegin = -1;
    4361             :           }
    4362             :         } else {
    4363           0 :           if (lastBegin < 0) {
    4364             :             // found the beginning of a word
    4365           0 :             lastBegin = j;
    4366             :           }
    4367             :         }
    4368             :       }
    4369             :       // last word
    4370           0 :       if (lastBegin >= 0)
    4371           0 :         queryTerms->AppendElement(Substring(searchTerms, lastBegin));
    4372             :     }
    4373           0 :     aTerms->AppendElement(queryTerms);
    4374             :   }
    4375           0 : }
    4376             : 
    4377             : } // namespace
    4378             : 
    4379             : 
    4380             : nsresult
    4381           0 : nsNavHistory::UpdateFrecency(int64_t aPlaceId)
    4382             : {
    4383           0 :   nsCOMPtr<mozIStorageAsyncStatement> updateFrecencyStmt = mDB->GetAsyncStatement(
    4384             :     "UPDATE moz_places "
    4385             :     "SET frecency = NOTIFY_FRECENCY("
    4386             :       "CALCULATE_FRECENCY(:page_id), url, guid, hidden, last_visit_date"
    4387             :     ") "
    4388             :     "WHERE id = :page_id"
    4389           0 :   );
    4390           0 :   NS_ENSURE_STATE(updateFrecencyStmt);
    4391           0 :   nsresult rv = updateFrecencyStmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"),
    4392           0 :                                                     aPlaceId);
    4393           0 :   NS_ENSURE_SUCCESS(rv, rv);
    4394           0 :   nsCOMPtr<mozIStorageAsyncStatement> updateHiddenStmt = mDB->GetAsyncStatement(
    4395             :     "UPDATE moz_places "
    4396             :     "SET hidden = 0 "
    4397             :     "WHERE id = :page_id AND frecency <> 0"
    4398           0 :   );
    4399           0 :   NS_ENSURE_STATE(updateHiddenStmt);
    4400           0 :   rv = updateHiddenStmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"),
    4401           0 :                                          aPlaceId);
    4402           0 :   NS_ENSURE_SUCCESS(rv, rv);
    4403             : 
    4404           0 :   nsCOMPtr<mozIStorageConnection> conn = mDB->MainConn();
    4405           0 :   if (!conn) {
    4406           0 :     return NS_ERROR_UNEXPECTED;
    4407             :   }
    4408             : 
    4409             :   mozIStorageBaseStatement *stmts[] = {
    4410           0 :     updateFrecencyStmt.get()
    4411           0 :   , updateHiddenStmt.get()
    4412           0 :   };
    4413             :   RefPtr<AsyncStatementCallbackNotifier> cb =
    4414           0 :     new AsyncStatementCallbackNotifier(TOPIC_FRECENCY_UPDATED);
    4415           0 :   nsCOMPtr<mozIStoragePendingStatement> ps;
    4416           0 :   rv = conn->ExecuteAsync(stmts, ArrayLength(stmts), cb,
    4417           0 :                                      getter_AddRefs(ps));
    4418           0 :   NS_ENSURE_SUCCESS(rv, rv);
    4419             : 
    4420           0 :   return NS_OK;
    4421             : }
    4422             : 
    4423             : 
    4424             : namespace {
    4425             : 
    4426           0 : class FixInvalidFrecenciesCallback : public AsyncStatementCallbackNotifier
    4427             : {
    4428             : public:
    4429           0 :   FixInvalidFrecenciesCallback()
    4430           0 :     : AsyncStatementCallbackNotifier(TOPIC_FRECENCY_UPDATED)
    4431             :   {
    4432           0 :   }
    4433             : 
    4434           0 :   NS_IMETHOD HandleCompletion(uint16_t aReason)
    4435             :   {
    4436           0 :     nsresult rv = AsyncStatementCallbackNotifier::HandleCompletion(aReason);
    4437           0 :     NS_ENSURE_SUCCESS(rv, rv);
    4438           0 :     if (aReason == REASON_FINISHED) {
    4439           0 :       nsNavHistory *navHistory = nsNavHistory::GetHistoryService();
    4440           0 :       NS_ENSURE_STATE(navHistory);
    4441           0 :       navHistory->NotifyManyFrecenciesChanged();
    4442             :     }
    4443           0 :     return NS_OK;
    4444             :   }
    4445             : };
    4446             : 
    4447             : } // namespace
    4448             : 
    4449             : nsresult
    4450           0 : nsNavHistory::FixInvalidFrecencies()
    4451             : {
    4452           0 :   nsCOMPtr<mozIStorageAsyncStatement> stmt = mDB->GetAsyncStatement(
    4453             :     "UPDATE moz_places "
    4454             :     "SET frecency = CALCULATE_FRECENCY(id) "
    4455             :     "WHERE frecency < 0"
    4456           0 :   );
    4457           0 :   NS_ENSURE_STATE(stmt);
    4458             : 
    4459             :   RefPtr<FixInvalidFrecenciesCallback> callback =
    4460           0 :     new FixInvalidFrecenciesCallback();
    4461           0 :   nsCOMPtr<mozIStoragePendingStatement> ps;
    4462           0 :   (void)stmt->ExecuteAsync(callback, getter_AddRefs(ps));
    4463             : 
    4464           0 :   return NS_OK;
    4465             : }
    4466             : 
    4467             : 
    4468             : #ifdef MOZ_XUL
    4469             : 
    4470             : nsresult
    4471           0 : nsNavHistory::AutoCompleteFeedback(int32_t aIndex,
    4472             :                                    nsIAutoCompleteController *aController)
    4473             : {
    4474           0 :   nsCOMPtr<mozIStorageAsyncStatement> stmt = mDB->GetAsyncStatement(
    4475             :     "INSERT OR REPLACE INTO moz_inputhistory "
    4476             :     // use_count will asymptotically approach the max of 10.
    4477             :     "SELECT h.id, IFNULL(i.input, :input_text), IFNULL(i.use_count, 0) * .9 + 1 "
    4478             :     "FROM moz_places h "
    4479             :     "LEFT JOIN moz_inputhistory i ON i.place_id = h.id AND i.input = :input_text "
    4480             :     "WHERE url_hash = hash(:page_url) AND url = :page_url "
    4481           0 :   );
    4482           0 :   NS_ENSURE_STATE(stmt);
    4483             : 
    4484           0 :   nsAutoString input;
    4485           0 :   nsresult rv = aController->GetSearchString(input);
    4486           0 :   NS_ENSURE_SUCCESS(rv, rv);
    4487           0 :   rv = stmt->BindStringByName(NS_LITERAL_CSTRING("input_text"), input);
    4488           0 :   NS_ENSURE_SUCCESS(rv, rv);
    4489             : 
    4490           0 :   nsAutoString url;
    4491           0 :   rv = aController->GetValueAt(aIndex, url);
    4492           0 :   NS_ENSURE_SUCCESS(rv, rv);
    4493           0 :   rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"),
    4494           0 :                        NS_ConvertUTF16toUTF8(url));
    4495           0 :   NS_ENSURE_SUCCESS(rv, rv);
    4496             : 
    4497             :   // We do the update asynchronously and we do not care about failures.
    4498             :   RefPtr<AsyncStatementCallbackNotifier> callback =
    4499           0 :     new AsyncStatementCallbackNotifier(TOPIC_AUTOCOMPLETE_FEEDBACK_UPDATED);
    4500           0 :   nsCOMPtr<mozIStoragePendingStatement> canceler;
    4501           0 :   rv = stmt->ExecuteAsync(callback, getter_AddRefs(canceler));
    4502           0 :   NS_ENSURE_SUCCESS(rv, rv);
    4503             : 
    4504           0 :   return NS_OK;
    4505             : }
    4506             : 
    4507             : #endif
    4508             : 
    4509             : 
    4510             : nsICollation *
    4511           0 : nsNavHistory::GetCollation()
    4512             : {
    4513           0 :   if (mCollation)
    4514           0 :     return mCollation;
    4515             : 
    4516             :   // collation
    4517             :   nsCOMPtr<nsICollationFactory> cfact =
    4518           0 :     do_CreateInstance(NS_COLLATIONFACTORY_CONTRACTID);
    4519           0 :   NS_ENSURE_TRUE(cfact, nullptr);
    4520           0 :   nsresult rv = cfact->CreateCollation(getter_AddRefs(mCollation));
    4521           0 :   NS_ENSURE_SUCCESS(rv, nullptr);
    4522             : 
    4523           0 :   return mCollation;
    4524             : }
    4525             : 
    4526             : nsIStringBundle *
    4527           0 : nsNavHistory::GetBundle()
    4528             : {
    4529           0 :   if (!mBundle) {
    4530             :     nsCOMPtr<nsIStringBundleService> bundleService =
    4531           0 :       services::GetStringBundleService();
    4532           0 :     NS_ENSURE_TRUE(bundleService, nullptr);
    4533           0 :     nsresult rv = bundleService->CreateBundle(
    4534             :         "chrome://places/locale/places.properties",
    4535           0 :         getter_AddRefs(mBundle));
    4536           0 :     NS_ENSURE_SUCCESS(rv, nullptr);
    4537             :   }
    4538           0 :   return mBundle;
    4539             : }

Generated by: LCOV version 1.13