Bug 766799 - Notify hidden visits to history observers.
authorMarco Bonardo <mbonardo@mozilla.com>
Thu, 20 Dec 2012 23:45:00 +0100
changeset 125833 5356d4e7814c557882af1dd8453bb836ae084dc9
parent 125832 2a2087aaedc55363913a4a337ea9db69e695bf88
child 125834 21db31128a721b6536bf5afb7c1920a86972d5b2
push id2151
push userlsblakk@mozilla.com
push dateTue, 19 Feb 2013 18:06:57 +0000
treeherdermozilla-beta@4952e88741ec [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs766799
milestone20.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 766799 - Notify hidden visits to history observers. Redirect sources and framed visits are considered hidden and thus not shown in common UI history queries. So far were not even notified, but the right thing to do is to let the observer handle them based on its needs. r=Mano sr=Mossop
toolkit/components/downloads/nsDownloadManager.cpp
toolkit/components/places/History.cpp
toolkit/components/places/nsINavHistoryService.idl
toolkit/components/places/nsNavBookmarks.cpp
toolkit/components/places/nsNavHistory.cpp
toolkit/components/places/nsNavHistory.h
toolkit/components/places/nsNavHistoryResult.cpp
toolkit/components/places/nsNavHistoryResult.h
toolkit/components/places/tests/browser/browser_redirect.js
toolkit/components/places/tests/queries/test_searchTerms_includeHidden.js
toolkit/components/places/tests/queries/test_sorting.js
toolkit/components/places/tests/unit/test_history_observer.js
--- a/toolkit/components/downloads/nsDownloadManager.cpp
+++ b/toolkit/components/downloads/nsDownloadManager.cpp
@@ -2322,17 +2322,17 @@ nsDownloadManager::OnEndUpdateBatch()
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDownloadManager::OnVisit(nsIURI *aURI, int64_t aVisitID, PRTime aTime,
                            int64_t aSessionID, int64_t aReferringID,
                            uint32_t aTransitionType, const nsACString& aGUID,
-                           uint32_t *aAdded)
+                           bool aHidden)
 {
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDownloadManager::OnTitleChanged(nsIURI *aURI,
                                   const nsAString &aPageTitle,
                                   const nsACString &aGUID)
--- a/toolkit/components/places/History.cpp
+++ b/toolkit/components/places/History.cpp
@@ -453,24 +453,23 @@ public:
     if (!navHistory) {
       NS_WARNING("Trying to notify about a visit but cannot get the history service!");
       return NS_OK;
     }
 
     nsCOMPtr<nsIURI> uri;
     (void)NS_NewURI(getter_AddRefs(uri), mPlace.spec);
 
-    // Notify nsNavHistory observers of visit, but only for certain types of
-    // visits to maintain consistency with nsNavHistory::GetQueryResults.
-    if (!mPlace.hidden &&
-        mPlace.transitionType != nsINavHistoryService::TRANSITION_EMBED &&
-        mPlace.transitionType != nsINavHistoryService::TRANSITION_FRAMED_LINK) {
+    // Notify the visit.  Note that TRANSITION_EMBED visits are never added
+    // to the database, thus cannot be queried and we don't notify them.
+    if (mPlace.transitionType != nsINavHistoryService::TRANSITION_EMBED) {
       navHistory->NotifyOnVisit(uri, mPlace.visitId, mPlace.visitTime,
                                 mPlace.sessionId, mReferrer.visitId,
-                                mPlace.transitionType, mPlace.guid);
+                                mPlace.transitionType, mPlace.guid,
+                                mPlace.hidden);
     }
 
     nsCOMPtr<nsIObserverService> obsService =
       mozilla::services::GetObserverService();
     if (obsService) {
       DebugOnly<nsresult> rv =
         obsService->NotifyObservers(uri, URI_VISIT_SAVED, nullptr);
       NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Could not notify observers");
--- a/toolkit/components/places/nsINavHistoryService.idl
+++ b/toolkit/components/places/nsINavHistoryService.idl
@@ -645,17 +645,17 @@ interface nsINavHistoryResult : nsISuppo
 
 /**
  * Similar to nsIRDFObserver for history. Note that we don't pass the data
  * source since that is always the global history.
  *
  * DANGER! If you are in the middle of a batch transaction, there may be a
  * database transaction active. You can still access the DB, but be careful.
  */
-[scriptable, uuid(c837f6ba-6ad7-4810-a425-8ce29e05d17e)]
+[scriptable, uuid(eb264079-8766-4e66-b9bf-2c8b586c74d3)]
 interface nsINavHistoryObserver : nsISupports
 {
   /**
    * Notifies you that a bunch of things are about to change, don't do any
    * heavy-duty processing until onEndUpdateBatch is called.
    */
   void onBeginUpdateBatch();
 
@@ -675,29 +675,26 @@ interface nsINavHistoryObserver : nsISup
    * (which will comprise the majority of visit notifications) to save work.
    *
    * @param aVisitID        ID of the visit that was just created.
    * @param aTime           Time of the visit
    * @param aSessionID      The ID of one connected sequence of visits.
    * @param aReferringID    The ID of the visit the user came from. 0 if empty.
    * @param aTransitionType One of nsINavHistory.TRANSITION_*
    * @param aGUID           The unique ID associated with the page.
-   * @param aAdded          Incremented by query nodes when the visited uri
-   *                        belongs to them. If no such query exists, the 
-   *                        history result creates a new query node dynamically.
-   *                        It is used in places views only and can be ignored.
+   * @param aHidden         Whether the visited page is marked as hidden.
    */
   void onVisit(in nsIURI aURI,
                in long long aVisitID,
                in PRTime aTime,
                in long long aSessionID,
                in long long aReferringID,
                in unsigned long aTransitionType,
                in ACString aGUID,
-               out unsigned long aAdded);
+               in boolean aHidden);
 
   /**
    * Called whenever either the "real" title or the custom title of the page
    * changed. BOTH TITLES ARE ALWAYS INCLUDED in this notification, even though
    * only one will change at a time. Often, consumers will want to display the
    * user title if it is available, and fall back to the page title (the one
    * specified in the <title> tag of the page).
    *
--- a/toolkit/components/places/nsNavBookmarks.cpp
+++ b/toolkit/components/places/nsNavBookmarks.cpp
@@ -45,20 +45,20 @@
   } \
   PR_END_MACRO
 
 #define TOPIC_PLACES_MAINTENANCE "places-maintenance-finished"
 
 using namespace mozilla;
 
 // These columns sit to the right of the kGetInfoIndex_* columns.
-const int32_t nsNavBookmarks::kGetChildrenIndex_Position = 14;
-const int32_t nsNavBookmarks::kGetChildrenIndex_Type = 15;
-const int32_t nsNavBookmarks::kGetChildrenIndex_PlaceID = 16;
-const int32_t nsNavBookmarks::kGetChildrenIndex_Guid = 17;
+const int32_t nsNavBookmarks::kGetChildrenIndex_Position = 15;
+const int32_t nsNavBookmarks::kGetChildrenIndex_Type = 16;
+const int32_t nsNavBookmarks::kGetChildrenIndex_PlaceID = 17;
+const int32_t nsNavBookmarks::kGetChildrenIndex_Guid = 18;
 
 using namespace mozilla::places;
 
 PLACES_FACTORY_SINGLETON_IMPLEMENTATION(nsNavBookmarks, gBookmarksService)
 
 #define BOOKMARKS_ANNO_PREFIX "bookmarks/"
 #define BOOKMARKS_TOOLBAR_FOLDER_ANNO NS_LITERAL_CSTRING(BOOKMARKS_ANNO_PREFIX "toolbarFolder")
 #define READ_ONLY_ANNO NS_LITERAL_CSTRING("placesInternal/READ_ONLY")
@@ -1065,17 +1065,18 @@ nsNavBookmarks::GetDescendantChildren(in
     // Select all children of a given folder, sorted by position.
     // This is a LEFT JOIN because not all bookmarks types have a place.
     // We construct a result where the first columns exactly match
     // kGetInfoIndex_* order, and additionally contains columns for position,
     // item_child, and folder_child from moz_bookmarks.
     nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
       "SELECT h.id, h.url, IFNULL(b.title, h.title), h.rev_host, h.visit_count, "
              "h.last_visit_date, f.url, null, b.id, b.dateAdded, b.lastModified, "
-             "b.parent, null, h.frecency, b.position, b.type, b.fk, b.guid "
+             "b.parent, null, h.frecency, h.hidden, b.position, b.type, b.fk, "
+             "b.guid "
       "FROM moz_bookmarks b "
       "LEFT JOIN moz_places h ON b.fk = h.id "
       "LEFT JOIN moz_favicons f ON h.favicon_id = f.id "
       "WHERE b.parent = :parent "
       "ORDER BY b.position ASC"
     );
     NS_ENSURE_STATE(stmt);
     mozStorageStatementScoper scoper(stmt);
@@ -1777,17 +1778,17 @@ nsNavBookmarks::QueryFolderChildren(
   // Select all children of a given folder, sorted by position.
   // This is a LEFT JOIN because not all bookmarks types have a place.
   // We construct a result where the first columns exactly match those returned
   // by mDBGetURLPageInfo, and additionally contains columns for position,
   // item_child, and folder_child from moz_bookmarks.
   nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
     "SELECT h.id, h.url, IFNULL(b.title, h.title), h.rev_host, h.visit_count, "
            "h.last_visit_date, f.url, null, b.id, b.dateAdded, b.lastModified, "
-           "b.parent, null, h.frecency, b.position, b.type, b.fk, "
+           "b.parent, null, h.frecency, h.hidden, b.position, b.type, b.fk, "
            "b.guid "
     "FROM moz_bookmarks b "
     "LEFT JOIN moz_places h ON b.fk = h.id "
     "LEFT JOIN moz_favicons f ON h.favicon_id = f.id "
     "WHERE b.parent = :parent "
     "ORDER BY b.position ASC"
   );
   NS_ENSURE_STATE(stmt);
@@ -1911,17 +1912,17 @@ nsNavBookmarks::QueryFolderChildrenAsync
   // Select all children of a given folder, sorted by position.
   // This is a LEFT JOIN because not all bookmarks types have a place.
   // We construct a result where the first columns exactly match those returned
   // by mDBGetURLPageInfo, and additionally contains columns for position,
   // item_child, and folder_child from moz_bookmarks.
   nsCOMPtr<mozIStorageAsyncStatement> stmt = mDB->GetAsyncStatement(
     "SELECT h.id, h.url, IFNULL(b.title, h.title), h.rev_host, h.visit_count, "
            "h.last_visit_date, f.url, null, b.id, b.dateAdded, b.lastModified, "
-           "b.parent, null, h.frecency, b.position, b.type, b.fk, "
+           "b.parent, null, h.frecency, h.hidden, b.position, b.type, b.fk, "
            "b.guid "
     "FROM moz_bookmarks b "
     "LEFT JOIN moz_places h ON b.fk = h.id "
     "LEFT JOIN moz_favicons f ON h.favicon_id = f.id "
     "WHERE b.parent = :parent "
     "ORDER BY b.position ASC"
   );
   NS_ENSURE_STATE(stmt);
@@ -2770,17 +2771,17 @@ nsNavBookmarks::OnEndUpdateBatch()
   return NS_OK;
 }
 
 
 NS_IMETHODIMP
 nsNavBookmarks::OnVisit(nsIURI* aURI, int64_t aVisitId, PRTime aTime,
                         int64_t aSessionID, int64_t aReferringID,
                         uint32_t aTransitionType, const nsACString& aGUID,
-                        uint32_t* aAdded)
+                        bool aHidden)
 {
   // If the page is bookmarked, notify observers for each associated bookmark.
   ItemVisitData visitData;
   nsresult rv = aURI->GetSpec(visitData.bookmark.url);
   NS_ENSURE_SUCCESS(rv, rv);
   visitData.visitId = aVisitId;
   visitData.time = aTime;
   visitData.transitionType = aTransitionType;
--- a/toolkit/components/places/nsNavHistory.cpp
+++ b/toolkit/components/places/nsNavHistory.cpp
@@ -96,17 +96,17 @@ using namespace mozilla::places;
 #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
 
 // 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)
 
-static const int64_t USECS_PER_DAY = PR_USEC_PER_SEC * 60 * 60 * 24;
+static const int64_t USECS_PER_DAY = (int64_t)PR_USEC_PER_SEC * 60 * 60 * 24;
 
 // character-set annotation
 #define CHARSET_ANNO NS_LITERAL_CSTRING("URIProperties/characterSet")
 
 // These macros are used when splitting history by date.
 // These are the day containers and catch-all final container.
 #define HISTORY_ADDITIONAL_DATE_CONT_NUM 3
 // We use a guess of the number of months considering all of them 30 days
@@ -246,16 +246,17 @@ const int32_t nsNavHistory::kGetInfoInde
 const int32_t nsNavHistory::kGetInfoIndex_FaviconURL = 6;
 const int32_t nsNavHistory::kGetInfoIndex_SessionId = 7;
 const int32_t nsNavHistory::kGetInfoIndex_ItemId = 8;
 const int32_t nsNavHistory::kGetInfoIndex_ItemDateAdded = 9;
 const int32_t nsNavHistory::kGetInfoIndex_ItemLastModified = 10;
 const int32_t nsNavHistory::kGetInfoIndex_ItemParentId = 11;
 const int32_t nsNavHistory::kGetInfoIndex_ItemTags = 12;
 const int32_t nsNavHistory::kGetInfoIndex_Frecency = 13;
+const int32_t nsNavHistory::kGetInfoIndex_Hidden = 14;
 
 PLACES_FACTORY_SINGLETON_IMPLEMENTATION(nsNavHistory, gHistoryService)
 
 
 nsNavHistory::nsNavHistory()
 : mBatchLevel(0)
 , mBatchDBTransaction(nullptr)
 , mCachedNow(0)
@@ -726,25 +727,25 @@ nsNavHistory::GetNewSessionID()
 
 void
 nsNavHistory::NotifyOnVisit(nsIURI* aURI,
                           int64_t aVisitID,
                           PRTime aTime,
                           int64_t aSessionID,
                           int64_t referringVisitID,
                           int32_t aTransitionType,
-                          const nsACString& aGUID)
+                          const nsACString& aGUID,
+                          bool aHidden)
 {
-  uint32_t added = 0;
   MOZ_ASSERT(!aGUID.IsEmpty());
   mHasHistoryEntries = 1;
   NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
                    nsINavHistoryObserver,
                    OnVisit(aURI, aVisitID, aTime, aSessionID,
-                           referringVisitID, aTransitionType, aGUID, &added));
+                           referringVisitID, aTransitionType, aGUID, aHidden));
 }
 
 void
 nsNavHistory::NotifyTitleChange(nsIURI* aURI,
                                 const nsString& aTitle,
                                 const nsACString& aGUID)
 {
   MOZ_ASSERT(!aGUID.IsEmpty());
@@ -957,16 +958,20 @@ nsNavHistory::GetUpdateRequirements(cons
 bool
 nsNavHistory::EvaluateQueryForNode(const nsCOMArray<nsNavHistoryQuery>& aQueries,
                                    nsNavHistoryQueryOptions* aOptions,
                                    nsNavHistoryResultNode* aNode)
 {
   // lazily created from the node's string when we need to match URIs
   nsCOMPtr<nsIURI> nodeUri;
 
+  // --- hidden ---
+  if (aNode->mHidden && !aOptions->IncludeHidden())
+    return false;
+
   for (int32_t i = 0; i < aQueries.Count(); i ++) {
     bool hasIt;
     nsCOMPtr<nsNavHistoryQuery> query = aQueries[i];
 
     // --- begin time ---
     query->GetHasBeginTime(&hasIt);
     if (hasIt) {
       PRTime beginTime = NormalizeTime(query->BeginTimeReference(),
@@ -1387,21 +1392,17 @@ nsNavHistory::AddVisit(nsIURI* aURI, PRT
     // New page
     newItem = true;
 
     // free the previous statement before we make a new one
     stmt->Reset();
     scoper.Abandon();
 
     // Hide only embedded links and redirects
-    // See the hidden computation code above for a little more explanation.
-    hidden = (int32_t)(aTransitionType == TRANSITION_EMBED ||
-                       aTransitionType == TRANSITION_FRAMED_LINK ||
-                       aIsRedirect);
-
+    hidden = (int32_t)GetHiddenState(aIsRedirect, aTransitionType);
     typed = (int32_t)(aTransitionType == TRANSITION_TYPED);
 
     // set as visited once, no title
     nsString voidString;
     voidString.SetIsVoid(true);
     rv = InternalAddNewPage(aURI, voidString, hidden == 1, typed == 1, 1,
                             true, &pageID, guid);
     NS_ENSURE_SUCCESS(rv, rv);
@@ -1428,23 +1429,20 @@ nsNavHistory::AddVisit(nsIURI* aURI, PRT
                         aTransitionType, aVisitID);
   transaction.Commit();
 
   // Update frecency (*after* the visit info is in the db)
   // Swallow errors here, since if we've gotten this far, it's more
   // important to notify the observers below.
   (void)UpdateFrecency(pageID);
 
-  // Notify observers: The hidden detection code must match that in
-  // GetQueryResults to maintain consistency.
-  // FIXME bug 325241: make a way to observe hidden URLs
-  if (!hidden) {
-    NotifyOnVisit(aURI, *aVisitID, aTime, aSessionID, referringVisitID,
-                  aTransitionType, guid);
-  }
+  // Notify the visit.  Note that TRANSITION_EMBED visits are never added
+  // to the database, thus cannot be queried and we don't notify them.
+  NotifyOnVisit(aURI, *aVisitID, aTime, aSessionID, referringVisitID,
+                aTransitionType, guid, hidden);
 
   // Normally docshell sends the link visited observer notification for us (this
   // will tell all the documents to update their visited link coloring).
   // However, for redirects and downloads (since we implement nsIDownloadHistory)
   // this will not happen and we need to send it ourselves.
   if (newItem && (aIsRedirect || aTransitionType == TRANSITION_DOWNLOAD)) {
     nsCOMPtr<nsIObserverService> obsService = services::GetObserverService();
     if (obsService)
@@ -1799,17 +1797,17 @@ PlacesSQLQueryBuilder::SelectAsURI()
       GetTagsSqlFragment(history->GetTagsFolder(),
                          NS_LITERAL_CSTRING("h.id"),
                          mHasSearchTerms,
                          tagsSqlFragment);
 
       mQueryString = NS_LITERAL_CSTRING(
         "SELECT h.id, h.url, h.title AS page_title, h.rev_host, h.visit_count, "
         "h.last_visit_date, f.url, null, null, null, null, null, ") +
-        tagsSqlFragment + NS_LITERAL_CSTRING(", h.frecency "
+        tagsSqlFragment + NS_LITERAL_CSTRING(", h.frecency, h.hidden "
         "FROM moz_places h "
         "LEFT JOIN moz_favicons f ON h.favicon_id = f.id "
         // WHERE 1 is a no-op since additonal conditions will start with AND.
         "WHERE 1 "
           "{QUERY_OPTIONS_VISITS} {QUERY_OPTIONS_PLACES} "
           "{ADDITIONAL_CONDITIONS} ");
       break;
 
@@ -1825,17 +1823,17 @@ PlacesSQLQueryBuilder::SelectAsURI()
                            NS_LITERAL_CSTRING("b2.fk"),
                            mHasSearchTerms,
                            tagsSqlFragment);
 
         mQueryString = NS_LITERAL_CSTRING(
           "SELECT b2.fk, h.url, COALESCE(b2.title, h.title) AS page_title, "
             "h.rev_host, h.visit_count, h.last_visit_date, f.url, null, b2.id, "
             "b2.dateAdded, b2.lastModified, b2.parent, ") +
-            tagsSqlFragment + NS_LITERAL_CSTRING(", h.frecency "
+            tagsSqlFragment + NS_LITERAL_CSTRING(", h.frecency, h.hidden "
           "FROM moz_bookmarks b2 "
           "JOIN (SELECT b.fk "
                 "FROM moz_bookmarks b "
                 // ADDITIONAL_CONDITIONS will filter on parent.
                 "WHERE b.type = 1 {ADDITIONAL_CONDITIONS} "
                 ") AS seed ON b2.fk = seed.fk "
           "JOIN moz_places h ON h.id = b2.fk "
           "LEFT OUTER JOIN moz_favicons f ON h.favicon_id = f.id "
@@ -1849,17 +1847,17 @@ PlacesSQLQueryBuilder::SelectAsURI()
         GetTagsSqlFragment(history->GetTagsFolder(),
                            NS_LITERAL_CSTRING("b.fk"),
                            mHasSearchTerms,
                            tagsSqlFragment);
         mQueryString = NS_LITERAL_CSTRING(
           "SELECT b.fk, h.url, COALESCE(b.title, h.title) AS page_title, "
             "h.rev_host, h.visit_count, h.last_visit_date, f.url, null, b.id, "
             "b.dateAdded, b.lastModified, b.parent, ") +
-            tagsSqlFragment + NS_LITERAL_CSTRING(", h.frecency "
+            tagsSqlFragment + NS_LITERAL_CSTRING(", h.frecency, h.hidden "
           "FROM moz_bookmarks b "
           "JOIN moz_places h ON b.fk = h.id "
           "LEFT OUTER JOIN moz_favicons f ON h.favicon_id = f.id "
           "WHERE NOT EXISTS "
               "(SELECT id FROM moz_bookmarks "
                 "WHERE id = b.parent AND parent = ") +
                   nsPrintfCString("%lld", history->GetTagsFolder()) +
               NS_LITERAL_CSTRING(") "
@@ -1881,17 +1879,17 @@ PlacesSQLQueryBuilder::SelectAsVisit()
   nsAutoCString tagsSqlFragment;
   GetTagsSqlFragment(history->GetTagsFolder(),
                      NS_LITERAL_CSTRING("h.id"),
                      mHasSearchTerms,
                      tagsSqlFragment);
   mQueryString = NS_LITERAL_CSTRING(
     "SELECT h.id, h.url, h.title AS page_title, h.rev_host, h.visit_count, "
       "v.visit_date, f.url, v.session, null, null, null, null, ") +
-      tagsSqlFragment + NS_LITERAL_CSTRING(", h.frecency "
+      tagsSqlFragment + NS_LITERAL_CSTRING(", h.frecency, h.hidden "
     "FROM moz_places h "
     "JOIN moz_historyvisits v ON h.id = v.place_id "
     "LEFT JOIN moz_favicons f ON h.favicon_id = f.id "
     // WHERE 1 is a no-op since additonal conditions will start with AND.
     "WHERE 1 "
       "{QUERY_OPTIONS_VISITS} {QUERY_OPTIONS_PLACES} "
       "{ADDITIONAL_CONDITIONS} ");
 
@@ -1916,17 +1914,17 @@ PlacesSQLQueryBuilder::SelectAsDay()
       (uint16_t)nsINavHistoryQueryOptions::RESULTS_AS_SITE_QUERY;
 
   // beginTime will become the node's time property, we don't use endTime
   // because it could overlap, and we use time to sort containers and find
   // insert position in a result.
   mQueryString = nsPrintfCString(
      "SELECT null, "
        "'place:type=%ld&sort=%ld&beginTime='||beginTime||'&endTime='||endTime, "
-      "dayTitle, null, null, beginTime, null, null, null, null, null, null "
+      "dayTitle, null, null, beginTime, null, null, null, null, null, null, null "
      "FROM (", // TOUTER BEGIN
      resultType,
      sortingMode);
 
   nsNavHistory *history = nsNavHistory::GetHistoryService();
   NS_ENSURE_STATE(history);
 
   int32_t daysOfHistory = history->GetDaysOfHistory();
@@ -2119,30 +2117,30 @@ PlacesSQLQueryBuilder::SelectAsSite()
                                        "{QUERY_OPTIONS_PLACES} "
                                        "{ADDITIONAL_CONDITIONS} ");
     timeConstraints.AssignLiteral("||'&beginTime='||:begin_time||"
                                     "'&endTime='||:end_time");
   }
 
   mQueryString = nsPrintfCString(
     "SELECT null, 'place:type=%ld&sort=%ld&domain=&domainIsHost=true'%s, "
-           ":localhost, :localhost, null, null, null, null, null, null, null "
+           ":localhost, :localhost, null, null, null, null, null, null, null, null "
     "WHERE EXISTS ( "
       "SELECT h.id FROM moz_places h "
       "%s "
       "WHERE h.hidden = 0 "
         "AND h.visit_count > 0 "
         "AND h.url BETWEEN 'file://' AND 'file:/~' "
       "%s "
       "LIMIT 1 "
     ") "
     "UNION ALL "
     "SELECT null, "
            "'place:type=%ld&sort=%ld&domain='||host||'&domainIsHost=true'%s, "
-           "host, host, null, null, null, null, null, null, null "
+           "host, host, null, null, null, null, null, null, null, null "
     "FROM ( "
       "SELECT get_unreversed_host(h.rev_host) AS host "
       "FROM moz_places h "
       "%s "
       "WHERE h.hidden = 0 "
         "AND h.rev_host <> '.' "
         "AND h.visit_count > 0 "
         "%s "
@@ -2172,17 +2170,17 @@ PlacesSQLQueryBuilder::SelectAsTag()
 
   // This allows sorting by date fields what is not possible with
   // other history queries.
   mHasDateColumns = true; 
 
   mQueryString = nsPrintfCString(
     "SELECT null, 'place:folder=' || id || '&queryType=%d&type=%ld', "
            "title, null, null, null, null, null, null, dateAdded, "
-           "lastModified, null, null "
+           "lastModified, null, null, null "
     "FROM moz_bookmarks "
     "WHERE parent = %lld",
     nsINavHistoryQueryOptions::QUERY_TYPE_BOOKMARKS,
     nsINavHistoryQueryOptions::RESULTS_AS_TAG_CONTENTS,
     history->GetTagsFolder()
   );
 
   return NS_OK;
@@ -2406,17 +2404,17 @@ nsNavHistory::ConstructQueryString(
         nsINavHistoryQueryOptions::SORT_BY_DATE_DESCENDING) ||
       IsOptimizableHistoryQuery(aQueries, aOptions,
         nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_DESCENDING)) {
     // Generate an optimized query for the history menu and most visited
     // smart bookmark.
     queryString = NS_LITERAL_CSTRING(
       "SELECT h.id, h.url, h.title AS page_title, h.rev_host, h.visit_count, h.last_visit_date, "
           "f.url, null, null, null, null, null, ") +
-          tagsSqlFragment + NS_LITERAL_CSTRING(", h.frecency "
+          tagsSqlFragment + NS_LITERAL_CSTRING(", h.frecency, h.hidden "
         "FROM moz_places h "
         "LEFT OUTER JOIN moz_favicons f ON h.favicon_id = f.id "
         "WHERE h.hidden = 0 "
           "AND EXISTS (SELECT id FROM moz_historyvisits WHERE place_id = h.id "
                        "AND visit_type NOT IN ") +
                        nsPrintfCString("(0,%d,%d) ",
                                        nsINavHistoryService::TRANSITION_EMBED,
                                        nsINavHistoryService::TRANSITION_FRAMED_LINK) +
@@ -4473,16 +4471,19 @@ nsNavHistory::RowToResult(mozIStorageVal
 
     if (itemId != -1) {
       (*aResult)->mItemId = itemId;
       (*aResult)->mFolderId = parentId;
       (*aResult)->mDateAdded = aRow->AsInt64(kGetInfoIndex_ItemDateAdded);
       (*aResult)->mLastModified = aRow->AsInt64(kGetInfoIndex_ItemLastModified);
     }
 
+    (*aResult)->mFrecency = aRow->AsInt32(kGetInfoIndex_Frecency);
+    (*aResult)->mHidden = !!aRow->AsInt32(kGetInfoIndex_Hidden);
+
     nsAutoString tags;
     rv = aRow->GetString(kGetInfoIndex_ItemTags, tags);
     NS_ENSURE_SUCCESS(rv, rv);
     if (!tags.IsVoid())
       (*aResult)->mTags.Assign(tags);
 
     NS_ADDREF(*aResult);
     return NS_OK;
@@ -4595,31 +4596,31 @@ nsNavHistory::VisitIdToResultNode(int64_
   {
     case nsNavHistoryQueryOptions::RESULTS_AS_VISIT:
     case nsNavHistoryQueryOptions::RESULTS_AS_FULL_VISIT:
       // visit query - want exact visit time
       // Should match kGetInfoIndex_* (see GetQueryResults)
       statement = mDB->GetStatement(NS_LITERAL_CSTRING(
         "SELECT h.id, h.url, h.title, h.rev_host, h.visit_count, "
                "v.visit_date, f.url, v.session, null, null, null, null, "
-               ) + tagsFragment + NS_LITERAL_CSTRING(", h.frecency "
+               ) + tagsFragment + NS_LITERAL_CSTRING(", h.frecency, h.hidden "
         "FROM moz_places h "
         "JOIN moz_historyvisits v ON h.id = v.place_id "
         "LEFT JOIN moz_favicons f ON h.favicon_id = f.id "
         "WHERE v.id = :visit_id ")
       );
       break;
 
     case nsNavHistoryQueryOptions::RESULTS_AS_URI:
       // URL results - want last visit time
       // Should match kGetInfoIndex_* (see GetQueryResults)
       statement = mDB->GetStatement(NS_LITERAL_CSTRING(
         "SELECT h.id, h.url, h.title, h.rev_host, h.visit_count, "
                "h.last_visit_date, f.url, null, null, null, null, null, "
-               ) + tagsFragment + NS_LITERAL_CSTRING(", h.frecency "
+               ) + tagsFragment + NS_LITERAL_CSTRING(", h.frecency, h.hidden "
         "FROM moz_places h "
         "JOIN moz_historyvisits v ON h.id = v.place_id "
         "LEFT JOIN moz_favicons f ON h.favicon_id = f.id "
         "WHERE v.id = :visit_id ")
       );
       break;
 
     default:
@@ -4655,17 +4656,17 @@ nsNavHistory::BookmarkIdToResultNode(int
   nsAutoCString tagsFragment;
   GetTagsSqlFragment(GetTagsFolder(), NS_LITERAL_CSTRING("h.id"),
                      true, tagsFragment);
   // Should match kGetInfoIndex_*
   nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(NS_LITERAL_CSTRING(
       "SELECT b.fk, h.url, COALESCE(b.title, h.title), "
              "h.rev_host, h.visit_count, h.last_visit_date, f.url, null, b.id, "
              "b.dateAdded, b.lastModified, b.parent, "
-             ) + tagsFragment + NS_LITERAL_CSTRING(", h.frecency "
+             ) + tagsFragment + NS_LITERAL_CSTRING(", h.frecency, h.hidden "
       "FROM moz_bookmarks b "
       "JOIN moz_places h ON b.fk = h.id "
       "LEFT JOIN moz_favicons f ON h.favicon_id = f.id "
       "WHERE b.id = :item_id ")
   );
   NS_ENSURE_STATE(stmt);
   mozStorageStatementScoper scoper(stmt);
 
@@ -4694,17 +4695,17 @@ nsNavHistory::URIToResultNode(nsIURI* aU
 {
   nsAutoCString tagsFragment;
   GetTagsSqlFragment(GetTagsFolder(), NS_LITERAL_CSTRING("h.id"),
                      true, tagsFragment);
   // Should match kGetInfoIndex_*
   nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(NS_LITERAL_CSTRING(
     "SELECT h.id, :page_url, h.title, h.rev_host, h.visit_count, "
            "h.last_visit_date, f.url, null, null, null, null, null, "
-           ) + tagsFragment + NS_LITERAL_CSTRING(", h.frecency "
+           ) + tagsFragment + NS_LITERAL_CSTRING(", h.frecency, h.hidden "
     "FROM moz_places h "
     "LEFT JOIN moz_favicons f ON h.favicon_id = f.id "
     "WHERE h.url = :page_url ")
   );
   NS_ENSURE_STATE(stmt);
   mozStorageStatementScoper scoper(stmt);
 
   nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), aURI);
--- a/toolkit/components/places/nsNavHistory.h
+++ b/toolkit/components/places/nsNavHistory.h
@@ -212,16 +212,17 @@ public:
   static const int32_t kGetInfoIndex_FaviconURL;
   static const int32_t kGetInfoIndex_SessionId;
   static const int32_t kGetInfoIndex_ItemId;
   static const int32_t kGetInfoIndex_ItemDateAdded;
   static const int32_t kGetInfoIndex_ItemLastModified;
   static const int32_t kGetInfoIndex_ItemParentId;
   static const int32_t kGetInfoIndex_ItemTags;
   static const int32_t kGetInfoIndex_Frecency;
+  static const int32_t kGetInfoIndex_Hidden;
 
   int64_t GetTagsFolder();
 
   // this actually executes a query and gives you results, it is used by
   // nsNavHistoryQueryResultNode
   nsresult GetQueryResults(nsNavHistoryQueryResultNode *aResultNode,
                            const nsCOMArray<nsNavHistoryQuery>& aQueries,
                            nsNavHistoryQueryOptions *aOptions,
@@ -406,17 +407,18 @@ public:
    * Fires onVisit event to nsINavHistoryService observers
    */
   void NotifyOnVisit(nsIURI* aURI,
                      int64_t aVisitID,
                      PRTime aTime,
                      int64_t aSessionID,
                      int64_t referringVisitID,
                      int32_t aTransitionType,
-                     const nsACString& aGUID);
+                     const nsACString& aGUID,
+                     bool aHidden);
 
   /**
    * Fires onTitleChanged event to nsINavHistoryService observers
    */
   void NotifyTitleChange(nsIURI* aURI,
                          const nsString& title,
                          const nsACString& aGUID);
 
--- a/toolkit/components/places/nsNavHistoryResult.cpp
+++ b/toolkit/components/places/nsNavHistoryResult.cpp
@@ -100,17 +100,18 @@ nsNavHistoryResultNode::nsNavHistoryResu
   mTime(aTime),
   mFaviconURI(aIconURI),
   mBookmarkIndex(-1),
   mItemId(-1),
   mFolderId(-1),
   mDateAdded(0),
   mLastModified(0),
   mIndentLevel(-1),
-  mFrecency(0)
+  mFrecency(0),
+  mHidden(false)
 {
   mTags.SetIsVoid(true);
 }
 
 
 NS_IMETHODIMP
 nsNavHistoryResultNode::GetIcon(nsACString& aIcon)
 {
@@ -2524,18 +2525,22 @@ nsNavHistoryQueryResultNode::OnEndUpdate
  * possible.
  */
 NS_IMETHODIMP
 nsNavHistoryQueryResultNode::OnVisit(nsIURI* aURI, int64_t aVisitId,
                                      PRTime aTime, int64_t aSessionId,
                                      int64_t aReferringId,
                                      uint32_t aTransitionType,
                                      const nsACString& aGUID,
+                                     bool aHidden,
                                      uint32_t* aAdded)
 {
+  if (aHidden && !mOptions->IncludeHidden())
+    return NS_OK;
+
   nsNavHistoryResult* result = GetResult();
   NS_ENSURE_STATE(result);
   if (result->mBatchInProgress &&
       ++mBatchChanges > MAX_BATCH_CHANGES_BEFORE_REFRESH) {
     nsresult rv = Refresh();
     NS_ENSURE_SUCCESS(rv, rv);
     return NS_OK;
   }
@@ -4714,23 +4719,23 @@ nsNavHistoryResult::OnItemMoved(int64_t 
   return NS_OK;
 }
 
 
 NS_IMETHODIMP
 nsNavHistoryResult::OnVisit(nsIURI* aURI, int64_t aVisitId, PRTime aTime,
                             int64_t aSessionId, int64_t aReferringId,
                             uint32_t aTransitionType, const nsACString& aGUID,
-                            uint32_t* aAdded)
+                            bool aHidden)
 {
   uint32_t added = 0;
 
   ENUMERATE_HISTORY_OBSERVERS(OnVisit(aURI, aVisitId, aTime, aSessionId,
                                       aReferringId, aTransitionType, aGUID,
-                                      &added));
+                                      aHidden, &added));
 
   if (!mRootNode->mExpanded)
     return NS_OK;
 
   // If this visit is accepted by an overlapped container, and not all
   // overlapped containers are visible, we should still call Refresh if the
   // visit falls into any of them.
   bool todayIsMissing = false;
--- a/toolkit/components/places/nsNavHistoryResult.h
+++ b/toolkit/components/places/nsNavHistoryResult.h
@@ -56,35 +56,49 @@ public:
 
 private:
   const int64_t mValue;
 };
 
 
 // Declare methods for implementing nsINavBookmarkObserver
 // and nsINavHistoryObserver (some methods, such as BeginUpdateBatch overlap)
