import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

View file

@ -0,0 +1,7 @@
"use strict";
module.exports = {
"extends": [
"../../../../../testing/xpcshell/xpcshell.eslintrc.js"
]
};

View file

@ -0,0 +1,19 @@
/* -*- 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 Ci = Components.interfaces;
var Cc = Components.classes;
var Cr = Components.results;
var Cu = Components.utils;
Cu.import("resource://gre/modules/Services.jsm");
// Import common head.
{
let commonFile = do_get_file("../head_common.js", false);
let uri = Services.io.newFileURI(commonFile);
Services.scriptloader.loadSubScript(uri.spec, this);
}

View file

@ -0,0 +1,257 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et: */
// Tests for `History.insert` and `History.insertMany`, as implemented in History.jsm
"use strict";
add_task(function* test_insert_error_cases() {
const TEST_URL = "http://mozilla.com";
Assert.throws(
() => PlacesUtils.history.insert(),
/TypeError: pageInfo must be an object/,
"passing a null into History.insert should throw a TypeError"
);
Assert.throws(
() => PlacesUtils.history.insert(1),
/TypeError: pageInfo must be an object/,
"passing a non object into History.insert should throw a TypeError"
);
Assert.throws(
() => PlacesUtils.history.insert({}),
/TypeError: PageInfo object must have a url property/,
"passing an object without a url to History.insert should throw a TypeError"
);
Assert.throws(
() => PlacesUtils.history.insert({url: 123}),
/TypeError: Invalid url or guid: 123/,
"passing an object with an invalid url to History.insert should throw a TypeError"
);
Assert.throws(
() => PlacesUtils.history.insert({url: TEST_URL}),
/TypeError: PageInfo object must have an array of visits/,
"passing an object without a visits property to History.insert should throw a TypeError"
);
Assert.throws(
() => PlacesUtils.history.insert({url: TEST_URL, visits: 1}),
/TypeError: PageInfo object must have an array of visits/,
"passing an object with a non-array visits property to History.insert should throw a TypeError"
);
Assert.throws(
() => PlacesUtils.history.insert({url: TEST_URL, visits: []}),
/TypeError: PageInfo object must have an array of visits/,
"passing an object with an empty array as the visits property to History.insert should throw a TypeError"
);
Assert.throws(
() => PlacesUtils.history.insert({
url: TEST_URL,
visits: [
{
transition: TRANSITION_LINK,
date: "a"
}
]}),
/TypeError: Expected a Date, got a/,
"passing a visit object with an invalid date to History.insert should throw a TypeError"
);
Assert.throws(
() => PlacesUtils.history.insert({
url: TEST_URL,
visits: [
{
transition: TRANSITION_LINK
},
{
transition: TRANSITION_LINK,
date: "a"
}
]}),
/TypeError: Expected a Date, got a/,
"passing a second visit object with an invalid date to History.insert should throw a TypeError"
);
let futureDate = new Date();
futureDate.setDate(futureDate.getDate() + 1000);
Assert.throws(
() => PlacesUtils.history.insert({
url: TEST_URL,
visits: [
{
transition: TRANSITION_LINK,
date: futureDate,
}
]}),
`TypeError: date: ${futureDate} is not a valid date`,
"passing a visit object with a future date to History.insert should throw a TypeError"
);
Assert.throws(
() => PlacesUtils.history.insert({
url: TEST_URL,
visits: [
{transition: "a"}
]}),
/TypeError: transition: a is not a valid transition type/,
"passing a visit object with an invalid transition to History.insert should throw a TypeError"
);
});
add_task(function* test_history_insert() {
const TEST_URL = "http://mozilla.com/";
let inserter = Task.async(function*(name, filter, referrer, date, transition) {
do_print(name);
do_print(`filter: ${filter}, referrer: ${referrer}, date: ${date}, transition: ${transition}`);
let uri = NetUtil.newURI(TEST_URL + Math.random());
let title = "Visit " + Math.random();
let pageInfo = {
title,
visits: [
{transition: transition, referrer: referrer, date: date, }
]
};
pageInfo.url = yield filter(uri);
let result = yield PlacesUtils.history.insert(pageInfo);
Assert.ok(PlacesUtils.isValidGuid(result.guid), "guid for pageInfo object is valid");
Assert.equal(uri.spec, result.url.href, "url is correct for pageInfo object");
Assert.equal(title, result.title, "title is correct for pageInfo object");
Assert.equal(TRANSITION_LINK, result.visits[0].transition, "transition is correct for pageInfo object");
if (referrer) {
Assert.equal(referrer, result.visits[0].referrer.href, "url of referrer for visit is correct");
} else {
Assert.equal(null, result.visits[0].referrer, "url of referrer for visit is correct");
}
if (date) {
Assert.equal(Number(date),
Number(result.visits[0].date),
"date of visit is correct");
}
Assert.ok(yield PlacesTestUtils.isPageInDB(uri), "Page was added");
Assert.ok(yield PlacesTestUtils.visitsInDB(uri), "Visit was added");
});
try {
for (let referrer of [TEST_URL, null]) {
for (let date of [new Date(), null]) {
for (let transition of [TRANSITION_LINK, null]) {
yield inserter("Testing History.insert() with an nsIURI", x => x, referrer, date, transition);
yield inserter("Testing History.insert() with a string url", x => x.spec, referrer, date, transition);
yield inserter("Testing History.insert() with a URL object", x => new URL(x.spec), referrer, date, transition);
}
}
}
} finally {
yield PlacesTestUtils.clearHistory();
}
});
add_task(function* test_insert_multiple_error_cases() {
let validPageInfo = {
url: "http://mozilla.com",
visits: [
{transition: TRANSITION_LINK}
]
};
Assert.throws(
() => PlacesUtils.history.insertMany(),
/TypeError: pageInfos must be an array/,
"passing a null into History.insertMany should throw a TypeError"
);
Assert.throws(
() => PlacesUtils.history.insertMany([]),
/TypeError: pageInfos may not be an empty array/,
"passing an empty array into History.insertMany should throw a TypeError"
);
Assert.throws(
() => PlacesUtils.history.insertMany([validPageInfo, {}]),
/TypeError: PageInfo object must have a url property/,
"passing a second invalid PageInfo object to History.insertMany should throw a TypeError"
);
});
add_task(function* test_history_insertMany() {
const BAD_URLS = ["about:config", "chrome://browser/content/browser.xul"];
const GOOD_URLS = [1, 2, 3].map(x => { return `http://mozilla.com/${x}`; });
let makePageInfos = Task.async(function*(urls, filter = x => x) {
let pageInfos = [];
for (let url of urls) {
let uri = NetUtil.newURI(url);
let pageInfo = {
title: `Visit to ${url}`,
visits: [
{transition: TRANSITION_LINK}
]
};
pageInfo.url = yield filter(uri);
pageInfos.push(pageInfo);
}
return pageInfos;
});
let inserter = Task.async(function*(name, filter, useCallbacks) {
do_print(name);
do_print(`filter: ${filter}`);
do_print(`useCallbacks: ${useCallbacks}`);
yield PlacesTestUtils.clearHistory();
let result;
let allUrls = GOOD_URLS.concat(BAD_URLS);
let pageInfos = yield makePageInfos(allUrls, filter);
if (useCallbacks) {
let onResultUrls = [];
let onErrorUrls = [];
result = yield PlacesUtils.history.insertMany(pageInfos, pageInfo => {
let url = pageInfo.url.href;
Assert.ok(GOOD_URLS.includes(url), "onResult callback called for correct url");
onResultUrls.push(url);
Assert.equal(`Visit to ${url}`, pageInfo.title, "onResult callback provides the correct title");
Assert.ok(PlacesUtils.isValidGuid(pageInfo.guid), "onResult callback provides a valid guid");
}, pageInfo => {
let url = pageInfo.url.href;
Assert.ok(BAD_URLS.includes(url), "onError callback called for correct uri");
onErrorUrls.push(url);
Assert.equal(undefined, pageInfo.title, "onError callback provides the correct title");
Assert.equal(undefined, pageInfo.guid, "onError callback provides the expected guid");
});
Assert.equal(GOOD_URLS.sort().toString(), onResultUrls.sort().toString(), "onResult callback was called for each good url");
Assert.equal(BAD_URLS.sort().toString(), onErrorUrls.sort().toString(), "onError callback was called for each bad url");
} else {
result = yield PlacesUtils.history.insertMany(pageInfos);
}
Assert.equal(undefined, result, "insertMany returned undefined");
for (let url of allUrls) {
let expected = GOOD_URLS.includes(url);
Assert.equal(expected, yield PlacesTestUtils.isPageInDB(url), `isPageInDB for ${url} is ${expected}`);
Assert.equal(expected, yield PlacesTestUtils.visitsInDB(url), `visitsInDB for ${url} is ${expected}`);
}
});
try {
for (let useCallbacks of [false, true]) {
yield inserter("Testing History.insertMany() with an nsIURI", x => x, useCallbacks);
yield inserter("Testing History.insertMany() with a string url", x => x.spec, useCallbacks);
yield inserter("Testing History.insertMany() with a URL object", x => new URL(x.spec), useCallbacks);
}
// Test rejection when no items added
let pageInfos = yield makePageInfos(BAD_URLS);
PlacesUtils.history.insertMany(pageInfos).then(() => {
Assert.ok(false, "History.insertMany rejected promise with all bad URLs");
}, error => {
Assert.equal("No items were added to history.", error.message, "History.insertMany rejected promise with all bad URLs");
});
} finally {
yield PlacesTestUtils.clearHistory();
}
});

