Bug 633266 - nsINavHistoryObserver: also pass in GUID whenever we pass in a URI. r=mak
authorPhilipp von Weitershausen <philipp@weitershausen.de>
Wed, 08 Jun 2011 17:10:29 +0200
changeset 70782 5cb0cca95254020b769f315bf8f6fc548537b176
parent 70781 c8b4de69103695b95a0d32ef741d5a98468a2eff
child 70783 00caba77e0f93f1412081db085b26ce684c1c5fc
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersmak
bugs633266
milestone7.0a1
Bug 633266 - nsINavHistoryObserver: also pass in GUID whenever we pass in a URI. r=mak Part 3: Implementation
toolkit/components/downloads/nsDownloadManager.cpp
toolkit/components/places/History.cpp
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/nsPlacesExpiration.js
--- a/toolkit/components/downloads/nsDownloadManager.cpp
+++ b/toolkit/components/downloads/nsDownloadManager.cpp
@@ -1846,35 +1846,36 @@ nsDownloadManager::OnEndUpdateBatch()
   mHistoryTransaction = nsnull;
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDownloadManager::OnVisit(nsIURI *aURI, PRInt64 aVisitID, PRTime aTime,
                            PRInt64 aSessionID, PRInt64 aReferringID,
-                           PRUint32 aTransitionType, PRUint32 *aAdded)
+                           PRUint32 aTransitionType, const nsACString& aGUID,
+                           PRUint32 *aAdded)
 {
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDownloadManager::OnTitleChanged(nsIURI *aURI, const nsAString &aPageTitle)
 {
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsDownloadManager::OnBeforeDeleteURI(nsIURI *aURI)
+nsDownloadManager::OnBeforeDeleteURI(nsIURI *aURI, const nsACString& aGUID)
 {
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsDownloadManager::OnDeleteURI(nsIURI *aURI)
+nsDownloadManager::OnDeleteURI(nsIURI *aURI, const nsACString& aGUID)
 {
   return RemoveDownloadsForURI(aURI);
 }
 
 NS_IMETHODIMP
 nsDownloadManager::OnClearHistory()
 {
   return CleanUp();
@@ -1883,17 +1884,18 @@ nsDownloadManager::OnClearHistory()
 NS_IMETHODIMP
 nsDownloadManager::OnPageChanged(nsIURI *aURI, PRUint32 aWhat,
                                  const nsAString &aValue)
 {
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsDownloadManager::OnDeleteVisits(nsIURI *aURI, PRTime aVisitTime)
+nsDownloadManager::OnDeleteVisits(nsIURI *aURI, PRTime aVisitTime,
+                                  const nsACString& aGUID)
 {
   // Don't bother removing downloads until the page is removed.
   return NS_OK;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 //// nsIObserver
 
--- a/toolkit/components/places/History.cpp
+++ b/toolkit/components/places/History.cpp
@@ -459,17 +459,17 @@ public:
 
     // 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) {
       navHistory->NotifyOnVisit(uri, mPlace.visitId, mPlace.visitTime,
                                 mPlace.sessionId, mReferrer.visitId,
-                                mPlace.transitionType);
+                                mPlace.transitionType, mPlace.guid);
     }
 
     nsCOMPtr<nsIObserverService> obsService =
       mozilla::services::GetObserverService();
     if (obsService) {
       DebugOnly<nsresult> rv =
         obsService->NotifyObservers(uri, URI_VISIT_SAVED, nsnull);
       NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Could not notify observers");
@@ -796,18 +796,19 @@ private:
       NS_ENSURE_SUCCESS(rv, rv);
     }
     // Otherwise, the page was not in moz_places, so now we have to add it.
     else {
       rv = mHistory->InsertPlace(aPlace);
       NS_ENSURE_SUCCESS(rv, rv);
 
       // We need the place id and guid of the page we just inserted when we
-      // have a callback.  No point in doing the disk I/O if we do not need it.
-      if (mCallback) {
+      // have a callback or when the GUID isn't known.  No point in doing the
+      // disk I/O if we do not need it.
+      if (mCallback || aPlace.guid.IsEmpty()) {
         bool exists = mHistory->FetchPageInfo(aPlace);
         if (!exists) {
           NS_NOTREACHED("should have an entry in moz_places");
         }
       }
     }
 
     rv = AddVisit(aPlace, aReferrer);
--- a/toolkit/components/places/nsNavBookmarks.cpp
+++ b/toolkit/components/places/nsNavBookmarks.cpp
@@ -3040,17 +3040,18 @@ nsNavBookmarks::OnEndUpdateBatch()
                    nsINavBookmarkObserver, OnEndUpdateBatch());
   return NS_OK;
 }
 
 
 NS_IMETHODIMP
 nsNavBookmarks::OnVisit(nsIURI* aURI, PRInt64 aVisitId, PRTime aTime,
                         PRInt64 aSessionID, PRInt64 aReferringID,
-                        PRUint32 aTransitionType, PRUint32* aAdded)
+                        PRUint32 aTransitionType, const nsACString& aGUID,
+                        PRUint32* aAdded)
 {
   // 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;
@@ -3058,24 +3059,24 @@ nsNavBookmarks::OnVisit(nsIURI* aURI, PR
   nsRefPtr< AsyncGetBookmarksForURI<ItemVisitMethod, ItemVisitData> > notifier =
     new AsyncGetBookmarksForURI<ItemVisitMethod, ItemVisitData>(this, &nsNavBookmarks::NotifyItemVisited, visitData);
   notifier->Init();
   return NS_OK;
 }
 
 
 NS_IMETHODIMP
-nsNavBookmarks::OnBeforeDeleteURI(nsIURI* aURI)
+nsNavBookmarks::OnBeforeDeleteURI(nsIURI* aURI, const nsACString& aGUID)
 {
   return NS_OK;
 }
 
 
 NS_IMETHODIMP
-nsNavBookmarks::OnDeleteURI(nsIURI* aURI)
+nsNavBookmarks::OnDeleteURI(nsIURI* aURI, const nsACString& aGUID)
 {
 #ifdef DEBUG
   nsNavHistory* history = nsNavHistory::GetHistoryService();
   PRInt64 placeId;
   NS_ABORT_IF_FALSE(
     history && NS_SUCCEEDED(history->GetUrlIdFor(aURI, &placeId, PR_FALSE)) && !placeId,
     "OnDeleteURI was notified for a page that still exists?"
   );
@@ -3143,17 +3144,18 @@ nsNavBookmarks::OnPageChanged(nsIURI* aU
       notifier->Init();
     }
   }
   return NS_OK;
 }
 
 
 NS_IMETHODIMP
-nsNavBookmarks::OnDeleteVisits(nsIURI* aURI, PRTime aVisitTime)
+nsNavBookmarks::OnDeleteVisits(nsIURI* aURI, PRTime aVisitTime,
+                               const nsACString& aGUID)
 {
   // Notify "cleartime" only if all visits to the page have been removed.
   if (!aVisitTime) {
     // If the page is bookmarked, notify observers for each associated bookmark.
     ItemChangeData changeData;
     nsresult rv = aURI->GetSpec(changeData.bookmark.url);
     NS_ENSURE_SUCCESS(rv, rv);
     changeData.property = NS_LITERAL_CSTRING("cleartime");
--- a/toolkit/components/places/nsNavHistory.cpp
+++ b/toolkit/components/places/nsNavHistory.cpp
@@ -23,16 +23,17 @@
  *   Dietrich Ayala <dietrich@mozilla.com>
  *   Seth Spitzer <sspitzer@mozilla.com>
  *   Asaf Romano <mano@mozilla.com>
  *   Marco Bonardo <mak77@bonardo.net>
  *   Edward Lee <edward.lee@engineering.uiuc.edu>
  *   Michael Ventnor <m.ventnor@gmail.com>
  *   Ehsan Akhgari <ehsan.akhgari@gmail.com>
  *   Drew Willcoxon <adw@mozilla.com>
+ *   Philipp von Weitershausen <philipp@weitershausen.de>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -1188,18 +1189,20 @@ nsNavHistory::InitTriggers()
 
 mozIStorageStatement*
 nsNavHistory::GetStatement(const nsCOMPtr<mozIStorageStatement>& aStmt)
 {
   // mCanNotify is set to false on shutdown.
   if (!mCanNotify)
     return nsnull;
 
+  // Note that this query violates the kGetInfoIndex_* convention in
+  // the last column.
   RETURN_IF_STMT(mDBGetURLPageInfo, NS_LITERAL_CSTRING(
-    "SELECT id, url, title, rev_host, visit_count "
+    "SELECT id, url, title, rev_host, visit_count, guid "
     "FROM moz_places "
     "WHERE url = :page_url "
   ));
 
   RETURN_IF_STMT(mDBGetIdPageInfo, NS_LITERAL_CSTRING(
     "SELECT id, url, title, rev_host, visit_count "
     "FROM moz_places "
     "WHERE id = :page_id "
@@ -1221,17 +1224,17 @@ nsNavHistory::GetStatement(const nsCOMPt
 
   RETURN_IF_STMT(mDBInsertVisit, NS_LITERAL_CSTRING(
     "INSERT INTO moz_historyvisits "
       "(from_visit, place_id, visit_date, visit_type, session) "
     "VALUES (:from_visit, :page_id, :visit_date, :visit_type, :session) "
   ));
 
   RETURN_IF_STMT(mDBGetPageVisitStats, NS_LITERAL_CSTRING(
-    "SELECT id, visit_count, typed, hidden "
+    "SELECT id, visit_count, typed, hidden, guid "
     "FROM moz_places "
     "WHERE url = :page_url "
   ));
 
   RETURN_IF_STMT(mDBIsPageVisited, NS_LITERAL_CSTRING(
     "SELECT h.id "
     "FROM moz_places h "
     "WHERE url = ?1 "
@@ -1717,17 +1720,18 @@ nsNavHistory::GetUrlIdFor(nsIURI* aURI, 
     if (hasEntry)
       return stmt->GetInt64(kGetInfoIndex_PageID, aEntryID);
   }
 
   if (aAutoCreate) {
     // create a new hidden, untyped, unvisited entry
     nsAutoString voidString;
     voidString.SetIsVoid(PR_TRUE);
-    return InternalAddNewPage(aURI, voidString, PR_TRUE, PR_FALSE, 0, PR_TRUE, aEntryID);
+    nsCAutoString guid;
+    return InternalAddNewPage(aURI, voidString, PR_TRUE, PR_FALSE, 0, PR_TRUE, aEntryID, guid);
   }
 
   // Doesn't exist: don't do anything, entry ID was already set to 0 above
   return NS_OK;
 }
 
 
 // nsNavHistory::InternalAddNewPage
@@ -1743,17 +1747,18 @@ nsNavHistory::GetUrlIdFor(nsIURI* aURI, 
 
 nsresult
 nsNavHistory::InternalAddNewPage(nsIURI* aURI,
                                  const nsAString& aTitle,
                                  PRBool aHidden,
                                  PRBool aTyped,
                                  PRInt32 aVisitCount,
                                  PRBool aCalculateFrecency,
-                                 PRInt64* aPageID)
+                                 PRInt64* aPageID,
+                                 nsACString& guid)
 {
   DECLARE_AND_ASSIGN_SCOPED_LAZY_STMT(stmt, mDBAddNewPage);
   nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), aURI);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (aTitle.IsVoid()) {
     rv = stmt->BindNullByName(NS_LITERAL_CSTRING("page_title"));
   }
@@ -1795,16 +1800,18 @@ nsNavHistory::InternalAddNewPage(nsIURI*
     rv = URIBinder::Bind(getIdStmt, NS_LITERAL_CSTRING("page_url"), aURI);
     NS_ENSURE_SUCCESS(rv, rv);
 
     PRBool hasResult = PR_FALSE;
     rv = getIdStmt->ExecuteStep(&hasResult);
     NS_ENSURE_SUCCESS(rv, rv);
     NS_ASSERTION(hasResult, "hasResult is false but the call succeeded?");
     pageId = getIdStmt->AsInt64(0);
+    rv = getIdStmt->GetUTF8String(5, guid);
+    NS_ENSURE_SUCCESS(rv, rv);
   }
 
   if (aCalculateFrecency) {
     rv = UpdateFrecency(pageId);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // If the caller wants the page ID, return it.
@@ -2019,23 +2026,25 @@ nsNavHistory::GetNewSessionID()
 
 
 void
 nsNavHistory::NotifyOnVisit(nsIURI* aURI,
                           PRInt64 aVisitID,
                           PRTime aTime,
                           PRInt64 aSessionID,
                           PRInt64 referringVisitID,
-                          PRInt32 aTransitionType)
+                          PRInt32 aTransitionType,
+                          const nsACString& aGUID)
 {
   PRUint32 added = 0;
+  MOZ_ASSERT(!aGUID.IsEmpty());
   NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
                    nsINavHistoryObserver,
                    OnVisit(aURI, aVisitID, aTime, aSessionID,
-                           referringVisitID, aTransitionType, &added));
+                           referringVisitID, aTransitionType, aGUID, &added));
 }
 
 void
 nsNavHistory::NotifyTitleChange(nsIURI* aURI, const nsString& aTitle)
 {
   NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
                    nsINavHistoryObserver, OnTitleChanged(aURI, aTitle));
 }
@@ -2601,16 +2610,17 @@ nsNavHistory::AddVisit(nsIURI* aURI, PRT
   // see if this is an update (revisit) or a new page
   DECLARE_AND_ASSIGN_SCOPED_LAZY_STMT(stmt, mDBGetPageVisitStats);
   rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), aURI);
   NS_ENSURE_SUCCESS(rv, rv);
   PRBool alreadyVisited = PR_FALSE;
   rv = stmt->ExecuteStep(&alreadyVisited);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  nsCAutoString guid;
   PRInt64 pageID = 0;
   PRInt32 hidden;
   PRInt32 typed;
   PRBool newItem = PR_FALSE; // used to send out notifications at the end
   if (alreadyVisited) {
     // Update the existing entry...
     rv = stmt->GetInt64(0, &pageID);
     NS_ENSURE_SUCCESS(rv, rv);
@@ -2622,16 +2632,19 @@ nsNavHistory::AddVisit(nsIURI* aURI, PRT
     PRInt32 oldTypedState = 0;
     rv = stmt->GetInt32(2, &oldTypedState);
     NS_ENSURE_SUCCESS(rv, rv);
 
     PRBool oldHiddenState = 0;
     rv = stmt->GetInt32(3, &oldHiddenState);
     NS_ENSURE_SUCCESS(rv, rv);
 
+    rv = stmt->GetUTF8String(4, guid);
+    NS_ENSURE_SUCCESS(rv, rv);
+
     // free the previous statement before we make a new one
     stmt->Reset();
     scoper.Abandon();
 
     // Note that we want to unhide any hidden pages that the user explicitly
     // types (aTransitionType == TRANSITION_TYPED) so that they will appear in
     // the history UI (sidebar, history menu, url bar autocomplete, etc).
     // Additionally, we don't want to hide any pages that are already unhidden.
@@ -2676,17 +2689,17 @@ nsNavHistory::AddVisit(nsIURI* aURI, PRT
                        aIsRedirect);
 
     typed = (PRInt32)(aTransitionType == TRANSITION_TYPED);
 
     // set as visited once, no title
     nsString voidString;
     voidString.SetIsVoid(PR_TRUE);
     rv = InternalAddNewPage(aURI, voidString, hidden == 1, typed == 1, 1,
-                            PR_TRUE, &pageID);
+                            PR_TRUE, &pageID, guid);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // Get the visit id for the referrer, if it exists.
   PRInt64 referringVisitID = 0;
   PRInt64 referringSessionID;
   PRTime referringTime;
   PRBool referrerIsSame;
@@ -2711,17 +2724,17 @@ nsNavHistory::AddVisit(nsIURI* aURI, PRT
   // 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);
+                  aTransitionType, guid);
   }
 
   // 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 (since we implement nsIGlobalHistory3) and downloads
   // (since we implement nsIDownloadHistory) this will not happen and we need to
   // send it ourselves.
   if (newItem && (aIsRedirect || aTransitionType == TRANSITION_DOWNLOAD)) {
@@ -4164,53 +4177,57 @@ nsNavHistory::CleanupPlacesOnVisitsDelet
 {
   // Return early if there is nothing to delete.
   if (aPlaceIdsQueryString.IsEmpty())
     return NS_OK;
 
   // Collect about-to-be-deleted URIs to notify onDeleteURI.
   nsCOMPtr<mozIStorageStatement> stmt;
   mDBConn->CreateStatement(NS_LITERAL_CSTRING(
-    "SELECT h.id, h.url, (SUBSTR(h.url, 1, 6) <> 'place:' "
-                         "AND NOT EXISTS (SELECT b.id FROM moz_bookmarks b "
-                                         "WHERE b.fk = h.id LIMIT 1)"
-                         ") as whole_entry "
+    "SELECT h.id, h.url, h.guid, "
+           "(SUBSTR(h.url, 1, 6) <> 'place:' "
+           " AND NOT EXISTS (SELECT b.id FROM moz_bookmarks b "
+                            "WHERE b.fk = h.id LIMIT 1)) as whole_entry "
     "FROM moz_places h "
     "WHERE h.id IN ( ") + aPlaceIdsQueryString + NS_LITERAL_CSTRING(") "
   ), getter_AddRefs(stmt));
   NS_ENSURE_STATE(stmt);
   nsCString filteredPlaceIds;
   nsCOMArray<nsIURI> URIs;
+  nsTArray<nsCString> GUIDs;
   PRBool hasMore;
   while (NS_SUCCEEDED(stmt->ExecuteStep(&hasMore)) && hasMore) {
     PRInt64 placeId;
     nsresult rv = stmt->GetInt64(0, &placeId);
     NS_ENSURE_SUCCESS(rv, rv);
     nsCAutoString URLString;
     rv = stmt->GetUTF8String(1, URLString);
+    nsCString guid;
+    rv = stmt->GetUTF8String(2, guid);
     PRInt32 wholeEntry;
-    rv = stmt->GetInt32(2, &wholeEntry);
+    rv = stmt->GetInt32(3, &wholeEntry);
     nsCOMPtr<nsIURI> uri;
     rv = NS_NewURI(getter_AddRefs(uri), URLString);
     NS_ENSURE_SUCCESS(rv, rv);
     if (wholeEntry) {
       if (!filteredPlaceIds.IsEmpty()) {
         filteredPlaceIds.AppendLiteral(",");
       }
       filteredPlaceIds.AppendInt(placeId);
       URIs.AppendObject(uri);
+      GUIDs.AppendElement(guid);
       // Notify we are about to remove this uri.
       NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
-                       nsINavHistoryObserver, OnBeforeDeleteURI(uri));
+                       nsINavHistoryObserver, OnBeforeDeleteURI(uri, guid));
     }
     else {
       // Notify that we will delete all visits for this page, but not the page
       // itself, since it's bookmarked or a place: query.
       NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
-                       nsINavHistoryObserver, OnDeleteVisits(uri, 0));
+                       nsINavHistoryObserver, OnDeleteVisits(uri, 0, guid));
     }
   }
 
   // if the entry is not bookmarked and is not a place: uri
   // then we can remove it from moz_places.
   // Note that we do NOT delete favicons. Any unreferenced favicons will be
   // deleted next time the browser is shut down.
   nsresult rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
@@ -4223,17 +4240,17 @@ nsNavHistory::CleanupPlacesOnVisitsDelet
   // frecency, or it would appear in the url bar autocomplete.
   // XXX this might be dog slow, further degrading delete perf.
   rv = FixInvalidFrecenciesForExcludedPlaces();
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Finally notify about the removed URIs.
   for (PRInt32 i = 0; i < URIs.Count(); ++i) {
     NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
-                     nsINavHistoryObserver, OnDeleteURI(URIs[i]));
+                     nsINavHistoryObserver, OnDeleteURI(URIs[i], GUIDs[i]));
   }
 
   return NS_OK;
 }
 
 
 // nsNavHistory::RemovePages
 //
