Bug 1261386 - Avoid history floding by repeated reloads. r=adw draft
authorMarco Bonardo <mbonardo@mozilla.com>
Thu, 26 May 2016 17:49:40 +0200
changeset 372160 ce5b6a40214a8f2d01b6f4043e7d738e513ad404
parent 371315 a33592050e2465ea62f9d62c42623c9c88b9808f
child 522109 b3bf07949c3fe252ba374b061ba2343c393a2b94
push id19451
push usermak77@bonardo.net
push dateFri, 27 May 2016 16:08:38 +0000
reviewersadw
bugs1261386
milestone49.0a1
Bug 1261386 - Avoid history floding by repeated reloads. r=adw MozReview-Commit-ID: FhU8nOoNUHb
browser/app/profile/firefox.js
services/sync/modules/engines/history.js
toolkit/components/places/History.cpp
toolkit/components/places/History.h
toolkit/components/places/History.jsm
toolkit/components/places/PlacesDBUtils.jsm
toolkit/components/places/SQLFunctions.cpp
toolkit/components/places/nsINavHistoryService.idl
toolkit/components/places/nsNavHistory.cpp
toolkit/components/places/nsNavHistory.h
toolkit/components/places/nsPlacesTriggers.h
toolkit/components/places/tests/head_common.js
toolkit/components/places/tests/unit/test_000_frecency.js
toolkit/components/places/tests/unit/test_425563.js
toolkit/components/places/tests/unit/test_454977.js
toolkit/components/places/tests/unit/test_async_history_api.js
toolkit/components/places/tests/unit/test_history_clear.js
toolkit/components/places/tests/unit/test_isURIVisited.js
toolkit/components/places/tests/unit/test_preventive_maintenance.js
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -829,16 +829,17 @@ pref("places.frecency.defaultBucketWeigh
 pref("places.frecency.embedVisitBonus", 0);
 pref("places.frecency.framedLinkVisitBonus", 0);
 pref("places.frecency.linkVisitBonus", 100);
 pref("places.frecency.typedVisitBonus", 2000);
 pref("places.frecency.bookmarkVisitBonus", 75);
 pref("places.frecency.downloadVisitBonus", 0);
 pref("places.frecency.permRedirectVisitBonus", 0);
 pref("places.frecency.tempRedirectVisitBonus", 0);
+pref("places.frecency.reloadVisitBonus", 0);
 pref("places.frecency.defaultVisitBonus", 0);
 
 // bonus (in percent) for place types for frecency calculations
 pref("places.frecency.unvisitedBookmarkBonus", 140);
 pref("places.frecency.unvisitedTypedBonus", 200);
 
 // Controls behavior of the "Add Exception" dialog launched from SSL error pages
 // 0 - don't pre-populate anything
--- a/services/sync/modules/engines/history.js
+++ b/services/sync/modules/engines/history.js
@@ -297,18 +297,18 @@ HistoryStore.prototype = {
       let visit = record.visits[k] = record.visits[i];
 
       if (!visit.date || typeof visit.date != "number") {
         this._log.warn("Encountered record with invalid visit date: "
                        + visit.date);
         continue;
       }
 
-      if (!visit.type || !(visit.type >= PlacesUtils.history.TRANSITION_LINK &&
-                           visit.type <= PlacesUtils.history.TRANSITION_FRAMED_LINK)) {
+      if (!visit.type ||
+          !Object.values(PlacesUtils.history.TRANSITIONS).includes(visit.type)) {
         this._log.warn("Encountered record with invalid visit type: " +
                        visit.type + "; ignoring.");
         continue;
       }
 
       // Dates need to be integers.
       visit.date = Math.round(visit.date);
 
--- a/toolkit/components/places/History.cpp
+++ b/toolkit/components/places/History.cpp
@@ -1205,18 +1205,18 @@ private:
     }
     rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("from_visit"),
                                aReferrer.visitId);
     NS_ENSURE_SUCCESS(rv, rv);
     rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("visit_date"),
                                _place.visitTime);
     NS_ENSURE_SUCCESS(rv, rv);
     uint32_t transitionType = _place.transitionType;
-    NS_ASSERTION(transitionType >= nsINavHistoryService::TRANSITION_LINK &&
-                 transitionType <= nsINavHistoryService::TRANSITION_FRAMED_LINK,
+    MOZ_ASSERT(transitionType >= nsINavHistoryService::TRANSITION_LINK &&
+               transitionType <= nsINavHistoryService::TRANSITION_RELOAD,
                  "Invalid transition type!");
     rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("visit_type"),
                                transitionType);
     NS_ENSURE_SUCCESS(rv, rv);
 
     mozStorageStatementScoper scoper(stmt);
     rv = stmt->Execute();
     NS_ENSURE_SUCCESS(rv, rv);
