bug 977177 - Fallback to the root domain icon. r=adw
When an icon for a specific page is not available, try to fallback for the root
domain icon.
This is valid for both getFaviconUrlForPage and getFaviconDataforPage, as well
as the page-icon: protocol.
MozReview-Commit-ID: JC4cx1PAY38
--- a/toolkit/components/places/FaviconHelpers.cpp
+++ b/toolkit/components/places/FaviconHelpers.cpp
@@ -364,46 +364,73 @@ FetchIconInfo(const RefPtr<Database>& aD
}
return NS_OK;
}
nsresult
FetchIconPerSpec(const RefPtr<Database>& aDB,
const nsACString& aPageSpec,
+ const nsACString& aPageHost,
IconData& aIconData,
uint16_t aPreferredWidth)
{
MOZ_ASSERT(!aPageSpec.IsEmpty(), "Page spec must not be empty.");
MOZ_ASSERT(!NS_IsMainThread());
+ enum IconType {
+ ePerfectMatch,
+ eRootMatch
+ };
+
nsCOMPtr<mozIStorageStatement> stmt = aDB->GetStatement(
"/* do not warn (bug no: not worth having a compound index) */ "
- "SELECT width, icon_url "
+ "SELECT width, icon_url, :type_perfect_match AS type "
"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 "
- "ORDER BY width DESC "
+ "UNION ALL "
+ "SELECT width, icon_url, :type_root_match AS type " // Fallback root domain icon.
+ "FROM moz_icons i "
+ "WHERE fixed_icon_url_hash = hash(fixup_url(:root_icon_url)) "
+ "ORDER BY type ASC, width DESC "
);
NS_ENSURE_STATE(stmt);
mozStorageStatementScoper scoper(stmt);
- nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("url"),
- aPageSpec);
+ 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);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsAutoCString rootIconFixedUrl(aPageHost);
+ if (!rootIconFixedUrl.IsEmpty()) {
+ rootIconFixedUrl.AppendLiteral("/favicon.ico");
+ }
+ rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("root_icon_url"),
+ rootIconFixedUrl);
NS_ENSURE_SUCCESS(rv, rv);
// Return the biggest icon close to the preferred width. It may be bigger
// or smaller if the preferred width isn't found.
bool hasResult;
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);
- if (width < aPreferredWidth && !aIconData.spec.IsEmpty()) {
+ IconType type = t == 0 ? ePerfectMatch : eRootMatch;
+ if (!aIconData.spec.IsEmpty() &&
+ (width < aPreferredWidth || type == eRootMatch)) {
+ // We found the best match, or we already found a match so we don't need
+ // to fallback to the root domain icon.
break;
}
rv = stmt->GetUTF8String(1, aIconData.spec);
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}
@@ -886,34 +913,36 @@ AsyncAssociateIconToPage::Run()
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////
//// AsyncGetFaviconURLForPage
AsyncGetFaviconURLForPage::AsyncGetFaviconURLForPage(
const nsACString& aPageSpec
+, const nsACString& aPageHost
, uint16_t aPreferredWidth
, nsIFaviconDataCallback* aCallback
) : mPreferredWidth(aPreferredWidth == 0 ? UINT16_MAX : aPreferredWidth)
, mCallback(new nsMainThreadPtrHolder<nsIFaviconDataCallback>(aCallback))
{
MOZ_ASSERT(NS_IsMainThread());
mPageSpec.Assign(aPageSpec);
+ mPageHost.Assign(aPageHost);
}
NS_IMETHODIMP
AsyncGetFaviconURLForPage::Run()
{
MOZ_ASSERT(!NS_IsMainThread());
RefPtr<Database> DB = Database::GetDatabase();
NS_ENSURE_STATE(DB);
IconData iconData;
- nsresult rv = FetchIconPerSpec(DB, mPageSpec, iconData, mPreferredWidth);
+ nsresult rv = FetchIconPerSpec(DB, mPageSpec, mPageHost, iconData, mPreferredWidth);
NS_ENSURE_SUCCESS(rv, rv);
// Now notify our callback of the icon spec we retrieved, even if empty.
PageData pageData;
pageData.spec.Assign(mPageSpec);
nsCOMPtr<nsIRunnable> event =
new NotifyIconObservers(iconData, pageData, mCallback);
@@ -923,34 +952,36 @@ AsyncGetFaviconURLForPage::Run()
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////
//// AsyncGetFaviconDataForPage
AsyncGetFaviconDataForPage::AsyncGetFaviconDataForPage(
const nsACString& aPageSpec
+, const nsACString& aPageHost
, uint16_t aPreferredWidth
, nsIFaviconDataCallback* aCallback
) : mPreferredWidth(aPreferredWidth == 0 ? UINT16_MAX : aPreferredWidth)
, mCallback(new nsMainThreadPtrHolder<nsIFaviconDataCallback>(aCallback))
{
MOZ_ASSERT(NS_IsMainThread());
mPageSpec.Assign(aPageSpec);
+ mPageHost.Assign(aPageHost);
}
NS_IMETHODIMP
AsyncGetFaviconDataForPage::Run()
{
MOZ_ASSERT(!NS_IsMainThread());
RefPtr<Database> DB = Database::GetDatabase();
NS_ENSURE_STATE(DB);
IconData iconData;
- nsresult rv = FetchIconPerSpec(DB, mPageSpec, iconData, mPreferredWidth);
+ nsresult rv = FetchIconPerSpec(DB, mPageSpec, mPageHost, iconData, mPreferredWidth);
NS_ENSURE_SUCCESS(rv, rv);
if (!iconData.spec.IsEmpty()) {
rv = FetchIconInfo(DB, mPreferredWidth, iconData);
if (NS_FAILED(rv)) {
iconData.spec.Truncate();
}
}
--- a/toolkit/components/places/FaviconHelpers.h
+++ b/toolkit/components/places/FaviconHelpers.h
@@ -209,29 +209,33 @@ class AsyncGetFaviconURLForPage final :
public:
NS_DECL_NSIRUNNABLE
/**
* Constructor.
*
* @param aPageSpec
* URL of the page whose favicon's URL we're fetching
+ * @param aPageHost
+ * Host of the page whose favicon's URL we're fetching
* @param aCallback
* function to be called once finished
* @param aPreferredWidth
* The preferred size for the icon
*/
AsyncGetFaviconURLForPage(const nsACString& aPageSpec,
+ const nsACString& aPageHost,
uint16_t aPreferredWidth,
nsIFaviconDataCallback* aCallback);
private:
uint16_t mPreferredWidth;
nsMainThreadPtrHandle<nsIFaviconDataCallback> mCallback;
nsCString mPageSpec;
+ nsCString mPageHost;
};
/**
* Asynchronously tries to get the URL and data of a page's favicon, then
* notifies the given observer.
*/
class AsyncGetFaviconDataForPage final : public Runnable
@@ -239,30 +243,34 @@ class AsyncGetFaviconDataForPage final :
public:
NS_DECL_NSIRUNNABLE
/**
* Constructor.
*
* @param aPageSpec
* URL of the page whose favicon URL and data we're fetching
+ * @param aPageHost
+ * Host of the page whose favicon's URL we're fetching
* @param aPreferredWidth
* The preferred size of the icon. We will try to return an icon close
* to this size.
* @param aCallback
* function to be called once finished
*/
AsyncGetFaviconDataForPage(const nsACString& aPageSpec,
+ const nsACString& aPageHost,
uint16_t aPreferredWidth,
nsIFaviconDataCallback* aCallback);
private:
uint16_t mPreferredWidth;
nsMainThreadPtrHandle<nsIFaviconDataCallback> mCallback;
nsCString mPageSpec;
+ nsCString mPageHost;
};
class AsyncReplaceFaviconData final : public Runnable
{
public:
NS_DECL_NSIRUNNABLE
explicit AsyncReplaceFaviconData(const IconData& aIcon);
--- a/toolkit/components/places/mozIAsyncFavicons.idl
+++ b/toolkit/components/places/mozIAsyncFavicons.idl
@@ -9,17 +9,17 @@ interface nsIURI;
interface nsIFaviconDataCallback;
interface nsIPrincipal;
interface mozIPlacesPendingOperation;
[scriptable, uuid(a9c81797-9133-4823-b55f-3646e67cfd41)]
interface mozIAsyncFavicons : nsISupports
{
/**
- * Declares that a given page uses a favicon with the given URI and
+ * Declares that a given page uses a favicon with the given URI and
* attempts to fetch and save the icon data by loading the favicon URI
* through an async network request.
*
* If the icon data already exists, we won't try to reload the icon unless
* aForceReload is true. Similarly, if the icon is in the failed favicon
* cache we won't do anything unless aForceReload is true, in which case
* we'll try to reload the favicon.
*
@@ -139,22 +139,24 @@ interface mozIAsyncFavicons : nsISupport
* Retrieves the favicon URI associated to the given page, if any.
*
* @param aPageURI
* URI of the page whose favicon URI we're looking up.
* @param aCallback
* This callback is always invoked to notify the result of the lookup.
* The aURI parameter will be the favicon URI, or null when no favicon
* is associated with the page or an error occurred while fetching it.
+ * aDataLen will be always 0, aData will be an empty array, and
+ * aMimeType will be an empty string, regardless of whether a favicon
+ * was found.
* @param aPreferredWidth
* The preferred icon width, 0 for the biggest available.
*
- * @note When the callback is invoked, aDataLen will be always 0, aData will
- * be an empty array, and aMimeType will be an empty string, regardless
- * of whether a favicon is associated with the page.
+ * @note If a favicon specific to this page cannot be found, this will try to
+ * fallback to the /favicon.ico for the root domain.
*
* @see nsIFaviconDataCallback in nsIFaviconService.idl.
*/
void getFaviconURLForPage(in nsIURI aPageURI,
in nsIFaviconDataCallback aCallback,
[optional] in unsigned short aPreferredWidth);
/**
@@ -169,15 +171,17 @@ interface mozIAsyncFavicons : nsISupport
* parameter will be the favicon URI, or null when no favicon is
* associated with the page or an error occurred while fetching it. If
* aURI is not null, the other parameters may contain the favicon data.
* However, if no favicon data is currently associated with the favicon
* URI, aDataLen will be 0, aData will be an empty array, and aMimeType
* will be an empty string.
* @param aPreferredWidth
* The preferred icon width, 0 for the biggest available.
+ * @note If a favicon specific to this page cannot be found, this will try to
+ * fallback to the /favicon.ico for the root domain.
*
* @see nsIFaviconDataCallback in nsIFaviconService.idl.
*/
void getFaviconDataForPage(in nsIURI aPageURI,
in nsIFaviconDataCallback aCallback,
[optional] in unsigned short aPreferredWidth);
};
--- a/toolkit/components/places/nsFaviconService.cpp
+++ b/toolkit/components/places/nsFaviconService.cpp
@@ -474,19 +474,22 @@ nsFaviconService::GetFaviconURLForPage(n
{
MOZ_ASSERT(NS_IsMainThread());
NS_ENSURE_ARG(aPageURI);
NS_ENSURE_ARG(aCallback);
nsAutoCString pageSpec;
nsresult rv = aPageURI->GetSpec(pageSpec);
NS_ENSURE_SUCCESS(rv, rv);
+ nsAutoCString pageHost;
+ // It's expected that some domains may not have a host.
+ Unused << aPageURI->GetHost(pageHost);
RefPtr<AsyncGetFaviconURLForPage> event =
- new AsyncGetFaviconURLForPage(pageSpec, aPreferredWidth, aCallback);
+ new AsyncGetFaviconURLForPage(pageSpec, pageHost, aPreferredWidth, aCallback);
RefPtr<Database> DB = Database::GetDatabase();
NS_ENSURE_STATE(DB);
DB->DispatchToAsyncThread(event);
return NS_OK;
}
@@ -497,19 +500,22 @@ nsFaviconService::GetFaviconDataForPage(
{
MOZ_ASSERT(NS_IsMainThread());
NS_ENSURE_ARG(aPageURI);
NS_ENSURE_ARG(aCallback);
nsAutoCString pageSpec;
nsresult rv = aPageURI->GetSpec(pageSpec);
NS_ENSURE_SUCCESS(rv, rv);
+ nsAutoCString pageHost;
+ // It's expected that some domains may not have a host.
+ Unused << aPageURI->GetHost(pageHost);
RefPtr<AsyncGetFaviconDataForPage> event =
- new AsyncGetFaviconDataForPage(pageSpec, aPreferredWidth, aCallback);
+ new AsyncGetFaviconDataForPage(pageSpec, pageHost, aPreferredWidth, aCallback);
RefPtr<Database> DB = Database::GetDatabase();
NS_ENSURE_STATE(DB);
DB->DispatchToAsyncThread(event);
return NS_OK;
}
nsresult
--- a/toolkit/components/places/tests/favicons/test_favicons_protocols_ref.js
+++ b/toolkit/components/places/tests/favicons/test_favicons_protocols_ref.js
@@ -1,15 +1,15 @@
/**
* This file tests the size ref on the icons protocols.
*/
const PAGE_URL = "http://icon.mozilla.org/";
-const ICON16_URL = "http://mochi.test:8888/tests/toolkit/components/places/tests/browser/favicon-normal16.png";
-const ICON32_URL = "http://mochi.test:8888/tests/toolkit/components/places/tests/browser/favicon-normal32.png";
+const ICON16_URL = "http://places.test/favicon-normal16.png";
+const ICON32_URL = "http://places.test/favicon-normal32.png";
add_task(function* () {
yield PlacesTestUtils.addVisits(PAGE_URL);
// Add 2 differently sized favicons for this page.
let data = readFileData(do_get_file("favicon-normal16.png"));
PlacesUtils.favicons.replaceFaviconData(NetUtil.newURI(ICON16_URL),
data, data.length, "image/png");
--- a/toolkit/components/places/tests/favicons/test_getFaviconDataForPage.js
+++ b/toolkit/components/places/tests/favicons/test_getFaviconDataForPage.js
@@ -1,54 +1,110 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
-/**
- * This file tests getFaviconDataForPage.
- */
-
-// Globals
-
const FAVICON_URI = NetUtil.newURI(do_get_file("favicon-normal32.png"));
const FAVICON_DATA = readFileData(do_get_file("favicon-normal32.png"));
const FAVICON_MIMETYPE = "image/png";
-
-// Tests
+const ICON16_URL = "http://places.test/favicon-normal16.png";
+const ICON32_URL = "http://places.test/favicon-normal32.png";
-function run_test() {
- // Check that the favicon loaded correctly before starting the actual tests.
+add_task(function* test_normal() {
do_check_eq(FAVICON_DATA.length, 344);
- run_next_test();
-}
-
-add_test(function test_normal() {
let pageURI = NetUtil.newURI("http://example.com/normal");
- PlacesTestUtils.addVisits(pageURI).then(function() {
+ yield PlacesTestUtils.addVisits(pageURI);
+ yield new Promise(resolve => {
PlacesUtils.favicons.setAndFetchFaviconForPage(
pageURI, FAVICON_URI, true,
PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE,
function() {
PlacesUtils.favicons.getFaviconDataForPage(pageURI,
function(aURI, aDataLen, aData, aMimeType) {
do_check_true(aURI.equals(FAVICON_URI));
do_check_eq(FAVICON_DATA.length, aDataLen);
do_check_true(compareArrays(FAVICON_DATA, aData));
do_check_eq(FAVICON_MIMETYPE, aMimeType);
- run_next_test();
+ resolve();
});
}, Services.scriptSecurityManager.getSystemPrincipal());
});
});
-add_test(function test_missing() {
+add_task(function* test_missing() {
let pageURI = NetUtil.newURI("http://example.com/missing");
- PlacesUtils.favicons.getFaviconDataForPage(pageURI,
- function(aURI, aDataLen, aData, aMimeType) {
- // Check also the expected data types.
- do_check_true(aURI === null);
- do_check_true(aDataLen === 0);
- do_check_true(aData.length === 0);
- do_check_true(aMimeType === "");
- run_next_test();
- });
+ yield new Promise(resolve => {
+ PlacesUtils.favicons.getFaviconDataForPage(pageURI,
+ function(aURI, aDataLen, aData, aMimeType) {
+ // Check also the expected data types.
+ do_check_true(aURI === null);
+ do_check_true(aDataLen === 0);
+ do_check_true(aData.length === 0);
+ do_check_true(aMimeType === "");
+ resolve();
+ });
+ });
});
+
+add_task(function* test_fallback() {
+ const ROOT_URL = "https://www.example.com/";
+ const ROOT_ICON_URL = ROOT_URL + "favicon.ico";
+ const SUBPAGE_URL = ROOT_URL + "/missing";
+
+ do_print("Set icon for the root");
+ yield PlacesTestUtils.addVisits(ROOT_URL);
+ let data = readFileData(do_get_file("favicon-normal16.png"));
+ PlacesUtils.favicons.replaceFaviconData(NetUtil.newURI(ROOT_ICON_URL),
+ data, data.length, "image/png");
+ yield setFaviconForPage(ROOT_URL, ROOT_ICON_URL);
+
+ do_print("check fallback icons");
+ yield new Promise(resolve => {
+ PlacesUtils.favicons.getFaviconDataForPage(NetUtil.newURI(ROOT_URL),
+ (aURI, aDataLen, aData, aMimeType) => {
+ Assert.equal(aURI.spec, ROOT_ICON_URL);
+ Assert.equal(aDataLen, data.length);
+ Assert.deepEqual(aData, data);
+ Assert.equal(aMimeType, "image/png");
+ resolve();
+ });
+ });
+ yield new Promise(resolve => {
+ PlacesUtils.favicons.getFaviconDataForPage(NetUtil.newURI(SUBPAGE_URL),
+ (aURI, aDataLen, aData, aMimeType) => {
+ Assert.equal(aURI.spec, ROOT_ICON_URL);
+ Assert.equal(aDataLen, data.length);
+ Assert.deepEqual(aData, data);
+ Assert.equal(aMimeType, "image/png");
+ resolve();
+ });
+ });
+
+ do_print("Now add a proper icon for the page");
+ yield PlacesTestUtils.addVisits(SUBPAGE_URL);
+ let data32 = readFileData(do_get_file("favicon-normal32.png"));
+ PlacesUtils.favicons.replaceFaviconData(NetUtil.newURI(ICON32_URL),
+ data32, data32.length, "image/png");
+ yield setFaviconForPage(SUBPAGE_URL, ICON32_URL);
+
+ do_print("check no fallback icons");
+ yield new Promise(resolve => {
+ PlacesUtils.favicons.getFaviconDataForPage(NetUtil.newURI(ROOT_URL),
+ (aURI, aDataLen, aData, aMimeType) => {
+ Assert.equal(aURI.spec, ROOT_ICON_URL);
+ Assert.equal(aDataLen, data.length);
+ Assert.deepEqual(aData, data);
+ Assert.equal(aMimeType, "image/png");
+ resolve();
+ });
+ });
+ yield new Promise(resolve => {
+ PlacesUtils.favicons.getFaviconDataForPage(NetUtil.newURI(SUBPAGE_URL),
+ (aURI, aDataLen, aData, aMimeType) => {
+ Assert.equal(aURI.spec, ICON32_URL);
+ Assert.equal(aDataLen, data32.length);
+ Assert.deepEqual(aData, data32);
+ Assert.equal(aMimeType, "image/png");
+ resolve();
+ });
+ });
+});
--- a/toolkit/components/places/tests/favicons/test_getFaviconURLForPage.js
+++ b/toolkit/components/places/tests/favicons/test_getFaviconURLForPage.js
@@ -1,48 +1,76 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
-/**
- * This file tests getFaviconURLForPage.
- */
-
-// Tests
+const ICON16_URL = "http://places.test/favicon-normal16.png";
+const ICON32_URL = "http://places.test/favicon-normal32.png";
-function run_test() {
- run_next_test();
-}
-
-add_test(function test_normal() {
+add_task(function* test_normal() {
let pageURI = NetUtil.newURI("http://example.com/normal");
- PlacesTestUtils.addVisits(pageURI).then(function() {
+ yield PlacesTestUtils.addVisits(pageURI);
+ yield new Promise(resolve => {
PlacesUtils.favicons.setAndFetchFaviconForPage(
pageURI, SMALLPNG_DATA_URI, true,
PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE,
function() {
PlacesUtils.favicons.getFaviconURLForPage(pageURI,
function(aURI, aDataLen, aData, aMimeType) {
do_check_true(aURI.equals(SMALLPNG_DATA_URI));
// Check also the expected data types.
do_check_true(aDataLen === 0);
do_check_true(aData.length === 0);
do_check_true(aMimeType === "");
- run_next_test();
+ resolve();
});
}, Services.scriptSecurityManager.getSystemPrincipal());
});
});
-add_test(function test_missing() {
+add_task(function* test_missing() {
let pageURI = NetUtil.newURI("http://example.com/missing");
- PlacesUtils.favicons.getFaviconURLForPage(pageURI,
- function(aURI, aDataLen, aData, aMimeType) {
- // Check also the expected data types.
- do_check_true(aURI === null);
- do_check_true(aDataLen === 0);
- do_check_true(aData.length === 0);
- do_check_true(aMimeType === "");
- run_next_test();
- });
+ yield new Promise(resolve => {
+ PlacesUtils.favicons.getFaviconURLForPage(pageURI,
+ function(aURI, aDataLen, aData, aMimeType) {
+ // Check also the expected data types.
+ do_check_true(aURI === null);
+ do_check_true(aDataLen === 0);
+ do_check_true(aData.length === 0);
+ do_check_true(aMimeType === "");
+ resolve();
+ });
+ });
});
+
+add_task(function* test_fallback() {
+ const ROOT_URL = "https://www.example.com/";
+ const ROOT_ICON_URL = ROOT_URL + "favicon.ico";
+ const SUBPAGE_URL = ROOT_URL + "/missing";
+
+ do_print("Set icon for the root");
+ yield PlacesTestUtils.addVisits(ROOT_URL);
+ let data = readFileData(do_get_file("favicon-normal16.png"));
+ PlacesUtils.favicons.replaceFaviconData(NetUtil.newURI(ROOT_ICON_URL),
+ data, data.length, "image/png");
+ yield setFaviconForPage(ROOT_URL, ROOT_ICON_URL);
+
+ do_print("check fallback icons");
+ Assert.equal(yield getFaviconUrlForPage(ROOT_URL), ROOT_ICON_URL,
+ "The root should have its favicon");
+ Assert.equal(yield getFaviconUrlForPage(SUBPAGE_URL), ROOT_ICON_URL,
+ "The page should fallback to the root icon");
+
+ do_print("Now add a proper icon for the page");
+ yield PlacesTestUtils.addVisits(SUBPAGE_URL);
+ let data32 = readFileData(do_get_file("favicon-normal32.png"));
+ PlacesUtils.favicons.replaceFaviconData(NetUtil.newURI(ICON32_URL),
+ data32, data32.length, "image/png");
+ yield setFaviconForPage(SUBPAGE_URL, ICON32_URL);
+
+ do_print("check no fallback icons");
+ Assert.equal(yield getFaviconUrlForPage(ROOT_URL), ROOT_ICON_URL,
+ "The root should still have its favicon");
+ Assert.equal(yield getFaviconUrlForPage(SUBPAGE_URL), ICON32_URL,
+ "The page should also have its icon");
+});
--- a/toolkit/components/places/tests/favicons/test_page-icon_protocol.js
+++ b/toolkit/components/places/tests/favicons/test_page-icon_protocol.js
@@ -45,22 +45,28 @@ add_task(function* setup() {
gDefaultFavicon = yield fetchIconForSpec(PlacesUtils.favicons.defaultFavicon);
gFavicon = yield fetchIconForSpec(ICON_DATA);
});
add_task(function* known_url() {
let {data, contentType} = yield fetchIconForSpec(TEST_URI.spec);
Assert.equal(contentType, gFavicon.contentType);
- Assert.ok(data == gFavicon.data, "Got the favicon data");
+ Assert.deepEqual(data, gFavicon.data, "Got the favicon data");
});
add_task(function* unknown_url() {
let {data, contentType} = yield fetchIconForSpec("http://www.moz.org/");
Assert.equal(contentType, gDefaultFavicon.contentType);
- Assert.ok(data == gDefaultFavicon.data, "Got the default favicon data");
+ Assert.deepEqual(data, gDefaultFavicon.data, "Got the default favicon data");
});
add_task(function* invalid_url() {
let {data, contentType} = yield fetchIconForSpec("test");
Assert.equal(contentType, gDefaultFavicon.contentType);
Assert.ok(data == gDefaultFavicon.data, "Got the default favicon data");
});
+
+add_task(function* subpage_url_fallback() {
+ let {data, contentType} = yield fetchIconForSpec("http://mozilla.org/missing");
+ Assert.equal(contentType, gFavicon.contentType);
+ Assert.deepEqual(data, gFavicon.data, "Got the root favicon data");
+});