@@ -5346,30 +5363,31 @@ nsNavHistory::AsyncExecuteLegacyQueries(
   return NS_OK;
 }
 
 
 // nsPIPlacesHistoryListenersNotifier ******************************************
 
 NS_IMETHODIMP
 nsNavHistory::NotifyOnPageExpired(nsIURI *aURI, PRTime aVisitTime,
-                                  PRBool aWholeEntry)
+                                  PRBool aWholeEntry, const nsACString& aGUID)
 {
   // Invalidate the cached value for whether there's history or not.
   mHasHistoryEntries = -1;
 
+  MOZ_ASSERT(!aGUID.IsEmpty());
   if (aWholeEntry) {
     // Notify our observers that the page has been removed.
     NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
-                     nsINavHistoryObserver, OnDeleteURI(aURI));
+                     nsINavHistoryObserver, OnDeleteURI(aURI, aGUID));
   }
   else {
     // Notify our observers that some visits for the page have been removed.
     NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
-                     nsINavHistoryObserver, OnDeleteVisits(aURI, aVisitTime));
+                     nsINavHistoryObserver, OnDeleteVisits(aURI, aVisitTime, aGUID));
   }
 
   return NS_OK;
 }
 
 // nsIObserver *****************************************************************
 
 NS_IMETHODIMP