@@ -1965,17 +1965,17 @@ StoreAndNotifyEmbedVisit(VisitData& aPla
 //// History
 
 History* History::gService = nullptr;
 
 History::History()
   : mShuttingDown(false)
   , mShutdownMutex("History::mShutdownMutex")
   , mObservers(VISIT_OBSERVERS_INITIAL_CACHE_LENGTH)
-  , mRecentlyVisitedURIsNextIndex(0)
+  , mRecentlyVisitedURIs(RECENTLY_VISITED_URIS_SIZE)
 {
   NS_ASSERTION(!gService, "Ruh-roh!  This service has already been created!");
   gService = this;
 
   nsCOMPtr<nsIObserverService> os = services::GetObserverService();
   NS_WARN_IF_FALSE(os, "Observer service was not found!");
   if (os) {
     (void)os->AddObserver(this, TOPIC_PLACES_SHUTDOWN, false);
@@ -2401,35 +2401,38 @@ History::Shutdown()
 
   if (mConcurrentStatementsHolder) {
     mConcurrentStatementsHolder->Shutdown();
   }
 }
 
 void
 History::AppendToRecentlyVisitedURIs(nsIURI* aURI) {
-  if (mRecentlyVisitedURIs.Length() < RECENTLY_VISITED_URI_SIZE) {
-    // Append a new element while the array is not full.
-    mRecentlyVisitedURIs.AppendElement(aURI);
-  } else {
-    // Otherwise, replace the oldest member.
-    mRecentlyVisitedURIsNextIndex %= RECENTLY_VISITED_URI_SIZE;
-    mRecentlyVisitedURIs.ElementAt(mRecentlyVisitedURIsNextIndex) = aURI;
-    mRecentlyVisitedURIsNextIndex++;
+  // Add a new entry, if necessary.
+  RecentURIKey* entry = mRecentlyVisitedURIs.GetEntry(aURI);
+  if (!entry) {
+    entry = mRecentlyVisitedURIs.PutEntry(aURI);
+  }
+  if (entry) {
+    entry->time = PR_Now();
+  }
+
+  // Expire oldest entries.
+  for (auto iter = mRecentlyVisitedURIs.Iter(); !iter.Done(); iter.Next()) {
+    RecentURIKey* entry = iter.Get();
+    if ((PR_Now() - entry->time) > RECENTLY_VISITED_URIS_MAX_AGE) {
+      iter.Remove();
+    }
   }
 }
 
 inline bool
 History::IsRecentlyVisitedURI(nsIURI* aURI) {
-  bool equals = false;
-  RecentlyVisitedArray::index_type i;
-  for (i = 0; i < mRecentlyVisitedURIs.Length() && !equals; ++i) {
-    aURI->Equals(mRecentlyVisitedURIs.ElementAt(i), &equals);
-  }
-  return equals;
+  RecentURIKey* entry = mRecentlyVisitedURIs.GetEntry(aURI);
+  return entry && (PR_Now() - entry->time) < RECENTLY_VISITED_URIS_MAX_AGE;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 //// IHistory
 
 NS_IMETHODIMP
 History::VisitURI(nsIURI* aURI,
                   nsIURI* aLastVisitedURI,
@@ -2461,22 +2464,24 @@ History::VisitURI(nsIURI* aURI,
   // Silently return if URI is something we shouldn't add to DB.
   bool canAdd;
   nsresult rv = navHistory->CanAddURI(aURI, &canAdd);
   NS_ENSURE_SUCCESS(rv, rv);
   if (!canAdd) {
     return NS_OK;
   }
 
+  // Do not save a reloaded uri if we have visited the same URI recently.
+  bool reload = false;
   if (aLastVisitedURI) {
-    bool same;
-    rv = aURI->Equals(aLastVisitedURI, &same);
+    rv = aURI->Equals(aLastVisitedURI, &reload);
     NS_ENSURE_SUCCESS(rv, rv);
-    if (same && IsRecentlyVisitedURI(aURI)) {
-      // Do not save refresh visits if we have visited this URI recently.
+    if (reload && IsRecentlyVisitedURI(aURI)) {
+      // Regardless we must update the stored visit time.
+      AppendToRecentlyVisitedURIs(aURI);
       return NS_OK;
     }
   }
 
   nsTArray<VisitData> placeArray(1);
   NS_ENSURE_TRUE(placeArray.AppendElement(VisitData(aURI, aLastVisitedURI)),
                  NS_ERROR_OUT_OF_MEMORY);
   VisitData& place = placeArray.ElementAt(0);
@@ -2502,16 +2507,19 @@ History::VisitURI(nsIURI* aURI,
     transitionType = nsINavHistoryService::TRANSITION_EMBED;
   }
   else if (aFlags & IHistory::REDIRECT_TEMPORARY) {
     transitionType = nsINavHistoryService::TRANSITION_REDIRECT_TEMPORARY;
   }
   else if (aFlags & IHistory::REDIRECT_PERMANENT) {
     transitionType = nsINavHistoryService::TRANSITION_REDIRECT_PERMANENT;
   }
+  else if (reload) {
+    transitionType = nsINavHistoryService::TRANSITION_RELOAD;
+  }
   else if ((recentFlags & nsNavHistory::RECENT_TYPED) &&
            !(aFlags & IHistory::UNRECOVERABLE_ERROR)) {
     // Don't mark error pages as typed, even if they were actually typed by
     // the user.  This is useful to limit their score in autocomplete.
     transitionType = nsINavHistoryService::TRANSITION_TYPED;
   }
   else if (recentFlags & nsNavHistory::RECENT_BOOKMARKED) {
     transitionType = nsINavHistoryService::TRANSITION_BOOKMARK;
@@ -2943,17 +2951,17 @@ History::UpdatePlaces(JS::Handle<JS::Val
       // We must have a date and a transaction type!
       rv = GetIntFromJSObject(aCtx, visit, "visitDate", &data.visitTime);
       NS_ENSURE_SUCCESS(rv, rv);
       uint32_t transitionType = 0;
       rv = GetIntFromJSObject(aCtx, visit, "transitionType", &transitionType);
       NS_ENSURE_SUCCESS(rv, rv);
       NS_ENSURE_ARG_RANGE(transitionType,
                           nsINavHistoryService::TRANSITION_LINK,
-                          nsINavHistoryService::TRANSITION_FRAMED_LINK);
+                          nsINavHistoryService::TRANSITION_RELOAD);
       data.SetTransitionType(transitionType);
       data.hidden = GetHiddenState(false, transitionType);
 
       // If the visit is an embed visit, we do not actually add it to the
       // database.
       if (transitionType == nsINavHistoryService::TRANSITION_EMBED) {
         StoreAndNotifyEmbedVisit(data, aCallback);
         visitData.RemoveElementAt(visitData.Length() - 1);
--- a/toolkit/components/places/History.h
+++ b/toolkit/components/places/History.h
@@ -28,18 +28,22 @@ namespace mozilla {
 namespace places {
 
 struct VisitData;
 class ConcurrentStatementsHolder;
 
 #define NS_HISTORYSERVICE_CID \
   {0x0937a705, 0x91a6, 0x417a, {0x82, 0x92, 0xb2, 0x2e, 0xb1, 0x0d, 0xa8, 0x6c}}
 
-// Max size of History::mRecentlyVisitedURIs
-#define RECENTLY_VISITED_URI_SIZE 8
+// Initial size of mRecentlyVisitedURIs.
+#define RECENTLY_VISITED_URIS_SIZE 64
+// Microseconds after which a visit can be expired from mRecentlyVisitedURIs.
+// If an URI is reloaded before this time, we don't store it in history.
+// Since a commonly found case is to reload every 5 minutes, we pick 6 minutes.
+#define RECENTLY_VISITED_URIS_MAX_AGE 6 * 60 * PR_USEC_PER_SEC
 
 class History final : public IHistory
                     , public nsIDownloadHistory
                     , public mozIAsyncHistory
                     , public nsIObserver
                     , public nsIMemoryReporter
 {
 public:
@@ -184,23 +188,35 @@ private:
       return array.ShallowSizeOfExcludingThis(aMallocSizeOf);
     }
     ObserverArray array;
   };
 
   nsTHashtable<KeyClass> mObservers;
 
   /**
-   * mRecentlyVisitedURIs remembers URIs which are recently added to the DB,
-   * to avoid saving these locations repeatedly in a short period.
+   * mRecentlyVisitedURIs remembers URIs which have been recently added to
+   * history, to avoid saving these locations repeatedly in a short period.
    */
-  typedef AutoTArray<nsCOMPtr<nsIURI>, RECENTLY_VISITED_URI_SIZE>
-          RecentlyVisitedArray;
-  RecentlyVisitedArray mRecentlyVisitedURIs;
-  RecentlyVisitedArray::index_type mRecentlyVisitedURIsNextIndex;
-
+  class RecentURIKey : public nsURIHashKey
+  {
+  public:
+    explicit RecentURIKey(const nsIURI* aURI) : nsURIHashKey(aURI)
+    {
+    }
+    RecentURIKey(const RecentURIKey& aOther) : nsURIHashKey(aOther)
+    {
+      NS_NOTREACHED("Do not call me!");
+    }
+    PRTime time;
+  };
+  nsTHashtable<RecentURIKey> mRecentlyVisitedURIs;
+  /**
+   * Whether aURI has been visited "recently".
+   * See RECENTLY_VISITED_URIS_MAX_AGE.
+   */
   bool IsRecentlyVisitedURI(nsIURI* aURI);
 };
 
 } // namespace places
 } // namespace mozilla
 
 #endif // mozilla_places_History_h_
--- a/toolkit/components/places/History.jsm
+++ b/toolkit/components/places/History.jsm
@@ -35,17 +35,17 @@
  * See the documentation of individual methods to find out which properties
  * are required for `PageInfo` arguments or returned for `PageInfo` results.
  *
  * A `VisitInfo` object is any object that contains A SUBSET of the following
  * properties:
  * - date: (Date)
  *     The time the visit occurred.
  * - transition: (number)
- *     How the user reached the page. See constants `TRANSITION_*`
+ *     How the user reached the page. See constants `TRANSITIONS.*`
  *     for the possible transition types.
  * - referrer: (URL)
  *          or (nsIURI)
  *          or (string)
  *     The referring URI of this visit. Note that `VisitInfo` passed
  *     as argument may hold `nsIURI` or `string` values for property `referrer`,
  *     but `VisitInfo` objects returned by this module always hold `URL`
  *     values.
@@ -338,61 +338,68 @@ this.History = Object.freeze({
     );
   },
 
   /**
    * Possible values for the `transition` property of `VisitInfo`
    * objects.
    */
 
-  /**
-   * The user followed a link and got a new toplevel window.
-   */
-  TRANSITION_LINK: Ci.nsINavHistoryService.TRANSITION_LINK,
+  TRANSITIONS: {
+    /**
+     * The user followed a link and got a new toplevel window.
+     */
+    LINK: Ci.nsINavHistoryService.TRANSITION_LINK,
 
-  /**
-   * The user typed the page's URL in the URL bar or selected it from
-   * URL bar autocomplete results, clicked on it from a history query
-   * (from the History sidebar, History menu, or history query in the
-   * personal toolbar or Places organizer.
-   */
-  TRANSITION_TYPED: Ci.nsINavHistoryService.TRANSITION_TYPED,
+    /**
+     * The user typed the page's URL in the URL bar or selected it from
+     * URL bar autocomplete results, clicked on it from a history query
+     * (from the History sidebar, History menu, or history query in the
+     * personal toolbar or Places organizer.
+     */
+    TYPED: Ci.nsINavHistoryService.TRANSITION_TYPED,
 
-  /**
-   * The user followed a bookmark to get to the page.
-   */
-  TRANSITION_BOOKMARK: Ci.nsINavHistoryService.TRANSITION_BOOKMARK,
+    /**
+     * The user followed a bookmark to get to the page.
+     */
+    BOOKMARK: Ci.nsINavHistoryService.TRANSITION_BOOKMARK,
 
-  /**
-   * Some inner content is loaded. This is true of all images on a
-   * page, and the contents of the iframe. It is also true of any
-   * content in a frame if the user did not explicitly follow a link
-   * to get there.
-   */
-  TRANSITION_EMBED: Ci.nsINavHistoryService.TRANSITION_EMBED,
+    /**
+     * Some inner content is loaded. This is true of all images on a
+     * page, and the contents of the iframe. It is also true of any
+     * content in a frame if the user did not explicitly follow a link
+     * to get there.
+     */
+    EMBED: Ci.nsINavHistoryService.TRANSITION_EMBED,
 
-  /**
-   * Set when the transition was a permanent redirect.
-   */
-  TRANSITION_REDIRECT_PERMANENT: Ci.nsINavHistoryService.TRANSITION_REDIRECT_PERMANENT,
+    /**
+     * Set when the transition was a permanent redirect.
+     */
+    REDIRECT_PERMANENT: Ci.nsINavHistoryService.TRANSITION_REDIRECT_PERMANENT,
 
-  /**
-   * Set when the transition was a temporary redirect.
-   */
-  TRANSITION_REDIRECT_TEMPORARY: Ci.nsINavHistoryService.TRANSITION_REDIRECT_TEMPORARY,
+    /**
+     * Set when the transition was a temporary redirect.
+     */
+    REDIRECT_TEMPORARY: Ci.nsINavHistoryService.TRANSITION_REDIRECT_TEMPORARY,
+
+    /**
+     * Set when the transition is a download.
+     */
+    DOWNLOAD: Ci.nsINavHistoryService.TRANSITION_DOWNLOAD,
 
-  /**
-   * Set when the transition is a download.
-   */
-  TRANSITION_DOWNLOAD: Ci.nsINavHistoryService.TRANSITION_REDIRECT_DOWNLOAD,
+    /**
+     * The user followed a link and got a visit in a frame.
+     */
+    FRAMED_LINK: Ci.nsINavHistoryService.TRANSITION_FRAMED_LINK,
 
-  /**
-   * The user followed a link and got a visit in a frame.
-   */
-  TRANSITION_FRAMED_LINK: Ci.nsINavHistoryService.TRANSITION_FRAMED_LINK,
+    /**
+     * The user reloaded a page.
+     */
+    RELOAD: Ci.nsINavHistoryService.TRANSITION_RELOAD,
+  },
 });
 
 
 /**
  * Normalize a key to either a string (if it is a valid GUID) or an
  * instance of `URL` (if it is a `URL`, `nsIURI`, or a string
  * representing a valid url).
  *
--- a/toolkit/components/places/PlacesDBUtils.jsm
+++ b/toolkit/components/places/PlacesDBUtils.jsm
@@ -685,23 +685,23 @@ this.PlacesDBUtils = {
              (SELECT id FROM moz_favicons WHERE id = h.favicon_id LIMIT 1)
        )`);
     cleanupStatements.push(fixInvalidFaviconIds);
 
     // L.2 recalculate visit_count and last_visit_date
     let fixVisitStats = DBConn.createAsyncStatement(
       `UPDATE moz_places
        SET visit_count = (SELECT count(*) FROM moz_historyvisits
-                          WHERE place_id = moz_places.id AND visit_type NOT IN (0,4,7,8)),
+                          WHERE place_id = moz_places.id AND visit_type NOT IN (0,4,7,8,9)),
            last_visit_date = (SELECT MAX(visit_date) FROM moz_historyvisits
                               WHERE place_id = moz_places.id)
        WHERE id IN (
          SELECT h.id FROM moz_places h
          WHERE visit_count <> (SELECT count(*) FROM moz_historyvisits v
-                               WHERE v.place_id = h.id AND visit_type NOT IN (0,4,7,8))
+                               WHERE v.place_id = h.id AND visit_type NOT IN (0,4,7,8,9))
             OR last_visit_date <> (SELECT MAX(visit_date) FROM moz_historyvisits v
                                    WHERE v.place_id = h.id)
        )`);
     cleanupStatements.push(fixVisitStats);
 
     // L.3 recalculate hidden for redirects.
     let fixRedirectsHidden = DBConn.createAsyncStatement(
       `UPDATE moz_places
--- a/toolkit/components/places/SQLFunctions.cpp
+++ b/toolkit/components/places/SQLFunctions.cpp
@@ -471,73 +471,74 @@ namespace places {
   NS_IMETHODIMP
   CalculateFrecencyFunction::OnFunctionCall(mozIStorageValueArray *aArguments,
                                             nsIVariant **_result)
   {
     // Fetch arguments.  Use default values if they were omitted.
     uint32_t numEntries;
     nsresult rv = aArguments->GetNumEntries(&numEntries);
     NS_ENSURE_SUCCESS(rv, rv);
-    NS_ASSERTION(numEntries > 0, "unexpected number of arguments");
+    MOZ_ASSERT(numEntries == 1, "unexpected number of arguments");
 
     int64_t pageId = aArguments->AsInt64(0);
-    int32_t typed = numEntries > 1 ? aArguments->AsInt32(1) : 0;
-    int32_t fullVisitCount = numEntries > 2 ? aArguments->AsInt32(2) : 0;
-    int64_t bookmarkId = numEntries > 3 ? aArguments->AsInt64(3) : 0;
+    MOZ_ASSERT(pageId > 0, "Should always pass a valid page id");
+    if (pageId <= 0) {
+      NS_ADDREF(*_result = new IntegerVariant(0));
+      return NS_OK;
+    }
+
+    int32_t typed = 0;
     int32_t visitCount = 0;
-    int32_t hidden = 0;
+    bool hasBookmark = false;
     int32_t isQuery = 0;
     float pointsForSampledVisits = 0.0;
+    int32_t numSampledVisits = 0;
+    int32_t bonus = 0;
 
     // This is a const version of the history object for thread-safety.
     const nsNavHistory* history = nsNavHistory::GetConstHistoryService();
     NS_ENSURE_STATE(history);
     RefPtr<Database> DB = Database::GetDatabase();
     NS_ENSURE_STATE(DB);
 
-    if (pageId > 0) {
-      // The page is already in the database, and we can fetch current
-      // params from the database.
+
+    // Fetch the page stats from the database.
+    {
       RefPtr<mozIStorageStatement> getPageInfo = DB->GetStatement(
-        "SELECT typed, hidden, visit_count, "
-          "(SELECT count(*) FROM moz_historyvisits WHERE place_id = :page_id), "
-          "EXISTS (SELECT 1 FROM moz_bookmarks WHERE fk = :page_id), "
-          "(url > 'place:' AND url < 'place;') "
+        "SELECT typed, visit_count, foreign_count, "
+               "(substr(url, 0, 7) = 'place:') "
         "FROM moz_places "
         "WHERE id = :page_id "
       );
       NS_ENSURE_STATE(getPageInfo);
       mozStorageStatementScoper infoScoper(getPageInfo);
 
       rv = getPageInfo->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), pageId);
       NS_ENSURE_SUCCESS(rv, rv);
 
-      bool hasResult;
+      bool hasResult = false;
       rv = getPageInfo->ExecuteStep(&hasResult);
-      NS_ENSURE_SUCCESS(rv, rv);
-      NS_ENSURE_TRUE(hasResult, NS_ERROR_UNEXPECTED);
+      NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && hasResult, NS_ERROR_UNEXPECTED);
+
       rv = getPageInfo->GetInt32(0, &typed);
       NS_ENSURE_SUCCESS(rv, rv);
-      rv = getPageInfo->GetInt32(1, &hidden);
+      rv = getPageInfo->GetInt32(1, &visitCount);
       NS_ENSURE_SUCCESS(rv, rv);
-      rv = getPageInfo->GetInt32(2, &visitCount);
-      NS_ENSURE_SUCCESS(rv, rv);
-      rv = getPageInfo->GetInt32(3, &fullVisitCount);
+      int32_t foreignCount = 0;
+      rv = getPageInfo->GetInt32(2, &foreignCount);
       NS_ENSURE_SUCCESS(rv, rv);
-      rv = getPageInfo->GetInt64(4, &bookmarkId);
-      NS_ENSURE_SUCCESS(rv, rv);
-      rv = getPageInfo->GetInt32(5, &isQuery);
+      hasBookmark = foreignCount > 0;
+      rv = getPageInfo->GetInt32(3, &isQuery);
       NS_ENSURE_SUCCESS(rv, rv);
+    }
 
-      // NOTE: This is not limited to visits with "visit_type NOT IN (0,4,7,8)"
-      // because otherwise it would not return any visit for those transitions
-      // causing an incorrect frecency, see CalculateFrecencyInternal().
+    if (visitCount > 0) {
+      // Get a sample of the last visits to the page, to calculate its weight.
       // In case of a temporary or permanent redirect, calculate the frecency
       // as if the original page was visited.
-      // Get a sample of the last visits to the page, to calculate its weight.
       nsCOMPtr<mozIStorageStatement> getVisits = DB->GetStatement(
         NS_LITERAL_CSTRING(
           "/* do not warn (bug 659740 - SQLite may ignore index if few visits exist) */"
           "SELECT "
             "ROUND((strftime('%s','now','localtime','utc') - v.visit_date/1000000)/86400), "
             "IFNULL(r.visit_type, v.visit_type), "
             "v.visit_date "
           "FROM moz_historyvisits v "
@@ -546,93 +547,85 @@ namespace places {
                                           nsINavHistoryService::TRANSITION_REDIRECT_TEMPORARY) +
         NS_LITERAL_CSTRING(
           "WHERE v.place_id = :page_id "
           "ORDER BY v.visit_date DESC "
         )
       );
       NS_ENSURE_STATE(getVisits);
       mozStorageStatementScoper visitsScoper(getVisits);
-
       rv = getVisits->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), pageId);
       NS_ENSURE_SUCCESS(rv, rv);
 
       // Fetch only a limited number of recent visits.
-      int32_t numSampledVisits = 0;
+      bool hasResult = false;
       for (int32_t maxVisits = history->GetNumVisitsForFrecency();
            numSampledVisits < maxVisits &&
            NS_SUCCEEDED(getVisits->ExecuteStep(&hasResult)) && hasResult;
            numSampledVisits++) {
         int32_t visitType;
         rv = getVisits->GetInt32(1, &visitType);
         NS_ENSURE_SUCCESS(rv, rv);
-        int32_t bonus = history->GetFrecencyTransitionBonus(visitType, true);
+        bonus = history->GetFrecencyTransitionBonus(visitType, true);
 
-        // Always add the bookmark visit bonus.
-        if (bookmarkId) {
+        // Add the bookmark visit bonus.
+        if (hasBookmark) {
           bonus += history->GetFrecencyTransitionBonus(nsINavHistoryService::TRANSITION_BOOKMARK, true);
         }
 
         // If bonus was zero, we can skip the work to determine the weight.
         if (bonus) {
           int32_t ageInDays = getVisits->AsInt32(0);
           int32_t weight = history->GetFrecencyAgedWeight(ageInDays);
           pointsForSampledVisits += (float)(weight * (bonus / 100.0));
         }
       }
-
-      // If we found some visits for this page, use the calculated weight.
-      if (numSampledVisits) {
-        // fix for bug #412219
-        if (!pointsForSampledVisits) {
-          // For URIs with zero points in the sampled recent visits
-          // but "browsing" type visits outside the sampling range, set
-          // frecency to -visit_count, so they're still shown in autocomplete.
-          NS_ADDREF(*_result = new IntegerVariant(-visitCount));
-        }
-        else {
-          // Estimate frecency using the last few visits.
-          // Use ceilf() so that we don't round down to 0, which
-          // would cause us to completely ignore the place during autocomplete.
-          NS_ADDREF(*_result = new IntegerVariant((int32_t) ceilf(fullVisitCount * ceilf(pointsForSampledVisits) / numSampledVisits)));
-        }
-
-        return NS_OK;
-      }
     }
 
-    // This page is unknown or has no visits.  It could have just been added, so
-    // use passed in or default values.
+    // If we sampled some visits for this page, use the calculated weight.
+    if (numSampledVisits) {
+      // We were unable to calculate points, maybe cause all the visits in the
+      // sample had a zero bonus. Though, we know the page has some past valid
+      // visit, or visit_count would be zero. Thus we set the frecency to
+      // -1, so they are still shown in autocomplete.
+      if (!pointsForSampledVisits) {
+        NS_ADDREF(*_result = new IntegerVariant(-1));
+      }
+      else {
+        // Estimate frecency using the sampled visits.
+        // Use ceilf() so that we don't round down to 0, which
+        // would cause us to completely ignore the place during autocomplete.
+        NS_ADDREF(*_result = new IntegerVariant((int32_t) ceilf(visitCount * ceilf(pointsForSampledVisits) / numSampledVisits)));
+      }
+      return NS_OK;
+    }
 
-    // The code below works well for guessing the frecency on import, and we'll
-    // correct later once we have visits.
-    // TODO: What if we don't have visits and we never visit?  We could end up
-    // with a really high value that keeps coming up in ac results? Should we
-    // only do this on import?  Have to figure it out.
-    int32_t bonus = 0;
+    // Otherwise this page has no visits, it may be bookmarked.
+    if (!hasBookmark || isQuery) {
+      NS_ADDREF(*_result = new IntegerVariant(0));
+      return NS_OK;
+    }
+
+    // For unvisited bookmarks, produce a non-zero frecency, so that they show
+    // up in URL bar autocomplete.
+    visitCount = 1;
 
     // Make it so something bookmarked and typed will have a higher frecency
     // than something just typed or just bookmarked.
-    if (bookmarkId && !isQuery) {
-      bonus += history->GetFrecencyTransitionBonus(nsINavHistoryService::TRANSITION_BOOKMARK, false);;
-      // For unvisited bookmarks, produce a non-zero frecency, so that they show
-      // up in URL bar autocomplete.
-      fullVisitCount = 1;
-    }
-
+    bonus += history->GetFrecencyTransitionBonus(nsINavHistoryService::TRANSITION_BOOKMARK, false);
     if (typed) {
       bonus += history->GetFrecencyTransitionBonus(nsINavHistoryService::TRANSITION_TYPED, false);
     }
 
     // Assume "now" as our ageInDays, so use the first bucket.
-    pointsForSampledVisits = history->GetFrecencyBucketWeight(1) * (bonus / (float)100.0); 
+    pointsForSampledVisits = history->GetFrecencyBucketWeight(1) * (bonus / (float)100.0);
 
     // use ceilf() so that we don't round down to 0, which
     // would cause us to completely ignore the place during autocomplete
-    NS_ADDREF(*_result = new IntegerVariant((int32_t) ceilf(fullVisitCount * ceilf(pointsForSampledVisits))));
+    NS_ADDREF(*_result = new IntegerVariant((int32_t) ceilf(visitCount * ceilf(pointsForSampledVisits))));
 
     return NS_OK;
   }
 
 ////////////////////////////////////////////////////////////////////////////////
 //// GUID Creation Function
 
   GenerateGUIDFunction::~GenerateGUIDFunction()
--- a/toolkit/components/places/nsINavHistoryService.idl
+++ b/toolkit/components/places/nsINavHistoryService.idl
@@ -1234,16 +1234,21 @@ interface nsINavHistoryService : nsISupp
 
   /**
    * This transition type means the user followed a link and got a visit in
    * a frame.
    */
   const unsigned long TRANSITION_FRAMED_LINK = 8;
 
   /**
+   * This transition type means the user reloaded the page.
+   */
+  const unsigned long TRANSITION_RELOAD = 9;
+
+  /**
    * Set when database is coherent
    */
   const unsigned short DATABASE_STATUS_OK = 0;
 
   /**
    * Set when database did not exist and we created a new one
    */
   const unsigned short DATABASE_STATUS_CREATE = 1;
--- a/toolkit/components/places/nsNavHistory.cpp
+++ b/toolkit/components/places/nsNavHistory.cpp
@@ -94,16 +94,18 @@ using namespace mozilla::places;
 #define PREF_FREC_TEMP_REDIRECT_VISIT_BONUS     "places.frecency.tempRedirectVisitBonus"
 #define PREF_FREC_TEMP_REDIRECT_VISIT_BONUS_DEF 0
 #define PREF_FREC_DEFAULT_VISIT_BONUS           "places.frecency.defaultVisitBonus"
 #define PREF_FREC_DEFAULT_VISIT_BONUS_DEF       0
 #define PREF_FREC_UNVISITED_BOOKMARK_BONUS      "places.frecency.unvisitedBookmarkBonus"
 #define PREF_FREC_UNVISITED_BOOKMARK_BONUS_DEF  140
 #define PREF_FREC_UNVISITED_TYPED_BONUS         "places.frecency.unvisitedTypedBonus"
 #define PREF_FREC_UNVISITED_TYPED_BONUS_DEF     200
+#define PREF_FREC_RELOAD_VISIT_BONUS            "places.frecency.reloadVisitBonus"
+#define PREF_FREC_RELOAD_VISIT_BONUS_DEF        0
 
 // In order to avoid calling PR_now() too often we use a cached "now" value
 // for repeating stuff.  These are milliseconds between "now" cache refreshes.
 #define RENEW_CACHED_NOW_TIMEOUT ((int32_t)3 * PR_MSEC_PER_SEC)
 
 // character-set annotation
 #define CHARSET_ANNO NS_LITERAL_CSTRING("URIProperties/characterSet")
 
@@ -468,16 +470,17 @@ nsNavHistory::LoadPrefs()
   FRECENCY_PREF(mTypedVisitBonus,          PREF_FREC_TYPED_VISIT_BONUS);
   FRECENCY_PREF(mBookmarkVisitBonus,       PREF_FREC_BOOKMARK_VISIT_BONUS);
   FRECENCY_PREF(mDownloadVisitBonus,       PREF_FREC_DOWNLOAD_VISIT_BONUS);
   FRECENCY_PREF(mPermRedirectVisitBonus,   PREF_FREC_PERM_REDIRECT_VISIT_BONUS);
   FRECENCY_PREF(mTempRedirectVisitBonus,   PREF_FREC_TEMP_REDIRECT_VISIT_BONUS);
   FRECENCY_PREF(mDefaultVisitBonus,        PREF_FREC_DEFAULT_VISIT_BONUS);
   FRECENCY_PREF(mUnvisitedBookmarkBonus,   PREF_FREC_UNVISITED_BOOKMARK_BONUS);
   FRECENCY_PREF(mUnvisitedTypedBonus,      PREF_FREC_UNVISITED_TYPED_BONUS);
+  FRECENCY_PREF(mReloadVisitBonus,         PREF_FREC_RELOAD_VISIT_BONUS);
   FRECENCY_PREF(mFirstBucketWeight,        PREF_FREC_FIRST_BUCKET_WEIGHT);
   FRECENCY_PREF(mSecondBucketWeight,       PREF_FREC_SECOND_BUCKET_WEIGHT);
   FRECENCY_PREF(mThirdBucketWeight,        PREF_FREC_THIRD_BUCKET_WEIGHT);
   FRECENCY_PREF(mFourthBucketWeight,       PREF_FREC_FOURTH_BUCKET_WEIGHT);
   FRECENCY_PREF(mDefaultWeight,            PREF_FREC_DEFAULT_BUCKET_WEIGHT);
 
 #undef FRECENCY_PREF
 }