View file

@ -0,0 +1,360 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et: */
// Tests for `History.remove`, as implemented in History.jsm
"use strict";
Cu.importGlobalProperties(["URL"]);
// Test removing a single page
add_task(function* test_remove_single() {
yield PlacesTestUtils.clearHistory();
yield PlacesUtils.bookmarks.eraseEverything();
let WITNESS_URI = NetUtil.newURI("http://mozilla.com/test_browserhistory/test_remove/" + Math.random());
yield PlacesTestUtils.addVisits(WITNESS_URI);
Assert.ok(page_in_database(WITNESS_URI));
let remover = Task.async(function*(name, filter, options) {
do_print(name);
do_print(JSON.stringify(options));
do_print("Setting up visit");
let uri = NetUtil.newURI("http://mozilla.com/test_browserhistory/test_remove/" + Math.random());
let title = "Visit " + Math.random();
yield PlacesTestUtils.addVisits({uri: uri, title: title});
Assert.ok(visits_in_database(uri), "History entry created");
let removeArg = yield filter(uri);
if (options.addBookmark) {
PlacesUtils.bookmarks.insertBookmark(
PlacesUtils.unfiledBookmarksFolderId,
uri,
PlacesUtils.bookmarks.DEFAULT_INDEX,
"test bookmark");
}
let shouldRemove = !options.addBookmark;
let observer;
let promiseObserved = new Promise((resolve, reject) => {
observer = {
onBeginUpdateBatch: function() {},
onEndUpdateBatch: function() {},
onVisit: function(aUri) {
reject(new Error("Unexpected call to onVisit " + aUri.spec));
},
onTitleChanged: function(aUri) {
reject(new Error("Unexpected call to onTitleChanged " + aUri.spec));
},
onClearHistory: function() {
reject("Unexpected call to onClearHistory");
},
onPageChanged: function(aUri) {
reject(new Error("Unexpected call to onPageChanged " + aUri.spec));
},
onFrecencyChanged: function(aURI) {
try {
Assert.ok(!shouldRemove, "Observing onFrecencyChanged");
Assert.equal(aURI.spec, uri.spec, "Observing effect on the right uri");
} finally {
resolve();
}
},
onManyFrecenciesChanged: function() {
try {
Assert.ok(!shouldRemove, "Observing onManyFrecenciesChanged");
} finally {
resolve();
}
},
onDeleteURI: function(aURI) {
try {
Assert.ok(shouldRemove, "Observing onDeleteURI");
Assert.equal(aURI.spec, uri.spec, "Observing effect on the right uri");
} finally {
resolve();
}
},
onDeleteVisits: function(aURI) {
Assert.equal(aURI.spec, uri.spec, "Observing onDeleteVisits on the right uri");
}
};
});
PlacesUtils.history.addObserver(observer, false);
do_print("Performing removal");
let removed = false;
if (options.useCallback) {
let onRowCalled = false;
let guid = do_get_guid_for_uri(uri);
removed = yield PlacesUtils.history.remove(removeArg, page => {
Assert.equal(onRowCalled, false, "Callback has not been called yet");
onRowCalled = true;
Assert.equal(page.url.href, uri.spec, "Callback provides the correct url");
Assert.equal(page.guid, guid, "Callback provides the correct guid");
Assert.equal(page.title, title, "Callback provides the correct title");
});
Assert.ok(onRowCalled, "Callback has been called");
} else {
removed = yield PlacesUtils.history.remove(removeArg);
}
yield promiseObserved;
PlacesUtils.history.removeObserver(observer);
Assert.equal(visits_in_database(uri), 0, "History entry has disappeared");
Assert.notEqual(visits_in_database(WITNESS_URI), 0, "Witness URI still has visits");
Assert.notEqual(page_in_database(WITNESS_URI), 0, "Witness URI is still here");
if (shouldRemove) {
Assert.ok(removed, "Something was removed");
Assert.equal(page_in_database(uri), 0, "Page has disappeared");
} else {
Assert.ok(!removed, "The page was not removed, as there was a bookmark");
Assert.notEqual(page_in_database(uri), 0, "The page is still present");
}
});
try {
for (let useCallback of [false, true]) {
for (let addBookmark of [false, true]) {
let options = { useCallback: useCallback, addBookmark: addBookmark };
yield remover("Testing History.remove() with a single URI", x => x, options);
yield remover("Testing History.remove() with a single string url", x => x.spec, options);
yield remover("Testing History.remove() with a single string guid", x => do_get_guid_for_uri(x), options);
yield remover("Testing History.remove() with a single URI in an array", x => [x], options);
yield remover("Testing History.remove() with a single string url in an array", x => [x.spec], options);
yield remover("Testing History.remove() with a single string guid in an array", x => [do_get_guid_for_uri(x)], options);
}
}
} finally {
yield PlacesTestUtils.clearHistory();
}
return;
});
// Test removing a list of pages
add_task(function* test_remove_many() {
const SIZE = 10;
yield PlacesTestUtils.clearHistory();
yield PlacesUtils.bookmarks.eraseEverything();
do_print("Adding a witness page");
let WITNESS_URI = NetUtil.newURI("http://mozilla.com/test_browserhistory/test_remove/" + Math.random());
yield PlacesTestUtils.addVisits(WITNESS_URI);
Assert.ok(page_in_database(WITNESS_URI), "Witness page added");
do_print("Generating samples");
let pages = [];
for (let i = 0; i < SIZE; ++i) {
let uri = NetUtil.newURI("http://mozilla.com/test_browserhistory/test_remove?sample=" + i + "&salt=" + Math.random());
let title = "Visit " + i + ", " + Math.random();
let hasBookmark = i % 3 == 0;
let page = {
uri: uri,
title: title,
hasBookmark: hasBookmark,
// `true` once `onResult` has been called for this page
onResultCalled: false,
// `true` once `onDeleteVisits` has been called for this page
onDeleteVisitsCalled: false,
// `true` once `onFrecencyChangedCalled` has been called for this page
onFrecencyChangedCalled: false,
// `true` once `onDeleteURI` has been called for this page
onDeleteURICalled: false,
};
do_print("Pushing: " + uri.spec);
pages.push(page);
yield PlacesTestUtils.addVisits(page);
page.guid = do_get_guid_for_uri(uri);
if (hasBookmark) {
PlacesUtils.bookmarks.insertBookmark(
PlacesUtils.unfiledBookmarksFolderId,
uri,
PlacesUtils.bookmarks.DEFAULT_INDEX,
"test bookmark " + i);
}
Assert.ok(page_in_database(uri), "Page added");
}
do_print("Mixing key types and introducing dangling keys");
let keys = [];
for (let i = 0; i < SIZE; ++i) {
if (i % 4 == 0) {
keys.push(pages[i].uri);
keys.push(NetUtil.newURI("http://example.org/dangling/nsIURI/" + i));
} else if (i % 4 == 1) {
keys.push(new URL(pages[i].uri.spec));
keys.push(new URL("http://example.org/dangling/URL/" + i));
} else if (i % 4 == 2) {
keys.push(pages[i].uri.spec);
keys.push("http://example.org/dangling/stringuri/" + i);
} else {
keys.push(pages[i].guid);
keys.push(("guid_" + i + "_01234567890").substr(0, 12));
}
}
let observer = {
onBeginUpdateBatch: function() {},
onEndUpdateBatch: function() {},
onVisit: function(aURI) {
Assert.ok(false, "Unexpected call to onVisit " + aURI.spec);
},
onTitleChanged: function(aURI) {
Assert.ok(false, "Unexpected call to onTitleChanged " + aURI.spec);
},
onClearHistory: function() {
Assert.ok(false, "Unexpected call to onClearHistory");
},
onPageChanged: function(aURI) {
Assert.ok(false, "Unexpected call to onPageChanged " + aURI.spec);
},
onFrecencyChanged: function(aURI) {
let origin = pages.find(x => x.uri.spec == aURI.spec);
Assert.ok(origin);
Assert.ok(origin.hasBookmark, "Observing onFrecencyChanged on a page with a bookmark");
origin.onFrecencyChangedCalled = true;
// We do not make sure that `origin.onFrecencyChangedCalled` is `false`, as
},
onManyFrecenciesChanged: function() {
Assert.ok(false, "Observing onManyFrecenciesChanges, this is most likely correct but not covered by this test");
},
onDeleteURI: function(aURI) {
let origin = pages.find(x => x.uri.spec == aURI.spec);
Assert.ok(origin);
Assert.ok(!origin.hasBookmark, "Observing onDeleteURI on a page without a bookmark");
Assert.ok(!origin.onDeleteURICalled, "Observing onDeleteURI for the first time");
origin.onDeleteURICalled = true;
},
onDeleteVisits: function(aURI) {
let origin = pages.find(x => x.uri.spec == aURI.spec);
Assert.ok(origin);
Assert.ok(!origin.onDeleteVisitsCalled, "Observing onDeleteVisits for the first time");
origin.onDeleteVisitsCalled = true;
}
};
PlacesUtils.history.addObserver(observer, false);
do_print("Removing the pages and checking the callbacks");
let removed = yield PlacesUtils.history.remove(keys, page => {
let origin = pages.find(candidate => candidate.uri.spec == page.url.href);
Assert.ok(origin, "onResult has a valid page");
Assert.ok(!origin.onResultCalled, "onResult has not seen this page yet");
origin.onResultCalled = true;
Assert.equal(page.guid, origin.guid, "onResult has the right guid");
Assert.equal(page.title, origin.title, "onResult has the right title");
});
Assert.ok(removed, "Something was removed");
PlacesUtils.history.removeObserver(observer);
do_print("Checking out results");
// By now the observers should have been called.
for (let i = 0; i < pages.length; ++i) {
let page = pages[i];
do_print("Page: " + i);
Assert.ok(page.onResultCalled, "We have reached the page from the callback");
Assert.ok(visits_in_database(page.uri) == 0, "History entry has disappeared");
Assert.equal(page_in_database(page.uri) != 0, page.hasBookmark, "Page is present only if it also has bookmarks");
Assert.equal(page.onFrecencyChangedCalled, page.onDeleteVisitsCalled, "onDeleteVisits was called iff onFrecencyChanged was called");
Assert.ok(page.onFrecencyChangedCalled ^ page.onDeleteURICalled, "Either onFrecencyChanged or onDeleteURI was called");
}
Assert.notEqual(visits_in_database(WITNESS_URI), 0, "Witness URI still has visits");
Assert.notEqual(page_in_database(WITNESS_URI), 0, "Witness URI is still here");
});
add_task(function* cleanup() {
yield PlacesTestUtils.clearHistory();
yield PlacesUtils.bookmarks.eraseEverything();
});
// Test the various error cases
add_task(function* test_error_cases() {
Assert.throws(
() => PlacesUtils.history.remove(),
/TypeError: Invalid url/,
"History.remove with no argument should throw a TypeError"
);
Assert.throws(
() => PlacesUtils.history.remove(null),
/TypeError: Invalid url/,
"History.remove with `null` should throw a TypeError"
);
Assert.throws(
() => PlacesUtils.history.remove(undefined),
/TypeError: Invalid url/,
"History.remove with `undefined` should throw a TypeError"
);
Assert.throws(
() => PlacesUtils.history.remove("not a guid, obviously"),
/TypeError: .* is not a valid URL/,
"History.remove with an ill-formed guid/url argument should throw a TypeError"
);
Assert.throws(
() => PlacesUtils.history.remove({"not the kind of object we know how to handle": true}),
/TypeError: Invalid url/,
"History.remove with an unexpected object should throw a TypeError"
);
Assert.throws(
() => PlacesUtils.history.remove([]),
/TypeError: Expected at least one page/,
"History.remove with an empty array should throw a TypeError"
);
Assert.throws(
() => PlacesUtils.history.remove([null]),
/TypeError: Invalid url or guid/,
"History.remove with an array containing null should throw a TypeError"
);
Assert.throws(
() => PlacesUtils.history.remove(["http://example.org", "not a guid, obviously"]),
/TypeError: .* is not a valid URL/,
"History.remove with an array containing an ill-formed guid/url argument should throw a TypeError"
);
Assert.throws(
() => PlacesUtils.history.remove(["0123456789ab"/* valid guid*/, null]),
/TypeError: Invalid url or guid: null/,
"History.remove with an array containing a guid and a second argument that is null should throw a TypeError"
);
Assert.throws(
() => PlacesUtils.history.remove(["http://example.org", {"not the kind of object we know how to handle": true}]),
/TypeError: Invalid url/,
"History.remove with an array containing an unexpected objecgt should throw a TypeError"
);
Assert.throws(
() => PlacesUtils.history.remove("http://example.org", "not a function, obviously"),
/TypeError: Invalid function/,
"History.remove with a second argument that is not a function argument should throw a TypeError"
);
try {
PlacesUtils.history.remove("http://example.org/I/have/clearly/not/been/added", null);
Assert.ok(true, "History.remove should ignore `null` as a second argument");
} catch (ex) {
Assert.ok(false, "History.remove should ignore `null` as a second argument");
}
});
add_task(function* test_orphans() {
let uri = NetUtil.newURI("http://moz.org/");
yield PlacesTestUtils.addVisits({ uri });
PlacesUtils.favicons.setAndFetchFaviconForPage(
uri, SMALLPNG_DATA_URI, true, PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE,
null, Services.scriptSecurityManager.getSystemPrincipal());
PlacesUtils.annotations.setPageAnnotation(uri, "test", "restval", 0,
PlacesUtils.annotations.EXPIRE_NEVER);
yield PlacesUtils.history.remove(uri);
Assert.ok(!(yield PlacesTestUtils.isPageInDB(uri)), "Page should have been removed");
let db = yield PlacesUtils.promiseDBConnection();
let rows = yield db.execute(`SELECT (SELECT count(*) FROM moz_annos) +
(SELECT count(*) FROM moz_favicons) AS count`);
Assert.equal(rows[0].getResultByName("count"), 0, "Should not find orphans");
});