-#define NS_DECL_BOOKMARK_HISTORY_OBSERVER                               \
+#define NS_DECL_BOOKMARK_HISTORY_OBSERVER_BASE                          \
   NS_DECL_NSINAVBOOKMARKOBSERVER                                        \
-  NS_IMETHOD OnVisit(nsIURI* aURI, int64_t aVisitId, PRTime aTime,      \
-                     int64_t aSessionId, int64_t aReferringId,          \
-                     uint32_t aTransitionType, const nsACString& aGUID, \
-                     uint32_t* aAdded);                                 \
   NS_IMETHOD OnTitleChanged(nsIURI* aURI, const nsAString& aPageTitle,  \
                             const nsACString& aGUID);                   \
   NS_IMETHOD OnBeforeDeleteURI(nsIURI *aURI, const nsACString& aGUID,   \
                                uint16_t aReason);                       \
   NS_IMETHOD OnDeleteURI(nsIURI *aURI, const nsACString& aGUID,         \
                          uint16_t aReason);                             \
   NS_IMETHOD OnClearHistory();                                          \
   NS_IMETHOD OnPageChanged(nsIURI *aURI, uint32_t aChangedAttribute,    \
                            const nsAString &aNewValue,                  \
                            const nsACString &aGUID);                    \
   NS_IMETHOD OnDeleteVisits(nsIURI* aURI, PRTime aVisitTime,            \
                             const nsACString& aGUID, uint16_t aReason);
 