--- a/toolkit/components/places/nsNavHistory.h
+++ b/toolkit/components/places/nsNavHistory.h
@@ -404,16 +404,18 @@ public:
       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_WARN_IF_FALSE(!aTransitionType, "new transition but no bonus for frecency");
         return mDefaultVisitBonus;
     }
   }
 
   int32_t GetNumVisitsForFrecency() const
@@ -477,19 +479,16 @@ protected:
   // Database handle.
   RefPtr<mozilla::places::Database> mDB;
 
   /**
    * Decays frecency and inputhistory values.  Runs on idle-daily.
    */
   nsresult DecayFrecency();
 
-  nsresult CalculateFrecency(int64_t aPageID, int32_t aTyped, int32_t aVisitCount, nsAutoCString &aURL, int32_t *aFrecency);
-  nsresult CalculateFrecencyInternal(int64_t aPageID, int32_t aTyped, int32_t aVisitCount, bool aIsBookmarked, int32_t *aFrecency);
-
   nsresult RemovePagesInternal(const nsCString& aPlaceIdsQueryString);
   nsresult CleanupPlacesOnVisitsDelete(const nsCString& aPlaceIdsQueryString);
 
   /**
    * Loads all of the preferences that we use into member variables.
    *
    * @note If mPrefBranch is nullptr, this does nothing.
    */