View file

@ -0,0 +1,316 @@
const JS_NOW = Date.now();
const DB_NOW = JS_NOW * 1000;
const TEST_URI = uri("http://example.com/");
const PLACE_URI = uri("place:queryType=0&sort=8&maxResults=10");
function* cleanup() {
yield PlacesTestUtils.clearHistory();
yield PlacesUtils.bookmarks.eraseEverything();
// This is needed to remove place: entries.
DBConn().executeSimpleSQL("DELETE FROM moz_places");
}
add_task(function* remove_visits_outside_unbookmarked_uri() {
do_print("*** TEST: Remove some visits outside valid timeframe from an unbookmarked URI");
do_print("Add 10 visits for the URI from way in the past.");
let visits = [];
for (let i = 0; i < 10; i++) {
visits.push({ uri: TEST_URI, visitDate: DB_NOW - 100000 - (i * 1000) });
}
yield PlacesTestUtils.addVisits(visits);
do_print("Remove visits using timerange outside the URI's visits.");
let filter = {
beginDate: new Date(JS_NOW - 10),
endDate: new Date(JS_NOW)
};
yield PlacesUtils.history.removeVisitsByFilter(filter);
yield PlacesTestUtils.promiseAsyncUpdates();
do_print("URI should still exist in moz_places.");
do_check_true(page_in_database(TEST_URI.spec));
do_print("Run a history query and check that all visits still exist.");
let query = PlacesUtils.history.getNewQuery();
let opts = PlacesUtils.history.getNewQueryOptions();
opts.resultType = opts.RESULTS_AS_VISIT;
opts.sortingMode = opts.SORT_BY_DATE_DESCENDING;
let root = PlacesUtils.history.executeQuery(query, opts).root;
root.containerOpen = true;
do_check_eq(root.childCount, 10);
for (let i = 0; i < root.childCount; i++) {
let visitTime = root.getChild(i).time;
do_check_eq(visitTime, DB_NOW - 100000 - (i * 1000));
}
root.containerOpen = false;
do_print("asyncHistory.isURIVisited should return true.");
do_check_true(yield promiseIsURIVisited(TEST_URI));
yield PlacesTestUtils.promiseAsyncUpdates();
do_print("Frecency should be positive.")
do_check_true(frecencyForUrl(TEST_URI) > 0);
yield cleanup();
});
add_task(function* remove_visits_outside_bookmarked_uri() {
do_print("*** TEST: Remove some visits outside valid timeframe from a bookmarked URI");
do_print("Add 10 visits for the URI from way in the past.");
let visits = [];
for (let i = 0; i < 10; i++) {
visits.push({ uri: TEST_URI, visitDate: DB_NOW - 100000 - (i * 1000) });
}
yield PlacesTestUtils.addVisits(visits);
do_print("Bookmark the URI.");
PlacesUtils.bookmarks.insertBookmark(PlacesUtils.unfiledBookmarksFolderId,
TEST_URI,
PlacesUtils.bookmarks.DEFAULT_INDEX,
"bookmark title");
yield PlacesTestUtils.promiseAsyncUpdates();
do_print("Remove visits using timerange outside the URI's visits.");
let filter = {
beginDate: new Date(JS_NOW - 10),
endDate: new Date(JS_NOW)
};
yield PlacesUtils.history.removeVisitsByFilter(filter);
yield PlacesTestUtils.promiseAsyncUpdates();
do_print("URI should still exist in moz_places.");
do_check_true(page_in_database(TEST_URI.spec));
do_print("Run a history query and check that all visits still exist.");
let query = PlacesUtils.history.getNewQuery();
let opts = PlacesUtils.history.getNewQueryOptions();
opts.resultType = opts.RESULTS_AS_VISIT;
opts.sortingMode = opts.SORT_BY_DATE_DESCENDING;
let root = PlacesUtils.history.executeQuery(query, opts).root;
root.containerOpen = true;
do_check_eq(root.childCount, 10);
for (let i = 0; i < root.childCount; i++) {
let visitTime = root.getChild(i).time;
do_check_eq(visitTime, DB_NOW - 100000 - (i * 1000));
}
root.containerOpen = false;
do_print("asyncHistory.isURIVisited should return true.");
do_check_true(yield promiseIsURIVisited(TEST_URI));
yield PlacesTestUtils.promiseAsyncUpdates();
do_print("Frecency should be positive.")
do_check_true(frecencyForUrl(TEST_URI) > 0);
yield cleanup();
});
add_task(function* remove_visits_unbookmarked_uri() {
do_print("*** TEST: Remove some visits from an unbookmarked URI");
do_print("Add 10 visits for the URI from now to 9 usecs in the past.");
let visits = [];
for (let i = 0; i < 10; i++) {
visits.push({ uri: TEST_URI, visitDate: DB_NOW - (i * 1000) });
}
yield PlacesTestUtils.addVisits(visits);
do_print("Remove the 5 most recent visits.");
let filter = {
beginDate: new Date(JS_NOW - 4),
endDate: new Date(JS_NOW)
};
yield PlacesUtils.history.removeVisitsByFilter(filter);
yield PlacesTestUtils.promiseAsyncUpdates();
do_print("URI should still exist in moz_places.");
do_check_true(page_in_database(TEST_URI.spec));
do_print("Run a history query and check that only the older 5 visits still exist.");
let query = PlacesUtils.history.getNewQuery();
let opts = PlacesUtils.history.getNewQueryOptions();
opts.resultType = opts.RESULTS_AS_VISIT;
opts.sortingMode = opts.SORT_BY_DATE_DESCENDING;
let root = PlacesUtils.history.executeQuery(query, opts).root;
root.containerOpen = true;
do_check_eq(root.childCount, 5);
for (let i = 0; i < root.childCount; i++) {
let visitTime = root.getChild(i).time;
do_check_eq(visitTime, DB_NOW - (i * 1000) - 5000);
}
root.containerOpen = false;
do_print("asyncHistory.isURIVisited should return true.");
do_check_true(yield promiseIsURIVisited(TEST_URI));
yield PlacesTestUtils.promiseAsyncUpdates();
do_print("Frecency should be positive.")
do_check_true(frecencyForUrl(TEST_URI) > 0);
yield cleanup();
});
add_task(function* remove_visits_bookmarked_uri() {
do_print("*** TEST: Remove some visits from a bookmarked URI");
do_print("Add 10 visits for the URI from now to 9 usecs in the past.");
let visits = [];
for (let i = 0; i < 10; i++) {
visits.push({ uri: TEST_URI, visitDate: DB_NOW - (i * 1000) });
}
yield PlacesTestUtils.addVisits(visits);
do_print("Bookmark the URI.");
PlacesUtils.bookmarks.insertBookmark(PlacesUtils.unfiledBookmarksFolderId,
TEST_URI,
PlacesUtils.bookmarks.DEFAULT_INDEX,
"bookmark title");
yield PlacesTestUtils.promiseAsyncUpdates();
do_print("Remove the 5 most recent visits.");
let filter = {
beginDate: new Date(JS_NOW - 4),
endDate: new Date(JS_NOW)
};
yield PlacesUtils.history.removeVisitsByFilter(filter);
yield PlacesTestUtils.promiseAsyncUpdates();
do_print("URI should still exist in moz_places.");
do_check_true(page_in_database(TEST_URI.spec));
do_print("Run a history query and check that only the older 5 visits still exist.");
let query = PlacesUtils.history.getNewQuery();
let opts = PlacesUtils.history.getNewQueryOptions();
opts.resultType = opts.RESULTS_AS_VISIT;
opts.sortingMode = opts.SORT_BY_DATE_DESCENDING;
let root = PlacesUtils.history.executeQuery(query, opts).root;
root.containerOpen = true;
do_check_eq(root.childCount, 5);
for (let i = 0; i < root.childCount; i++) {
let visitTime = root.getChild(i).time;
do_check_eq(visitTime, DB_NOW - (i * 1000) - 5000);
}
root.containerOpen = false;
do_print("asyncHistory.isURIVisited should return true.");
do_check_true(yield promiseIsURIVisited(TEST_URI));
yield PlacesTestUtils.promiseAsyncUpdates()
do_print("Frecency should be positive.")
do_check_true(frecencyForUrl(TEST_URI) > 0);
yield cleanup();
});
add_task(function* remove_all_visits_unbookmarked_uri() {
do_print("*** TEST: Remove all visits from an unbookmarked URI");
do_print("Add some visits for the URI.");
let visits = [];
for (let i = 0; i < 10; i++) {
visits.push({ uri: TEST_URI, visitDate: DB_NOW - (i * 1000) });
}
yield PlacesTestUtils.addVisits(visits);
do_print("Remove all visits.");
let filter = {
beginDate: new Date(JS_NOW - 10),
endDate: new Date(JS_NOW)
};
yield PlacesUtils.history.removeVisitsByFilter(filter);
yield PlacesTestUtils.promiseAsyncUpdates();
do_print("URI should no longer exist in moz_places.");
do_check_false(page_in_database(TEST_URI.spec));
do_print("Run a history query and check that no visits exist.");
let query = PlacesUtils.history.getNewQuery();
let opts = PlacesUtils.history.getNewQueryOptions();
opts.resultType = opts.RESULTS_AS_VISIT;
opts.sortingMode = opts.SORT_BY_DATE_DESCENDING;
let root = PlacesUtils.history.executeQuery(query, opts).root;
root.containerOpen = true;
do_check_eq(root.childCount, 0);
root.containerOpen = false;
do_print("asyncHistory.isURIVisited should return false.");
do_check_false(yield promiseIsURIVisited(TEST_URI));
yield cleanup();
});
add_task(function* remove_all_visits_bookmarked_uri() {
do_print("*** TEST: Remove all visits from a bookmarked URI");
do_print("Add some visits for the URI.");
let visits = [];
for (let i = 0; i < 10; i++) {
visits.push({ uri: TEST_URI, visitDate: DB_NOW - (i * 1000) });
}
yield PlacesTestUtils.addVisits(visits);
do_print("Bookmark the URI.");
PlacesUtils.bookmarks.insertBookmark(PlacesUtils.unfiledBookmarksFolderId,
TEST_URI,
PlacesUtils.bookmarks.DEFAULT_INDEX,
"bookmark title");
yield PlacesTestUtils.promiseAsyncUpdates();
let initialFrecency = frecencyForUrl(TEST_URI);
do_print("Remove all visits.");
let filter = {
beginDate: new Date(JS_NOW - 10),
endDate: new Date(JS_NOW)
};
yield PlacesUtils.history.removeVisitsByFilter(filter);
yield PlacesTestUtils.promiseAsyncUpdates();
do_print("URI should still exist in moz_places.");
do_check_true(page_in_database(TEST_URI.spec));
do_print("Run a history query and check that no visits exist.");
let query = PlacesUtils.history.getNewQuery();
let opts = PlacesUtils.history.getNewQueryOptions();
opts.resultType = opts.RESULTS_AS_VISIT;
opts.sortingMode = opts.SORT_BY_DATE_DESCENDING;
let root = PlacesUtils.history.executeQuery(query, opts).root;
root.containerOpen = true;
do_check_eq(root.childCount, 0);
root.containerOpen = false;
do_print("asyncHistory.isURIVisited should return false.");
do_check_false(yield promiseIsURIVisited(TEST_URI));
do_print("nsINavBookmarksService.isBookmarked should return true.");
do_check_true(PlacesUtils.bookmarks.isBookmarked(TEST_URI));
yield PlacesTestUtils.promiseAsyncUpdates();
do_print("Frecency should be smaller.")
do_check_true(frecencyForUrl(TEST_URI) < initialFrecency);
yield cleanup();
});
add_task(function* remove_all_visits_bookmarked_uri() {
do_print("*** TEST: Remove some visits from a zero frecency URI retains zero frecency");
do_print("Add some visits for the URI.");
yield PlacesTestUtils.addVisits([
{ uri: TEST_URI, transition: TRANSITION_FRAMED_LINK, visitDate: (DB_NOW - 86400000000000) },
{ uri: TEST_URI, transition: TRANSITION_FRAMED_LINK, visitDate: DB_NOW }
]);
do_print("Remove newer visit.");
let filter = {
beginDate: new Date(JS_NOW - 10),
endDate: new Date(JS_NOW)
};
yield PlacesUtils.history.removeVisitsByFilter(filter);
yield PlacesTestUtils.promiseAsyncUpdates();
do_print("URI should still exist in moz_places.");
do_check_true(page_in_database(TEST_URI.spec));
do_print("Frecency should be zero.")
do_check_eq(frecencyForUrl(TEST_URI), 0);
yield cleanup();
});