@@ -6835,25 +6853,28 @@ nsNavHistory::AddPageWithVisits(nsIURI *
   PRBool alreadyVisited = PR_FALSE;
   rv = stmt->ExecuteStep(&alreadyVisited);
   NS_ENSURE_SUCCESS(rv, rv);
 
   PRInt64 placeId = 0;
   PRInt32 typed = 0;
   PRInt32 hidden = 0;
 
+  nsCAutoString guid;
   if (alreadyVisited) {
     // Update the existing entry
     rv = stmt->GetInt64(0, &placeId);
     NS_ENSURE_SUCCESS(rv, rv);
     // We don't mind visit_count
     rv = stmt->GetInt32(2, &typed);
     NS_ENSURE_SUCCESS(rv, rv);
     rv = stmt->GetInt32(3, &hidden);
     NS_ENSURE_SUCCESS(rv, rv);
+    rv = stmt->GetUTF8String(4, guid);
+    NS_ENSURE_SUCCESS(rv, rv);
 
     if (typed == 0 && aTransitionType == TRANSITION_TYPED) {
       typed = 1;
       // Update with new stats
       DECLARE_AND_ASSIGN_SCOPED_LAZY_STMT(updateStmt, mDBUpdatePageVisitStats);
       rv = updateStmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), placeId);
       NS_ENSURE_SUCCESS(rv, rv);
       rv = updateStmt->BindInt32ByName(NS_LITERAL_CSTRING("hidden"), hidden);
@@ -6863,17 +6884,17 @@ nsNavHistory::AddPageWithVisits(nsIURI *
 
       rv = updateStmt->Execute();
       NS_ENSURE_SUCCESS(rv, rv);
     }
   } else {
     // Insert the new place entry
     rv = InternalAddNewPage(aURI, aTitle, hidden == 1,
                             aTransitionType == TRANSITION_TYPED, 0,
-                            PR_FALSE, &placeId);
+                            PR_FALSE, &placeId, guid);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   NS_ASSERTION(placeId != 0, "Cannot add a visit to a nonexistent page");
 
   if (aFirstVisitDate != -1) {
     // Add the first visit
     PRInt64 visitId;
--- a/toolkit/components/places/nsNavHistory.h
+++ b/toolkit/components/places/nsNavHistory.h
@@ -590,22 +590,24 @@ public:
   /**
    * Fires onVisit event to nsINavHistoryService observers
    */
   void NotifyOnVisit(nsIURI* aURI,
                      PRInt64 aVisitID,
                      PRTime aTime,
                      PRInt64 aSessionID,
                      PRInt64 referringVisitID,
-                     PRInt32 aTransitionType);
+                     PRInt32 aTransitionType,
+                     const nsACString& aGUID);
 
   /**
    * Fires onTitleChanged event to nsINavHistoryService observers
    */
