Bug 453529 - Retain embed visits and places into the temp table in memory, r=sdwilsh
--- a/toolkit/components/places/src/nsNavHistoryExpire.cpp
+++ b/toolkit/components/places/src/nsNavHistoryExpire.cpp
@@ -92,22 +92,16 @@ struct nsNavHistoryExpireRecord {
#define PREF_SANITIZE_ON_SHUTDOWN "privacy.sanitize.sanitizeOnShutdown"
#define PREF_SANITIZE_ITEM_HISTORY "privacy.item.history"
// Expiration policy amounts (in microseconds)
const PRTime EXPIRATION_POLICY_DAYS = ((PRTime)7 * 86400 * PR_USEC_PER_SEC);
const PRTime EXPIRATION_POLICY_WEEKS = ((PRTime)30 * 86400 * PR_USEC_PER_SEC);
const PRTime EXPIRATION_POLICY_MONTHS = ((PRTime)180 * 86400 * PR_USEC_PER_SEC);
-// Expiration policy for embedded links (bug #401722)
-const PRTime EMBEDDED_LINK_LIFETIME = ((PRTime)1 * 86400 * PR_USEC_PER_SEC);
-
-// Expiration cap for embedded visits
-#define EXPIRATION_CAP_EMBEDDED 500
-
// Expiration cap for dangling moz_places records
#define EXPIRATION_CAP_PLACES 500
// History preferences
#define PREF_BRANCH_BASE "browser."
#define PREF_BROWSER_HISTORY_EXPIRE_DAYS "history_expire_days"
// nsNavHistoryExpire::nsNavHistoryExpire
@@ -196,23 +190,16 @@ nsNavHistoryExpire::OnQuit()
if (mTimer)
mTimer->Cancel();
// Handle degenerate runs:
nsresult rv = ExpireForDegenerateRuns();
if (NS_FAILED(rv))
NS_WARNING("ExpireForDegenerateRuns failed.");
- // Expire embedded links
- // NOTE: This must come before ExpireHistoryParanoid, or the moz_places
- // records won't be immediately deleted.
- rv = ExpireEmbeddedLinks(connection);
- if (NS_FAILED(rv))
- NS_WARNING("ExpireEmbeddedLinks failed.");
-
// Determine whether we can skip partially expiration of dangling entries
// because we be doing a full expiration on shutdown in ClearHistory()
nsCOMPtr<nsIPrefBranch> prefs(do_GetService("@mozilla.org/preferences-service;1"));
PRBool sanitizeOnShutdown, sanitizeHistory;
prefs->GetBoolPref(PREF_SANITIZE_ON_SHUTDOWN, &sanitizeOnShutdown);
prefs->GetBoolPref(PREF_SANITIZE_ITEM_HISTORY, &sanitizeHistory);
if (sanitizeHistory && sanitizeOnShutdown)
return;
@@ -891,58 +878,16 @@ nsNavHistoryExpire::ExpireAnnotations(mo
NS_ENSURE_SUCCESS(rv, rv);
rv = transaction.Commit();
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
-
-// nsNavHistoryExpire::ExpireEmbeddedLinks
-
-nsresult
-nsNavHistoryExpire::ExpireEmbeddedLinks(mozIStorageConnection* aConnection)
-{
- PRTime maxEmbeddedAge = PR_Now() - EMBEDDED_LINK_LIFETIME;
- nsCOMPtr<mozIStorageStatement> expireEmbeddedLinksStatement;
- // Note: This query also removes visit_type = 0 entries, for bug #375777.
- nsresult rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
- "DELETE FROM moz_historyvisits_view WHERE id IN ("
- "SELECT * FROM ( "
- "SELECT id FROM moz_historyvisits_temp "
- "WHERE visit_date < ?1 "
- "AND visit_type IN (") +
- nsPrintfCString("%d", nsINavHistoryService::TRANSITION_EMBED) +
- NS_LITERAL_CSTRING(", 0) "
- "LIMIT ?2 "
- ") "
- "UNION ALL "
- "SELECT * FROM ( "
- "SELECT id FROM moz_historyvisits "
- "WHERE visit_date < ?1 "
- "AND visit_type IN (") +
- nsPrintfCString("%d", nsINavHistoryService::TRANSITION_EMBED) +
- NS_LITERAL_CSTRING(", 0) "
- "LIMIT ?2 "
- ") "
- "LIMIT ?2 "
- ")"),
- getter_AddRefs(expireEmbeddedLinksStatement));
- NS_ENSURE_SUCCESS(rv, rv);
- rv = expireEmbeddedLinksStatement->BindInt64Parameter(0, maxEmbeddedAge);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = expireEmbeddedLinksStatement->BindInt32Parameter(1, EXPIRATION_CAP_EMBEDDED);
- NS_ENSURE_SUCCESS(rv, rv);
- rv = expireEmbeddedLinksStatement->Execute();
- NS_ENSURE_SUCCESS(rv, rv);
- return NS_OK;
-}
-
-
// nsNavHistoryExpire::ExpireHistoryParanoid
//
// Deletes any dangling history entries that aren't associated with any
// visits, bookmarks or "place:" URIs.
//
// The aMaxRecords parameter is an optional cap on the number of
// records to delete. If its value is -1, all records will be deleted.
--- a/toolkit/components/places/src/nsNavHistoryExpire.h
+++ b/toolkit/components/places/src/nsNavHistoryExpire.h
@@ -79,17 +79,16 @@ protected:
// global statistics
PRUint32 mAddCount;
PRUint32 mExpiredItems;
nsresult DoPartialExpiration();
nsresult ExpireAnnotations(mozIStorageConnection* aConnection);
- nsresult ExpireEmbeddedLinks(mozIStorageConnection* aConnection);
// parts of ExpireItems
nsresult FindVisits(PRTime aExpireThreshold, PRUint32 aNumToExpire,
mozIStorageConnection* aConnection,
nsTArray<nsNavHistoryExpireRecord>& aRecords);
nsresult EraseVisits(mozIStorageConnection* aConnection,
const nsTArray<nsNavHistoryExpireRecord>& aRecords);
nsresult EraseHistory(mozIStorageConnection* aConnection,
--- a/toolkit/components/places/src/nsPlacesDBFlush.js
+++ b/toolkit/components/places/src/nsPlacesDBFlush.js
@@ -252,17 +252,38 @@ nsPlacesDBFlush.prototype = {
* @param aTableName
* name of the table to build statement for, as moz_{TableName}_temp.
*/
_getSyncTableStatement: function DBFlush_getSyncTableStatement(aTableName)
{
// Delete all the data in the temp table.
// We have triggers setup that ensure that the data is transferred over
// upon deletion.
- return this._db.createStatement("DELETE FROM moz_" + aTableName + "_temp");
+ let condition = "";
+ switch(aTableName) {
+ case "historyvisits":
+ // For history table we want to leave embed visits in memory, since
+ // those are expired with current session, so we are filtering them out.
+ condition = "WHERE visit_type <> " + Ci.nsINavHistoryService.TRANSITION_EMBED;
+ break;
+ case "places":
+ // For places table we want to leave places associated with embed visits
+ // in memory, they usually have hidden = 1 and at least an embed visit
+ // in historyvisits_temp table.
+ condition = "WHERE id IN (SELECT id FROM moz_places_temp h " +
+ "WHERE h.hidden <> 1 OR NOT EXISTS ( " +
+ "SELECT id FROM moz_historyvisits_temp " +
+ "WHERE place_id = h.id AND visit_type = " +
+ Ci.nsINavHistoryService.TRANSITION_EMBED +
+ " LIMIT 1) " +
+ ")";
+ break;
+ }
+
+ return this._db.createStatement("DELETE FROM moz_" + aTableName + "_temp " + condition);
},
/**
* Creates a new timer based on this._syncInterval.
*
* @returns a REPEATING_SLACK nsITimer that runs every this._syncInterval.
*/
_newTimer: function DBFlush_newTimer()
new file mode 100644
--- /dev/null
+++ b/toolkit/components/places/tests/sync/test_database_sync_embed_visits.js
@@ -0,0 +1,173 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=2 sts=2 expandtab
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Marco Bonardo <mak77@bonardo.net> (Original Author)
+ *
+ * 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
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+ /*
+ * This test checks that embed visits are not synced down to disk, we hold
+ * them in memory since they're going to be purged at session close
+ */
+
+var hs = Cc["@mozilla.org/browser/nav-history-service;1"].
+ getService(Ci.nsINavHistoryService);
+var dbConn = hs.QueryInterface(Ci.nsPIPlacesDatabase).DBConnection;
+var bs = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
+ getService(Ci.nsINavBookmarksService);
+var prefs = Cc["@mozilla.org/preferences-service;1"].
+ getService(Ci.nsIPrefService).
+ getBranch("places.");
+var os = Cc["@mozilla.org/observer-service;1"].
+ getService(Ci.nsIObserverService);
+
+const TEST_URI = "http://test.com/";
+const EMBED_URI = "http://embed.test.com/";
+const PLACE_URI = "place:test.com/";
+
+const kSyncPrefName = "syncDBTableIntervalInSecs";
+const SYNC_INTERVAL = 1;
+const kSyncFinished = "places-sync-finished";
+
+var transitions = [ hs.TRANSITION_LINK,
+ hs.TRANSITION_TYPED,
+ hs.TRANSITION_BOOKMARK,
+ hs.TRANSITION_EMBED,
+ hs.TRANSITION_REDIRECT_PERMANENT,
+ hs.TRANSITION_REDIRECT_TEMPORARY,
+ hs.TRANSITION_DOWNLOAD ];
+
+var observer = {
+ observe: function(aSubject, aTopic, aData) {
+ if (aTopic == kSyncFinished && this.visitId != -1) {
+ // remove the observer, we don't need to observe sync on quit
+ os.removeObserver(this, kSyncFinished);
+ dump("\n\n");
+ dump_table("moz_places_temp");
+ dump_table("moz_places");
+ dump("\n\n");
+ // Check that moz_places table has been correctly synced
+ var stmt = dbConn.createStatement(
+ "SELECT id FROM moz_places WHERE url = :url");
+ stmt.params["url"] = TEST_URI;
+ do_check_true(stmt.executeStep());
+ stmt.finalize();
+ stmt = dbConn.createStatement(
+ "SELECT id FROM moz_places_temp WHERE url = :url");
+ stmt.params["url"] = TEST_URI;
+ do_check_false(stmt.executeStep());
+ stmt.finalize();
+
+ stmt = dbConn.createStatement(
+ "SELECT id FROM moz_places WHERE url = :url");
+ stmt.params["url"] = EMBED_URI;
+ do_check_false(stmt.executeStep());
+ stmt.finalize();
+ stmt = dbConn.createStatement(
+ "SELECT id FROM moz_places_temp WHERE url = :url");
+ stmt.params["url"] = EMBED_URI;
+ do_check_true(stmt.executeStep());
+ stmt.finalize();
+
+ stmt = dbConn.createStatement(
+ "SELECT id FROM moz_places WHERE url = :url");
+ stmt.params["url"] = PLACE_URI;
+ do_check_true(stmt.executeStep());
+ stmt.finalize();
+ stmt = dbConn.createStatement(
+ "SELECT id FROM moz_places_temp WHERE url = :url");
+ stmt.params["url"] = PLACE_URI;
+ do_check_false(stmt.executeStep());
+ stmt.finalize();
+
+ // Check that all visits but embed ones are in disk table
+ stmt = dbConn.createStatement(
+ "SELECT count(*) FROM moz_historyvisits h WHERE visit_type <> :t_embed");
+ stmt.params["t_embed"] = hs.TRANSITION_EMBED;
+ do_check_true(stmt.executeStep());
+ do_check_eq(stmt.getInt32(0), (transitions.length - 1) * 2);
+ stmt.finalize();
+ stmt = dbConn.createStatement(
+ "SELECT id FROM moz_historyvisits h WHERE visit_type = :t_embed");
+ stmt.params["t_embed"] = hs.TRANSITION_EMBED;
+ do_check_false(stmt.executeStep());
+ stmt.finalize();
+ stmt = dbConn.createStatement(
+ "SELECT id FROM moz_historyvisits_temp h WHERE visit_type = :t_embed");
+ stmt.params["t_embed"] = hs.TRANSITION_EMBED;
+ do_check_true(stmt.executeStep());
+ stmt.finalize();
+ stmt = dbConn.createStatement(
+ "SELECT id FROM moz_historyvisits_temp h WHERE visit_type <> :t_embed");
+ stmt.params["t_embed"] = hs.TRANSITION_EMBED;
+ do_check_false(stmt.executeStep());
+ stmt.finalize();
+
+ do_test_finished();
+ }
+ }
+}
+
+function run_test()
+{
+ // First set the preference for the timer to a small value
+ prefs.setIntPref(kSyncPrefName, SYNC_INTERVAL);
+
+ // Add a visit for every transition type on TEST_URI
+ // Embed visit should stay in temp table, while other should be synced
+ // Place entry for this uri should be synced to disk table
+ transitions.forEach(function addVisit(aTransition) {
+ hs.addVisit(uri(TEST_URI), Date.now() * 1000, null,
+ aTransition, false, 0);
+ });
+
+ // Add an embed visit for EMBED_URI
+ // Embed visit should stay in temp table
+ // Place entry for this uri should stay in temp table
+ hs.addVisit(uri(EMBED_URI), Date.now() * 1000, null,
+ hs.TRANSITION_EMBED, false, 0);
+
+ // Add a visit for every transition type on PLACE_URI
+ // Embed visit should stay in temp table
+ // Place entry for this uri should be synced to disk table
+ transitions.forEach(function addVisit(aTransition) {
+ hs.addVisit(uri(PLACE_URI), Date.now() * 1000, null,
+ aTransition, false, 0);
+ });
+
+ os.addObserver(observer, kSyncFinished, false);
+
+ do_test_pending();
+}