Bug 977177 - Don't expire root domain icons with history, and don't associate pages to them. r=adw

Root domain icons should be retained as far as possible, and only expired when
a domain is removed from the database. We do that through the moz_hosts trigger.
Additionally, since we return root domain icons without the need for a direct
association, we can save a lot of database space and performance by not storing
associations at all for root domain icons.
This has some downsides, since we don't exactly know if the page was really
associated with that root icon or not, but the perf gains are far superior.
Note that we still create associations during migration, since it would be too
expensive to examine every single url to figure out if it's a root domain icon.

MozReview-Commit-ID: 3mlfcOV8ixC

--HG--
extra : rebase_source : 789300afb5d700095bd83aabf1a37200218ff210
This commit is contained in:
Marco Bonardo 2017-03-30 15:16:41 +02:00
parent 0a4d59cc55
commit 4a1b0e63d5
11 changed files with 216 additions and 92 deletions

View File

@ -194,14 +194,15 @@ SetIconInfo(const RefPtr<Database>& aDB,
nsCOMPtr<mozIStorageStatement> insertStmt = aDB->GetStatement(
"INSERT INTO moz_icons "
"(icon_url, fixed_icon_url_hash, width, expire_ms, data) "
"VALUES (:url, hash(fixup_url(:url)), :width, :expire, :data) "
"(icon_url, fixed_icon_url_hash, width, root, expire_ms, data) "
"VALUES (:url, hash(fixup_url(:url)), :width, :root, :expire, :data) "
);
NS_ENSURE_STATE(insertStmt);
nsCOMPtr<mozIStorageStatement> updateStmt = aDB->GetStatement(
"UPDATE moz_icons SET width = :width, "
"expire_ms = :expire, "
"data = :data "
"data = :data, "
"root = :root "
"WHERE id = :id "
);
NS_ENSURE_STATE(updateStmt);
@ -232,9 +233,13 @@ SetIconInfo(const RefPtr<Database>& aDB,
rv = updateStmt->BindInt64ByName(NS_LITERAL_CSTRING("expire"),
aIcon.expiration / 1000);
NS_ENSURE_SUCCESS(rv, rv);
rv = updateStmt->BindInt32ByName(NS_LITERAL_CSTRING("root"),
aIcon.rootIcon);
NS_ENSURE_SUCCESS(rv, rv);
rv = updateStmt->BindBlobByName(NS_LITERAL_CSTRING("data"),
TO_INTBUFFER(payload.data),
payload.data.Length());
NS_ENSURE_SUCCESS(rv, rv);
rv = updateStmt->Execute();
NS_ENSURE_SUCCESS(rv, rv);
// Set the new payload id.
@ -247,6 +252,10 @@ SetIconInfo(const RefPtr<Database>& aDB,
rv = insertStmt->BindInt32ByName(NS_LITERAL_CSTRING("width"),
payload.width);
NS_ENSURE_SUCCESS(rv, rv);
rv = insertStmt->BindInt32ByName(NS_LITERAL_CSTRING("root"),
aIcon.rootIcon);
NS_ENSURE_SUCCESS(rv, rv);
rv = insertStmt->BindInt64ByName(NS_LITERAL_CSTRING("expire"),
aIcon.expiration / 1000);
NS_ENSURE_SUCCESS(rv, rv);
@ -306,7 +315,7 @@ FetchIconInfo(const RefPtr<Database>& aDB,
nsCOMPtr<mozIStorageStatement> stmt = aDB->GetStatement(
"/* do not warn (bug no: not worth having a compound index) */ "
"SELECT id, expire_ms, data, width "
"SELECT id, expire_ms, data, width, root "
"FROM moz_icons "
"WHERE fixed_icon_url_hash = hash(fixup_url(:url)) "
"AND icon_url = :url "
@ -340,19 +349,23 @@ FetchIconInfo(const RefPtr<Database>& aDB,
uint32_t dataLen = 0;
rv = stmt->GetBlob(2, &dataLen, &data);
MOZ_ASSERT(NS_SUCCEEDED(rv));
payload.data.Adopt(TO_CHARBUFFER(data), dataLen);
int32_t width;
rv = stmt->GetInt32(3, &width);
MOZ_ASSERT(NS_SUCCEEDED(rv));
payload.width = width;
if (payload.width == UINT16_MAX) {
payload.mimeType.AssignLiteral(SVG_MIME_TYPE);
} else {
payload.mimeType.AssignLiteral(PNG_MIME_TYPE);
}
int32_t rootIcon;
rv = stmt->GetInt32(4, &rootIcon);
MOZ_ASSERT(NS_SUCCEEDED(rv));
_icon.rootIcon = rootIcon;
if (aPreferredWidth == 0 || _icon.payloads.Length() == 0) {
_icon.payloads.AppendElement(payload);
} else if (payload.width >= aPreferredWidth) {
@ -376,33 +389,28 @@ FetchIconPerSpec(const RefPtr<Database>& aDB,
MOZ_ASSERT(!aPageSpec.IsEmpty(), "Page spec must not be empty.");
MOZ_ASSERT(!NS_IsMainThread());
enum IconType {
ePerfectMatch,
eRootMatch
};
// This selects both associated and root domain icons, ordered by width,
// where an associated icon has priority over a root domain icon.
// Regardless, note that while this way we are far more efficient, we lost
// associations with root domain icons, so it's possible we'll return one
// for a specific size when an associated icon for that size doesn't exist.
nsCOMPtr<mozIStorageStatement> stmt = aDB->GetStatement(
"/* do not warn (bug no: not worth having a compound index) */ "
"SELECT width, icon_url, :type_perfect_match AS type "
"SELECT width, icon_url, root "
"FROM moz_icons i "
"JOIN moz_icons_to_pages ON i.id = icon_id "
"JOIN moz_pages_w_icons p ON p.id = page_id "
"WHERE page_url_hash = hash(:url) AND page_url = :url "
"UNION ALL "
"SELECT width, icon_url, :type_root_match AS type " // Fallback root domain icon.
"SELECT width, icon_url, root "
"FROM moz_icons i "
"WHERE fixed_icon_url_hash = hash(fixup_url(:root_icon_url)) "
"ORDER BY type ASC, width DESC "
"ORDER BY width DESC, root ASC "
);
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scoper(stmt);
nsresult rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("type_perfect_match"),
ePerfectMatch);
NS_ENSURE_SUCCESS(rv, rv);
rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("type_root_match"), eRootMatch);
NS_ENSURE_SUCCESS(rv, rv);
rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("url"), aPageSpec);
nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("url"), aPageSpec);
NS_ENSURE_SUCCESS(rv, rv);
nsAutoCString rootIconFixedUrl(aPageHost);
if (!rootIconFixedUrl.IsEmpty()) {
@ -418,12 +426,7 @@ FetchIconPerSpec(const RefPtr<Database>& aDB,
while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
int32_t width;
rv = stmt->GetInt32(0, &width);
int32_t t;
rv = stmt->GetInt32(2, &t);
NS_ENSURE_SUCCESS(rv, rv);
IconType type = t == 0 ? ePerfectMatch : eRootMatch;
if (!aIconData.spec.IsEmpty() &&
(width < aPreferredWidth || type == eRootMatch)) {
if (!aIconData.spec.IsEmpty() && width < aPreferredWidth) {
// We found the best match, or we already found a match so we don't need
// to fallback to the root domain icon.
break;
@ -830,70 +833,75 @@ AsyncAssociateIconToPage::Run()
return NS_OK;
}
// The page may have associated payloads already, and those could have to be
// expired. For example at a certain point a page could decide to stop serving
// its usual 16px and 32px pngs, and use an svg instead.
// On the other side, we could also be in the process of adding more payloads
// to this page, and we should not expire the payloads we just added.
// For this, we use the expiration field as an indicator and remove relations
// based on it being elapsed. We don't remove orphan icons at this time since
// it would have a cost. The privacy hit is limited since history removal
// methods already expire orphan icons.
if (mPage.id != 0) {
// Don't associate pages to root domain icons, since those will be returned
// regardless. This saves a lot of work and database space since we don't
// need to store urls and relations.
if (!mIcon.rootIcon) {
// The page may have associated payloads already, and those could have to be
// expired. For example at a certain point a page could decide to stop serving
// its usual 16px and 32px pngs, and use an svg instead.
// On the other side, we could also be in the process of adding more payloads
// to this page, and we should not expire the payloads we just added.
// For this, we use the expiration field as an indicator and remove relations
// based on it being elapsed. We don't remove orphan icons at this time since
// it would have a cost. The privacy hit is limited since history removal
// methods already expire orphan icons.
if (mPage.id != 0) {
nsCOMPtr<mozIStorageStatement> stmt;
stmt = DB->GetStatement(
"DELETE FROM moz_icons_to_pages WHERE icon_id IN ( "
"SELECT icon_id FROM moz_icons_to_pages "
"JOIN moz_icons i ON icon_id = i.id "
"WHERE page_id = :page_id "
"AND expire_ms < strftime('%s','now','localtime','start of day','-7 days','utc') * 1000 "
") "
);
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scoper(stmt);
rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), mPage.id);
NS_ENSURE_SUCCESS(rv, rv);
rv = stmt->Execute();
NS_ENSURE_SUCCESS(rv, rv);
} else {
// We need to create the page entry.
// By default, we use the place id for the insertion. While we can't
// guarantee 1:1 mapping, in general it should do.
nsCOMPtr<mozIStorageStatement> stmt;
stmt = DB->GetStatement(
"INSERT OR IGNORE INTO moz_pages_w_icons (page_url, page_url_hash) "
"VALUES (:page_url, hash(:page_url)) "
);
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scoper(stmt);
rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), mPage.spec);
NS_ENSURE_SUCCESS(rv, rv);
rv = stmt->Execute();
NS_ENSURE_SUCCESS(rv, rv);
}
// Then we can create the relations.
nsCOMPtr<mozIStorageStatement> stmt;
stmt = DB->GetStatement(
"DELETE FROM moz_icons_to_pages WHERE icon_id IN ( "
"SELECT icon_id FROM moz_icons_to_pages "
"JOIN moz_icons i ON icon_id = i.id "
"WHERE page_id = :page_id "
"AND expire_ms < strftime('%s','now','localtime','start of day','-7 days','utc') * 1000 "
") "
"INSERT OR IGNORE INTO moz_icons_to_pages (page_id, icon_id) "
"VALUES ((SELECT id from moz_pages_w_icons WHERE page_url_hash = hash(:page_url) AND page_url = :page_url), "
":icon_id) "
);
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scoper(stmt);
rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), mPage.id);
NS_ENSURE_SUCCESS(rv, rv);
rv = stmt->Execute();
NS_ENSURE_SUCCESS(rv, rv);
} else {
// We need to create the page entry.
// By default, we use the place id for the insertion. While we can't
// guarantee 1:1 mapping, in general it should do.
nsCOMPtr<mozIStorageStatement> stmt;
stmt = DB->GetStatement(
"INSERT OR IGNORE INTO moz_pages_w_icons (page_url, page_url_hash) "
"VALUES (:page_url, hash(:page_url)) "
);
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scoper(stmt);
rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), mPage.spec);
NS_ENSURE_SUCCESS(rv, rv);
rv = stmt->Execute();
NS_ENSURE_SUCCESS(rv, rv);
}
// Then we can create the relations.
nsCOMPtr<mozIStorageStatement> stmt;
stmt = DB->GetStatement(
"INSERT OR IGNORE INTO moz_icons_to_pages (page_id, icon_id) "
"VALUES ((SELECT id from moz_pages_w_icons WHERE page_url_hash = hash(:page_url) AND page_url = :page_url), "
":icon_id) "
);
NS_ENSURE_STATE(stmt);
// For some reason using BindingParamsArray here fails execution, so we must
// execute the statements one by one.
// In the future we may want to investigate the reasons, sounds like related
// to contraints.
for (const auto& payload : mIcon.payloads) {
mozStorageStatementScoper scoper(stmt);
nsCOMPtr<mozIStorageBindingParams> params;
rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), mPage.spec);
NS_ENSURE_SUCCESS(rv, rv);
rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("icon_id"), payload.id);
NS_ENSURE_SUCCESS(rv, rv);
rv = stmt->Execute();
NS_ENSURE_SUCCESS(rv, rv);
// For some reason using BindingParamsArray here fails execution, so we must
// execute the statements one by one.
// In the future we may want to investigate the reasons, sounds like related
// to contraints.
for (const auto& payload : mIcon.payloads) {
mozStorageStatementScoper scoper(stmt);
nsCOMPtr<mozIStorageBindingParams> params;
rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), mPage.spec);
NS_ENSURE_SUCCESS(rv, rv);
rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("icon_id"), payload.id);
NS_ENSURE_SUCCESS(rv, rv);
rv = stmt->Execute();
NS_ENSURE_SUCCESS(rv, rv);
}
}
mIcon.status |= ICON_STATUS_ASSOCIATED;
@ -1231,7 +1239,7 @@ FetchAndConvertUnsupportedPayloads::ConvertPayload(int64_t aId,
// Exclude invalid mime types.
if (aPayload.Length() == 0 ||
!imgLoader::SupportImageWithMimeType(PromiseFlatCString(aMimeType).get(),
AcceptedMimeTypes::IMAGES)) {
AcceptedMimeTypes::IMAGES_AND_DOCUMENTS)) {
return NS_ERROR_FAILURE;
}