-  void NotifyTitleChange(nsIURI* aURI, const nsString& title);
+  void NotifyTitleChange(nsIURI* aURI,
+                         const nsString& title);
 
   bool isBatching() {
     return mBatchLevel > 0;
   }
 
 private:
   ~nsNavHistory();
 
@@ -727,17 +729,17 @@ protected:
 
   nsresult AddVisitChain(nsIURI* aURI, PRTime aTime,
                          PRBool aToplevel, PRBool aRedirect,
                          nsIURI* aReferrer, PRInt64* aVisitID,
                          PRInt64* aSessionID);
   nsresult InternalAddNewPage(nsIURI* aURI, const nsAString& aTitle,
                               PRBool aHidden, PRBool aTyped,
                               PRInt32 aVisitCount, PRBool aCalculateFrecency,
-                              PRInt64* aPageID);
+                              PRInt64* aPageID, nsACString& guid);
   nsresult InternalAddVisit(PRInt64 aPageID, PRInt64 aReferringVisit,
                             PRInt64 aSessionID, PRTime aTime,
                             PRInt32 aTransitionType, PRInt64* aVisitID);
   PRBool FindLastVisit(nsIURI* aURI,
                        PRInt64* aVisitID,
                        PRTime* aTime,
                        PRInt64* aSessionID);
   PRBool IsURIStringVisited(const nsACString& url);