+// The internal version has an output aAdded parameter, it is incremented by
+// query nodes when the visited uri belongs to them. If no such query exists,
+// the history result creates a new query node dynamically.
+#define NS_DECL_BOOKMARK_HISTORY_OBSERVER_INTERNAL                      \
+  NS_DECL_BOOKMARK_HISTORY_OBSERVER_BASE                                \
+  NS_IMETHOD OnVisit(nsIURI* aURI, int64_t aVisitId, PRTime aTime,      \
+                     int64_t aSessionId, int64_t aReferringId,          \
+                     uint32_t aTransitionType, const nsACString& aGUID, \
+                     bool aHidden, uint32_t* aAdded);
+
+// The external version is used by results.
+#define NS_DECL_BOOKMARK_HISTORY_OBSERVER_EXTERNAL                      \
+  NS_DECL_BOOKMARK_HISTORY_OBSERVER_BASE                                \
+  NS_IMETHOD OnVisit(nsIURI* aURI, int64_t aVisitId, PRTime aTime,      \
+                     int64_t aSessionId, int64_t aReferringId,          \
+                     uint32_t aTransitionType, const nsACString& aGUID, \
+                     bool aHidden);
+
 // nsNavHistoryResult
 //
 //    nsNavHistory creates this object and fills in mChildren (by getting
 //    it through GetTopLevel()). Then FilledAllResults() is called to finish
 //    object initialization.
 
 #define NS_NAVHISTORYRESULT_IID \
   { 0x455d1d40, 0x1b9b, 0x40e6, { 0xa6, 0x41, 0x8b, 0xb7, 0xe8, 0x82, 0x23, 0x87 } }
