--- a/browser/components/downloads/test/browser/browser_indicatorDrop.js
+++ b/browser/components/downloads/test/browser/browser_indicatorDrop.js
@@ -3,17 +3,17 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
XPCOMUtils.defineLazyModuleGetter(this, "HttpServer",
"resource://testing-common/httpd.js");
registerCleanupFunction(async function() {
await task_resetState();
- await task_clearHistory();
+ await PlacesUtils.history.clear();
});
add_task(async function test_indicatorDrop() {
let downloadButton = document.getElementById("downloads-button");
ok(downloadButton, "download button present");
let scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
getService(Ci.mozIJSSubScriptLoader);
--- a/browser/components/downloads/test/browser/browser_libraryDrop.js
+++ b/browser/components/downloads/test/browser/browser_libraryDrop.js
@@ -3,17 +3,17 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
XPCOMUtils.defineLazyModuleGetter(this, "HttpServer",
"resource://testing-common/httpd.js");
registerCleanupFunction(async function() {
await task_resetState();
- await task_clearHistory();
+ await PlacesUtils.history.clear();
});
add_task(async function test_indicatorDrop() {
let scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
getService(Ci.mozIJSSubScriptLoader);
let EventUtils = {};
scriptLoader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/EventUtils.js", EventUtils);
--- a/browser/components/downloads/test/browser/head.js
+++ b/browser/components/downloads/test/browser/head.js
@@ -158,26 +158,16 @@ function startServer() {
});
}
function httpUrl(aFileName) {
return "http://localhost:" + gHttpServer.identity.primaryPort + "/" +
aFileName;
}
-function task_clearHistory() {
- return new Promise(function(resolve) {
- Services.obs.addObserver(function observeCH(aSubject, aTopic, aData) {
- Services.obs.removeObserver(observeCH, PlacesUtils.TOPIC_EXPIRATION_FINISHED);
- resolve();
- }, PlacesUtils.TOPIC_EXPIRATION_FINISHED);
- PlacesUtils.history.clear();
- });
-}
-
function openLibrary(aLeftPaneRoot) {
let library = window.openDialog("chrome://browser/content/places/places.xul",
"", "chrome,toolbar=yes,dialog=no,resizable",
aLeftPaneRoot);
return new Promise(resolve => {
waitForFocus(resolve, library);
});
--- a/toolkit/components/places/History.jsm
+++ b/toolkit/components/places/History.jsm
@@ -745,38 +745,66 @@ var invalidateFrecencies = async functio
SET hidden = 0
WHERE id in (${ ids })
AND frecency <> 0`
);
};
// Inner implementation of History.clear().
var clear = async function(db) {
- // Remove all history.
- await db.execute("DELETE FROM moz_historyvisits");
+ await db.executeTransaction(async function() {
+ // Remove all non-bookmarked places entries first, this will speed up the
+ // triggers work.
+ await db.execute(`DELETE FROM moz_places WHERE foreign_count = 0`);
+ await db.execute(`DELETE FROM moz_updatehosts_temp`);
+
+ // Expire orphan icons.
+ await db.executeCached(`DELETE FROM moz_pages_w_icons
+ WHERE page_url_hash NOT IN (SELECT url_hash FROM moz_places)`);
+ await db.executeCached(`DELETE FROM moz_icons
+ WHERE root = 0 AND id NOT IN (SELECT icon_id FROM moz_icons_to_pages)`);
+
+ // Expire annotations.
+ await db.execute(`DELETE FROM moz_items_annos WHERE expiration = :expire_session`,
+ { expire_session: Ci.nsIAnnotationService.EXPIRE_SESSION });
+ await db.execute(`DELETE FROM moz_annos WHERE id in (
+ SELECT a.id FROM moz_annos a
+ LEFT JOIN moz_places h ON a.place_id = h.id
+ WHERE h.id IS NULL
+ OR expiration = :expire_session
+ OR (expiration = :expire_with_history
+ AND h.last_visit_date ISNULL)
+ )`, { expire_session: Ci.nsIAnnotationService.EXPIRE_SESSION,
+ expire_with_history: Ci.nsIAnnotationService.EXPIRE_WITH_HISTORY });
+
+ // Expire inputhistory.
+ await db.execute(`DELETE FROM moz_inputhistory WHERE place_id IN (
+ SELECT i.place_id FROM moz_inputhistory i
+ LEFT JOIN moz_places h ON h.id = i.place_id
+ WHERE h.id IS NULL)`);
+
+ // Remove all history.
+ await db.execute("DELETE FROM moz_historyvisits");
+
+ // Invalidate frecencies for the remaining places.
+ await db.execute(`UPDATE moz_places SET frecency =
+ (CASE
+ WHEN url_hash BETWEEN hash("place", "prefix_lo") AND
+ hash("place", "prefix_hi")
+ THEN 0
+ ELSE -1
+ END)
+ WHERE frecency > 0`);
+ });
// Clear the registered embed visits.
PlacesUtils.history.clearEmbedVisits();
- // Expiration will take care of orphans.
let observers = PlacesUtils.history.getObservers();
notify(observers, "onClearHistory");
-
- // Invalidate frecencies for the remaining places. This must happen
- // after the notification to ensure it runs enqueued to expiration.
- await db.execute(
- `UPDATE moz_places SET frecency =
- (CASE
- WHEN url_hash BETWEEN hash("place", "prefix_lo") AND
- hash("place", "prefix_hi")
- THEN 0
- ELSE -1
- END)
- WHERE frecency > 0`);
-
// Notify frecency change observers.
notify(observers, "onManyFrecenciesChanged");
};
/**
* Clean up pages whose history has been modified, by either
* removing them entirely (if they are marked for removal,
* typically because all visits have been removed and there
--- a/toolkit/components/places/nsPlacesExpiration.js
+++ b/toolkit/components/places/nsPlacesExpiration.js
@@ -7,17 +7,16 @@
/**
* This component handles history and orphans expiration through asynchronous
* Storage statements.
* Expiration runs:
* - At idle, but just once, we stop any other kind of expiration during idle
* to preserve batteries in portable devices.
* - At shutdown, only if the database is dirty, we should still avoid to
* expire too heavily on shutdown.
- * - On ClearHistory we run a full expiration for privacy reasons.
* - On a repeating timer we expire in small chunks.
*
* Expiration algorithm will adapt itself based on:
* - Memory size of the device.
* - Status of the database (clean or dirty).
*/
const Cc = Components.classes;
@@ -104,21 +103,16 @@ const EXPIRE_AGGRESSIVITY_MULTIPLIER = 3
// Used as a fall back value when it's not possible to calculate the real value.
const URIENTRY_AVG_SIZE = 600;
// Seconds of idle time before starting a larger expiration step.
// Notice during idle we stop the expiration timer since we don't want to hurt
// stand-by or mobile devices batteries.
const IDLE_TIMEOUT_SECONDS = 5 * 60;
-// If a clear history ran just before we shutdown, we will skip most of the
-// expiration at shutdown. This is maximum number of seconds from last
-// clearHistory to decide to skip expiration at shutdown.
-const SHUTDOWN_WITH_RECENT_CLEARHISTORY_TIMEOUT_SECONDS = 10;
-
// If the number of pages over history limit is greater than this threshold,
// expiration will be more aggressive, to bring back history to a saner size.
const OVERLIMIT_PAGES_THRESHOLD = 1000;
const MSECS_PER_DAY = 86400000;
const ANNOS_EXPIRE_POLICIES = [
{ bind: "expire_days",
type: Ci.nsIAnnotationService.EXPIRE_DAYS,
@@ -129,17 +123,17 @@ const ANNOS_EXPIRE_POLICIES = [
{ bind: "expire_months",
type: Ci.nsIAnnotationService.EXPIRE_MONTHS,
time: 180 * 1000 * MSECS_PER_DAY },
];
// When we expire we can use these limits:
// - SMALL for usual partial expirations, will expire a small chunk.
// - LARGE for idle or shutdown expirations, will expire a large chunk.
-// - UNLIMITED for clearHistory, will expire everything.
+// - UNLIMITED will expire all the orphans.
// - DEBUG will use a known limit, passed along with the debug notification.
const LIMIT = {
SMALL: 0,
LARGE: 1,
UNLIMITED: 2,
DEBUG: 3,
};
@@ -149,21 +143,20 @@ const STATUS = {
DIRTY: 1,
UNKNOWN: 2,
};
// Represents actions on which a query will run.
const ACTION = {
TIMED: 1 << 0, // happens every this._interval
TIMED_OVERLIMIT: 1 << 1, // like TIMED but only when history is over limits
- CLEAR_HISTORY: 1 << 2, // happens when history is cleared
- SHUTDOWN_DIRTY: 1 << 3, // happens at shutdown for DIRTY state
- IDLE_DIRTY: 1 << 4, // happens on idle for DIRTY state
- IDLE_DAILY: 1 << 5, // happens once a day on idle
- DEBUG: 1 << 6, // happens on TOPIC_DEBUG_START_EXPIRATION
+ SHUTDOWN_DIRTY: 1 << 2, // happens at shutdown for DIRTY state
+ IDLE_DIRTY: 1 << 3, // happens on idle for DIRTY state
+ IDLE_DAILY: 1 << 4, // happens once a day on idle
+ DEBUG: 1 << 5, // happens on TOPIC_DEBUG_START_EXPIRATION
};
// The queries we use to expire.
const EXPIRATION_QUERIES = {
// Some visits can be expired more often than others, cause they are less
// useful to the user and can pollute awesomebar results:
// 1. urls over 255 chars
@@ -235,161 +228,127 @@ const EXPIRATION_QUERIES = {
QUERY_EXPIRE_URIS: {
sql: `DELETE FROM moz_places WHERE id IN (
SELECT p_id FROM expiration_notify WHERE p_id NOTNULL
) AND foreign_count = 0 AND last_visit_date ISNULL`,
actions: ACTION.TIMED | ACTION.TIMED_OVERLIMIT | ACTION.SHUTDOWN_DIRTY |
ACTION.IDLE_DIRTY | ACTION.IDLE_DAILY | ACTION.DEBUG
},
- // Expire orphan URIs from the database.
- QUERY_SILENT_EXPIRE_ORPHAN_URIS: {
- sql: `DELETE FROM moz_places WHERE id IN (
- SELECT h.id
- FROM moz_places h
- LEFT JOIN moz_historyvisits v ON h.id = v.place_id
- WHERE h.last_visit_date IS NULL
- AND h.foreign_count = 0
- AND v.id IS NULL
- LIMIT :limit_uris
- )`,
- actions: ACTION.CLEAR_HISTORY
- },
-
// Hosts accumulated during the places delete are updated through a trigger
// (see nsPlacesTriggers.h).
QUERY_UPDATE_HOSTS: {
sql: `DELETE FROM moz_updatehosts_temp`,
- actions: ACTION.CLEAR_HISTORY | ACTION.TIMED | ACTION.TIMED_OVERLIMIT |
- ACTION.SHUTDOWN_DIRTY | ACTION.IDLE_DIRTY | ACTION.IDLE_DAILY |
- ACTION.DEBUG
+ actions: ACTION.TIMED | ACTION.TIMED_OVERLIMIT | ACTION.SHUTDOWN_DIRTY |
+ ACTION.IDLE_DIRTY | ACTION.IDLE_DAILY | ACTION.DEBUG
},
// Expire orphan pages from the icons database.
QUERY_EXPIRE_FAVICONS_PAGES: {
sql: `DELETE FROM moz_pages_w_icons
WHERE page_url_hash NOT IN (
SELECT url_hash FROM moz_places
)`,
- actions: ACTION.TIMED_OVERLIMIT | ACTION.CLEAR_HISTORY |
- ACTION.SHUTDOWN_DIRTY | ACTION.IDLE_DIRTY | ACTION.IDLE_DAILY |
- ACTION.DEBUG
+ actions: ACTION.TIMED_OVERLIMIT | ACTION.SHUTDOWN_DIRTY |
+ ACTION.IDLE_DIRTY | ACTION.IDLE_DAILY | ACTION.DEBUG
},
// Expire orphan icons from the database.
QUERY_EXPIRE_FAVICONS: {
sql: `DELETE FROM moz_icons
WHERE root = 0 AND id NOT IN (
SELECT icon_id FROM moz_icons_to_pages
)`,
- actions: ACTION.TIMED_OVERLIMIT | ACTION.CLEAR_HISTORY |
- ACTION.SHUTDOWN_DIRTY | ACTION.IDLE_DIRTY | ACTION.IDLE_DAILY |
- ACTION.DEBUG
+ actions: ACTION.TIMED_OVERLIMIT | ACTION.SHUTDOWN_DIRTY |
+ ACTION.IDLE_DIRTY | ACTION.IDLE_DAILY | ACTION.DEBUG
},
// Expire orphan page annotations from the database.
QUERY_EXPIRE_ANNOS: {
sql: `DELETE FROM moz_annos WHERE id in (
SELECT a.id FROM moz_annos a
LEFT JOIN moz_places h ON a.place_id = h.id
WHERE h.id IS NULL
LIMIT :limit_annos
)`,
- actions: ACTION.TIMED | ACTION.TIMED_OVERLIMIT | ACTION.CLEAR_HISTORY |
- ACTION.SHUTDOWN_DIRTY | ACTION.IDLE_DIRTY | ACTION.IDLE_DAILY |
- ACTION.DEBUG
+ actions: ACTION.TIMED | ACTION.TIMED_OVERLIMIT | ACTION.SHUTDOWN_DIRTY |
+ ACTION.IDLE_DIRTY | ACTION.IDLE_DAILY | ACTION.DEBUG
},
// Expire page annotations based on expiration policy.
QUERY_EXPIRE_ANNOS_WITH_POLICY: {
sql: `DELETE FROM moz_annos
WHERE (expiration = :expire_days
AND :expire_days_time > MAX(lastModified, dateAdded))
OR (expiration = :expire_weeks
AND :expire_weeks_time > MAX(lastModified, dateAdded))
OR (expiration = :expire_months
AND :expire_months_time > MAX(lastModified, dateAdded))`,
- actions: ACTION.TIMED | ACTION.TIMED_OVERLIMIT | ACTION.CLEAR_HISTORY |
- ACTION.SHUTDOWN_DIRTY | ACTION.IDLE_DIRTY | ACTION.IDLE_DAILY |
- ACTION.DEBUG
+ actions: ACTION.TIMED | ACTION.TIMED_OVERLIMIT | ACTION.SHUTDOWN_DIRTY |
+ ACTION.IDLE_DIRTY | ACTION.IDLE_DAILY | ACTION.DEBUG
},
// Expire items annotations based on expiration policy.
QUERY_EXPIRE_ITEMS_ANNOS_WITH_POLICY: {
sql: `DELETE FROM moz_items_annos
WHERE (expiration = :expire_days
AND :expire_days_time > MAX(lastModified, dateAdded))
OR (expiration = :expire_weeks
AND :expire_weeks_time > MAX(lastModified, dateAdded))
OR (expiration = :expire_months
AND :expire_months_time > MAX(lastModified, dateAdded))`,
- actions: ACTION.TIMED | ACTION.TIMED_OVERLIMIT | ACTION.CLEAR_HISTORY |
- ACTION.SHUTDOWN_DIRTY | ACTION.IDLE_DIRTY | ACTION.IDLE_DAILY |
- ACTION.DEBUG
+ actions: ACTION.TIMED | ACTION.TIMED_OVERLIMIT | ACTION.SHUTDOWN_DIRTY |
+ ACTION.IDLE_DIRTY | ACTION.IDLE_DAILY | ACTION.DEBUG
},
// Expire page annotations based on expiration policy.
QUERY_EXPIRE_ANNOS_WITH_HISTORY: {
sql: `DELETE FROM moz_annos
WHERE expiration = :expire_with_history
AND NOT EXISTS (SELECT id FROM moz_historyvisits
WHERE place_id = moz_annos.place_id LIMIT 1)`,
- actions: ACTION.TIMED | ACTION.TIMED_OVERLIMIT | ACTION.CLEAR_HISTORY |
- ACTION.SHUTDOWN_DIRTY | ACTION.IDLE_DIRTY | ACTION.IDLE_DAILY |
- ACTION.DEBUG
+ actions: ACTION.TIMED | ACTION.TIMED_OVERLIMIT | ACTION.SHUTDOWN_DIRTY |
+ ACTION.IDLE_DIRTY | ACTION.IDLE_DAILY | ACTION.DEBUG
},
// Expire item annos without a corresponding item id.
QUERY_EXPIRE_ITEMS_ANNOS: {
sql: `DELETE FROM moz_items_annos WHERE id IN (
SELECT a.id FROM moz_items_annos a
LEFT JOIN moz_bookmarks b ON a.item_id = b.id
WHERE b.id IS NULL
LIMIT :limit_annos
)`,
- actions: ACTION.CLEAR_HISTORY | ACTION.IDLE_DAILY | ACTION.DEBUG
+ actions: ACTION.IDLE_DAILY | ACTION.DEBUG
},
// Expire all annotation names without a corresponding annotation.
QUERY_EXPIRE_ANNO_ATTRIBUTES: {
sql: `DELETE FROM moz_anno_attributes WHERE id IN (
SELECT n.id FROM moz_anno_attributes n
LEFT JOIN moz_annos a ON n.id = a.anno_attribute_id
LEFT JOIN moz_items_annos t ON n.id = t.anno_attribute_id
WHERE a.anno_attribute_id IS NULL
AND t.anno_attribute_id IS NULL
LIMIT :limit_annos
)`,
- actions: ACTION.CLEAR_HISTORY | ACTION.SHUTDOWN_DIRTY | ACTION.IDLE_DIRTY |
+ actions: ACTION.SHUTDOWN_DIRTY | ACTION.IDLE_DIRTY |
ACTION.IDLE_DAILY | ACTION.DEBUG
},
// Expire orphan inputhistory.
QUERY_EXPIRE_INPUTHISTORY: {
sql: `DELETE FROM moz_inputhistory WHERE place_id IN (
SELECT i.place_id FROM moz_inputhistory i
LEFT JOIN moz_places h ON h.id = i.place_id
WHERE h.id IS NULL
LIMIT :limit_inputhistory
)`,
- actions: ACTION.TIMED | ACTION.TIMED_OVERLIMIT | ACTION.CLEAR_HISTORY |
- ACTION.SHUTDOWN_DIRTY | ACTION.IDLE_DIRTY | ACTION.IDLE_DAILY |
- ACTION.DEBUG
- },
-
- // Expire all session annotations. Should only be called at shutdown.
- QUERY_EXPIRE_ANNOS_SESSION: {
- sql: "DELETE FROM moz_annos WHERE expiration = :expire_session",
- actions: ACTION.CLEAR_HISTORY | ACTION.DEBUG
- },
-
- // Expire all session item annotations. Should only be called at shutdown.
- QUERY_EXPIRE_ITEMS_ANNOS_SESSION: {
- sql: "DELETE FROM moz_items_annos WHERE expiration = :expire_session",
- actions: ACTION.CLEAR_HISTORY | ACTION.DEBUG
+ actions: ACTION.TIMED | ACTION.TIMED_OVERLIMIT | ACTION.SHUTDOWN_DIRTY |
+ ACTION.IDLE_DIRTY | ACTION.IDLE_DAILY | 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, guid, MAX(visit_date) AS visit_date,
MAX(IFNULL(MIN(p_id, 1), MIN(v_id, 0))) AS whole_entry,
@@ -492,22 +451,19 @@ nsPlacesExpiration.prototype = {
this._shuttingDown = true;
this.expireOnIdle = false;
if (this._timer) {
this._timer.cancel();
this._timer = null;
}
- // If we didn't ran a clearHistory recently and database is dirty, we
- // want to expire some entries, to speed up the expiration process.
- let hasRecentClearHistory =
- Date.now() - this._lastClearHistoryTime <
- SHUTDOWN_WITH_RECENT_CLEARHISTORY_TIMEOUT_SECONDS * 1000;
- if (!hasRecentClearHistory && this.status == STATUS.DIRTY) {
+ // If the database is dirty, we want to expire some entries, to speed up
+ // the expiration process.
+ if (this.status == STATUS.DIRTY) {
this._expireWithActionAndLimit(ACTION.SHUTDOWN_DIRTY, LIMIT.LARGE);
}
this._finalizeInternalStatements();
} else if (aTopic == TOPIC_PREF_CHANGED) {
this._loadPrefsPromise = this._loadPrefs().then(() => {
if (aData == PREF_INTERVAL_SECONDS) {
// Renew the timer with the new interval value.
@@ -572,22 +528,19 @@ nsPlacesExpiration.prototype = {
onEndUpdateBatch: function PEX_onEndUpdateBatch() {
this._inBatchMode = false;
// Restore timer.
if (!this._timer)
this._newTimer();
},
- _lastClearHistoryTime: 0,
onClearHistory: function PEX_onClearHistory() {
- this._lastClearHistoryTime = Date.now();
- // Expire orphans. History status is clean after a clear history.
+ // History status is clean after a clear history.
this.status = STATUS.CLEAN;
- this._expireWithActionAndLimit(ACTION.CLEAR_HISTORY, LIMIT.UNLIMITED);
},
onVisit() {},
onTitleChanged() {},
onDeleteURI() {},
onPageChanged() {},
onDeleteVisits() {},
@@ -977,19 +930,16 @@ nsPlacesExpiration.prototype = {
// Avoid expiring all visits in case of an unlimited debug expiration,
// just remove orphans instead.
params.limit_visits =
aLimit == LIMIT.DEBUG && baseLimit == -1 ? 0 : baseLimit;
break;
case "QUERY_FIND_URIS_TO_EXPIRE":
params.limit_uris = baseLimit;
break;
- case "QUERY_SILENT_EXPIRE_ORPHAN_URIS":
- params.limit_uris = baseLimit;
- break;
case "QUERY_EXPIRE_ANNOS":
// Each page may have multiple annos.
params.limit_annos = baseLimit * EXPIRE_AGGRESSIVITY_MULTIPLIER;
break;
case "QUERY_EXPIRE_ANNOS_WITH_POLICY":
case "QUERY_EXPIRE_ITEMS_ANNOS_WITH_POLICY":
let microNow = Date.now() * 1000;
ANNOS_EXPIRE_POLICIES.forEach(function(policy) {
@@ -1004,20 +954,16 @@ nsPlacesExpiration.prototype = {
params.limit_annos = baseLimit;
break;
case "QUERY_EXPIRE_ANNO_ATTRIBUTES":
params.limit_annos = baseLimit;
break;
case "QUERY_EXPIRE_INPUTHISTORY":
params.limit_inputhistory = baseLimit;
break;
- case "QUERY_EXPIRE_ANNOS_SESSION":
- case "QUERY_EXPIRE_ITEMS_ANNOS_SESSION":
- params.expire_session = Ci.nsIAnnotationService.EXPIRE_SESSION;
- break;
}
return stmt;
},
/**
* Creates a new timer based on this._interval.
*
--- a/toolkit/components/places/tests/PlacesTestUtils.jsm
+++ b/toolkit/components/places/tests/PlacesTestUtils.jsm
@@ -123,26 +123,21 @@ this.PlacesTestUtils = Object.freeze({
},
/**
* Clear all history.
*
* @return {Promise}
* @resolves When history was cleared successfully.
* @rejects JavaScript exception.
+ *
+ * @deprecated New consumers should directly use PlacesUtils.history.clear().
*/
clearHistory() {
- let expirationFinished = new Promise(resolve => {
- Services.obs.addObserver(function observe(subj, topic, data) {
- Services.obs.removeObserver(observe, topic);
- resolve();
- }, PlacesUtils.TOPIC_EXPIRATION_FINISHED);
- });
-
- return Promise.all([expirationFinished, PlacesUtils.history.clear()]);
+ return PlacesUtils.history.clear();
},
/**
* Waits for all pending async statements on the default connection.
*
* @return {Promise}
* @resolves When all pending async statements finished.
* @rejects Never.
--- a/toolkit/components/places/tests/expiration/test_clearHistory.js
+++ b/toolkit/components/places/tests/expiration/test_clearHistory.js
@@ -6,79 +6,18 @@
/**
* What this is aimed to test:
*
* History.clear() should expire everything but bookmarked pages and valid
* annos.
*/
-var as = PlacesUtils.annotations;
-
-/**
- * Creates an aged annotation.
- *
- * @param aIdentifier Either a page url or an item id.
- * @param aIdentifier Name of the annotation.
- * @param aValue Value for the annotation.
- * @param aExpirePolicy Expiration policy of the annotation.
- * @param aAgeInDays Age in days of the annotation.
- * @param [optional] aLastModifiedAgeInDays Age in days of the annotation, for lastModified.
- */
-var now = Date.now();
-function add_old_anno(aIdentifier, aName, aValue, aExpirePolicy,
- aAgeInDays, aLastModifiedAgeInDays) {
- let expireDate = (now - (aAgeInDays * 86400 * 1000)) * 1000;
- let lastModifiedDate = 0;
- if (aLastModifiedAgeInDays)
- lastModifiedDate = (now - (aLastModifiedAgeInDays * 86400 * 1000)) * 1000;
-
- let sql;
- if (typeof(aIdentifier) == "number") {
- // Item annotation.
- as.setItemAnnotation(aIdentifier, aName, aValue, 0, aExpirePolicy);
- // Update dateAdded for the last added annotation.
- sql = "UPDATE moz_items_annos SET dateAdded = :expire_date, lastModified = :last_modified " +
- "WHERE id = ( " +
- "SELECT a.id FROM moz_items_annos a " +
- "JOIN moz_anno_attributes n ON n.id = a.anno_attribute_id " +
- "WHERE a.item_id = :id " +
- "AND n.name = :anno_name " +
- "ORDER BY a.dateAdded DESC LIMIT 1 " +
- ")";
- } else if (aIdentifier instanceof Ci.nsIURI) {
- // Page annotation.
- as.setPageAnnotation(aIdentifier, aName, aValue, 0, aExpirePolicy);
- // Update dateAdded for the last added annotation.
- sql = "UPDATE moz_annos SET dateAdded = :expire_date, lastModified = :last_modified " +
- "WHERE id = ( " +
- "SELECT a.id FROM moz_annos a " +
- "JOIN moz_anno_attributes n ON n.id = a.anno_attribute_id " +
- "JOIN moz_places h on h.id = a.place_id " +
- "WHERE h.url_hash = hash(:id) AND h.url = :id " +
- "AND n.name = :anno_name " +
- "ORDER BY a.dateAdded DESC LIMIT 1 " +
- ")";
- } else
- do_throw("Wrong identifier type");
-
- let stmt = DBConn().createStatement(sql);
- stmt.params.id = (typeof(aIdentifier) == "number") ? aIdentifier
- : aIdentifier.spec;
- stmt.params.expire_date = expireDate;
- stmt.params.last_modified = lastModifiedDate;
- stmt.params.anno_name = aName;
- try {
- stmt.executeStep();
- } finally {
- stmt.finalize();
- }
-}
-
add_task(async function test_historyClear() {
+ let as = PlacesUtils.annotations;
// Set interval to a large value so we don't expire on it.
setInterval(3600); // 1h
// Expire all expirable pages.
setMaxPages(0);
// Add some bookmarked page with visit and annotations.
for (let i = 0; i < 5; i++) {
@@ -94,48 +33,38 @@ add_task(async function test_historyClea
// Will persist because it's an EXPIRE_NEVER item anno.
as.setItemAnnotation(id, "persist", "test", 0, as.EXPIRE_NEVER);
// Will persist because the page is bookmarked.
as.setPageAnnotation(pageURI, "persist", "test", 0, as.EXPIRE_NEVER);
// All EXPIRE_SESSION annotations are expected to expire on clear history.
as.setItemAnnotation(id, "expire_session", "test", 0, as.EXPIRE_SESSION);
as.setPageAnnotation(pageURI, "expire_session", "test", 0, as.EXPIRE_SESSION);
// Annotations with timed policy will expire regardless bookmarked status.
- add_old_anno(id, "expire_days", "test", as.EXPIRE_DAYS, 8);
- add_old_anno(id, "expire_weeks", "test", as.EXPIRE_WEEKS, 31);
- add_old_anno(id, "expire_months", "test", as.EXPIRE_MONTHS, 181);
- add_old_anno(pageURI, "expire_days", "test", as.EXPIRE_DAYS, 8);
- add_old_anno(pageURI, "expire_weeks", "test", as.EXPIRE_WEEKS, 31);
- add_old_anno(pageURI, "expire_months", "test", as.EXPIRE_MONTHS, 181);
}
// Add some visited page and annotations for each.
for (let i = 0; i < 5; i++) {
// All page annotations related to these expired pages are expected to
// expire as well.
let pageURI = uri("http://page_anno." + i + ".mozilla.org/");
await PlacesTestUtils.addVisits({ uri: pageURI });
as.setPageAnnotation(pageURI, "expire", "test", 0, as.EXPIRE_NEVER);
as.setPageAnnotation(pageURI, "expire_session", "test", 0, as.EXPIRE_SESSION);
- add_old_anno(pageURI, "expire_days", "test", as.EXPIRE_DAYS, 8);
- add_old_anno(pageURI, "expire_weeks", "test", as.EXPIRE_WEEKS, 31);
- add_old_anno(pageURI, "expire_months", "test", as.EXPIRE_MONTHS, 181);
}
// Expire all visits for the bookmarks
await PlacesTestUtils.clearHistory();
- ["expire_days", "expire_weeks", "expire_months", "expire_session",
+ ["expire_session",
"expire"].forEach(function(aAnno) {
let pages = as.getPagesWithAnnotation(aAnno);
do_check_eq(pages.length, 0);
});
- ["expire_days", "expire_weeks", "expire_months", "expire_session",
- "expire"].forEach(function(aAnno) {
+ ["expire_session", "expire"].forEach(function(aAnno) {
let items = as.getItemsWithAnnotation(aAnno);
do_check_eq(items.length, 0);
});
let pages = as.getPagesWithAnnotation("persist");
do_check_eq(pages.length, 5);
let items = as.getItemsWithAnnotation("persist");
--- a/toolkit/components/places/tests/expiration/test_notifications.js
+++ b/toolkit/components/places/tests/expiration/test_notifications.js
@@ -20,17 +20,17 @@ var gObserver = {
}
};
os.addObserver(gObserver, PlacesUtils.TOPIC_EXPIRATION_FINISHED);
function run_test() {
// Set interval to a large value so we don't expire on it.
setInterval(3600); // 1h
- PlacesTestUtils.clearHistory();
+ promiseForceExpirationStep(1);
do_timeout(2000, check_result);
do_test_pending();
}
function check_result() {
os.removeObserver(gObserver, PlacesUtils.TOPIC_EXPIRATION_FINISHED);
do_check_eq(gObserver.notifications, 1);
--- a/toolkit/components/places/tests/expiration/test_pref_interval.js
+++ b/toolkit/components/places/tests/expiration/test_pref_interval.js
@@ -12,50 +12,47 @@
// PREF_INTERVAL_SECONDS_NOTSET in nsPlacesExpiration.
const DEFAULT_TIMER_DELAY_SECONDS = 3 * 60;
// Sync this with the const value in the component.
const EXPIRE_AGGRESSIVITY_MULTIPLIER = 3;
var tests = [
- // This test should be the first, so the interval won't be influenced by
- // status of history.
{ desc: "Set interval to 1s.",
interval: 1,
- expectedTimerDelay: 1
+ expectedTimerDelay: 1 * EXPIRE_AGGRESSIVITY_MULTIPLIER
},
{ desc: "Set interval to a negative value.",
interval: -1,
- expectedTimerDelay: DEFAULT_TIMER_DELAY_SECONDS
+ expectedTimerDelay: DEFAULT_TIMER_DELAY_SECONDS * EXPIRE_AGGRESSIVITY_MULTIPLIER
},
{ desc: "Set interval to 0.",
interval: 0,
- expectedTimerDelay: DEFAULT_TIMER_DELAY_SECONDS
+ expectedTimerDelay: DEFAULT_TIMER_DELAY_SECONDS * EXPIRE_AGGRESSIVITY_MULTIPLIER
},
{ desc: "Set interval to a large value.",
interval: 100,
- expectedTimerDelay: 100
+ expectedTimerDelay: 100 * EXPIRE_AGGRESSIVITY_MULTIPLIER
},
];
add_task(async function test() {
// The pref should not exist by default.
Assert.throws(() => getInterval());
// Force the component, so it will start observing preferences.
force_expiration_start();
for (let currentTest of tests) {
+ currentTest = tests.shift();
print(currentTest.desc);
let promise = promiseTopicObserved("test-interval-changed");
setInterval(currentTest.interval);
let [, data] = await promise;
- Assert.equal(data, currentTest.expectedTimerDelay * EXPIRE_AGGRESSIVITY_MULTIPLIER);
+ Assert.equal(data, currentTest.expectedTimerDelay);
}
-
clearInterval();
});
-
--- a/toolkit/components/places/tests/unit/test_history_clear.js
+++ b/toolkit/components/places/tests/unit/test_history_clear.js
@@ -1,41 +1,16 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
var mDBConn = DBConn();
-function promiseOnClearHistoryObserved() {
- return new Promise(resolve => {
-
- let historyObserver = {
- onBeginUpdateBatch() {},
- onEndUpdateBatch() {},
- onVisit() {},
- onTitleChanged() {},
- onDeleteURI(aURI) {},
- onPageChanged() {},
- onDeleteVisits() {},
-
- onClearHistory() {
- PlacesUtils.history.removeObserver(this, false);
- resolve();
- },
-
- QueryInterface: XPCOMUtils.generateQI([
- Ci.nsINavHistoryObserver,
- ])
- }
- PlacesUtils.history.addObserver(historyObserver);
- });
-}
-
add_task(async function test_history_clear() {
await PlacesTestUtils.addVisits([
{ uri: uri("http://typed.mozilla.org/"),
transition: TRANSITION_TYPED },
{ uri: uri("http://link.mozilla.org/"),
transition: TRANSITION_LINK },
{ uri: uri("http://download.mozilla.org/"),
transition: TRANSITION_DOWNLOAD },
@@ -73,24 +48,24 @@ add_task(async function test_history_cle
{ uri: uri("http://typed.mozilla.org/"),
transition: TRANSITION_BOOKMARK },
{ uri: uri("http://frecency.mozilla.org/"),
transition: TRANSITION_LINK },
]);
await PlacesTestUtils.promiseAsyncUpdates();
// Clear history and wait for the onClearHistory notification.
- let promiseWaitClearHistory = promiseOnClearHistoryObserved();
+ let promiseClearHistory =
+ PlacesTestUtils.waitForNotification("onClearHistory", () => true, "history");
PlacesUtils.history.clear();
- await promiseWaitClearHistory;
+ await promiseClearHistory;
// check browserHistory returns no entries
do_check_eq(0, PlacesUtils.history.hasHistoryEntries);
- await promiseTopicObserved(PlacesUtils.TOPIC_EXPIRATION_FINISHED);
await PlacesTestUtils.promiseAsyncUpdates();
// Check that frecency for not cleared items (bookmarks) has been converted
// to -1.
let stmt = mDBConn.createStatement(
"SELECT h.id FROM moz_places h WHERE h.frecency > 0 ");
do_check_false(stmt.executeStep());
stmt.finalize();