--- a/toolkit/components/places/nsNavHistoryResult.cpp
+++ b/toolkit/components/places/nsNavHistoryResult.cpp
@@ -2874,16 +2874,17 @@ nsNavHistoryQueryResultNode::OnEndUpdate
  * common update operation and it is important that it be as efficient as
  * possible.
  */
 NS_IMETHODIMP
 nsNavHistoryQueryResultNode::OnVisit(nsIURI* aURI, PRInt64 aVisitId,
                                      PRTime aTime, PRInt64 aSessionId,
                                      PRInt64 aReferringId,
                                      PRUint32 aTransitionType,
+                                     const nsACString& aGUID,
                                      PRUint32* aAdded)
 {
   nsNavHistory* history = nsNavHistory::GetHistoryService();
   NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY);
 
   nsresult rv;
   nsRefPtr<nsNavHistoryResultNode> addition;
   switch(mLiveUpdate) {
@@ -3056,27 +3057,29 @@ nsNavHistoryQueryResultNode::OnTitleChan
     }
   }
 
   return ChangeTitles(aURI, newTitle, PR_TRUE, onlyOneEntry);
 }
 
 
 NS_IMETHODIMP
-nsNavHistoryQueryResultNode::OnBeforeDeleteURI(nsIURI *aURI)
+nsNavHistoryQueryResultNode::OnBeforeDeleteURI(nsIURI *aURI,
+                                               const nsACString& aGUID)
 {
   return NS_OK;
 }
 
 /**
  * Here, we can always live update by just deleting all occurrences of
  * the given URI.
  */
 NS_IMETHODIMP