View File

@ -87,6 +87,7 @@ struct IconData
: expiration(0)
, fetchMode(FETCH_NEVER)
, status(ICON_STATUS_UNKNOWN)
, rootIcon(0)
{
}
@ -94,6 +95,7 @@ struct IconData
PRTime expiration;
enum AsyncFaviconFetchMode fetchMode;
uint16_t status; // This is a bitset, see ICON_STATUS_* defines above.
uint8_t rootIcon;
nsTArray<IconPayload> payloads;
};

View File

@ -705,7 +705,7 @@ var cleanupPages = Task.async(function*(db, pages) {
yield db.executeCached(`DELETE FROM moz_pages_w_icons
WHERE page_url_hash NOT IN (SELECT url_hash FROM moz_places)`);
yield db.executeCached(`DELETE FROM moz_icons
WHERE id NOT IN (SELECT icon_id FROM moz_icons_to_pages)`);
WHERE root = 0 AND id NOT IN (SELECT icon_id FROM moz_icons_to_pages)`);
yield db.execute(`DELETE FROM moz_annos
WHERE place_id IN ( ${ idsList } )`);
yield db.execute(`DELETE FROM moz_inputhistory

View File

@ -602,7 +602,7 @@ this.PlacesDBUtils = {
cleanupStatements.push(deleteOrphanIconPages);
let deleteOrphanIcons = DBConn.createAsyncStatement(
`DELETE FROM moz_icons WHERE id NOT IN (
`DELETE FROM moz_icons WHERE root = 0 AND id NOT IN (
SELECT icon_id FROM moz_icons_to_pages
)`);
cleanupStatements.push(deleteOrphanIcons);

View File

@ -338,6 +338,11 @@ nsFaviconService::SetAndFetchFaviconForPage(nsIURI* aPageURI,
icon.fetchMode = aForceReload ? FETCH_ALWAYS : FETCH_IF_MISSING;
rv = aFaviconURI->GetSpec(icon.spec);
NS_ENSURE_SUCCESS(rv, rv);
nsAutoCString path;
rv = aFaviconURI->GetPath(path);
if (NS_SUCCEEDED(rv) && path.EqualsLiteral("/favicon.ico")) {
icon.rootIcon = 1;
}
}
// If the page url points to an image, the icon's url will be the same.
@ -378,7 +383,7 @@ nsFaviconService::ReplaceFaviconData(nsIURI* aFaviconURI,
NS_ENSURE_ARG(aDataLen > 0);
NS_ENSURE_ARG(aMimeType.Length() > 0);
NS_ENSURE_ARG(imgLoader::SupportImageWithMimeType(PromiseFlatCString(aMimeType).get(),
AcceptedMimeTypes::IMAGES));
AcceptedMimeTypes::IMAGES_AND_DOCUMENTS));
if (aExpiration == 0) {
aExpiration = PR_Now() + MAX_FAVICON_EXPIRATION;
@ -406,10 +411,18 @@ nsFaviconService::ReplaceFaviconData(nsIURI* aFaviconURI,
iconData->fetchMode = FETCH_NEVER;
nsresult rv = aFaviconURI->GetSpec(iconData->spec);
NS_ENSURE_SUCCESS(rv, rv);
nsAutoCString path;
rv = aFaviconURI->GetPath(path);
if (NS_SUCCEEDED(rv) && path.EqualsLiteral("/favicon.ico")) {
iconData->rootIcon = 1;
}
IconPayload payload;
payload.mimeType = aMimeType;
payload.data.Assign(TO_CHARBUFFER(aData), aDataLen);
if (payload.mimeType.EqualsLiteral(SVG_MIME_TYPE)) {
payload.width = UINT16_MAX;
}
// There may already be a previous payload, so ensure to only have one.
iconData->payloads.Clear();
iconData->payloads.AppendElement(payload);

View File

@ -2496,6 +2496,18 @@ nsNavHistory::CleanupPlacesOnVisitsDelete(const nsCString& aPlaceIdsQueryString)
);
NS_ENSURE_SUCCESS(rv, rv);
// Expire orphan icons.
rv = mDB->MainConn()->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
"DELETE FROM moz_pages_w_icons "
"WHERE page_url_hash NOT IN (SELECT url_hash FROM moz_places) "
));
NS_ENSURE_SUCCESS(rv, rv);
rv = mDB->MainConn()->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
"DELETE FROM moz_icons "
"WHERE root = 0 AND id NOT IN (SELECT icon_id FROM moz_icons_to_pages) "
));
NS_ENSURE_SUCCESS(rv, rv);
// Hosts accumulated during the places delete are updated through a trigger
// (see nsPlacesTriggers.h).
rv = mDB->MainConn()->ExecuteSimpleSQL(

View File

@ -283,7 +283,7 @@ const EXPIRATION_QUERIES = {
// Expire orphan icons from the database.
QUERY_EXPIRE_FAVICONS: {
sql: `DELETE FROM moz_icons
WHERE id NOT IN (
WHERE root = 0 AND id NOT IN (
SELECT icon_id FROM moz_icons_to_pages
)`,
actions: ACTION.TIMED_OVERLIMIT | ACTION.CLEAR_HISTORY |

View File

@ -191,6 +191,7 @@
"icon_url TEXT NOT NULL, " \
"fixed_icon_url_hash INTEGER NOT NULL, " \
"width INTEGER NOT NULL DEFAULT 0, " \
"root INTEGER NOT NULL DEFAULT 0, " \
"color INTEGER, " \
"expire_ms INTEGER NOT NULL DEFAULT 0, " \
"data BLOB " \

View File

@ -146,8 +146,10 @@
"SET prefix = (" HOSTS_PREFIX_PRIORITY_FRAGMENT ") " \
"WHERE host = OLD.host; " \
"DELETE FROM moz_icons " \
"WHERE fixed_icon_url_hash = hash(OLD.host || '/favicon.ico') " \
"AND fixup_url(icon_url) = OLD.host || '/favicon.ico';" \
"WHERE fixed_icon_url_hash = hash(fixup_url(OLD.host || '/favicon.ico')) " \
"AND fixup_url(icon_url) = fixup_url(OLD.host || '/favicon.ico') "\
"AND NOT EXISTS (SELECT 1 FROM moz_hosts WHERE host = OLD.host " \
"OR host = fixup_url(OLD.host));" \
"END" \
)

View File

@ -0,0 +1,85 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* This file tests root icons associations and expiration
*/
add_task(function* () {
let pageURI = NetUtil.newURI("http://www.places.test/page/");
yield PlacesTestUtils.addVisits(pageURI);
let faviconURI = NetUtil.newURI("http://www.places.test/favicon.ico");
PlacesUtils.favicons.replaceFaviconDataFromDataURL(
faviconURI, SMALLPNG_DATA_URI.spec, 0, Services.scriptSecurityManager.getSystemPrincipal());
yield setFaviconForPage(pageURI, faviconURI);
// Sanity checks.
Assert.equal(yield getFaviconUrlForPage(pageURI), faviconURI.spec);
Assert.equal(yield getFaviconUrlForPage("https://places.test/somethingelse/"),
faviconURI.spec);
// Check database entries.
let db = yield PlacesUtils.promiseDBConnection();
let rows = yield db.execute("SELECT * FROM moz_icons");
Assert.equal(rows.length, 1, "There should only be 1 icon entry");
Assert.equal(rows[0].getResultByName("root"), 1, "It should be marked as a root icon");
rows = yield db.execute("SELECT * FROM moz_pages_w_icons");
Assert.equal(rows.length, 0, "There should be no page entry");
rows = yield db.execute("SELECT * FROM moz_icons_to_pages");
Assert.equal(rows.length, 0, "There should be no relation entry");
// Add another pages to the same host. The icon should not be removed.
yield PlacesTestUtils.addVisits("http://places.test/page2/");
yield PlacesUtils.history.remove(pageURI);
// Still works since the icon has not been removed.
Assert.equal(yield getFaviconUrlForPage(pageURI), faviconURI.spec);
// Remove all the pages for the given domain.
yield PlacesUtils.history.remove("http://places.test/page2/");
// The icon should be removed along with the domain.
rows = yield db.execute("SELECT * FROM moz_icons");
Assert.equal(rows.length, 0, "The icon should have been removed");
});
add_task(function* test_removePagesByTimeframe() {
// Add a visit in the past with no directly associated icon.
yield PlacesTestUtils.addVisits({uri: "http://www.places.test/old/", visitDate: new Date(Date.now() - 86400000)});
let pageURI = NetUtil.newURI("http://www.places.test/page/");
yield PlacesTestUtils.addVisits(pageURI);
let faviconURI = NetUtil.newURI("http://www.places.test/page/favicon.ico");
let rootIconURI = NetUtil.newURI("http://www.places.test/favicon.ico");
PlacesUtils.favicons.replaceFaviconDataFromDataURL(
faviconURI, SMALLSVG_DATA_URI.spec, 0, Services.scriptSecurityManager.getSystemPrincipal());
yield setFaviconForPage(pageURI, faviconURI);
PlacesUtils.favicons.replaceFaviconDataFromDataURL(
rootIconURI, SMALLPNG_DATA_URI.spec, 0, Services.scriptSecurityManager.getSystemPrincipal());
yield setFaviconForPage(pageURI, rootIconURI);
// Sanity checks.
Assert.equal(yield getFaviconUrlForPage(pageURI),
faviconURI.spec, "Should get the biggest icon");
Assert.equal(yield getFaviconUrlForPage(pageURI, 1),
rootIconURI.spec, "Should get the smallest icon");
Assert.equal(yield getFaviconUrlForPage("http://www.places.test/old/"),
rootIconURI.spec, "Should get the root icon");
PlacesUtils.history.removePagesByTimeframe(
PlacesUtils.toPRTime(Date.now() - 20000),
PlacesUtils.toPRTime(new Date())
);
// Check database entries.
let db = yield PlacesUtils.promiseDBConnection();
let rows = yield db.execute("SELECT * FROM moz_icons");
Assert.equal(rows.length, 1, "There should only be 1 icon entry");
Assert.equal(rows[0].getResultByName("root"), 1, "It should be marked as a root icon");
rows = yield db.execute("SELECT * FROM moz_pages_w_icons");
Assert.equal(rows.length, 0, "There should be no page entry");
rows = yield db.execute("SELECT * FROM moz_icons_to_pages");
Assert.equal(rows.length, 0, "There should be no relation entry");
PlacesUtils.history.removePagesByTimeframe(0, PlacesUtils.toPRTime(new Date()));
rows = yield db.execute("SELECT * FROM moz_icons");
Assert.equal(rows.length, 0, "There should be no icon entry");
});

View File

@ -35,4 +35,5 @@ support-files =
[test_query_result_favicon_changed_on_child.js]
[test_replaceFaviconData.js]
[test_replaceFaviconDataFromDataURL.js]
[test_root_icons.js]
[test_svg_favicon.js]