View file

@ -0,0 +1,345 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et: */
// Tests for `History.removeVisitsByFilter`, as implemented in History.jsm
"use strict";
Cu.importGlobalProperties(["URL"]);
Cu.import("resource://gre/modules/PromiseUtils.jsm", this);
add_task(function* test_removeVisitsByFilter() {
let referenceDate = new Date(1999, 9, 9, 9, 9);
// Populate a database with 20 entries, remove a subset of entries,
// ensure consistency.
let remover = Task.async(function*(options) {
do_print("Remover with options " + JSON.stringify(options));
let SAMPLE_SIZE = options.sampleSize;
yield PlacesTestUtils.clearHistory();
yield PlacesUtils.bookmarks.eraseEverything();
// Populate the database.
// Create `SAMPLE_SIZE` visits, from the oldest to the newest.
let bookmarkIndices = new Set(options.bookmarks);
let visits = [];
let frecencyChangePromises = new Map();
let uriDeletePromises = new Map();
let getURL = options.url ?
i => "http://mozilla.com/test_browserhistory/test_removeVisitsByFilter/removeme/byurl/" + Math.floor(i / (SAMPLE_SIZE / 5)) + "/" :
i => "http://mozilla.com/test_browserhistory/test_removeVisitsByFilter/removeme/" + i + "/" + Math.random();
for (let i = 0; i < SAMPLE_SIZE; ++i) {
let spec = getURL(i);
let uri = NetUtil.newURI(spec);
let jsDate = new Date(Number(referenceDate) + 3600 * 1000 * i);
let dbDate = jsDate * 1000;
let hasBookmark = bookmarkIndices.has(i);
let hasOwnBookmark = hasBookmark;
if (!hasOwnBookmark && options.url) {
// Also mark as bookmarked if one of the earlier bookmarked items has the same URL.
hasBookmark =
options.bookmarks.filter(n => n < i).some(n => visits[n].uri.spec == spec && visits[n].test.hasBookmark);
}
do_print("Generating " + uri.spec + ", " + dbDate);
let visit = {
uri,
title: "visit " + i,
visitDate: dbDate,
test: {
// `visitDate`, as a Date
jsDate: jsDate,
// `true` if we expect that the visit will be removed
toRemove: false,
// `true` if `onRow` informed of the removal of this visit
announcedByOnRow: false,
// `true` if there is a bookmark for this URI, i.e. of the page
// should not be entirely removed.
hasBookmark: hasBookmark,
onFrecencyChanged: null,
onDeleteURI: null,
},
};
visits.push(visit);
if (hasOwnBookmark) {
do_print("Adding a bookmark to visit " + i);
yield PlacesUtils.bookmarks.insert({
url: uri,
parentGuid: PlacesUtils.bookmarks.unfiledGuid,
title: "test bookmark"
});
do_print("Bookmark added");
}
}
do_print("Adding visits");
yield PlacesTestUtils.addVisits(visits);
do_print("Preparing filters");
let filter = {
};
let beginIndex = 0;
let endIndex = visits.length - 1;
if ("begin" in options) {
let ms = Number(visits[options.begin].test.jsDate) - 1000;
filter.beginDate = new Date(ms);
beginIndex = options.begin;
}
if ("end" in options) {
let ms = Number(visits[options.end].test.jsDate) + 1000;
filter.endDate = new Date(ms);
endIndex = options.end;
}
if ("limit" in options) {
endIndex = beginIndex + options.limit - 1; // -1 because the start index is inclusive.
filter.limit = options.limit;
}
let removedItems = visits.slice(beginIndex);
endIndex -= beginIndex;
if (options.url) {
let rawURL = "";
switch (options.url) {
case 1:
filter.url = new URL(removedItems[0].uri.spec);
rawURL = filter.url.href;
break;
case 2:
filter.url = removedItems[0].uri;
rawURL = filter.url.spec;
break;
case 3:
filter.url = removedItems[0].uri.spec;
rawURL = filter.url;
break;
}
endIndex = Math.min(endIndex, removedItems.findIndex((v, index) => v.uri.spec != rawURL) - 1);
}
removedItems.splice(endIndex + 1);
let remainingItems = visits.filter(v => !removedItems.includes(v));
for (let i = 0; i < removedItems.length; i++) {
let test = removedItems[i].test;
do_print("Marking visit " + (beginIndex + i) + " as expecting removal");
test.toRemove = true;
if (test.hasBookmark ||
(options.url && remainingItems.some(v => v.uri.spec == removedItems[i].uri.spec))) {
frecencyChangePromises.set(removedItems[i].uri.spec, PromiseUtils.defer());
} else if (!options.url || i == 0) {
uriDeletePromises.set(removedItems[i].uri.spec, PromiseUtils.defer());
}
}
let observer = {
deferred: PromiseUtils.defer(),
onBeginUpdateBatch: function() {},
onEndUpdateBatch: function() {},
onVisit: function(uri) {
this.deferred.reject(new Error("Unexpected call to onVisit " + uri.spec));
},
onTitleChanged: function(uri) {
this.deferred.reject(new Error("Unexpected call to onTitleChanged " + uri.spec));
},
onClearHistory: function() {
this.deferred.reject("Unexpected call to onClearHistory");
},
onPageChanged: function(uri) {
this.deferred.reject(new Error("Unexpected call to onPageChanged " + uri.spec));
},
onFrecencyChanged: function(aURI) {
do_print("onFrecencyChanged " + aURI.spec);
let deferred = frecencyChangePromises.get(aURI.spec);
Assert.ok(!!deferred, "Observing onFrecencyChanged");
deferred.resolve();
},
onManyFrecenciesChanged: function() {
do_print("Many frecencies changed");
for (let [, deferred] of frecencyChangePromises) {
deferred.resolve();
}
},
onDeleteURI: function(aURI) {
do_print("onDeleteURI " + aURI.spec);
let deferred = uriDeletePromises.get(aURI.spec);
Assert.ok(!!deferred, "Observing onDeleteURI");
deferred.resolve();
},
onDeleteVisits: function(aURI) {
// Not sure we can test anything.
}
};
PlacesUtils.history.addObserver(observer, false);
let cbarg;
if (options.useCallback) {
do_print("Setting up callback");
cbarg = [info => {
for (let visit of visits) {
do_print("Comparing " + info.date + " and " + visit.test.jsDate);
if (Math.abs(visit.test.jsDate - info.date) < 100) { // Assume rounding errors
Assert.ok(!visit.test.announcedByOnRow,
"This is the first time we announce the removal of this visit");
Assert.ok(visit.test.toRemove,
"This is a visit we intended to remove");
visit.test.announcedByOnRow = true;
return;
}
}
Assert.ok(false, "Could not find the visit we attempt to remove");
}];
} else {
do_print("No callback");
cbarg = [];
}
let result = yield PlacesUtils.history.removeVisitsByFilter(filter, ...cbarg);
Assert.ok(result, "Removal succeeded");
// Make sure that we have eliminated exactly the entries we expected
// to eliminate.
for (let i = 0; i < visits.length; ++i) {
let visit = visits[i];
do_print("Controlling the results on visit " + i);
let remainingVisitsForURI = remainingItems.filter(v => visit.uri.spec == v.uri.spec).length;
Assert.equal(
visits_in_database(visit.uri),
remainingVisitsForURI,
"Visit is still present iff expected");
if (options.useCallback) {
Assert.equal(
visit.test.toRemove,
visit.test.announcedByOnRow,
"Visit removal has been announced by onResult iff expected");
}
if (visit.test.hasBookmark || remainingVisitsForURI) {
Assert.notEqual(page_in_database(visit.uri), 0, "The page should still appear in the db");
} else {
Assert.equal(page_in_database(visit.uri), 0, "The page should have been removed from the db");
}
}
// Make sure that the observer has been called wherever applicable.
do_print("Checking URI delete promises.");
yield Promise.all(Array.from(uriDeletePromises.values()));
do_print("Checking frecency change promises.");
yield Promise.all(Array.from(frecencyChangePromises.values()));
PlacesUtils.history.removeObserver(observer);
});
let size = 20;
for (let range of [
{begin: 0},
{end: 19},
{begin: 0, end: 10},
{begin: 3, end: 4},
{begin: 5, end: 8, limit: 2},
{begin: 10, end: 18, limit: 5},
]) {
for (let bookmarks of [[], [5, 6]]) {
let options = {
sampleSize: size,
bookmarks: bookmarks,
};
if ("begin" in range) {
options.begin = range.begin;
}
if ("end" in range) {
options.end = range.end;
}
if ("limit" in range) {
options.limit = range.limit;
}
yield remover(options);
options.url = 1;
yield remover(options);
options.url = 2;
yield remover(options);
options.url = 3;
yield remover(options);
}
}
yield PlacesTestUtils.clearHistory();
});
// Test the various error cases
add_task(function* test_error_cases() {
Assert.throws(
() => PlacesUtils.history.removeVisitsByFilter(),
/TypeError: Expected a filter/
);
Assert.throws(
() => PlacesUtils.history.removeVisitsByFilter("obviously, not a filter"),
/TypeError: Expected a filter/
);
Assert.throws(
() => PlacesUtils.history.removeVisitsByFilter({}),
/TypeError: Expected a non-empty filter/
);
Assert.throws(
() => PlacesUtils.history.removeVisitsByFilter({beginDate: "now"}),
/TypeError: Expected a Date/
);
Assert.throws(
() => PlacesUtils.history.removeVisitsByFilter({beginDate: Date.now()}),
/TypeError: Expected a Date/
);
Assert.throws(
() => PlacesUtils.history.removeVisitsByFilter({beginDate: new Date()}, "obviously, not a callback"),
/TypeError: Invalid function/
);
Assert.throws(
() => PlacesUtils.history.removeVisitsByFilter({beginDate: new Date(1000), endDate: new Date(0)}),
/TypeError: `beginDate` should be at least as old/
);
Assert.throws(
() => PlacesUtils.history.removeVisitsByFilter({limit: {}}),
/Expected a non-zero positive integer as a limit/
);
Assert.throws(
() => PlacesUtils.history.removeVisitsByFilter({limit: -1}),
/Expected a non-zero positive integer as a limit/
);
Assert.throws(
() => PlacesUtils.history.removeVisitsByFilter({limit: 0.1}),
/Expected a non-zero positive integer as a limit/
);
Assert.throws(
() => PlacesUtils.history.removeVisitsByFilter({limit: Infinity}),
/Expected a non-zero positive integer as a limit/
);
Assert.throws(
() => PlacesUtils.history.removeVisitsByFilter({url: {}}),
/Expected a valid URL for `url`/
);
Assert.throws(
() => PlacesUtils.history.removeVisitsByFilter({url: 0}),
/Expected a valid URL for `url`/
);
Assert.throws(
() => PlacesUtils.history.removeVisitsByFilter({beginDate: new Date(1000), endDate: new Date(0)}),
/TypeError: `beginDate` should be at least as old/
);
Assert.throws(
() => PlacesUtils.history.removeVisitsByFilter({beginDate: new Date(1000), endDate: new Date(0)}),
/TypeError: `beginDate` should be at least as old/
);
});
add_task(function* test_orphans() {
let uri = NetUtil.newURI("http://moz.org/");
yield PlacesTestUtils.addVisits({ uri });
PlacesUtils.favicons.setAndFetchFaviconForPage(
uri, SMALLPNG_DATA_URI, true, PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE,
null, Services.scriptSecurityManager.getSystemPrincipal());
PlacesUtils.annotations.setPageAnnotation(uri, "test", "restval", 0,
PlacesUtils.annotations.EXPIRE_NEVER);
yield PlacesUtils.history.removeVisitsByFilter({ beginDate: new Date(1999, 9, 9, 9, 9),
endDate: new Date() });
Assert.ok(!(yield PlacesTestUtils.isPageInDB(uri)), "Page should have been removed");
let db = yield PlacesUtils.promiseDBConnection();
let rows = yield db.execute(`SELECT (SELECT count(*) FROM moz_annos) +
(SELECT count(*) FROM moz_favicons) AS count`);
Assert.equal(rows[0].getResultByName("count"), 0, "Should not find orphans");
});