-nsNavHistoryQueryResultNode::OnDeleteURI(nsIURI *aURI)
+nsNavHistoryQueryResultNode::OnDeleteURI(nsIURI *aURI,
+                                         const nsACString& aGUID)
 {
   if (IsContainersQuery()) {
     // Incremental updates of query returning queries are pretty much
     // complicated.  In this case it's possible one of the child queries has
     // no more children and it should be removed.  Unfortunately there is no
     // way to know that without executing the child query and counting results.
     nsresult rv = Refresh();
     NS_ENSURE_SUCCESS(rv, rv);
@@ -3160,25 +3163,26 @@ nsNavHistoryQueryResultNode::OnPageChang
     default:
       NS_WARNING("Unknown page changed notification");
   }
   return NS_OK;
 }
 
 
 NS_IMETHODIMP
-nsNavHistoryQueryResultNode::OnDeleteVisits(nsIURI* aURI, PRTime aVisitTime)
+nsNavHistoryQueryResultNode::OnDeleteVisits(nsIURI* aURI, PRTime aVisitTime,
+                                            const nsACString& aGUID)
 {
   NS_PRECONDITION(mOptions->QueryType() == nsINavHistoryQueryOptions::QUERY_TYPE_HISTORY,
                   "Bookmarks queries should not get a OnDeleteVisits notification");
   if (aVisitTime == 0) {
     // All visits for this uri have been removed, but the uri won't be removed
     // from the databse, most likely because it's a bookmark.  For a history
     // query this is equivalent to a onDeleteURI notification.
-    nsresult rv = OnDeleteURI(aURI);
+    nsresult rv = OnDeleteURI(aURI, aGUID);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return NS_OK;
 }
 
 nsresult
 nsNavHistoryQueryResultNode::NotifyIfTagsChanged(nsIURI* aURI)
@@ -5059,22 +5063,24 @@ nsNavHistoryResult::OnItemMoved(PRInt64 
                                           aNewParentGUID));
   return NS_OK;
 }
 
 
 NS_IMETHODIMP
 nsNavHistoryResult::OnVisit(nsIURI* aURI, PRInt64 aVisitId, PRTime aTime,
                             PRInt64 aSessionId, PRInt64 aReferringId,
-                            PRUint32 aTransitionType, PRUint32* aAdded)
+                            PRUint32 aTransitionType, const nsACString& aGUID,
+                            PRUint32* aAdded)
 {
   PRUint32 added = 0;
 
   ENUMERATE_HISTORY_OBSERVERS(OnVisit(aURI, aVisitId, aTime, aSessionId,
-                                      aReferringId, aTransitionType, &added));
+                                      aReferringId, aTransitionType, aGUID,
+                                      &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.
   PRBool todayIsMissing = PR_FALSE;
@@ -5125,26 +5131,26 @@ NS_IMETHODIMP
 nsNavHistoryResult::OnTitleChanged(nsIURI* aURI, const nsAString& aPageTitle)
 {
   ENUMERATE_HISTORY_OBSERVERS(OnTitleChanged(aURI, aPageTitle));
   return NS_OK;
 }
 
 
 NS_IMETHODIMP
-nsNavHistoryResult::OnBeforeDeleteURI(nsIURI *aURI)
+nsNavHistoryResult::OnBeforeDeleteURI(nsIURI *aURI, const nsACString& aGUID)
 {
   return NS_OK;
 }
 
 
 NS_IMETHODIMP
-nsNavHistoryResult::OnDeleteURI(nsIURI *aURI)
+nsNavHistoryResult::OnDeleteURI(nsIURI *aURI, const nsACString& aGUID)
 {
-  ENUMERATE_HISTORY_OBSERVERS(OnDeleteURI(aURI));
+  ENUMERATE_HISTORY_OBSERVERS(OnDeleteURI(aURI, aGUID));
   return NS_OK;
 }
 
 
 NS_IMETHODIMP
 nsNavHistoryResult::OnClearHistory()
 {
   ENUMERATE_HISTORY_OBSERVERS(OnClearHistory());
@@ -5160,13 +5166,14 @@ nsNavHistoryResult::OnPageChanged(nsIURI
   return NS_OK;
 }
 
 
 /**
  * Don't do anything when visits expire.
  */
 NS_IMETHODIMP
-nsNavHistoryResult::OnDeleteVisits(nsIURI* aURI, PRTime aVisitTime)
+nsNavHistoryResult::OnDeleteVisits(nsIURI* aURI, PRTime aVisitTime,
+                                   const nsACString& aGUID)
 {
-  ENUMERATE_HISTORY_OBSERVERS(OnDeleteVisits(aURI, aVisitTime));
+  ENUMERATE_HISTORY_OBSERVERS(OnDeleteVisits(aURI, aVisitTime, aGUID));
   return NS_OK;
 }
--- a/toolkit/components/places/nsNavHistoryResult.h
+++ b/toolkit/components/places/nsNavHistoryResult.h
@@ -93,24 +93,26 @@ private:
 
 
 // Declare methods for implementing nsINavBookmarkObserver
 // and nsINavHistoryObserver (some methods, such as BeginUpdateBatch overlap)
 #define NS_DECL_BOOKMARK_HISTORY_OBSERVER                               \
   NS_DECL_NSINAVBOOKMARKOBSERVER                                        \
   NS_IMETHOD OnVisit(nsIURI* aURI, PRInt64 aVisitId, PRTime aTime,      \
                      PRInt64 aSessionId, PRInt64 aReferringId,          \
-                     PRUint32 aTransitionType, PRUint32* aAdded);       \
+                     PRUint32 aTransitionType, const nsACString& aGUID, \
+                     PRUint32* aAdded);                                 \
   NS_IMETHOD OnTitleChanged(nsIURI* aURI, const nsAString& aPageTitle); \
-  NS_IMETHOD OnBeforeDeleteURI(nsIURI *aURI);                           \
-  NS_IMETHOD OnDeleteURI(nsIURI *aURI);                                 \
+  NS_IMETHOD OnBeforeDeleteURI(nsIURI *aURI, const nsACString& aGUID);  \
+  NS_IMETHOD OnDeleteURI(nsIURI *aURI, const nsACString& aGUID);        \
   NS_IMETHOD OnClearHistory();                                          \
   NS_IMETHOD OnPageChanged(nsIURI *aURI, PRUint32 aWhat,                \
                            const nsAString &aValue);                    \
-  NS_IMETHOD OnDeleteVisits(nsIURI* aURI, PRTime aVisitTime);
+  NS_IMETHOD OnDeleteVisits(nsIURI* aURI, PRTime aVisitTime,            \
+                            const nsACString& aGUID);
 
 // 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 \
--- a/toolkit/components/places/nsPlacesExpiration.js
+++ b/toolkit/components/places/nsPlacesExpiration.js
@@ -207,18 +207,18 @@ const ACTION = {
 
 // The queries we use to expire.
 const EXPIRATION_QUERIES = {
 
   // Finds visits to be expired.  Will return nothing if we are not over the
   // unique URIs limit.
   QUERY_FIND_VISITS_TO_EXPIRE: {
     sql: "INSERT INTO expiration_notify "
-       +   "(v_id, url, visit_date, expected_results) "
-       + "SELECT v.id, h.url, v.visit_date, :limit_visits "
+       +   "(v_id, url, guid, visit_date, expected_results) "
+       + "SELECT v.id, h.url, h.guid, v.visit_date, :limit_visits "
        + "FROM moz_historyvisits v "
        + "JOIN moz_places h ON h.id = v.place_id "
        + "WHERE (SELECT COUNT(*) FROM moz_places) > :max_uris "
        + "ORDER BY v.visit_date ASC "
        + "LIMIT :limit_visits",
     actions: ACTION.TIMED_OVERLIMIT | ACTION.SHUTDOWN | ACTION.IDLE |
              ACTION.DEBUG
   },
@@ -232,18 +232,18 @@ const EXPIRATION_QUERIES = {
              ACTION.DEBUG
   },
 
   // Finds orphan URIs in the database.
   // Notice we won't notify single removed URIs on removeAllPages, so we don't
   // run this query in such a case, but just delete URIs.
   QUERY_FIND_URIS_TO_EXPIRE: {
     sql: "INSERT INTO expiration_notify "
-       +   "(p_id, url, visit_date, expected_results) "
-       + "SELECT h.id, h.url, h.last_visit_date, :limit_uris "
+       +   "(p_id, url, guid, visit_date, expected_results) "
+       + "SELECT h.id, h.url, h.guid, h.last_visit_date, :limit_uris "
        + "FROM moz_places h "
        + "LEFT JOIN moz_historyvisits v ON h.id = v.place_id "
        + "LEFT JOIN moz_bookmarks b ON h.id = b.fk "
        + "WHERE v.id IS NULL "
        +   "AND b.id IS NULL "
        +   "AND h.ROWID <> IFNULL(:null_skips_last, (SELECT MAX(ROWID) FROM moz_places)) "
        + "LIMIT :limit_uris",
     actions: ACTION.TIMED | ACTION.TIMED_OVERLIMIT | ACTION.SHUTDOWN |
@@ -383,17 +383,17 @@ const EXPIRATION_QUERIES = {
     actions: ACTION.CLEAR_HISTORY | ACTION.SHUTDOWN | ACTION.CLEAN_SHUTDOWN |
              ACTION.DEBUG
   },
 
   // Select entries for notifications.
   // If p_id is set whole_entry = 1, then we have expired the full page.
   // Either p_id or v_id are always set.
   QUERY_SELECT_NOTIFICATIONS: {
-    sql: "SELECT url, MAX(visit_date) AS visit_date, "
+    sql: "SELECT url, guid, MAX(visit_date) AS visit_date, "
        +        "MAX(IFNULL(MIN(p_id, 1), MIN(v_id, 0))) AS whole_entry, "
        +        "expected_results "
        + "FROM expiration_notify "
        + "GROUP BY url",
     actions: ACTION.TIMED | ACTION.TIMED_OVERLIMIT | ACTION.SHUTDOWN |
              ACTION.IDLE | ACTION.DEBUG
   },
 
@@ -444,16 +444,17 @@ function nsPlacesExpiration()
 
     // Create the temporary notifications table.
     let stmt = db.createAsyncStatement(
       "CREATE TEMP TABLE expiration_notify ( "
     + "  id INTEGER PRIMARY KEY "
     + ", v_id INTEGER "
     + ", p_id INTEGER "
     + ", url TEXT NOT NULL "
+    + ", guid TEXT NOT NULL "
     + ", visit_date INTEGER "
     + ", expected_results INTEGER NOT NULL "
     + ") ");
     stmt.executeAsync();
     stmt.finalize();
 
     return db;
   });
@@ -627,20 +628,21 @@ nsPlacesExpiration.prototype = {
     let row;
     while (row = aResultSet.getNextRow()) {
       if (!("_expectedResultsCount" in this))
         this._expectedResultsCount = row.getResultByName("expected_results");
       if (this._expectedResultsCount > 0)
         this._expectedResultsCount--;
 
       let uri = Services.io.newURI(row.getResultByName("url"), null, null);
+      let guid = row.getResultByName("guid");
       let visitDate = row.getResultByName("visit_date");
       let wholeEntry = row.getResultByName("whole_entry");
       // Dispatch expiration notifications to history.
-      this._hsn.notifyOnPageExpired(uri, visitDate, wholeEntry);
+      this._hsn.notifyOnPageExpired(uri, visitDate, wholeEntry, guid);
     }
   },
 
   handleError: function PEX_handleError(aError)
   {
     Cu.reportError("Async statement execution returned with '" +
                    aError.result + "', '" + aError.message + "'");
   },