Bug 1261386 - Avoid history flooding from repeated reloads. r=adw
authorMarco Bonardo <mbonardo@mozilla.com>
Thu, 26 May 2016 17:49:40 +0200
changeset 300894 5119ac972910e0f2f90c2cd90552201905526075
parent 300893 90899efbaf73e014bf4aa02c8a62bd554dee1f19
child 300895 11880fa53889c4b79a2b6289958ee3b8031eb03f
push id30323
push usercbook@mozilla.com
push dateWed, 08 Jun 2016 09:56:55 +0000
treeherdermozilla-central@33230ff64650 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersadw
bugs1261386
milestone50.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1261386 - Avoid history flooding from 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
@@ -831,16 +831,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,39 @@ 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();
+  }
+
+  // Remove entries older than RECENTLY_VISITED_URIS_MAX_AGE.
+  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);
+  // Check if the entry exists and is younger than RECENTLY_VISITED_URIS_MAX_AGE.
+  return entry && (PR_Now() - entry->time) < RECENTLY_VISITED_URIS_MAX_AGE;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 //// IHistory
 
 NS_IMETHODIMP
 History::VisitURI(nsIURI* aURI,
                   nsIURI* aLastVisitedURI,
@@ -2461,22 +2465,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 +2508,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 +2952,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,24 @@ 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.
+// When an URI is reloaded we only take into account the first visit to it, and
+// ignore any subsequent visits, if they happen before this time has elapsed.
+// A commonly found case is to reload a page every 5 minutes, so we pick a time
+// larger than that.
+#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 +190,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.
@@ -403,61 +403,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,
+  },
 });
 
 /**
  * Validate an input PageInfo object, returning a valid PageInfo object.
  *
  * @param pageInfo: (PageInfo)
  * @return (PageInfo)
  */
@@ -479,17 +486,17 @@ function validatePageInfo(pageInfo) {
   }
 
   if (!pageInfo.visits || !Array.isArray(pageInfo.visits) || !pageInfo.visits.length) {
     throw new TypeError("PageInfo object must have an array of visits");
   }
   for (let inVisit of pageInfo.visits) {
     let visit = {
       date: new Date(),
-      transition: inVisit.transition || History.TRANSITION_LINK,
+      transition: inVisit.transition || History.TRANSITIONS.LINK,
     };
 
     if (!isValidTransitionType(visit.transition)) {
       throw new TypeError(`transition: ${visit.transition} is not a valid transition type`);
     }
 
     if (inVisit.date) {
       ensureDate(inVisit.date);
@@ -536,26 +543,17 @@ function convertForUpdatePlaces(pageInfo
 
 /**
  * Is a value a valid transition type?
  *
  * @param transitionType: (String)
  * @return (Boolean)
  */
 function isValidTransitionType(transitionType) {
-  return [
-    History.TRANSITION_LINK,
-    History.TRANSITION_TYPED,
-    History.TRANSITION_BOOKMARK,
-    History.TRANSITION_EMBED,
-    History.TRANSITION_REDIRECT_PERMANENT,
-    History.TRANSITION_REDIRECT_TEMPORARY,
-    History.TRANSITION_DOWNLOAD,
-    History.TRANSITION_FRAMED_LINK
-  ].includes(transitionType);
+  return Object.values(History.TRANSITIONS).includes(transitionType);
 }
 
 /**
  * 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).
  *
  * @throws (TypeError)
--- 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
@@ -1253,16 +1253,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 page has been reloaded.
+   */
+  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")
 
@@ -477,16 +479,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
@@ -407,16 +407,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
@@ -480,19 +482,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.
    */
@@ -605,16 +604,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();