View file

@ -0,0 +1,52 @@
// Test that repeated additions of the same URI through updatePlaces, properly
// update from_visit and notify titleChanged.
add_task(function* test() {
let uri = "http://test.com/";
let promiseTitleChangedNotifications = new Promise(resolve => {
let historyObserver = {
_count: 0,
__proto__: NavHistoryObserver.prototype,
onTitleChanged(aURI, aTitle, aGUID) {
Assert.equal(aURI.spec, uri, "Should notify the proper url");
if (++this._count == 2) {
PlacesUtils.history.removeObserver(historyObserver);
resolve();
}
}
};
PlacesUtils.history.addObserver(historyObserver, false);
});
// This repeats the url on purpose, don't merge it into a single place entry.
yield PlacesTestUtils.addVisits([
{ uri, title: "test" },
{ uri, referrer: uri, title: "test2" },
]);
let options = PlacesUtils.history.getNewQueryOptions();
let query = PlacesUtils.history.getNewQuery();
query.uri = NetUtil.newURI(uri);
options.resultType = options.RESULTS_AS_VISIT;
let root = PlacesUtils.history.executeQuery(query, options).root;
root.containerOpen = true;
Assert.equal(root.childCount, 2);
let child = root.getChild(0);
Assert.equal(child.visitType, TRANSITION_LINK, "Visit type should be TRANSITION_LINK");
Assert.equal(child.visitId, 1, "Visit ID should be 1");
Assert.equal(child.fromVisitId, -1, "Should have no referrer visit ID");
Assert.equal(child.title, "test2", "Should have the correct title");
child = root.getChild(1);
Assert.equal(child.visitType, TRANSITION_LINK, "Visit type should be TRANSITION_LINK");
Assert.equal(child.visitId, 2, "Visit ID should be 2");
Assert.equal(child.fromVisitId, 1, "First visit should be the referring visit");
Assert.equal(child.title, "test2", "Should have the correct title");
root.containerOpen = false;
yield promiseTitleChangedNotifications;
});

View file

@ -0,0 +1,9 @@
[DEFAULT]
head = head_history.js
tail =
[test_insert.js]
[test_remove.js]
[test_removeVisits.js]
[test_removeVisitsByFilter.js]
[test_updatePlaces_sameUri_titleChanged.js]