@@ -602,16 +601,17 @@ protected:
   int32_t mTypedVisitBonus;
   int32_t mBookmarkVisitBonus;
   int32_t mDownloadVisitBonus;
   int32_t mPermRedirectVisitBonus;
   int32_t mTempRedirectVisitBonus;
   int32_t mDefaultVisitBonus;
   int32_t mUnvisitedBookmarkBonus;
   int32_t mUnvisitedTypedBonus;
+  int32_t mReloadVisitBonus;
 
   // in nsNavHistoryQuery.cpp
   nsresult TokensToQueries(const nsTArray<QueryKeyValuePair>& aTokens,
                            nsCOMArray<nsNavHistoryQuery>* aQueries,
                            nsNavHistoryQueryOptions* aOptions);
 
   int64_t mTagsFolder;
 
--- a/toolkit/components/places/nsPlacesTriggers.h
+++ b/toolkit/components/places/nsPlacesTriggers.h
@@ -9,19 +9,20 @@
 #ifndef __nsPlacesTriggers_h__
 #define __nsPlacesTriggers_h__
 
 /**
  * Exclude these visit types:
  *  0 - invalid
  *  4 - EMBED
  *  7 - DOWNLOAD
- *  7 - FRAMED_LINK
+ *  8 - FRAMED_LINK
+ *  9 - RELOAD
  **/