@@ -101,17 +115,17 @@ public:
                                    nsNavHistoryContainerResultNode* aRoot,
                                    bool aBatchInProgress,
                                    nsNavHistoryResult** result);
 
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_NAVHISTORYRESULT_IID)
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_NSINAVHISTORYRESULT
-  NS_DECL_BOOKMARK_HISTORY_OBSERVER
+  NS_DECL_BOOKMARK_HISTORY_OBSERVER_EXTERNAL
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsNavHistoryResult, nsINavHistoryResult)
 
   void AddHistoryObserver(nsNavHistoryQueryResultNode* aNode);
   void AddBookmarkFolderObserver(nsNavHistoryFolderResultNode* aNode, int64_t aFolder);
   void AddAllBookmarksObserver(nsNavHistoryQueryResultNode* aNode);
   void RemoveHistoryObserver(nsNavHistoryQueryResultNode* aNode);
   void RemoveBookmarkFolderObserver(nsNavHistoryFolderResultNode* aNode, int64_t aFolder);
   void RemoveAllBookmarksObserver(nsNavHistoryQueryResultNode* aNode);
@@ -357,17 +371,21 @@ public:
   int64_t mFolderId;
   PRTime mDateAdded;
   PRTime mLastModified;
 
   // The indent level of this node. The root node will have a value of -1.  The
   // root's children will have a value of 0, and so on.
   int32_t mIndentLevel;
 
