/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef nsNavHistory_h_ #define nsNavHistory_h_ #include "nsINavHistoryService.h" #include "nsIStringBundle.h" #include "nsITimer.h" #include "nsMaybeWeakPtr.h" #include "nsCategoryCache.h" #include "nsNetCID.h" #include "nsToolkitCompsCID.h" #include "nsURIHashKey.h" #include "nsTHashtable.h" #include "nsNavHistoryResult.h" #include "nsNavHistoryQuery.h" #include "Database.h" #include "mozilla/Atomics.h" #include "mozilla/Attributes.h" #include "mozilla/intl/Collator.h" #include "mozilla/UniquePtr.h" #include "mozIStorageVacuumParticipant.h" #define QUERYUPDATE_TIME 0 #define QUERYUPDATE_SIMPLE 1 #define QUERYUPDATE_COMPLEX 2 #define QUERYUPDATE_COMPLEX_WITH_BOOKMARKS 3 #define QUERYUPDATE_HOST 4 #define QUERYUPDATE_MOBILEPREF 5 #define QUERYUPDATE_NONE 6 // Clamp title and URL to generously large, but not too large, length. // See bug 319004 for details. #define URI_LENGTH_MAX 65536 #define TITLE_LENGTH_MAX 4096 // Microsecond timeout for "recent" events such as typed and bookmark following. // If you typed it more than this time ago, it's not recent. #define RECENT_EVENT_THRESHOLD PRTime((int64_t)15 * 60 * PR_USEC_PER_SEC) // The preference we watch to know when the mobile bookmarks folder is filled by // sync. #define MOBILE_BOOKMARKS_PREF "browser.bookmarks.showMobileBookmarks" // The guid of the mobile bookmarks virtual query. #define MOBILE_BOOKMARKS_VIRTUAL_GUID "mobile_____v" #define ROOT_GUID "root________" #define MENU_ROOT_GUID "menu________" #define TOOLBAR_ROOT_GUID "toolbar_____" #define UNFILED_ROOT_GUID "unfiled_____" #define TAGS_ROOT_GUID "tags________" #define MOBILE_ROOT_GUID "mobile______" class mozIStorageValueArray; class nsIAutoCompleteController; class nsIEffectiveTLDService; class nsIIDNService; class nsNavHistory; class PlacesSQLQueryBuilder; // nsNavHistory class nsNavHistory final : public nsSupportsWeakReference, public nsINavHistoryService, public nsIObserver, public mozIStorageVacuumParticipant { friend class PlacesSQLQueryBuilder; public: nsNavHistory(); NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_NSINAVHISTORYSERVICE NS_DECL_NSIOBSERVER NS_DECL_MOZISTORAGEVACUUMPARTICIPANT /** * Obtains the nsNavHistory object. */ static already_AddRefed GetSingleton(); /** * Initializes the nsNavHistory object. This should only be called once. */ nsresult Init(); /** * Used by other components in the places directory such as the annotation * service to get a reference to this history object. Returns a pointer to * the service if it exists. Otherwise creates one. Returns nullptr on error. */ static nsNavHistory* GetHistoryService() { if (!gHistoryService) { nsCOMPtr serv = do_GetService(NS_NAVHISTORYSERVICE_CONTRACTID); NS_ENSURE_TRUE(serv, nullptr); NS_ASSERTION(gHistoryService, "Should have static instance pointer now"); } return gHistoryService; } /** * Used by other components in the places directory to get a reference to a * const version of this history object. * * @return A pointer to a const version of the service if it exists, * nullptr otherwise. */ static const nsNavHistory* GetConstHistoryService() { const nsNavHistory* const history = gHistoryService; return history; } /** * Fetches the database id and the GUID associated to the given URI. * * @param aURI * The page to look for. * @param _pageId * Will be set to the database id associated with the page. * If the page doesn't exist, this will be zero. * @param _GUID * Will be set to the unique id associated with the page. * If the page doesn't exist, this will be empty. * @note This DOES NOT check for bad URLs other than that they're nonempty. */ nsresult GetIdForPage(nsIURI* aURI, int64_t* _pageId, nsCString& _GUID); /** * Fetches the database id and the GUID associated to the given URI, creating * a new database entry if one doesn't exist yet. * * @param aURI * The page to look for or create. * @param _pageId * Will be set to the database id associated with the page. * @param _GUID * Will be set to the unique id associated with the page. * @note This DOES NOT check for bad URLs other than that they're nonempty. * @note This DOES NOT update frecency of the page. */ nsresult GetOrCreateIdForPage(nsIURI* aURI, int64_t* _pageId, nsCString& _GUID); /** * These functions return non-owning references to the locale-specific * objects for places components. */ nsIStringBundle* GetBundle(); const mozilla::intl::Collator* GetCollator(); void GetStringFromName(const char* aName, nsACString& aResult); void GetAgeInDaysString(int32_t aInt, const char* aName, nsACString& aResult); static void GetMonthName(const PRExplodedTime& aTime, nsACString& aResult); static void GetMonthYear(const PRExplodedTime& aTime, nsACString& aResult); // Returns whether history is enabled or not. bool IsHistoryDisabled() { return !mHistoryEnabled; } // Returns whether or not diacritics must match in history text searches. bool MatchDiacritics() const { return mMatchDiacritics; } // Constants for the columns returned by the above statement. static const int32_t kGetInfoIndex_PageID; static const int32_t kGetInfoIndex_URL; static const int32_t kGetInfoIndex_Title; static const int32_t kGetInfoIndex_RevHost; static const int32_t kGetInfoIndex_VisitCount; static const int32_t kGetInfoIndex_VisitDate; static const int32_t kGetInfoIndex_FaviconURL; static const int32_t kGetInfoIndex_ItemId; static const int32_t kGetInfoIndex_ItemDateAdded; static const int32_t kGetInfoIndex_ItemLastModified; static const int32_t kGetInfoIndex_ItemParentId; static const int32_t kGetInfoIndex_ItemTags; static const int32_t kGetInfoIndex_Frecency; static const int32_t kGetInfoIndex_Hidden; static const int32_t kGetInfoIndex_Guid; static const int32_t kGetInfoIndex_VisitId; static const int32_t kGetInfoIndex_FromVisitId; static const int32_t kGetInfoIndex_VisitType; int64_t GetTagsFolder(); // this actually executes a query and gives you results, it is used by // nsNavHistoryQueryResultNode nsresult GetQueryResults(nsNavHistoryQueryResultNode* aResultNode, const RefPtr& aQuery, const RefPtr& aOptions, nsCOMArray* aResults); // Take a row of kGetInfoIndex_* columns and construct a ResultNode. // The row must contain the full set of columns. nsresult RowToResult(mozIStorageValueArray* aRow, nsNavHistoryQueryOptions* aOptions, nsNavHistoryResultNode** aResult); nsresult QueryRowToResult(int64_t aItemId, const nsACString& aBookmarkGuid, const nsACString& aURI, const nsACString& aTitle, uint32_t aAccessCount, PRTime aTime, nsNavHistoryResultNode** aNode); nsresult VisitIdToResultNode(int64_t visitId, nsNavHistoryQueryOptions* aOptions, nsNavHistoryResultNode** aResult); nsresult BookmarkIdToResultNode(int64_t aBookmarkId, nsNavHistoryQueryOptions* aOptions, nsNavHistoryResultNode** aResult); nsresult URIToResultNode(nsIURI* aURI, nsNavHistoryQueryOptions* aOptions, nsNavHistoryResultNode** aResult); /** * Returns current number of days stored in history. */ int32_t GetDaysOfHistory(); void DomainNameFromURI(nsIURI* aURI, nsACString& aDomainName); static PRTime NormalizeTime(uint32_t aRelative, PRTime aOffset); typedef nsTHashMap StringHash; enum RecentEventFlags { RECENT_TYPED = 1 << 0, // User typed in URL recently RECENT_ACTIVATED = 1 << 1, // User tapped URL link recently RECENT_BOOKMARKED = 1 << 2 // User bookmarked URL recently }; /** * Returns any recent activity done with a URL. * @return Any recent events associated with this URI. Each bit is set * according to RecentEventFlags enum values. */ uint32_t GetRecentFlags(nsIURI* aURI); /** * Whether there are visits. * Note: This may cause synchronous I/O. */ bool hasHistoryEntries(); /** * Returns whether the specified url has a embed visit. * * @param aURI * URI of the page. * @return whether the page has a embed visit. */ bool hasEmbedVisit(nsIURI* aURI); int32_t GetFrecencyAgedWeight(int32_t aAgeInDays) const { if (aAgeInDays <= mFirstBucketCutoffInDays) { return mFirstBucketWeight; } if (aAgeInDays <= mSecondBucketCutoffInDays) { return mSecondBucketWeight; } if (aAgeInDays <= mThirdBucketCutoffInDays) { return mThirdBucketWeight; } if (aAgeInDays <= mFourthBucketCutoffInDays) { return mFourthBucketWeight; } return mDefaultWeight; } int32_t GetFrecencyTransitionBonus(int32_t aTransitionType, bool aVisited, bool aRedirect = false) const { if (aRedirect) { return mRedirectSourceVisitBonus; } switch (aTransitionType) { case nsINavHistoryService::TRANSITION_EMBED: return mEmbedVisitBonus; case nsINavHistoryService::TRANSITION_FRAMED_LINK: return mFramedLinkVisitBonus; case nsINavHistoryService::TRANSITION_LINK: return mLinkVisitBonus; case nsINavHistoryService::TRANSITION_TYPED: return aVisited ? mTypedVisitBonus : mUnvisitedTypedBonus; case nsINavHistoryService::TRANSITION_BOOKMARK: return aVisited ? mBookmarkVisitBonus : mUnvisitedBookmarkBonus; case nsINavHistoryService::TRANSITION_DOWNLOAD: return mDownloadVisitBonus; case nsINavHistoryService::TRANSITION_REDIRECT_PERMANENT: return mPermRedirectVisitBonus; case nsINavHistoryService::TRANSITION_REDIRECT_TEMPORARY: return mTempRedirectVisitBonus; case nsINavHistoryService::TRANSITION_RELOAD: return mReloadVisitBonus; default: // 0 == undefined (see bug #375777 for details) NS_WARNING_ASSERTION(!aTransitionType, "new transition but no bonus for frecency"); return mDefaultVisitBonus; } } int32_t GetNumVisitsForFrecency() const { return mNumVisitsForFrecency; } /** * Updates and invalidates the mDaysOfHistory cache. Should be * called whenever a visit is added. */ void UpdateDaysOfHistory(PRTime visitTime); /** * Store last insterted id for a table. */ static mozilla::Atomic sLastInsertedPlaceId; static mozilla::Atomic sLastInsertedVisitId; /** * Tracks whether frecency is currently being decayed. */ static mozilla::Atomic sIsFrecencyDecaying; /** * Tracks whether there's frecency to be recalculated. */ static mozilla::Atomic sShouldStartFrecencyRecalculation; static void StoreLastInsertedId(const nsACString& aTable, const int64_t aLastInsertedId); static nsresult FilterResultSet( nsNavHistoryQueryResultNode* aParentNode, const nsCOMArray& aSet, nsCOMArray* aFiltered, const RefPtr& aQuery, nsNavHistoryQueryOptions* aOptions); static void InvalidateDaysOfHistory(); private: ~nsNavHistory(); // used by GetHistoryService static nsNavHistory* gHistoryService; static mozilla::Atomic sDaysOfHistory; protected: // Database handle. RefPtr mDB; /** * Loads all of the preferences that we use into member variables. * * @note If mPrefBranch is nullptr, this does nothing. */ void LoadPrefs(); /** * Calculates and returns value for mCachedNow. * This is an hack to avoid calling PR_Now() too often, as is the case when * we're asked the ageindays of many history entries in a row. A timer is * set which will clear our valid flag after a short timeout. */ PRTime GetNow(); PRTime mCachedNow; nsCOMPtr mExpireNowTimer; /** * Called when the cached now value is expired and needs renewal. */ static void expireNowTimerCallback(nsITimer* aTimer, void* aClosure); nsresult ConstructQueryString( const RefPtr& aQuery, const RefPtr& aOptions, nsCString& queryString, bool& aParamsPresent, StringHash& aAddParams); nsresult QueryToSelectClause(const RefPtr& aQuery, const RefPtr& aOptions, nsCString* aClause); nsresult BindQueryClauseParameters( mozIStorageBaseStatement* statement, const RefPtr& aQuery, const RefPtr& aOptions); nsresult ResultsAsList(mozIStorageStatement* statement, nsNavHistoryQueryOptions* aOptions, nsCOMArray* aResults); // effective tld service nsCOMPtr mTLDService; nsCOMPtr mIDNService; // localization nsCOMPtr mBundle; mozilla::UniquePtr mCollator; // recent events typedef nsTHashMap RecentEventHash; RecentEventHash mRecentTyped; RecentEventHash mRecentLink; RecentEventHash mRecentBookmark; bool CheckIsRecentEvent(RecentEventHash* hashTable, const nsACString& url); void ExpireNonrecentEvents(RecentEventHash* hashTable); // Whether history is enabled or not. // Will mimic value of the places.history.enabled preference. bool mHistoryEnabled; // Whether or not diacritics must match in history text searches. // Will mimic value of the places.search.matchDiacritics preference. bool mMatchDiacritics; // Frecency preferences. int32_t mNumVisitsForFrecency; int32_t mFirstBucketCutoffInDays; int32_t mSecondBucketCutoffInDays; int32_t mThirdBucketCutoffInDays; int32_t mFourthBucketCutoffInDays; int32_t mFirstBucketWeight; int32_t mSecondBucketWeight; int32_t mThirdBucketWeight; int32_t mFourthBucketWeight; int32_t mDefaultWeight; int32_t mEmbedVisitBonus; int32_t mFramedLinkVisitBonus; int32_t mLinkVisitBonus; int32_t mTypedVisitBonus; int32_t mBookmarkVisitBonus; int32_t mDownloadVisitBonus; int32_t mPermRedirectVisitBonus; int32_t mTempRedirectVisitBonus; int32_t mRedirectSourceVisitBonus; int32_t mDefaultVisitBonus; int32_t mUnvisitedBookmarkBonus; int32_t mUnvisitedTypedBonus; int32_t mReloadVisitBonus; nsresult RecalculateOriginFrecencyStatsInternal(); // in nsNavHistoryQuery.cpp nsresult TokensToQuery( const nsTArray& aTokens, nsNavHistoryQuery* aQuery, nsNavHistoryQueryOptions* aOptions); int64_t mTagsFolder; int64_t mLastCachedStartOfDay; int64_t mLastCachedEndOfDay; }; #define PLACES_URI_PREFIX "place:" /* Returns true if the given URI represents a history query. */ inline bool IsQueryURI(const nsCString& uri) { return StringBeginsWith(uri, nsLiteralCString(PLACES_URI_PREFIX)); } /* Extracts the query string from a query URI. */ inline const nsDependentCSubstring QueryURIToQuery(const nsCString& uri) { NS_ASSERTION(IsQueryURI(uri), "should only be called for query URIs"); return Substring(uri, nsLiteralCString(PLACES_URI_PREFIX).Length()); } #endif // nsNavHistory_h_