-#define EXCLUDED_VISIT_TYPES "0, 4, 7, 8"
+#define EXCLUDED_VISIT_TYPES "0, 4, 7, 8, 9"
 
 /**
  * This triggers update visit_count and last_visit_date based on historyvisits
  * table changes.
  */
 #define CREATE_HISTORYVISITS_AFTERINSERT_TRIGGER NS_LITERAL_CSTRING( \
   "CREATE TEMP TRIGGER moz_historyvisits_afterinsert_v2_trigger " \
   "AFTER INSERT ON moz_historyvisits FOR EACH ROW " \
--- a/toolkit/components/places/tests/head_common.js
+++ b/toolkit/components/places/tests/head_common.js
@@ -13,16 +13,17 @@ const NS_APP_PROFILE_DIR_STARTUP = "Prof
 const TRANSITION_LINK = Ci.nsINavHistoryService.TRANSITION_LINK;
 const TRANSITION_TYPED = Ci.nsINavHistoryService.TRANSITION_TYPED;
 const TRANSITION_BOOKMARK = Ci.nsINavHistoryService.TRANSITION_BOOKMARK;
 const TRANSITION_EMBED = Ci.nsINavHistoryService.TRANSITION_EMBED;
 const TRANSITION_FRAMED_LINK = Ci.nsINavHistoryService.TRANSITION_FRAMED_LINK;
 const TRANSITION_REDIRECT_PERMANENT = Ci.nsINavHistoryService.TRANSITION_REDIRECT_PERMANENT;
 const TRANSITION_REDIRECT_TEMPORARY = Ci.nsINavHistoryService.TRANSITION_REDIRECT_TEMPORARY;
 const TRANSITION_DOWNLOAD = Ci.nsINavHistoryService.TRANSITION_DOWNLOAD;
