Bug 1336518 - Move Sync history queries into PlacesSyncUtils. r=kitcambridge

MozReview-Commit-ID: Lood8ivLeJf

--HG--
extra : rebase_source : dcb8207378afc6ce2995acfe4f235f3ed728a188
This commit is contained in:
tiago 2017-07-30 17:46:56 -03:00
Родитель 9cb48aea09
Коммит 7a9b4d8b79
5 изменённых файлов: 408 добавлений и 137 удалений

Просмотреть файл

@ -9,7 +9,8 @@ var Ci = Components.interfaces;
var Cu = Components.utils;
var Cr = Components.results;
const HISTORY_TTL = 5184000; // 60 days
const HISTORY_TTL = 5184000; // 60 days in milliseconds
const THIRTY_DAYS_IN_MS = 2592000000; // 30 days in milliseconds
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://services-common/async.js");
@ -21,6 +22,9 @@ Cu.import("resource://services-sync/util.js");
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
"resource://gre/modules/PlacesUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PlacesSyncUtils",
"resource://gre/modules/PlacesSyncUtils.jsm");
this.HistoryRec = function HistoryRec(collection, id) {
CryptoWrapper.call(this, collection, id);
}
@ -71,41 +75,8 @@ HistoryEngine.prototype = {
return {};
}
let db = PlacesUtils.history.QueryInterface(Ci.nsPIPlacesDatabase)
.DBConnection;
// Filter out hidden pages and `TRANSITION_FRAMED_LINK` visits. These are
// excluded when rendering the history menu, so we use the same constraints
// for Sync. We also don't want to sync `TRANSITION_EMBED` visits, but those
// aren't stored in the database.
for (let startIndex = 0;
startIndex < modifiedGUIDs.length;
startIndex += SQLITE_MAX_VARIABLE_NUMBER) {
let chunkLength = Math.min(SQLITE_MAX_VARIABLE_NUMBER,
modifiedGUIDs.length - startIndex);
let query = `
SELECT DISTINCT p.guid FROM moz_places p
JOIN moz_historyvisits v ON p.id = v.place_id
WHERE p.guid IN (${new Array(chunkLength).fill("?").join(",")}) AND
(p.hidden = 1 OR v.visit_type IN (0,
${PlacesUtils.history.TRANSITION_FRAMED_LINK}))
`;
let statement = db.createAsyncStatement(query);
try {
for (let i = 0; i < chunkLength; i++) {
statement.bindByIndex(i, modifiedGUIDs[startIndex + i]);
}
let results = Async.querySpinningly(statement, ["guid"]);
let guids = results.map(result => result.guid);
this._tracker.removeChangedID(...guids);
} finally {
statement.finalize();
}
}
let guidsToRemove = await PlacesSyncUtils.history.determineNonSyncableGuids(modifiedGUIDs);
this._tracker.removeChangedID(...guidsToRemove);
return this._tracker.changedIDs;
},
};
@ -146,112 +117,59 @@ HistoryStore.prototype = {
return this._stmts[query] = db.createAsyncStatement(query);
},
get _setGUIDStm() {
return this._getStmt(
"UPDATE moz_places " +
"SET guid = :guid " +
"WHERE url_hash = hash(:page_url) AND url = :page_url");
},
// Some helper functions to handle GUIDs
setGUID: function setGUID(uri, guid) {
uri = uri.spec ? uri.spec : uri;
async setGUID(uri, guid) {
if (!guid) {
guid = Utils.makeGUID();
}
let stmt = this._setGUIDStm;
stmt.params.guid = guid;
stmt.params.page_url = uri;
Async.querySpinningly(stmt);
try {
await PlacesSyncUtils.history.changeGuid(uri, guid);
} catch (e) {
this._log.error("Error setting GUID ${guid} for URI ${uri}", guid, uri);
}
return guid;
},
get _guidStm() {
return this._getStmt(
"SELECT guid " +
"FROM moz_places " +
"WHERE url_hash = hash(:page_url) AND url = :page_url");
},
_guidCols: ["guid"],
GUIDForUri: function GUIDForUri(uri, create) {
let stm = this._guidStm;
stm.params.page_url = uri.spec ? uri.spec : uri;
async GUIDForUri(uri, create) {
// Use the existing GUID if it exists
let result = Async.querySpinningly(stm, this._guidCols)[0];
if (result && result.guid)
return result.guid;
let guid;
try {
guid = await PlacesSyncUtils.history.fetchGuidForURL(uri);
} catch (e) {
this._log.error("Error fetching GUID for URL ${uri}", uri);
}
// Give the uri a GUID if it doesn't have one
if (create)
// If the URI has an existing GUID, return it.
if (guid) {
return guid;
}
// If the URI doesn't have a GUID and we were indicated to create one.
if (create) {
return this.setGUID(uri);
},
}
get _visitStm() {
return this._getStmt(`/* do not warn (bug 599936) */
SELECT visit_type type, visit_date date
FROM moz_historyvisits
JOIN moz_places h ON h.id = place_id
WHERE url_hash = hash(:url) AND url = :url
ORDER BY date DESC LIMIT 20`);
},
_visitCols: ["date", "type"],
get _urlStm() {
return this._getStmt(
"SELECT url, title, frecency " +
"FROM moz_places " +
"WHERE guid = :guid");
},
_urlCols: ["url", "title", "frecency"],
get _allUrlStm() {
// Filter out hidden pages and framed link visits. See `pullNewChanges`
// for more info.
return this._getStmt(`
SELECT DISTINCT p.url
FROM moz_places p
JOIN moz_historyvisits v ON p.id = v.place_id
WHERE p.last_visit_date > :cutoff_date AND
p.hidden = 0 AND
v.visit_type NOT IN (0,
${PlacesUtils.history.TRANSITION_FRAMED_LINK})
ORDER BY frecency DESC
LIMIT :max_results`);
},
_allUrlCols: ["url"],
// See bug 320831 for why we use SQL here
_getVisits: function HistStore__getVisits(uri) {
this._visitStm.params.url = uri;
return Async.querySpinningly(this._visitStm, this._visitCols);
},
// See bug 468732 for why we use SQL here
_findURLByGUID: function HistStore__findURLByGUID(guid) {
this._urlStm.params.guid = guid;
return Async.querySpinningly(this._urlStm, this._urlCols)[0];
// If the URI doesn't have a GUID and we didn't create one for it.
return null;
},
async changeItemID(oldID, newID) {
this.setGUID(this._findURLByGUID(oldID).url, newID);
this.setGUID(await PlacesSyncUtils.history.fetchURLInfoForGuid(oldID).url, newID);
},
async getAllIDs() {
// Only get places visited within the last 30 days (30*24*60*60*1000ms)
this._allUrlStm.params.cutoff_date = (Date.now() - 2592000000) * 1000;
this._allUrlStm.params.max_results = MAX_HISTORY_UPLOAD;
let urls = await PlacesSyncUtils.history.getAllURLs({ since: new Date((Date.now() - THIRTY_DAYS_IN_MS)), limit: MAX_HISTORY_UPLOAD });
let urls = Async.querySpinningly(this._allUrlStm, this._allUrlCols);
let self = this;
return urls.reduce(function(ids, item) {
ids[self.GUIDForUri(item.url, true)] = item.url;
return ids;
}, {});
let urlsByGUID = {};
for (let url of urls) {
let guid = await this.GUIDForUri(url, true);
urlsByGUID[guid] = url;
}
return urlsByGUID;
},
async applyIncomingBatch(records) {
@ -274,7 +192,7 @@ HistoryStore.prototype = {
// No further processing needed. Remove it from the list.
shouldApply = false;
} else {
shouldApply = this._recordToPlaceInfo(record);
shouldApply = await this._recordToPlaceInfo(record);
}
} catch (ex) {
if (Async.isShutdownException(ex)) {
@ -315,7 +233,7 @@ HistoryStore.prototype = {
* returns true if the record is to be applied, false otherwise
* (no visits to add, etc.),
*/
_recordToPlaceInfo: function _recordToPlaceInfo(record) {
async _recordToPlaceInfo(record) {
// Sort out invalid URIs and ones Places just simply doesn't want.
record.uri = Utils.makeURI(record.histUri);
if (!record.uri) {
@ -339,7 +257,13 @@ HistoryStore.prototype = {
// the same timestamp and type as a local one won't get applied.
// To avoid creating new objects, we rewrite the query result so we
// can simply check for containment below.
let curVisits = this._getVisits(record.histUri);
let curVisits = [];
try {
curVisits = await PlacesSyncUtils.history.fetchVisitsForURL(record.histUri);
} catch (e) {
this._log.error("Error while fetching visits for URL ${record.histUri}", record.histUri);
}
let i, k;
for (i = 0; i < curVisits.length; i++) {
curVisits[i] = curVisits[i].date + "," + curVisits[i].type;
@ -402,17 +326,22 @@ HistoryStore.prototype = {
},
async itemExists(id) {
return !!this._findURLByGUID(id);
return !!(await PlacesSyncUtils.history.fetchURLInfoForGuid(id));
},
async createRecord(id, collection) {
let foo = this._findURLByGUID(id);
let foo = await PlacesSyncUtils.history.fetchURLInfoForGuid(id);
let record = new HistoryRec(collection, id);
if (foo) {
record.histUri = foo.url;
record.title = foo.title;
record.sortindex = foo.frecency;
record.visits = this._getVisits(record.histUri);
try {
record.visits = await PlacesSyncUtils.history.fetchVisitsForURL(record.histUri);
} catch (e) {
this._log.error("Error while fetching visits for URL ${record.histUri}", record.histUri);
record.visits = [];
}
} else {
record.deleted = true;
}

Просмотреть файл

@ -137,7 +137,7 @@ add_task(async function test_store_create() {
]);
await onVisitObserved;
try {
do_check_attribute_count(Async.promiseSpinningly(store.getAllIDs()), 2);
do_check_attribute_count(await store.getAllIDs(), 2);
let queryres = queryHistoryVisits(tburi);
do_check_eq(queryres.length, 1);
do_check_eq(queryres[0].time, TIMESTAMP3);

Просмотреть файл

@ -144,7 +144,7 @@ add_task(async function test_track_delete() {
// This isn't present because we weren't tracking when it was visited.
await addVisit("track_delete");
let uri = Utils.makeURI("http://getfirefox.com/track_delete");
let guid = engine._store.GUIDForUri(uri);
let guid = await engine._store.GUIDForUri(uri.spec);
await verifyTrackerEmpty();
await startTracking();
@ -162,7 +162,7 @@ add_task(async function test_track_delete() {
add_task(async function test_dont_track_expiration() {
_("Expirations are not tracked.");
let uriToRemove = await addVisit("to_remove");
let guidToRemove = engine._store.GUIDForUri(uriToRemove);
let guidToRemove = await engine._store.GUIDForUri(uriToRemove.spec);
await resetTracker();
await verifyTrackerEmpty();
@ -216,19 +216,19 @@ add_task(async function test_filter_hidden() {
_("Add visit; should be hidden by the redirect");
let hiddenURI = await addVisit("hidden");
let hiddenGUID = engine._store.GUIDForUri(hiddenURI);
let hiddenGUID = await engine._store.GUIDForUri(hiddenURI.spec);
_(`Hidden visit GUID: ${hiddenGUID}`);
_("Add redirect visit; should be tracked");
let trackedURI = await addVisit("redirect", hiddenURI,
let trackedURI = await addVisit("redirect", hiddenURI.spec,
PlacesUtils.history.TRANSITION_REDIRECT_PERMANENT);
let trackedGUID = engine._store.GUIDForUri(trackedURI);
let trackedGUID = await engine._store.GUIDForUri(trackedURI.spec);
_(`Tracked visit GUID: ${trackedGUID}`);
_("Add visit for framed link; should be ignored");
let embedURI = await addVisit("framed_link", null,
PlacesUtils.history.TRANSITION_FRAMED_LINK);
let embedGUID = engine._store.GUIDForUri(embedURI);
let embedGUID = await engine._store.GUIDForUri(embedURI.spec);
_(`Framed link visit GUID: ${embedGUID}`);
_("Run Places maintenance to mark redirect visit as hidden");

Просмотреть файл

@ -29,6 +29,7 @@ var PlacesSyncUtils = {};
const { SOURCE_SYNC } = Ci.nsINavBookmarksService;
const MICROSECONDS_PER_SECOND = 1000000;
const SQLITE_MAX_VARIABLE_NUMBER = 999;
const ORGANIZER_QUERY_ANNO = "PlacesOrganizer/OrganizerQuery";
const ORGANIZER_ALL_BOOKMARKS_ANNO_VALUE = "AllBookmarks";
@ -59,7 +60,27 @@ XPCOMUtils.defineLazyGetter(this, "ROOTS", () =>
Object.keys(ROOT_SYNC_ID_TO_GUID)
);
/**
* Auxiliary generator function that yields an array in chunks
*
* @param array
* @param chunkLength
* @yields {Array} New Array with the next chunkLength elements of array. If the array has less than chunkLength elements, yields all of them
*/
function* chunkArray(array, chunkLength) {
let startIndex = 0;
while (startIndex < array.length) {
yield array.slice(startIndex, startIndex += chunkLength);
}
}
const HistorySyncUtils = PlacesSyncUtils.history = Object.freeze({
/**
* Fetches the frecency for the URL provided
*
* @param url
* @returns {Number} The frecency of the given url
*/
async fetchURLFrecency(url) {
let canonicalURL = PlacesUtils.SYNC_BOOKMARK_VALIDATORS.url(url);
@ -71,8 +92,156 @@ const HistorySyncUtils = PlacesSyncUtils.history = Object.freeze({
LIMIT 1`,
{ url: canonicalURL.href }
);
return rows.length ? rows[0].getResultByName("frecency") : -1;
},
/**
* Filters syncable places from a collection of places guids.
*
* @param guids
*
* @returns {Array} new Array with the guids that aren't syncable
*/
async determineNonSyncableGuids(guids) {
// Filter out hidden pages and `TRANSITION_FRAMED_LINK` visits. These are
// excluded when rendering the history menu, so we use the same constraints
// for Sync. We also don't want to sync `TRANSITION_EMBED` visits, but those
// aren't stored in the database.
let db = await PlacesUtils.promiseDBConnection();
let nonSyncableGuids = [];
for (let chunk of chunkArray(guids, SQLITE_MAX_VARIABLE_NUMBER)) {
let rows = await db.execute(`
SELECT DISTINCT p.guid FROM moz_places p
JOIN moz_historyvisits v ON p.id = v.place_id
WHERE p.guid IN (${new Array(chunk.length).fill("?").join(",")}) AND
(p.hidden = 1 OR v.visit_type IN (0,
${PlacesUtils.history.TRANSITION_FRAMED_LINK}))
`, chunk);
nonSyncableGuids = nonSyncableGuids.concat(rows.map(row => row.getResultByName("guid")));
}
return nonSyncableGuids;
},
/**
* Change the guid of the given uri
*
* @param uri
* @param guid
*/
changeGuid(uri, guid) {
let canonicalURL = PlacesUtils.SYNC_BOOKMARK_VALIDATORS.url(uri);
let validatedGuid = PlacesUtils.BOOKMARK_VALIDATORS.guid(guid);
return PlacesUtils.withConnectionWrapper("HistorySyncUtils: changeGuid",
async function(db) {
await db.executeCached(`
UPDATE moz_places
SET guid = :guid
WHERE url_hash = hash(:page_url) AND url = :page_url`,
{guid: validatedGuid, page_url: canonicalURL.href});
});
},
/**
* Fetch the last 20 visits (date and type of it) corresponding to a given url
*
* @param url
* @returns {Array} Each element of the Array is an object with members: date and type
*/
async fetchVisitsForURL(url) {
let canonicalURL = PlacesUtils.SYNC_BOOKMARK_VALIDATORS.url(url);
let db = await PlacesUtils.promiseDBConnection();
let rows = await db.executeCached(`
SELECT visit_type type, visit_date date
FROM moz_historyvisits
JOIN moz_places h ON h.id = place_id
WHERE url_hash = hash(:url) AND url = :url
ORDER BY date DESC LIMIT 20`, { url: canonicalURL.href }
);
return rows.map(row => {
let visitDate = row.getResultByName("date");
let visitType = row.getResultByName("type");
return { date: visitDate, type: visitType };
});
},
/**
* Fetches the guid of a uri
*
* @param uri
* @returns {String} The guid of the given uri
*/
async fetchGuidForURL(url) {
let canonicalURL = PlacesUtils.SYNC_BOOKMARK_VALIDATORS.url(url);
let db = await PlacesUtils.promiseDBConnection();
let rows = await db.executeCached(`
SELECT guid
FROM moz_places
WHERE url_hash = hash(:page_url) AND url = :page_url`,
{ page_url: canonicalURL.href }
);
if (rows.length == 0) {
return null;
}
return rows[0].getResultByName("guid");
},
/**
* Fetch information about a guid (url, title and frecency)
*
* @param guid
* @returns {Object} Object with three members: url, title and frecency of the given guid
*/
async fetchURLInfoForGuid(guid) {
let db = await PlacesUtils.promiseDBConnection();
let rows = await db.executeCached(`
SELECT url, title, frecency
FROM moz_places
WHERE guid = :guid`,
{ guid }
);
if (rows.length === 0) {
return null;
}
return {
url: rows[0].getResultByName("url"),
title: rows[0].getResultByName("title"),
frecency: rows[0].getResultByName("frecency"),
};
},
/**
* Get all URLs filtered by the limit and since members of the options object.
*
* @param options
* Options object with two members, since and limit. Both of them must be provided
* @returns {Array} - Up to limit number of URLs starting from the date provided by since
*/
async getAllURLs(options) {
// Check that the limit property is finite number.
if (!Number.isFinite(options.limit)) {
throw new Error("The number provided in options.limit is not finite.");
}
// Check that the since property is of type Date.
if (!options.since || Object.prototype.toString.call(options.since) != "[object Date]") {
throw new Error("The property since of the options object must be of type Date.");
}
let db = await PlacesUtils.promiseDBConnection();
let sinceInMicroseconds = PlacesUtils.toPRTime(options.since);
let rows = await db.executeCached(`
SELECT DISTINCT p.url
FROM moz_places p
JOIN moz_historyvisits v ON p.id = v.place_id
WHERE p.last_visit_date > :cutoff_date AND
p.hidden = 0 AND
v.visit_type NOT IN (0,
${PlacesUtils.history.TRANSITION_FRAMED_LINK})
ORDER BY frecency DESC
LIMIT :max_results`,
{ cutoff_date: sinceInMicroseconds, max_results: options.limit }
);
return rows.map(row => row.getResultByName("url"));
},
});
const BookmarkSyncUtils = PlacesSyncUtils.bookmarks = Object.freeze({

Просмотреть файл

@ -170,15 +170,188 @@ add_task(async function test_fetchURLFrecency() {
}
for (let url of arrayOfURLsToVisit) {
let frecency = await PlacesSyncUtils.history.fetchURLFrecency(url);
equal(typeof frecency, "number");
notEqual(frecency, -1);
equal(typeof frecency, "number", "The frecency should be of type: number");
notEqual(frecency, -1, "The frecency of this url should be different than -1");
}
// Do not add visits to the following URLs, and then check if frecency for those URLs is -1.
let arrayOfURLsNotVisited = ["https://bugzilla.org", "https://example.org"];
for (let url of arrayOfURLsNotVisited) {
let frecency = await PlacesSyncUtils.history.fetchURLFrecency(url);
equal(frecency, -1);
equal(frecency, -1, "The frecency of this url should be -1");
}
// Remove the visits added during this test.
await PlacesTestUtils.clearHistory();
});
add_task(async function test_determineNonSyncableGuids() {
// Add visits to the following URLs with different transition types.
let arrayOfVisits = [{ uri: "https://www.mozilla.org/en-US/", transition: TRANSITION_TYPED },
{ uri: "http://getfirefox.com/", transition: TRANSITION_LINK },
{ uri: "http://getthunderbird.com/", transition: TRANSITION_FRAMED_LINK }];
for (let visit of arrayOfVisits) {
await PlacesTestUtils.addVisits(visit);
}
// Fetch the guid for each visit.
let guids = [];
let dictURLGuid = {};
for (let visit of arrayOfVisits) {
let guid = await PlacesSyncUtils.history.fetchGuidForURL(visit.uri);
guids.push(guid);
dictURLGuid[visit.uri] = guid;
}
// Filter the visits.
let filteredGuids = await PlacesSyncUtils.history.determineNonSyncableGuids(guids);
// Check if the filtered visits are of type TRANSITION_FRAMED_LINK.
for (let visit of arrayOfVisits) {
if (visit.transition === TRANSITION_FRAMED_LINK) {
ok(filteredGuids.includes(dictURLGuid[visit.uri]), "This url should be one of the filtered guids.");
} else {
ok(!filteredGuids.includes(dictURLGuid[visit.uri]), "This url should not be one of the filtered guids.");
}
}
// Remove the visits added during this test.
await PlacesTestUtils.clearHistory();
});
add_task(async function test_changeGuid() {
// Add some visits of the following URLs.
let arrayOfURLsToVisit = ["https://www.mozilla.org/en-US/", "http://getfirefox.com/", "http://getthunderbird.com/"];
for (let url of arrayOfURLsToVisit) {
await PlacesTestUtils.addVisits(url);
}
for (let url of arrayOfURLsToVisit) {
let originalGuid = await PlacesSyncUtils.history.fetchGuidForURL(url);
let newGuid = makeGuid();
// Change the original GUID for the new GUID.
await PlacesSyncUtils.history.changeGuid(url, newGuid);
// Fetch the GUID for this URL.
let newGuidFetched = await PlacesSyncUtils.history.fetchGuidForURL(url);
// Check that the URL has the new GUID as its GUID and not the original one.
equal(newGuid, newGuidFetched, "These should be equal since we changed the guid for the visit.");
notEqual(originalGuid, newGuidFetched, "These should be different since we changed the guid for the visit.");
}
// Remove the visits added during this test.
await PlacesTestUtils.clearHistory();
});
add_task(async function test_fetchVisitsForURL() {
// Get the date for this moment and a date for a minute ago.
let now = new Date();
let aMinuteAgo = new Date(now.getTime() - (1 * 60000));
// Add some visits of the following URLs, specifying the transition and the visit date.
let arrayOfVisits = [{ uri: "https://www.mozilla.org/en-US/", transition: TRANSITION_TYPED, visitDate: aMinuteAgo },
{ uri: "http://getfirefox.com/", transition: TRANSITION_LINK, visitDate: aMinuteAgo },
{ uri: "http://getthunderbird.com/", transition: TRANSITION_LINK, visitDate: aMinuteAgo }];
for (let elem of arrayOfVisits) {
await PlacesTestUtils.addVisits(elem);
}
for (let elem of arrayOfVisits) {
// Fetch all the visits for this URL.
let visits = await PlacesSyncUtils.history.fetchVisitsForURL(elem.uri);
// Since the visit we added will be the last one in the collection of visits, we get the index of it.
let iLast = visits.length - 1;
// The date is saved in _micro_seconds, here we change it to milliseconds.
let dateInMilliseconds = visits[iLast].date * 0.001;
// Check that the info we provided for this URL is the same one retrieved.
equal(dateInMilliseconds, elem.visitDate.getTime(), "The date we provided should be the same we retrieved.");
equal(visits[iLast].type, elem.transition, "The transition type we provided should be the same we retrieved.");
}
// Remove the visits added during this test.
await PlacesTestUtils.clearHistory();
});
add_task(async function test_fetchGuidForURL() {
// Add some visits of the following URLs.
let arrayOfURLsToVisit = ["https://www.mozilla.org/en-US/", "http://getfirefox.com/", "http://getthunderbird.com/"];
for (let url of arrayOfURLsToVisit) {
await PlacesTestUtils.addVisits(url);
}
// This tries to test fetchGuidForURL in two ways:
// 1- By fetching the GUID, and then using that GUID to retrieve the info of the visit.
// It then compares the URL with the URL that is on the visits info.
// 2- By creating a new GUID, changing the GUID for the visit, fetching the GUID and comparing them.
for (let url of arrayOfURLsToVisit) {
let guid = await PlacesSyncUtils.history.fetchGuidForURL(url)
let info = await PlacesSyncUtils.history.fetchURLInfoForGuid(guid);
let newGuid = makeGuid();
await PlacesSyncUtils.history.changeGuid(url, newGuid);
let newGuid2 = await PlacesSyncUtils.history.fetchGuidForURL(url);
equal(url, info.url, "The url provided and the url retrieved should be the same.");
equal(newGuid, newGuid2, "The changed guid and the retrieved guid should be the same.");
}
// Remove the visits added during this test.
await PlacesTestUtils.clearHistory();
});
add_task(async function test_fetchURLInfoForGuid() {
// Add some visits of the following URLs. specifying the title.
let visits = [{ uri: "https://www.mozilla.org/en-US/", title: "mozilla" },
{ uri: "http://getfirefox.com/", title: "firefox" },
{ uri: "http://getthunderbird.com/", title: "thunderbird" }];
for (let visit of visits) {
await PlacesTestUtils.addVisits(visit);
}
for (let visit of visits) {
let guid = await PlacesSyncUtils.history.fetchGuidForURL(visit.uri);
let info = await PlacesSyncUtils.history.fetchURLInfoForGuid(guid);
// Compare the info returned by fetchURLInfoForGuid,
// URL and title should match while frecency must be different than -1.
equal(info.url, visit.uri, "The url provided should be the same as the url retrieved.");
equal(info.title, visit.title, "The title provided should be the same as the title retrieved.");
notEqual(info.frecency, -1, "The frecency of the visit should be different than -1.");
}
// Create a "fake" GUID and check that the result of fetchURLInfoForGuid is null.
let guid = makeGuid();
let info = await PlacesSyncUtils.history.fetchURLInfoForGuid(guid);
equal(info, null, "The information object of a non-existent guid should be null.");
// Remove the visits added during this test.
await PlacesTestUtils.clearHistory();
});
add_task(async function test_getAllURLs() {
// Add some visits of the following URLs.
let arrayOfURLsToVisit = ["https://www.mozilla.org/en-US/", "http://getfirefox.com/", "http://getthunderbird.com/"];
for (let url of arrayOfURLsToVisit) {
await PlacesTestUtils.addVisits(url);
}
// Get all URLs.
let allURLs = await PlacesSyncUtils.history.getAllURLs({ since: new Date(Date.now() - 2592000000), limit: 5000 });
// The amount of URLs must be the same in both collections.
equal(allURLs.length, arrayOfURLsToVisit.length, "The amount of urls retrived should match the amount of urls provided.");
// Check that the correct URLs were retrived.
for (let url of arrayOfURLsToVisit) {
ok(allURLs.includes(url), "The urls retrieved should match the ones used in this test.");
}
// Remove the visits added during this test.
await PlacesTestUtils.clearHistory();
});
add_task(async function test_order() {