-  int32_t mFrecency; // Containers have 0 frecency.
+  // Frecency of the page.  Valid only for URI nodes.
+  int32_t mFrecency;
+
+  // Hidden status of the page.  Valid only for URI nodes.
+  bool mHidden;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsNavHistoryResultNode, NS_NAVHISTORYRESULTNODE_IID)
 
 // nsNavHistoryVisitResultNode
 
 #define NS_IMPLEMENT_VISITRESULT \
   NS_IMETHOD GetUri(nsACString& aURI) { aURI = mURI; return NS_OK; } \
@@ -690,17 +708,17 @@ public:
     { return nsNavHistoryContainerResultNode::GetChildrenReadOnly(aChildrenReadOnly); }
   NS_DECL_NSINAVHISTORYQUERYRESULTNODE
 
   bool CanExpand();
   bool IsContainersQuery();
 
   virtual nsresult OpenContainer();
 
-  NS_DECL_BOOKMARK_HISTORY_OBSERVER
+  NS_DECL_BOOKMARK_HISTORY_OBSERVER_INTERNAL
   virtual void OnRemoving();
 
 public:
   // this constructs lazily mURI from mQueries and mOptions, call
   // VerifyQueriesSerialized either this or mQueries/mOptions should be valid
   nsresult VerifyQueriesSerialized();
 
   // these may be constructed lazily from mURI, call VerifyQueriesParsed