+const TRANSITION_RELOAD = Ci.nsINavHistoryService.TRANSITION_RELOAD;
 
 const TITLE_LENGTH_MAX = 4096;
 
 Cu.importGlobalProperties(["URL"]);
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
                                   "resource://gre/modules/FileUtils.jsm");
--- a/toolkit/components/places/tests/unit/test_000_frecency.js
+++ b/toolkit/components/places/tests/unit/test_000_frecency.js
@@ -38,16 +38,17 @@ var bonusPrefs = {
   embedVisitBonus: Ci.nsINavHistoryService.TRANSITION_EMBED,
   framedLinkVisitBonus: Ci.nsINavHistoryService.TRANSITION_FRAMED_LINK,
   linkVisitBonus: Ci.nsINavHistoryService.TRANSITION_LINK,
   typedVisitBonus: Ci.nsINavHistoryService.TRANSITION_TYPED,
   bookmarkVisitBonus: Ci.nsINavHistoryService.TRANSITION_BOOKMARK,
   downloadVisitBonus: Ci.nsINavHistoryService.TRANSITION_DOWNLOAD,
   permRedirectVisitBonus: Ci.nsINavHistoryService.TRANSITION_REDIRECT_PERMANENT,
   tempRedirectVisitBonus: Ci.nsINavHistoryService.TRANSITION_REDIRECT_TEMPORARY,
+  reloadVisitBonus: Ci.nsINavHistoryService.TRANSITION_RELOAD,
 };
 
 // create test data
 var searchTerm = "frecency";
 var results = [];
 var matchCount = 0;
 var now = Date.now();
 var prefPrefix = "places.frecency.";
@@ -105,16 +106,17 @@ function* task_initializeBucket(bucket) 
       if (visitType == Ci.nsINavHistoryService.TRANSITION_BOOKMARK)
         bonusValue = bonusValue * 2;
 
       let points = Math.ceil(1 * ((bonusValue / parseFloat(100.000000)).toFixed(6) * weight) / 1);
       if (!points) {
         if (visitType == Ci.nsINavHistoryService.TRANSITION_EMBED ||
             visitType == Ci.nsINavHistoryService.TRANSITION_FRAMED_LINK ||
             visitType == Ci.nsINavHistoryService.TRANSITION_DOWNLOAD ||
+            visitType == Ci.nsINavHistoryService.TRANSITION_RELOAD ||
             bonusName == "defaultVisitBonus")
           frecency = 0;
         else
           frecency = -1;
       }
       else
         frecency = points;
       calculatedURI = uri("http://" + searchTerm + ".com/" +
--- a/toolkit/components/places/tests/unit/test_425563.js
+++ b/toolkit/components/places/tests/unit/test_425563.js
@@ -14,17 +14,18 @@ add_task(function* test_execute()
   let count_visited_URIs = ["http://www.test-link.com/",
                             "http://www.test-typed.com/",
                             "http://www.test-bookmark.com/",
                             "http://www.test-redirect-permanent.com/",
                             "http://www.test-redirect-temporary.com/"];
 
   let notcount_visited_URIs = ["http://www.test-embed.com/",
                                "http://www.test-download.com/",
-                               "http://www.test-framed.com/"];
+                               "http://www.test-framed.com/",
+                               "http://www.test-reload.com/"];
 
   // add visits, one for each transition type
   yield PlacesTestUtils.addVisits([
     { uri: uri("http://www.test-link.com/"),
       transition: TRANSITION_LINK },
     { uri: uri("http://www.test-typed.com/"),
       transition: TRANSITION_TYPED },
     { uri: uri("http://www.test-bookmark.com/"),
@@ -34,16 +35,18 @@ add_task(function* test_execute()
     { uri: uri("http://www.test-framed.com/"),
       transition: TRANSITION_FRAMED_LINK },
     { uri: uri("http://www.test-redirect-permanent.com/"),
       transition: TRANSITION_REDIRECT_PERMANENT },
     { uri: uri("http://www.test-redirect-temporary.com/"),
       transition: TRANSITION_REDIRECT_TEMPORARY },
     { uri: uri("http://www.test-download.com/"),
       transition: TRANSITION_DOWNLOAD },
+    { uri: uri("http://www.test-reload.com/"),
+      transition: TRANSITION_RELOAD },
   ]);
 
   // check that all links are marked as visited
   for (let visited_uri of count_visited_URIs) {
     do_check_true(yield promiseIsURIVisited(uri(visited_uri)));
   }
   for (let visited_uri of notcount_visited_URIs) {
     do_check_true(yield promiseIsURIVisited(uri(visited_uri)));
--- a/toolkit/components/places/tests/unit/test_454977.js
+++ b/toolkit/components/places/tests/unit/test_454977.js
@@ -30,17 +30,18 @@ function* task_add_visit(aURI, aVisitTyp
   });
 
   let visitId = yield deferUpdatePlaces;
 
   // Increase visit_count if applicable
   if (aVisitType != 0 &&
       aVisitType != TRANSITION_EMBED &&
       aVisitType != TRANSITION_FRAMED_LINK &&
-      aVisitType != TRANSITION_DOWNLOAD) {
+      aVisitType != TRANSITION_DOWNLOAD &&
+      aVisitType != TRANSITION_RELOAD) {
     visit_count ++;
   }
 
   // Get the place id
   if (visitId > 0) {
     let sql = "SELECT place_id FROM moz_historyvisits WHERE id = ?1";
     let stmt = DBConn().createStatement(sql);
     stmt.bindByIndex(0, visitId);
@@ -102,14 +103,22 @@ add_task(function* test_execute()
   check_results(0, 1);
 
   // Add a visit that force unhide and check the place id.
   // - We expect that the place gets hidden = 0 while retaining the same
   //   place id and a correct visit_count.
   do_check_eq((yield task_add_visit(TEST_URI, TRANSITION_TYPED)), placeId);
   check_results(1, 1);
 
+  // Add a visit that should not increase visit_count
+  do_check_eq((yield task_add_visit(TEST_URI, TRANSITION_RELOAD)), placeId);
+  check_results(1, 1);
+
+  // Add a visit that should not increase visit_count
+  do_check_eq((yield task_add_visit(TEST_URI, TRANSITION_DOWNLOAD)), placeId);
+  check_results(1, 1);
+
   // Add a visit, check that hidden is not overwritten
   // - We expect that the place has still hidden = 0, while retaining
   //   correct visit_count.
   yield task_add_visit(TEST_URI, TRANSITION_EMBED);
   check_results(1, 1);
 });
--- a/toolkit/components/places/tests/unit/test_async_history_api.js
+++ b/toolkit/components/places/tests/unit/test_async_history_api.js
@@ -328,17 +328,17 @@ add_task(function* test_add_visit_invali
     yield promiseUpdatePlaces(place);
     do_throw("Should have thrown!");
   }
   catch (e) {
     do_check_eq(e.result, Cr.NS_ERROR_INVALID_ARG);
   }
 
   // Now, test something that has a transition type greater than the last one.
-  place.visits[0] = new VisitInfo(TRANSITION_FRAMED_LINK + 1);
+  place.visits[0] = new VisitInfo(TRANSITION_RELOAD + 1);
   try {
     yield promiseUpdatePlaces(place);
     do_throw("Should have thrown!");
   }
   catch (e) {
     do_check_eq(e.result, Cr.NS_ERROR_INVALID_ARG);
   }
 });
@@ -642,19 +642,18 @@ add_task(function* test_handleCompletion
 
 add_task(function* test_add_visit() {
   const VISIT_TIME = Date.now() * 1000;
   let place = {
     uri: NetUtil.newURI(TEST_DOMAIN + "test_add_visit"),
     title: "test_add_visit title",
     visits: [],
   };
-  for (let transitionType = TRANSITION_LINK;
-       transitionType <= TRANSITION_FRAMED_LINK;
-       transitionType++) {
+  for (let t in PlacesUtils.history.TRANSITIONS) {
+    let transitionType = PlacesUtils.history.TRANSITIONS[t];
     place.visits.push(new VisitInfo(transitionType, VISIT_TIME));
   }
   do_check_false(yield promiseIsURIVisited(place.uri));
 
   let callbackCount = 0;
   let placesResult = yield promiseUpdatePlaces(place);
   if (placesResult.errors.length > 0) {
     do_throw("Unexpected error.");
@@ -667,18 +666,18 @@ add_task(function* test_add_visit() {
     do_check_eq(placeInfo.frecency, -1); // We don't pass frecency here!
     do_check_eq(placeInfo.title, place.title);
 
     // Check mozIVisitInfo properties.
     let visits = placeInfo.visits;
     do_check_eq(visits.length, 1);
     let visit = visits[0];
     do_check_eq(visit.visitDate, VISIT_TIME);
-    do_check_true(visit.transitionType >= TRANSITION_LINK &&
-                    visit.transitionType <= TRANSITION_FRAMED_LINK);
+    let transitions =
+    do_check_true(Object.values(PlacesUtils.history.TRANSITIONS).includes(visit.transitionType));
     do_check_true(visit.referrerURI === null);
 
     // For TRANSITION_EMBED visits, many properties will always be zero or
     // undefined.
     if (visit.transitionType == TRANSITION_EMBED) {
       // Check mozIPlaceInfo properties.
       do_check_eq(placeInfo.placeId, 0, '//');
       do_check_eq(placeInfo.guid, null);
@@ -701,19 +700,18 @@ add_task(function* test_add_visit() {
       yield PlacesTestUtils.promiseAsyncUpdates();
     }
   }
 });
 
 add_task(function* test_properties_saved() {
   // Check each transition type to make sure it is saved properly.
   let places = [];
-  for (let transitionType = TRANSITION_LINK;
-       transitionType <= TRANSITION_FRAMED_LINK;
-       transitionType++) {
+  for (let t in PlacesUtils.history.TRANSITIONS) {
+    let transitionType = PlacesUtils.history.TRANSITIONS[t];
     let place = {
       uri: NetUtil.newURI(TEST_DOMAIN + "test_properties_saved/" +
                           transitionType),
       title: "test_properties_saved test",
       visits: [
         new VisitInfo(transitionType),
       ],
     };
--- a/toolkit/components/places/tests/unit/test_history_clear.js
+++ b/toolkit/components/places/tests/unit/test_history_clear.js
@@ -74,17 +74,17 @@ add_task(function* test_history_clear()
   // Add an expire never annotation
   // Actually expire never annotations are removed as soon as a page is removed
   // from the database, so this should act as a normal visit.
   PlacesUtils.annotations.setPageAnnotation(uri("http://download.mozilla.org/"),
                                             "never", "never", 0,
                                             PlacesUtils.annotations.EXPIRE_NEVER);
 
   // Add a bookmark
-  // Bookmarked page should have history cleared and frecency = -old_visit_count
+  // Bookmarked page should have history cleared and frecency = -1
   PlacesUtils.bookmarks.insertBookmark(PlacesUtils.unfiledBookmarksFolderId,
                                        uri("http://typed.mozilla.org/"),
                                        PlacesUtils.bookmarks.DEFAULT_INDEX,
                                        "bookmark");
 
   yield PlacesTestUtils.addVisits([
     { uri: uri("http://typed.mozilla.org/"),
       transition: TRANSITION_BOOKMARK },
@@ -100,18 +100,17 @@ add_task(function* test_history_clear()
 
   // check browserHistory returns no entries
   do_check_eq(0, PlacesUtils.history.hasHistoryEntries);
 
   yield promiseTopicObserved(PlacesUtils.TOPIC_EXPIRATION_FINISHED);
   yield PlacesTestUtils.promiseAsyncUpdates();
 
   // Check that frecency for not cleared items (bookmarks) has been converted
-  // to -MAX(visit_count, 1), so we will be able to recalculate frecency
-  // starting from most frecent bookmarks.
+  // to -1.
   stmt = mDBConn.createStatement(
     "SELECT h.id FROM moz_places h WHERE h.frecency > 0 ");
   do_check_false(stmt.executeStep());
   stmt.finalize();
 
   stmt = mDBConn.createStatement(
     `SELECT h.id FROM moz_places h WHERE h.frecency < 0
        AND EXISTS (SELECT id FROM moz_bookmarks WHERE fk = h.id) LIMIT 1`);
--- a/toolkit/components/places/tests/unit/test_isURIVisited.js
+++ b/toolkit/components/places/tests/unit/test_isURIVisited.js
@@ -18,45 +18,34 @@ const SCHEMES = {
   "view-source:http://": false,
   "chrome://browser/content/browser.xul?": false,
   "resource://": false,
   "data:,": false,
   "wyciwyg:/0/http://": false,
   "javascript:": false,
 };
 
-const TRANSITIONS = [
-  TRANSITION_LINK,
-  TRANSITION_TYPED,
-  TRANSITION_BOOKMARK,
-  TRANSITION_EMBED,
-  TRANSITION_FRAMED_LINK,
-  TRANSITION_REDIRECT_PERMANENT,
-  TRANSITION_REDIRECT_TEMPORARY,
-  TRANSITION_DOWNLOAD,
-];
-
 var gRunner;
 function run_test()
 {
   do_test_pending();
   gRunner = step();
   gRunner.next();
 }
 
 function* step()
 {
   let history = Cc["@mozilla.org/browser/history;1"]
                   .getService(Ci.mozIAsyncHistory);
 
   for (let scheme in SCHEMES) {
     do_print("Testing scheme " + scheme);
-    for (let i = 0; i < TRANSITIONS.length; i++) {
-      let transition = TRANSITIONS[i];
-      do_print("With transition " + transition);
+    for (let t in PlacesUtils.history.TRANSITIONS) {
+      do_print("With transition " + t);
+      let transition = PlacesUtils.history.TRANSITIONS[t];
 
       let uri = NetUtil.newURI(scheme + "mozilla.org/");
 
       history.isURIVisited(uri, function(aURI, aIsVisited) {
         do_check_true(uri.equals(aURI));
         do_check_false(aIsVisited);
 
         let callback = {
--- a/toolkit/components/places/tests/unit/test_preventive_maintenance.js
+++ b/toolkit/components/places/tests/unit/test_preventive_maintenance.js
@@ -1128,17 +1128,17 @@ tests.push({
     addPlace(url);
     setVisitCount(url, 10);
     setLastVisitDate(url, now++);
   },
 
   check: function() {
     let stmt = mDBConn.createStatement(
       `SELECT h.id FROM moz_places h
-       JOIN moz_historyvisits v ON v.place_id = h.id AND visit_type NOT IN (0,4,7,8)
+       JOIN moz_historyvisits v ON v.place_id = h.id AND visit_type NOT IN (0,4,7,8,9)
        GROUP BY h.id HAVING h.visit_count <> count(*)
        UNION ALL
        SELECT h.id FROM moz_places h
        JOIN moz_historyvisits v ON v.place_id = h.id
        GROUP BY h.id HAVING h.last_visit_date <> MAX(v.visit_date)`
     );
     do_check_false(stmt.executeStep());
     stmt.finalize();