--- a/toolkit/components/places/tests/browser/browser_redirect.js
+++ b/toolkit/components/places/tests/browser/browser_redirect.js
@@ -11,22 +11,30 @@ function test() {
   gBrowser.selectedTab = gBrowser.addTab();
   registerCleanupFunction(function() {
     gBrowser.removeCurrentTab();
   });
   gBrowser.selectedTab.linkedBrowser.loadURI(REDIRECT_URI.spec);
 
   // Create and add history observer.
   let historyObserver = {
+    _redirectNotified: false,
     onVisit: function (aURI, aVisitID, aTime, aSessionID, aReferringID,
                       aTransitionType) {
       info("Received onVisit: " + aURI.spec);
+
+      if (aURI.equals(REDIRECT_URI)) {
+        this._redirectNotified = true;
+        // Wait for the target page notification.
+        return;
+      }
+
       PlacesUtils.history.removeObserver(historyObserver);
 
-      ok(aURI.equals(TARGET_URI), "The redirect source should not be notified");
+      ok(this._redirectNotified, "The redirect should have been notified");
 
       fieldForUrl(REDIRECT_URI, "frecency", function (aFrecency) {
         ok(aFrecency != 0, "Frecency or the redirecting page should not be 0");
 
         fieldForUrl(REDIRECT_URI, "hidden", function (aHidden) {
           is(aHidden, 1, "The redirecting page should be hidden");
 
           fieldForUrl(TARGET_URI, "frecency", function (aFrecency) {
--- a/toolkit/components/places/tests/queries/test_searchTerms_includeHidden.js
+++ b/toolkit/components/places/tests/queries/test_searchTerms_includeHidden.js
@@ -19,26 +19,35 @@ const VISITS = [
   { isVisit: true,
     transType: TRANSITION_TYPED,
     uri: "http://target.example.com/",
     title: "example",
     lastVisit: timeInMicroseconds--
   }
 ];
 
+const HIDDEN_VISITS = [
+  { isVisit: true,
+    transType: TRANSITION_FRAMED_LINK,
+    uri: "http://hidden.example.com/",
+    title: "red",
+    lastVisit: timeInMicroseconds--
+  },
+];
+
 const TEST_DATA = [
   { searchTerms: "example",
     includeHidden: true,
     expectedResults: 2
   },
   { searchTerms: "example",
     includeHidden: false,
     expectedResults: 1
   },
-  { searchTerms: "redir",
+  { searchTerms: "red",
     includeHidden: true,
     expectedResults: 1
   }
 ];
 
 function run_test()
 {
   run_next_test();
@@ -54,13 +63,22 @@ add_task(function test_searchTerms_inclu
   for (let data of TEST_DATA) {
     let query = PlacesUtils.history.getNewQuery();
     query.searchTerms = data.searchTerms;
     let options = PlacesUtils.history.getNewQueryOptions();
     options.includeHidden = data.includeHidden;
 
     let root = PlacesUtils.history.executeQuery(query, options).root;
     root.containerOpen = true;
+
     let cc = root.childCount;
+    // Live update with hidden visits.
+    yield task_populateDB(HIDDEN_VISITS);
+    let cc_update = root.childCount;
+
     root.containerOpen = false;
+
     do_check_eq(cc, data.expectedResults);
+    do_check_eq(cc_update, data.expectedResults + (data.includeHidden ? 1 : 0));
+
+    PlacesUtils.bhistory.removePage(uri("http://hidden.example.com/"));
   }
 });
--- a/toolkit/components/places/tests/queries/test_sorting.js
+++ b/toolkit/components/places/tests/queries/test_sorting.js
@@ -1223,19 +1223,19 @@ tests.push({
         isDetails: true,
         uri: "http://best.com/",
         lastVisit: timeInMicroseconds++,
         title: "moz",
         isInQuery: true },
     ];
 
     this._sortedData = [
-      this._unsortedData[2],
       this._unsortedData[3],
       this._unsortedData[5],
+      this._unsortedData[2],
     ];
 
     // This function in head_queries.js creates our database with the above data
     yield task_populateDB(this._unsortedData);
   },
 
   check: function() {
     var query = PlacesUtils.history.getNewQuery();
--- a/toolkit/components/places/tests/unit/test_history_observer.js
+++ b/toolkit/components/places/tests/unit/test_history_observer.js
@@ -33,51 +33,73 @@ function onNotify(callback) {
     callback.apply(this, arguments);
     deferred.resolve();
   };
   PlacesUtils.history.addObserver(obs, false);
   return deferred.promise;
 }
 
 /**
- * Asynchronous task that adds a TRANSITION_TYPED visit to the history database.
+ * Asynchronous task that adds a visit to the history database.
  */
-function task_add_visit(uri, timestamp) {
+function task_add_visit(uri, timestamp, transition) {
   uri = uri || NetUtil.newURI("http://firefox.com/");
   timestamp = timestamp || Date.now() * 1000;
   yield promiseAddVisits({
     uri: uri,
-    transition: TRANSITION_TYPED,
+    transition: transition || TRANSITION_TYPED,
     visitDate: timestamp
   });
   throw new Task.Result([uri, timestamp]);
 }
 
 function run_test() {
   run_next_test();
 }
 
 add_task(function test_onVisit() {
   let promiseNotify = onNotify(function onVisit(aURI, aVisitID, aTime,
                                                 aSessionID, aReferringID,
-                                                aTransitionType, aGUID) {
+                                                aTransitionType, aGUID,
+                                                aHidden) {
     do_check_true(aURI.equals(testuri));
     do_check_true(aVisitID > 0);
     do_check_eq(aTime, testtime);
     do_check_true(aSessionID > 0);
     do_check_eq(aReferringID, 0);
-    do_check_eq(aTransitionType, Ci.nsINavHistoryService.TRANSITION_TYPED);
+    do_check_eq(aTransitionType, TRANSITION_TYPED);
     do_check_guid_for_uri(aURI, aGUID);
+    do_check_false(aHidden);
   });
   let testuri = NetUtil.newURI("http://firefox.com/");
   let testtime = Date.now() * 1000;
   yield task_add_visit(testuri, testtime);
   yield promiseNotify;
 });
 
+add_task(function test_onVisit() {
+  let promiseNotify = onNotify(function onVisit(aURI, aVisitID, aTime,
+                                                aSessionID, aReferringID,
+                                                aTransitionType, aGUID,
+                                                aHidden) {
+    do_check_true(aURI.equals(testuri));
+    do_check_true(aVisitID > 0);
+    do_check_eq(aTime, testtime);
+    do_check_true(aSessionID > 0);
+    do_check_eq(aReferringID, 0);
+    do_check_eq(aTransitionType, TRANSITION_FRAMED_LINK);
+    do_check_guid_for_uri(aURI, aGUID);
+    do_check_true(aHidden);
+  });
+  let testuri = NetUtil.newURI("http://hidden.firefox.com/");
+  let testtime = Date.now() * 1000;
+  yield task_add_visit(testuri, testtime, TRANSITION_FRAMED_LINK);
+  yield promiseNotify;
+});
+
 add_task(function test_onBeforeDeleteURI() {
   let promiseNotify = onNotify(function onBeforeDeleteURI(aURI, aGUID,
                                                           aReason) {
     do_check_true(aURI.equals(testuri));
     do_check_guid_for_uri(aURI, aGUID);
     do_check_eq(aReason, Ci.nsINavHistoryObserver.REASON_DELETED);
   });
   let [testuri] = yield task_add_visit();