Backed out 11 changesets (bug 1915762) for causing node failures in gecko.d.ts CLOSED TREE

Backed out changeset 5c7edcb3b105 (bug 1915762)
Backed out changeset 05691232a461 (bug 1915762)
Backed out changeset 964a29c9a07a (bug 1915762)
Backed out changeset 5d74e419e249 (bug 1915762)
Backed out changeset 677c35e884d3 (bug 1915762)
Backed out changeset 8a7b0b87a0ef (bug 1915762)
Backed out changeset 5e38449cd4d9 (bug 1915762)
Backed out changeset ec8a0553d97a (bug 1915762)
Backed out changeset fb2232e5f3b5 (bug 1915762)
Backed out changeset b290501f5832 (bug 1915762)
Backed out changeset 9955e42e749b (bug 1915762)
This commit is contained in:
Cristian Tuns
2025-04-22 22:15:19 -04:00
parent 2b6e70239f
commit a9d8f21da6
50 changed files with 1714 additions and 1040 deletions

View File

@@ -101,13 +101,25 @@ export class AboutReaderParent extends JSWindowActorParent {
let preferredWidth = message.data.preferredWidth || 0;
let uri = Services.io.newURI(message.data.url);
let result = await lazy.PlacesUtils.favicons.getFaviconForPage(
let result = await new Promise(resolve => {
lazy.PlacesUtils.favicons.getFaviconURLForPage(
uri,
iconUri => {
if (iconUri) {
resolve({
url: message.data.url,
faviconUrl: iconUri.spec,
});
} else {
resolve(null);
}
},
preferredWidth
);
});
this.callListeners(message);
return result && { url: uri.spec, faviconUrl: result.uri.spec };
return result;
} catch (ex) {
console.error(
"Error requesting favicon URL for about:reader content: ",

View File

@@ -31,10 +31,17 @@ add_task(async function browser_loader() {
// Ensure the favicon has not been stored.
/* eslint-disable mozilla/no-arbitrary-setTimeout */
await new Promise(resolve => setTimeout(resolve, 1000));
let favicon = await PlacesUtils.favicons.getFaviconForPage(
Services.io.newURI(PAGE_URL)
await new Promise((resolve, reject) => {
PlacesUtils.favicons.getFaviconURLForPage(
Services.io.newURI(PAGE_URL),
foundIconURI => {
if (foundIconURI) {
reject(new Error("An icon has been stored " + foundIconURI.spec));
}
resolve();
}
);
Assert.ok(!favicon);
});
BrowserTestUtils.removeTab(tab);
});
@@ -61,10 +68,17 @@ async function later_addition(iconUrl) {
// Ensure the favicon has not been stored.
/* eslint-disable mozilla/no-arbitrary-setTimeout */
await new Promise(resolve => setTimeout(resolve, 1000));
let favicon = await PlacesUtils.favicons.getFaviconForPage(
Services.io.newURI(PAGE_URL)
await new Promise((resolve, reject) => {
PlacesUtils.favicons.getFaviconURLForPage(
Services.io.newURI(PAGE_URL),
foundIconURI => {
if (foundIconURI) {
reject(new Error("An icon has been stored " + foundIconURI.spec));
}
resolve();
}
);
Assert.ok(!favicon);
});
BrowserTestUtils.removeTab(tab);
}
@@ -113,10 +127,13 @@ add_task(async function root_icon_stored() {
},
async function () {
await TestUtils.waitForCondition(async () => {
let favicon = await PlacesUtils.favicons.getFaviconForPage(
Services.io.newURI("http://www.nostore.com/page")
let uri = await new Promise(resolve =>
PlacesUtils.favicons.getFaviconURLForPage(
Services.io.newURI("http://www.nostore.com/page"),
resolve
)
);
return favicon?.uri.spec == "http://www.nostore.com/favicon.ico";
return uri?.spec == "http://www.nostore.com/favicon.ico";
}, "wait for the favicon to be stored");
Assert.ok(await noStorePromise, "Should have received no-store header");
}
@@ -157,10 +174,13 @@ add_task(async function root_icon_after_pageshow_stored() {
},
async function () {
await TestUtils.waitForCondition(async () => {
let favicon = await PlacesUtils.favicons.getFaviconForPage(
Services.io.newURI("http://rootafterpageshow.com/page")
let uri = await new Promise(resolve =>
PlacesUtils.favicons.getFaviconURLForPage(
Services.io.newURI("http://rootafterpageshow.com/page"),
resolve
)
);
return favicon?.uri.spec == "http://rootafterpageshow.com/favicon.ico";
return uri?.spec == "http://rootafterpageshow.com/favicon.ico";
}, "wait for the favicon to be stored");
}
);

View File

@@ -21,14 +21,22 @@ async function test_icon(pageUrl, iconUrl) {
// Ensure the favicon has been stored.
await storedIconPromise;
let favicon = await PlacesUtils.favicons.getFaviconForPage(
Services.io.newURI(pageUrl)
);
await new Promise((resolve, reject) => {
PlacesUtils.favicons.getFaviconURLForPage(
Services.io.newURI(pageUrl),
foundIconURI => {
if (foundIconURI) {
Assert.equal(
favicon.uri.spec,
foundIconURI.spec,
iconUrl,
"Should have stored the expected icon."
);
resolve();
}
reject();
}
);
});
});
}

View File

@@ -220,9 +220,12 @@ add_task(async function test_initial_bookmarks() {
add_task(async function checkFavicon() {
let bookmark1url = CURRENT_POLICY.policies.Bookmarks[0].URL;
let result = await PlacesUtils.favicons.getFaviconForPage(
Services.io.newURI(bookmark1url)
let result = await new Promise(resolve => {
PlacesUtils.favicons.getFaviconDataForPage(
Services.io.newURI(bookmark1url),
(uri, _, data) => resolve({ uri, data })
);
});
is(
result.uri.spec,
@@ -232,7 +235,7 @@ add_task(async function checkFavicon() {
// data is an array of octets, which will be a bit hard to compare against
// FAVICON_DATA, which is base64 encoded. Checking the expected length should
// be good indication that this is working properly.
is(result.rawData.length, 464, "Favicon data has the correct length");
is(result.data.length, 464, "Favicon data has the correct length");
let faviconsExpiredNotification = TestUtils.topicObserved(
"places-favicons-expired"
@@ -244,11 +247,15 @@ add_task(async function checkFavicon() {
add_task(async function checkNetworkFavicon() {
let bookmarkURL = CURRENT_POLICY.policies.Bookmarks[1].URL;
let result = await PlacesUtils.favicons.getFaviconForPage(
Services.io.newURI(bookmarkURL)
let result = await new Promise(resolve => {
PlacesUtils.favicons.getFaviconDataForPage(
Services.io.newURI(bookmarkURL),
(uri, _, data) => resolve({ uri, data })
);
});
is(result, null, "Favicon should not be loaded");
is(result.uri, null, "Favicon should not be loaded");
is(result.data.length, 0, "Favicon data should be empty");
});
add_task(async function test_remove_Bookmark_2() {

View File

@@ -88,6 +88,22 @@ async function promiseMigration(
return Promise.all(promises);
}
/**
* Function that returns a favicon url for a given page url
*
* @param {string} uri
* The Bookmark URI
* @returns {string} faviconURI
* The Favicon URI
*/
async function getFaviconForPageURI(uri) {
let faviconURI = await new Promise(resolve => {
PlacesUtils.favicons.getFaviconDataForPage(uri, favURI => {
resolve(favURI);
});
});
return faviconURI;
}
/**
* Takes an array of page URIs and checks that the favicon was imported for each page URI
@@ -96,8 +112,8 @@ async function promiseMigration(
*/
async function assertFavicons(pageURIs) {
for (let uri of pageURIs) {
let favicon = await PlacesUtils.favicons.getFaviconForPage(uri);
Assert.ok(favicon, `Got favicon for ${favicon.uri.spec}`);
let faviconURI = await getFaviconForPageURI(uri);
Assert.ok(faviconURI, `Got favicon for ${uri.spec}`);
}
}
@@ -112,12 +128,17 @@ async function assertFavicons(pageURIs) {
* Expected mime type of the favicon.
*/
async function assertFavicon(pageURI, expectedImageData, expectedMimeType) {
let result = await PlacesUtils.favicons.getFaviconForPage(
Services.io.newURI(pageURI)
let result = await new Promise(resolve => {
PlacesUtils.favicons.getFaviconDataForPage(
Services.io.newURI(pageURI),
(faviconURI, dataLen, imageData, mimeType) => {
resolve({ faviconURI, dataLen, imageData, mimeType });
}
);
});
Assert.ok(!!result, `Got favicon for ${pageURI}`);
Assert.equal(
result.rawData.join(","),
result.imageData.join(","),
expectedImageData.join(","),
"Image data is correct"
);

View File

@@ -89,8 +89,17 @@ add_task(async function () {
Assert.equal(menuItem.title, "Menu Link After");
// Check no favicon exists for this bookmark
let favicon = await PlacesUtils.favicons.getFaviconForPage(menuItem.url.URI);
Assert.equal(favicon, null, "Favicon should not be found");
await Assert.rejects(
waitForResolvedPromise(
() => {
return PlacesUtils.promiseFaviconData(menuItem.url.href);
},
"Favicon not found",
10
),
/Favicon\snot\sfound/,
"Favicon not found"
);
// Check the custom bookmarks exist on toolbar.
let toolbarItem = await PlacesUtils.bookmarks.fetch({
@@ -100,12 +109,21 @@ add_task(async function () {
Assert.equal(toolbarItem.title, "Toolbar Link Before");
// Check the custom favicon exist for this bookmark
favicon = await PlacesUtils.favicons.getFaviconForPage(toolbarItem.url.URI);
Assert.ok(favicon, "Favicon should be found");
Assert.equal(favicon.uri.spec, "https://example.org/favicon.png");
Assert.greater(favicon.rawData.length, 0);
Assert.equal(favicon.mimeType, "image/png");
Assert.equal(favicon.dataURI.spec, SMALLPNG_DATA_URI.spec);
let faviconItem = await waitForResolvedPromise(
() => {
return PlacesUtils.promiseFaviconData(toolbarItem.url.href);
},
"Favicon not found",
10
);
Assert.equal(faviconItem.uri.spec, "https://example.org/favicon.png");
Assert.greater(faviconItem.dataLen, 0);
Assert.equal(faviconItem.mimeType, "image/png");
let base64Icon =
"data:image/png;base64," +
base64EncodeString(String.fromCharCode.apply(String, faviconItem.data));
Assert.equal(base64Icon, SMALLPNG_DATA_URI.spec);
toolbarItem = await PlacesUtils.bookmarks.fetch({
parentGuid: PlacesUtils.bookmarks.toolbarGuid,

View File

@@ -8,6 +8,7 @@
#include "nsGNOMEShellSearchProvider.h"
#include "nsToolkitCompsCID.h"
#include "nsIFaviconService.h"
#include "base/message_loop.h" // for MessageLoop
#include "base/task.h" // for NewRunnableMethod, etc
#include "mozilla/gfx/2D.h"
@@ -23,7 +24,6 @@
#include "nsIOpenTabsProvider.h"
#include "imgIContainer.h"
#include "imgITools.h"
#include "mozilla/places/nsFaviconService.h"
using namespace mozilla;
using namespace mozilla::gfx;
@@ -62,6 +62,27 @@ static const char* introspect_template =
"</interface>\n"
"</node>\n";
class AsyncFaviconDataReady final : public nsIFaviconDataCallback {
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIFAVICONDATACALLBACK
AsyncFaviconDataReady(RefPtr<nsGNOMEShellHistorySearchResult> aSearchResult,
int aIconIndex, int aTimeStamp)
: mSearchResult(std::move(aSearchResult)),
mIconIndex(aIconIndex),
mTimeStamp(aTimeStamp) {}
private:
~AsyncFaviconDataReady() {}
RefPtr<nsGNOMEShellHistorySearchResult> mSearchResult;
int mIconIndex;
int mTimeStamp;
};
NS_IMPL_ISUPPORTS(AsyncFaviconDataReady, nsIFaviconDataCallback)
// Inspired by SurfaceToPackedBGRA
static UniquePtr<uint8_t[]> SurfaceToPackedRGBA(DataSourceSurface* aSurface) {
IntSize size = aSurface->GetSize();
@@ -95,35 +116,22 @@ static UniquePtr<uint8_t[]> SurfaceToPackedRGBA(DataSourceSurface* aSurface) {
return imageBuffer;
}
static nsresult UpdateHistoryIcon(
const places::FaviconPromise::ResolveOrRejectValue& aPromiseResult,
const RefPtr<nsGNOMEShellHistorySearchResult>& aSearchResult,
int aIconIndex, int aTimeStamp) {
NS_IMETHODIMP
AsyncFaviconDataReady::OnComplete(nsIURI* aFaviconURI, uint32_t aDataLen,
const uint8_t* aData,
const nsACString& aMimeType,
uint16_t aWidth) {
// This is a callback from some previous search so we don't want it
if (aTimeStamp != aSearchResult->GetTimeStamp()) {
if (mTimeStamp != mSearchResult->GetTimeStamp() || !aData || !aDataLen) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIFavicon> favicon =
aPromiseResult.IsResolve() ? aPromiseResult.ResolveValue() : nullptr;
if (!favicon) {
return NS_ERROR_FAILURE;
}
// Get favicon content.
nsTArray<uint8_t> rawData;
nsresult rv = favicon->GetRawData(rawData);
NS_ENSURE_SUCCESS(rv, rv);
nsAutoCString mimeType;
rv = favicon->GetMimeType(mimeType);
NS_ENSURE_SUCCESS(rv, rv);
// Decode the image from the format it was returned to us in (probably PNG)
nsCOMPtr<imgIContainer> container;
nsCOMPtr<imgITools> imgtool = do_CreateInstance("@mozilla.org/image/tools;1");
rv = imgtool->DecodeImageFromBuffer(
reinterpret_cast<const char*>(rawData.Elements()), rawData.Length(),
mimeType, getter_AddRefs(container));
nsresult rv = imgtool->DecodeImageFromBuffer(
reinterpret_cast<const char*>(aData), aDataLen, aMimeType,
getter_AddRefs(container));
NS_ENSURE_SUCCESS(rv, rv);
RefPtr<SourceSurface> surface = container->GetFrame(
@@ -141,9 +149,9 @@ static nsresult UpdateHistoryIcon(
return NS_ERROR_OUT_OF_MEMORY;
}
aSearchResult->SetHistoryIcon(aTimeStamp, std::move(data),
mSearchResult->SetHistoryIcon(mTimeStamp, std::move(data),
surface->GetSize().width,
surface->GetSize().height, aIconIndex);
surface->GetSize().height, mIconIndex);
return NS_OK;
}
@@ -424,7 +432,8 @@ void nsGNOMEShellHistorySearchResult::HandleSearchResultReply() {
nsresult rv = mHistResultContainer->GetChildCount(&childCount);
if (NS_SUCCEEDED(rv) && childCount > 0) {
// Obtain the favicon service and get the favicon for the specified page
auto* favIconSvc = nsFaviconService::GetFaviconService();
nsCOMPtr<nsIFaviconService> favIconSvc(
do_GetService("@mozilla.org/browser/favicon-service;1"));
nsCOMPtr<nsIIOService> ios(do_GetService(NS_IOSERVICE_CONTRACTID));
if (childCount > MAX_SEARCH_RESULTS_NUM) {
@@ -444,15 +453,11 @@ void nsGNOMEShellHistorySearchResult::HandleSearchResultReply() {
nsAutoCString uri;
child->GetUri(uri);
RefPtr<nsGNOMEShellHistorySearchResult> self = this;
nsCOMPtr<nsIURI> iconIri;
ios->NewURI(uri, nullptr, nullptr, getter_AddRefs(iconIri));
favIconSvc->AsyncGetFaviconForPage(iconIri)->Then(
GetMainThreadSerialEventTarget(), __func__,
[self, iconIndex = i, timeStamp = mTimeStamp](
const places::FaviconPromise::ResolveOrRejectValue& aResult) {
UpdateHistoryIcon(aResult, self, iconIndex, timeStamp);
});
nsCOMPtr<nsIFaviconDataCallback> callback =
new AsyncFaviconDataReady(this, i, mTimeStamp);
favIconSvc->GetFaviconDataForPage(iconIri, callback, 0);
bool isOpen = false;
for (const auto& openuri : mOpenTabs) {

View File

@@ -1199,16 +1199,18 @@ export class FaviconProvider {
* @param {nsIURI} uri
* Page to check for favicon data
* @returns {object}
* Favicon info object. If there is no data in DB, return null.
* A promise of an object (possibly null) containing the data
*/
async getFaviconInfo(uri) {
let favicon = await lazy.PlacesUtils.favicons.getFaviconForPage(
getFaviconInfo(uri) {
return new Promise(resolve =>
lazy.PlacesUtils.favicons.getFaviconDataForPage(
uri,
// Package up the icon data in an object if we have it; otherwise null
(iconUri, faviconLength, favicon, mimeType, faviconSize) =>
resolve(iconUri ? { iconUri, faviconSize } : null),
lazy.NewTabUtils.activityStreamProvider.THUMB_FAVICON_SIZE
)
);
return favicon
? { iconUri: favicon.uri, faviconSize: favicon.width }
: null;
}
/**

View File

@@ -110,9 +110,9 @@ add_task(async function test_fetchIcon_with_valid_favicon() {
await feed.fetchIcon(TEST_PAGE_URL.spec);
info("Check the database");
const result = await PlacesUtils.favicons.getFaviconForPage(TEST_PAGE_URL);
const result = await PlacesUtils.promiseFaviconData(TEST_PAGE_URL);
Assert.equal(result.mimeType, "image/svg+xml");
Assert.equal(result.width, 65535);
Assert.equal(result.size, 65535);
info("Clean up");
await PlacesTestUtils.clearFavicons();
@@ -141,13 +141,14 @@ add_task(async function test_fetchIcon_with_invalid_favicon() {
await feed.fetchIcon(TEST_PAGE_URL.spec);
info("Check the database");
const result = await PlacesUtils.favicons.getFaviconForPage(TEST_PAGE_URL);
const result = await PlacesUtils.promiseFaviconData(TEST_PAGE_URL);
// eslint-disable-next-line no-use-before-define
const expectedFaviconData = readFileData(TEST_FAVICON_FILE);
Assert.equal(result.uri.spec, `${TEST_FAVICON_URL.spec}#tippytop`);
Assert.deepEqual(result.rawData, expectedFaviconData);
Assert.equal(result.dataLen, expectedFaviconData.length);
Assert.deepEqual(result.data, expectedFaviconData);
Assert.equal(result.mimeType, "image/png");
Assert.equal(result.width, 16);
Assert.equal(result.size, 16);
info("Clean up");
await PlacesTestUtils.clearFavicons();
@@ -185,16 +186,15 @@ add_task(async function test_fetchIconFromRedirects_with_valid_favicon() {
info("Check the database");
await TestUtils.waitForCondition(async () => {
const result = await PlacesUtils.favicons.getFaviconForPage(destination);
const result = await PlacesUtils.promiseFaviconData(destination);
return !!result;
});
const sourceResult =
await PlacesUtils.favicons.getFaviconForPage(TEST_PAGE_URL);
const destinationResult =
await PlacesUtils.favicons.getFaviconForPage(destination);
Assert.deepEqual(destinationResult.rawData, sourceResult.rawData);
const sourceResult = await PlacesUtils.promiseFaviconData(TEST_PAGE_URL);
const destinationResult = await PlacesUtils.promiseFaviconData(destination);
Assert.equal(destinationResult.dataLen, sourceResult.dataLen);
Assert.deepEqual(destinationResult.data, sourceResult.data);
Assert.equal(destinationResult.mimeType, sourceResult.mimeType);
Assert.equal(destinationResult.width, sourceResult.width);
Assert.equal(destinationResult.size, sourceResult.size);
info("Clean up");
await PlacesTestUtils.clearFavicons();
@@ -264,7 +264,9 @@ add_task(async function test_fetchIcon_withNetworkFetch() {
// Set up mocks
PlacesUtils.favicons = {
getFaviconForPage: sandbox.stub().returns(Promise.resolve(null)),
getFaviconDataForPage: sandbox
.stub()
.callsArgWith(1, null, 0, null, null, 0),
setFaviconForPage: sandbox.spy(),
copyFavicons: sandbox.spy(),
};
@@ -296,10 +298,9 @@ add_task(async function test_fetchIcon_withInvalidDataInDb() {
const sandbox = sinon.createSandbox();
// Set up mocks
PlacesUtils.favicons = {
// Invalid since no width.
getFaviconForPage: sandbox
getFaviconDataForPage: sandbox
.stub()
.returns(Promise.resolve({ iconUri: { spec: FAKE_SMALLPNG_DATA_URI } })),
.callsArgWith(1, { spec: FAKE_SMALLPNG_DATA_URI }, 0, null, null, 0),
setFaviconForPage: sandbox.spy(),
copyFavicons: sandbox.spy(),
};
@@ -334,11 +335,15 @@ add_task(async function test_fetchIcon_withValidDataInDb() {
const sandbox = sinon.createSandbox();
// Set up mocks
PlacesUtils.favicons = {
getFaviconForPage: sandbox.stub().returns(
Promise.resolve({
iconUri: { spec: FAKE_SMALLPNG_DATA_URI },
width: 100,
})
getFaviconDataForPage: sandbox
.stub()
.callsArgWith(
1,
{ spec: FAKE_SMALLPNG_DATA_URI },
100,
["dummy icon data"],
"image/png",
16
),
setFaviconForPage: sandbox.spy(),
copyFavicons: sandbox.spy(),
@@ -365,7 +370,9 @@ add_task(async function test_fetchIcon_withNoTippyTopData() {
let feed = new FaviconProvider();
// Set up mocks
PlacesUtils.favicons = {
getFaviconForPage: sandbox.stub().returns(Promise.resolve(null)),
getFaviconDataForPage: sandbox
.stub()
.callsArgWith(1, null, 0, null, null, 0),
setFaviconForPage: sandbox.spy(),
copyFavicons: sandbox.spy(),
};

View File

@@ -42,11 +42,10 @@ add_task(async function () {
".qa-debug-target-item-icon"
);
// Note this relies on PlaceUtils.favicons.getFaviconForPage() returning the
// favicon that has same data-url as the one provided in the test page. If the
// implementation changes and PlaceUtils returns a different base64 from the
// one we defined, we can instead load the image and check a few pixels to
// verify it matches the expected icon.
// Note this relies on PlaceUtils.promiseFaviconData returning the same data-url as the
// one provided in the test page. If the implementation changes and PlaceUtils returns a
// different base64 from the one we defined, we can instead load the image and check a
// few pixels to verify it matches the expected icon.
is(
faviconTabIcon.src,
EXPECTED_FAVICON,

View File

@@ -232,23 +232,19 @@ declare namespace MockedExports {
interface FaviconData {
uri: nsIURI;
rawData: number[];
dataLen: number;
data: number[];
mimeType: string;
width: number;
size: number;
}
const PlaceUtilsSYSMJS: {
PlacesUtils: {
favicons: {
getFaviconForPage: (
pageUrl: nsIURI,
promiseFaviconData: (
pageUrl: string | URL | nsIURI,
preferredWidth?: number
) => Promise<FaviconData>;
// TS-TODO: Add the rest.
},
toURI: (
string | URL | nsIURI
) => nsIURI
};
};

View File

@@ -460,18 +460,17 @@ async function getPageFavicons(pageUrls) {
}
// Get the data of favicons and return them.
const { favicons, toURI } = lazy.PlacesUtils();
const { promiseFaviconData } = lazy.PlacesUtils();
const promises = pageUrls.map(pageUrl =>
favicons
.getFaviconForPage(toURI(pageUrl), /* preferredWidth = */ 32)
promiseFaviconData(pageUrl, /* preferredWidth = */ 32)
.then(favicon => {
// Check if data is found in the database and return it if so.
if (favicon.rawData.length) {
if (favicon.dataLen > 0 && favicon.data) {
return {
// PlacesUtils returns a number array for the data. Converting it to
// the Uint8Array here to send it to the tab more efficiently.
data: new Uint8Array(favicon.rawData).buffer,
data: new Uint8Array(favicon.data).buffer,
mimeType: favicon.mimeType,
};
}

View File

@@ -221,10 +221,10 @@ class TabDescriptorActor extends Actor {
}
try {
const favicon = await lazy.PlacesUtils.favicons.getFaviconForPage(
lazy.PlacesUtils.toURI(this._getUrl())
const { data } = await lazy.PlacesUtils.promiseFaviconData(
this._getUrl()
);
return favicon.rawData;
return data;
} catch (e) {
// Favicon unavailable for this url.
return null;

View File

@@ -2,6 +2,8 @@
* 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/. */
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
import { Target } from "chrome://remote/content/cdp/targets/Target.sys.mjs";
const lazy = {};
@@ -12,6 +14,13 @@ ChromeUtils.defineESModuleGetters(lazy, {
TabSession: "chrome://remote/content/cdp/sessions/TabSession.sys.mjs",
});
XPCOMUtils.defineLazyServiceGetter(
lazy,
"Favicons",
"@mozilla.org/browser/favicon-service;1",
"nsIFaviconService"
);
/**
* Target for a local tab or a remoted frame.
*/
@@ -83,6 +92,19 @@ export class TabTarget extends Target {
return null;
}
/** @returns {Promise<string|null>} */
get faviconUrl() {
return new Promise(resolve => {
lazy.Favicons.getFaviconURLForPage(this.browser.currentURI, url => {
if (url) {
resolve(url.spec);
} else {
resolve(null);
}
});
});
}
get title() {
return this.browsingContext.currentWindowGlobal.documentTitle;
}

View File

@@ -425,10 +425,9 @@ export const TabProvider = {
encoder.encode(thisTab.title + thisTab.lastUsed + url).byteLength + 100;
// Use the favicon service for the icon url - we can wait for the promises at the end.
let iconPromise = lazy.PlacesUtils.favicons
.getFaviconForPage(lazy.PlacesUtils.toURI(url))
.then(favicon => {
thisTab.icon = favicon.uri.spec;
let iconPromise = lazy.PlacesUtils.promiseFaviconData(url)
.then(iconData => {
thisTab.icon = iconData.uri.spec;
})
.catch(() => {
log.trace(

View File

@@ -39,9 +39,7 @@ XPCOMUtils.defineLazyPreferenceGetter(
*/
async function setIconToFavicon(icon, origin) {
try {
let iconData = await lazy.PlacesUtils.favicons.getFaviconForPage(
lazy.PlacesUtils.toURI(origin)
);
let iconData = await lazy.PlacesUtils.promiseFaviconData(origin);
icon.src = iconData.uri.spec;
} catch {
icon.src = "chrome://global/skin/icons/defaultFavicon.svg";

View File

@@ -77,6 +77,17 @@ const MICROSEC_PER_SEC = 1000000;
const EXPORT_INDENT = " "; // four spaces
function base64EncodeString(aString) {
let stream = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(
Ci.nsIStringInputStream
);
stream.setByteStringData(aString);
let encoder = Cc["@mozilla.org/scriptablebase64encoder;1"].createInstance(
Ci.nsIScriptableBase64Encoder
);
return encoder.encodeToString(stream, aString.length);
}
/**
* Provides HTML escaping for use in HTML attributes and body of the bookmarks
* file, compatible with the old bookmarks system.
@@ -1043,19 +1054,21 @@ BookmarkExporter.prototype = {
if (!aItem.iconUri) {
return;
}
let favicon;
try {
let favicon = await PlacesUtils.favicons.getFaviconForPage(
PlacesUtils.toURI(aItem.uri)
);
favicon = await PlacesUtils.promiseFaviconData(aItem.uri);
} catch (ex) {
console.error("Unexpected Error trying to fetch icon data");
return;
}
this._writeAttribute("ICON_URI", escapeUrl(favicon.uri.spec));
if (favicon?.rawData.length && !favicon.uri.schemeIs("chrome")) {
this._writeAttribute("ICON", favicon.dataURI.spec);
}
} catch (ex) {
console.error("Unexpected Error trying to fetch icon data");
if (!favicon.uri.schemeIs("chrome") && favicon.dataLen > 0) {
let faviconContents =
"data:image/png;base64," +
base64EncodeString(String.fromCharCode.apply(String, favicon.data));
this._writeAttribute("ICON", faviconContents);
}
},
};

View File

@@ -868,6 +868,83 @@ AsyncSetIconForPage::Run() {
return (rv = event.Run());
}
////////////////////////////////////////////////////////////////////////////////
//// AsyncGetFaviconURLForPage
AsyncGetFaviconURLForPage::AsyncGetFaviconURLForPage(
const nsCOMPtr<nsIURI>& aPageURI, uint16_t aPreferredWidth,
nsIFaviconDataCallback* aCallback)
: Runnable("places::AsyncGetFaviconURLForPage"),
mPreferredWidth(aPreferredWidth == 0 ? UINT16_MAX : aPreferredWidth),
mCallback(new nsMainThreadPtrHolder<nsIFaviconDataCallback>(
"AsyncGetFaviconURLForPage::mCallback", aCallback)),
mPageURI(aPageURI) {
MOZ_ASSERT(NS_IsMainThread());
}
NS_IMETHODIMP
AsyncGetFaviconURLForPage::Run() {
MOZ_ASSERT(!NS_IsMainThread());
RefPtr<Database> DB = Database::GetDatabase();
NS_ENSURE_STATE(DB);
IconData iconData;
nsresult rv = FetchIconPerSpec(DB, mPageURI, iconData, mPreferredWidth);
NS_ENSURE_SUCCESS(rv, rv);
// Now notify our callback of the icon spec we retrieved, even if empty.
PageData pageData;
mPageURI->GetSpec(pageData.spec);
nsCOMPtr<nsIRunnable> event =
new NotifyIconObservers(iconData, pageData, mCallback);
rv = NS_DispatchToMainThread(event);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////
//// AsyncGetFaviconDataForPage
AsyncGetFaviconDataForPage::AsyncGetFaviconDataForPage(
const nsCOMPtr<nsIURI>& aPageURI, uint16_t aPreferredWidth,
nsIFaviconDataCallback* aCallback)
: Runnable("places::AsyncGetFaviconDataForPage"),
mPreferredWidth(aPreferredWidth == 0 ? UINT16_MAX : aPreferredWidth),
mCallback(new nsMainThreadPtrHolder<nsIFaviconDataCallback>(
"AsyncGetFaviconDataForPage::mCallback", aCallback)),
mPageURI(aPageURI) {
MOZ_ASSERT(NS_IsMainThread());
}
NS_IMETHODIMP
AsyncGetFaviconDataForPage::Run() {
MOZ_ASSERT(!NS_IsMainThread());
RefPtr<Database> DB = Database::GetDatabase();
NS_ENSURE_STATE(DB);
IconData iconData;
nsresult rv = FetchIconPerSpec(DB, mPageURI, iconData, mPreferredWidth);
NS_ENSURE_SUCCESS(rv, rv);
if (!iconData.spec.IsEmpty()) {
rv = FetchIconInfo(DB, mPreferredWidth, iconData);
if (NS_FAILED(rv)) {
iconData.spec.Truncate();
}
}
PageData pageData;
mPageURI->GetSpec(pageData.spec);
nsCOMPtr<nsIRunnable> event =
new NotifyIconObservers(iconData, pageData, mCallback);
rv = NS_DispatchToMainThread(event);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////
//// AsyncGetFaviconForPageRunnable

View File

@@ -159,6 +159,63 @@ class AsyncSetIconForPage final : public Runnable {
PageData mPage;
};
/**
* Asynchronously tries to get the URL of a page's favicon, then notifies the
* given observer.
*/
class AsyncGetFaviconURLForPage final : public Runnable {
public:
NS_DECL_NSIRUNNABLE
/**
* Constructor.
*
* @param aPageURI
* URI 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 nsCOMPtr<nsIURI>& aPageURI,
uint16_t aPreferredWidth,
nsIFaviconDataCallback* aCallback);
private:
uint16_t mPreferredWidth;
nsMainThreadPtrHandle<nsIFaviconDataCallback> mCallback;
nsCOMPtr<nsIURI> mPageURI;
};
/**
* Asynchronously tries to get the URL and data of a page's favicon, then
* notifies the given observer.
*/
class AsyncGetFaviconDataForPage final : public Runnable {
public:
NS_DECL_NSIRUNNABLE
/**
* Constructor.
*
* @param aPageURI
* URI 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 nsCOMPtr<nsIURI>& aPageURI,
uint16_t aPreferredWidth,
nsIFaviconDataCallback* aCallback);
private:
uint16_t mPreferredWidth;
nsMainThreadPtrHandle<nsIFaviconDataCallback> mCallback;
nsCOMPtr<nsIURI> mPageURI;
};
using FaviconPromise =
mozilla::MozPromise<nsCOMPtr<nsIFavicon>, nsresult, true>;

View File

@@ -39,39 +39,6 @@ struct FaviconMetadata {
uint16_t mWidth = 0;
};
static nsresult GetFaviconMetadata(
const FaviconPromise::ResolveOrRejectValue& aResult,
FaviconMetadata& aMetadata) {
if (aResult.IsReject()) {
return NS_ERROR_NOT_AVAILABLE;
}
nsCOMPtr<nsIFavicon> favicon = aResult.ResolveValue();
if (!favicon) {
return NS_ERROR_NOT_AVAILABLE;
}
nsTArray<uint8_t> rawData;
favicon->GetRawData(rawData);
if (rawData.IsEmpty()) {
return NS_ERROR_NOT_AVAILABLE;
}
nsCOMPtr<nsIInputStream> stream;
nsresult rv = NS_NewByteInputStream(
getter_AddRefs(stream),
AsChars(Span{rawData.Elements(), rawData.Length()}), NS_ASSIGNMENT_COPY);
NS_ENSURE_SUCCESS(rv, rv);
favicon->GetWidth(&aMetadata.mWidth);
favicon->GetMimeType(aMetadata.mContentType);
aMetadata.mStream = stream;
aMetadata.mContentLength = rawData.Length();
return NS_OK;
}
void RecordIconSizeTelemetry(nsIURI* uri, const FaviconMetadata& metadata) {
uint16_t preferredSize = INT16_MAX;
auto* faviconService = nsFaviconService::GetFaviconService();
@@ -166,6 +133,66 @@ static nsresult StreamDefaultFavicon(nsIURI* aURI, nsILoadInfo* aLoadInfo,
return NS_OK;
}
namespace {
class FaviconDataCallback final : public nsIFaviconDataCallback {
public:
FaviconDataCallback(nsIURI* aURI, nsILoadInfo* aLoadInfo)
: mURI(aURI), mLoadInfo(aLoadInfo) {
MOZ_ASSERT(aURI);
MOZ_ASSERT(aLoadInfo);
}
NS_DECL_ISUPPORTS
NS_DECL_NSIFAVICONDATACALLBACK
RefPtr<FaviconMetadataPromise> Promise() {
return mPromiseHolder.Ensure(__func__);
}
private:
~FaviconDataCallback();
nsCOMPtr<nsIURI> mURI;
MozPromiseHolder<FaviconMetadataPromise> mPromiseHolder;
nsCOMPtr<nsILoadInfo> mLoadInfo;
};
NS_IMPL_ISUPPORTS(FaviconDataCallback, nsIFaviconDataCallback);
FaviconDataCallback::~FaviconDataCallback() {
mPromiseHolder.RejectIfExists(NS_ERROR_FAILURE, __func__);
}
NS_IMETHODIMP FaviconDataCallback::OnComplete(nsIURI* aURI, uint32_t aDataLen,
const uint8_t* aData,
const nsACString& aMimeType,
uint16_t aWidth) {
if (!aDataLen) {
mPromiseHolder.Reject(NS_ERROR_NOT_AVAILABLE, __func__);
return NS_OK;
}
nsCOMPtr<nsIInputStream> inputStream;
nsresult rv =
NS_NewByteInputStream(getter_AddRefs(inputStream),
AsChars(Span{aData, aDataLen}), NS_ASSIGNMENT_COPY);
if (NS_FAILED(rv)) {
mPromiseHolder.Reject(rv, __func__);
return rv;
}
FaviconMetadata metadata;
metadata.mStream = inputStream;
metadata.mContentType = aMimeType;
metadata.mContentLength = aDataLen;
metadata.mWidth = aWidth;
mPromiseHolder.Resolve(std::move(metadata), __func__);
return NS_OK;
}
} // namespace
NS_IMPL_ISUPPORTS(PageIconProtocolHandler, nsIProtocolHandler,
nsISupportsWeakReference);
@@ -235,30 +262,34 @@ nsresult PageIconProtocolHandler::NewChannelInternal(nsIURI* aURI,
nsresult rv = channel->SetLoadInfo(aLoadInfo);
NS_ENSURE_SUCCESS(rv, rv);
GetFaviconData(aURI)->Then(
GetFaviconData(aURI, aLoadInfo)
->Then(
GetMainThreadSerialEventTarget(), __func__,
[pipeOut, channel, uri = nsCOMPtr{aURI}, loadInfo = nsCOMPtr{aLoadInfo}](
const FaviconPromise::ResolveOrRejectValue& aResult) {
FaviconMetadata metadata;
if (NS_SUCCEEDED(GetFaviconMetadata(aResult, metadata))) {
channel->SetContentType(metadata.mContentType);
channel->SetContentLength(metadata.mContentLength);
[pipeOut, channel,
uri = nsCOMPtr{aURI}](const FaviconMetadata& aMetadata) {
channel->SetContentType(aMetadata.mContentType);
channel->SetContentLength(aMetadata.mContentLength);
nsresult rv;
const nsCOMPtr<nsIEventTarget> target =
do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
channel->CancelWithReason(NS_BINDING_ABORTED,
"GetFaviconData failed"_ns);
return;
}
rv = NS_AsyncCopy(metadata.mStream, pipeOut, target);
rv = NS_AsyncCopy(aMetadata.mStream, pipeOut, target);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
RecordIconSizeTelemetry(uri, metadata);
} else {
RecordIconSizeTelemetry(uri, aMetadata);
},
[uri = nsCOMPtr{aURI}, loadInfo = nsCOMPtr{aLoadInfo}, pipeOut,
channel](nsresult aRv) {
// There are a few reasons why this might fail. For example, one
// reason is that the URI might not actually be properly parsable.
// In that case, we'll try one last time to stream the default
@@ -266,18 +297,17 @@ nsresult PageIconProtocolHandler::NewChannelInternal(nsIURI* aURI,
channel->SetContentType(nsLiteralCString(FAVICON_DEFAULT_MIMETYPE));
channel->SetContentLength(-1);
Unused << StreamDefaultFavicon(uri, loadInfo, pipeOut);
}
});
channel.forget(aOutChannel);
return NS_OK;
}
RefPtr<FaviconPromise> PageIconProtocolHandler::GetFaviconData(
nsIURI* aPageIconURI) {
RefPtr<FaviconMetadataPromise> PageIconProtocolHandler::GetFaviconData(
nsIURI* aPageIconURI, nsILoadInfo* aLoadInfo) {
auto* faviconService = nsFaviconService::GetFaviconService();
if (MOZ_UNLIKELY(!faviconService)) {
return FaviconPromise::CreateAndReject(NS_ERROR_UNEXPECTED, __func__);
return FaviconMetadataPromise::CreateAndReject(NS_ERROR_UNEXPECTED,
__func__);
}
uint16_t preferredSize = 0;
@@ -285,16 +315,26 @@ RefPtr<FaviconPromise> PageIconProtocolHandler::GetFaviconData(
nsCOMPtr<nsIURI> pageURI;
nsresult rv;
{
// NOTE: We don't need to strip #size= fragments because
// GetFaviconDataForPage strips them when doing the database lookup.
nsAutoCString pageQuery;
aPageIconURI->GetPathQueryRef(pageQuery);
rv = NS_NewURI(getter_AddRefs(pageURI), pageQuery);
if (NS_FAILED(rv)) {
return FaviconPromise::CreateAndReject(rv, __func__);
return FaviconMetadataPromise::CreateAndReject(rv, __func__);
}
}
return faviconService->AsyncGetFaviconForPage(pageURI, preferredSize);
auto faviconCallback =
MakeRefPtr<FaviconDataCallback>(aPageIconURI, aLoadInfo);
rv = faviconService->GetFaviconDataForPage(pageURI, faviconCallback,
preferredSize);
if (NS_FAILED(rv)) {
return FaviconMetadataPromise::CreateAndReject(rv, __func__);
}
return faviconCallback->Promise();
}
RefPtr<RemoteStreamPromise> PageIconProtocolHandler::NewStream(
@@ -329,28 +369,26 @@ RefPtr<RemoteStreamPromise> PageIconProtocolHandler::NewStream(
nsCOMPtr<nsILoadInfo> loadInfo(aLoadInfo);
RefPtr<PageIconProtocolHandler> self = this;
GetFaviconData(uri)->Then(
GetFaviconData(uri, loadInfo)
->Then(
GetMainThreadSerialEventTarget(), __func__,
[self, uri, loadInfo, outerPromise, childURI = nsCOMPtr{aChildURI}](
const FaviconPromise::ResolveOrRejectValue& aResult) {
FaviconMetadata metadata;
if (NS_SUCCEEDED(GetFaviconMetadata(aResult, metadata))) {
RecordIconSizeTelemetry(childURI, metadata);
RemoteStreamInfo info(metadata.mStream, metadata.mContentType,
metadata.mContentLength);
[outerPromise,
childURI = nsCOMPtr{aChildURI}](const FaviconMetadata& aMetadata) {
RecordIconSizeTelemetry(childURI, aMetadata);
RemoteStreamInfo info(aMetadata.mStream, aMetadata.mContentType,
aMetadata.mContentLength);
outerPromise->Resolve(std::move(info), __func__);
} else {
},
[self, uri, loadInfo, outerPromise](nsresult aRv) {
nsCOMPtr<nsIAsyncInputStream> pipeIn;
nsCOMPtr<nsIAsyncOutputStream> pipeOut;
self->GetStreams(getter_AddRefs(pipeIn), getter_AddRefs(pipeOut));
RemoteStreamInfo info(pipeIn,
nsLiteralCString(FAVICON_DEFAULT_MIMETYPE), -1);
RemoteStreamInfo info(
pipeIn, nsLiteralCString(FAVICON_DEFAULT_MIMETYPE), -1);
Unused << StreamDefaultFavicon(uri, loadInfo, pipeOut);
outerPromise->Resolve(std::move(info), __func__);
}
});
return outerPromise;
}

View File

@@ -10,7 +10,6 @@
#include "mozilla/MozPromise.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/net/RemoteStreamGetter.h"
#include "mozilla/places/nsFaviconService.h"
#include "nsIProtocolHandler.h"
#include "nsThreadUtils.h"
#include "nsWeakReference.h"
@@ -18,6 +17,8 @@
namespace mozilla::places {
struct FaviconMetadata;
using FaviconMetadataPromise =
mozilla::MozPromise<FaviconMetadata, nsresult, false>;
using net::RemoteStreamPromise;
@@ -73,7 +74,8 @@ class PageIconProtocolHandler final : public nsIProtocolHandler,
nsILoadInfo* aLoadInfo,
nsIChannel** aRetVal);
RefPtr<FaviconPromise> GetFaviconData(nsIURI* aPageIconURI);
RefPtr<FaviconMetadataPromise> GetFaviconData(nsIURI* aPageIconURI,
nsILoadInfo* aLoadInfo);
nsresult NewChannelInternal(nsIURI*, nsILoadInfo*, nsIChannel**);

View File

@@ -1621,6 +1621,37 @@ export var PlacesUtils = {
return db.executeBeforeShutdown(name, task);
},
/**
* Gets favicon data for a given page url.
*
* @param {string|URL|nsIURI} aPageUrl
* Url of the page to get favicon for.
* @param {number} [preferredWidth]
* The preferred width of the favicon in pixels. The default value of 0
* returns the largest icon available.
* @returns {Promise<{uri: nsIURI, dataLen: number, data: number[], mimeType: string, size: number}>}
* Resolves an object representing a favicon entry.
* Rejects if the given url has no associated favicon.
*/
promiseFaviconData(aPageUrl, preferredWidth = 0) {
return new Promise((resolve, reject) => {
if (!(aPageUrl instanceof Ci.nsIURI)) {
aPageUrl = PlacesUtils.toURI(aPageUrl);
}
PlacesUtils.favicons.getFaviconDataForPage(
aPageUrl,
function (uri, dataLen, data, mimeType, size) {
if (uri) {
resolve({ uri, dataLen, data, mimeType, size });
} else {
reject();
}
},
preferredWidth
);
});
},
/**
* Returns the passed URL with a #size ref for the specified size and
* devicePixelRatio.

View File

@@ -417,6 +417,53 @@ nsFaviconService::SetFaviconForPage(nsIURI* aPageURI, nsIURI* aFaviconURI,
return NS_OK;
}
NS_IMETHODIMP
nsFaviconService::GetFaviconURLForPage(nsIURI* aPageURI,
nsIFaviconDataCallback* aCallback,
uint16_t aPreferredWidth) {
MOZ_ASSERT(NS_IsMainThread());
NS_ENSURE_ARG(aPageURI);
NS_ENSURE_ARG(aCallback);
// Use the default value, may be UINT16_MAX if a default is not set.
if (aPreferredWidth == 0) {
aPreferredWidth = mDefaultIconURIPreferredSize;
}
nsCOMPtr<nsIURI> pageURI = GetExposableURI(aPageURI);
RefPtr<AsyncGetFaviconURLForPage> event =
new AsyncGetFaviconURLForPage(pageURI, aPreferredWidth, aCallback);
RefPtr<Database> DB = Database::GetDatabase();
NS_ENSURE_STATE(DB);
DB->DispatchToAsyncThread(event);
return NS_OK;
}
NS_IMETHODIMP
nsFaviconService::GetFaviconDataForPage(nsIURI* aPageURI,
nsIFaviconDataCallback* aCallback,
uint16_t aPreferredWidth) {
MOZ_ASSERT(NS_IsMainThread());
NS_ENSURE_ARG(aPageURI);
NS_ENSURE_ARG(aCallback);
// Use the default value, may be UINT16_MAX if a default is not set.
if (aPreferredWidth == 0) {
aPreferredWidth = mDefaultIconURIPreferredSize;
}
nsCOMPtr<nsIURI> pageURI = GetExposableURI(aPageURI);
RefPtr<AsyncGetFaviconDataForPage> event =
new AsyncGetFaviconDataForPage(pageURI, aPreferredWidth, aCallback);
RefPtr<Database> DB = Database::GetDatabase();
NS_ENSURE_STATE(DB);
DB->DispatchToAsyncThread(event);
return NS_OK;
}
NS_IMETHODIMP
nsFaviconService::GetFaviconForPage(nsIURI* aPageURI, uint16_t aPreferredWidth,
JSContext* aContext = nullptr,

View File

@@ -120,6 +120,58 @@ interface nsIFaviconService : nsISupports
[optional] in boolean isRichIcon
);
/**
* 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 [optional] aPreferredWidth
* The preferred icon width, skip or pass 0 for the default value,
* set through setDefaultIconURIPreferredSize.
*
* @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);
/**
* Retrieves the favicon URI and data associated to the given page, if any.
* If the page icon is not available, it will try to return the root domain
* icon data, when it's known.
*
* @param aPageURI
* URI of the page whose favicon URI and data 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. 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 [optional] aPreferredWidth
* The preferred icon width, skip or pass 0 for the default value,
* set through setDefaultIconURIPreferredSize.
* @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);
/**
* Retrieves the favicon URI and data URL associated to the given page, if any.
* If the page icon is not available, it will try to return the root domain

View File

@@ -176,22 +176,6 @@ export var PlacesTestUtils = Object.freeze({
);
},
/*
* Helper function to call PlacesUtils.favicons.getFaviconForPage(). This
* function throws an error if the status of
* PlacesUtils.favicons.setFaviconForPage() is not success.
*
* @param {string or URL or nsIURI} pageURI
* @param {Number} [optional] preferredWidth
* @return {Promise<nsIFavicon>} resolved with favicon data
*/
getFaviconForPage(pageURI, preferredWidth = 0) {
return lazy.PlacesUtils.favicons.getFaviconForPage(
lazy.PlacesUtils.toURI(pageURI),
preferredWidth
);
},
/**
* Get favicon data for given URL from database.
*

View File

@@ -33,10 +33,14 @@ function test() {
}
testOnWindow(true, function (win) {
waitForTabLoad(win, async function () {
let favicon = await PlacesTestUtils.getFaviconForPage(pageURI);
is(favicon, null, "No result should be found");
waitForTabLoad(win, function () {
PlacesUtils.favicons.getFaviconURLForPage(
NetUtil.newURI(pageURI),
function (uri) {
is(uri, null, "No result should be found");
finish();
}
);
});
});
}

View File

@@ -357,9 +357,8 @@ add_task(async function test_expire_icons() {
if (entry.icon) {
await PlacesTestUtils.setFaviconForPage(entry.page, entry.icon, dataUrl);
let favicon = await PlacesTestUtils.getFaviconForPage(entry.page);
Assert.equal(
favicon.uri.spec,
await getFaviconUrlForPage(entry.page),
entry.icon,
"Sanity check the icon exists"
);
@@ -395,9 +394,8 @@ add_task(async function test_expire_icons() {
});
}
if (entry.icon) {
let favicon = await PlacesTestUtils.getFaviconForPage(entry.page);
Assert.equal(
favicon.uri.spec,
await getFaviconUrlForPage(entry.page),
entry.icon,
"Sanity check the initial icon value"
);
@@ -411,19 +409,30 @@ add_task(async function test_expire_icons() {
for (let entry of entries) {
Assert.ok(page_in_database(entry.page));
let favicon = await PlacesTestUtils.getFaviconForPage(entry.page);
if (!entry.removed) {
Assert.equal(favicon.uri.spec, entry.icon, entry.desc);
Assert.equal(
await getFaviconUrlForPage(entry.page),
entry.icon,
entry.desc
);
continue;
}
if (entry.root) {
Assert.equal(favicon.uri.spec, entry.root, entry.desc);
Assert.equal(
await getFaviconUrlForPage(entry.page),
entry.root,
entry.desc
);
continue;
}
if (entry.icon) {
await Assert.equal(favicon, null, entry.desc);
await Assert.rejects(
getFaviconUrlForPage(entry.page),
/Unable to find an icon/,
entry.desc
);
continue;
}

View File

@@ -28,18 +28,26 @@ let uniqueFaviconId = 0;
* @param aExpectedData
* Expected icon data, expressed as an array of byte values.
* If set null, skip the test for the favicon data.
* @param aCallback
* This function is called after the check finished.
*/
async function checkFaviconDataForPage(
function checkFaviconDataForPage(
aPageURI,
aExpectedMimeType,
aExpectedData
aExpectedData,
aCallback
) {
let favicon = await PlacesTestUtils.getFaviconForPage(aPageURI);
Assert.equal(aExpectedMimeType, favicon.mimeType);
PlacesUtils.favicons.getFaviconDataForPage(
aPageURI,
async function (aURI, aDataLen, aData, aMimeType) {
Assert.equal(aExpectedMimeType, aMimeType);
if (aExpectedData) {
Assert.ok(compareArrays(aExpectedData, favicon.rawData));
Assert.ok(compareArrays(aExpectedData, aData));
}
await check_guid_for_uri(aPageURI);
aCallback();
}
);
}
/**
@@ -47,10 +55,18 @@ async function checkFaviconDataForPage(
*
* @param aPageURI
* nsIURI object for the page to check.
* @param aCallback
* This function is called after the check finished.
*/
async function checkFaviconMissingForPage(aPageURI) {
let favicon = await PlacesTestUtils.getFaviconForPage(aPageURI);
Assert.ok(!favicon);
function checkFaviconMissingForPage(aPageURI, aCallback) {
PlacesUtils.favicons.getFaviconURLForPage(aPageURI, function (aURI) {
Assert.ok(aURI === null);
aCallback();
});
}
function promiseFaviconMissingForPage(aPageURI) {
return new Promise(resolve => checkFaviconMissingForPage(aPageURI, resolve));
}
function promiseFaviconChanged(aExpectedPageURI, aExpectedFaviconURI) {

View File

@@ -117,12 +117,12 @@ add_task(async function test_copyFavicons() {
);
await promiseChange;
Assert.equal(
(await PlacesTestUtils.getFaviconForPage(TEST_URI2, 1)).uri.spec,
await getFaviconUrlForPage(TEST_URI2, 1),
SMALLPNG_DATA_URI.spec,
"Small icon found"
);
Assert.equal(
(await PlacesTestUtils.getFaviconForPage(TEST_URI2)).uri.spec,
await getFaviconUrlForPage(TEST_URI2),
SMALLSVG_DATA_URI.spec,
"Large icon found"
);
@@ -140,12 +140,12 @@ add_task(async function test_copyFavicons() {
);
await promiseChange;
Assert.equal(
(await PlacesTestUtils.getFaviconForPage(TEST_URI3, 1)).uri.spec,
await getFaviconUrlForPage(TEST_URI3, 1),
SMALLPNG_DATA_URI.spec,
"Small icon found"
);
Assert.equal(
(await PlacesTestUtils.getFaviconForPage(TEST_URI3)).uri.spec,
await getFaviconUrlForPage(TEST_URI3),
SMALLSVG_DATA_URI.spec,
"Large icon found"
);
@@ -181,12 +181,12 @@ add_task(async function test_copyFavicons_overlap() {
);
await promiseChange;
Assert.equal(
(await PlacesTestUtils.getFaviconForPage(TEST_URI2, 1)).uri.spec,
await getFaviconUrlForPage(TEST_URI2, 1),
SMALLPNG_DATA_URI.spec,
"Small icon found"
);
Assert.equal(
(await PlacesTestUtils.getFaviconForPage(TEST_URI2)).uri.spec,
await getFaviconUrlForPage(TEST_URI2),
SMALLSVG_DATA_URI.spec,
"Large icon found"
);

View File

@@ -41,6 +41,6 @@ add_task(async function test_expireAllFavicons() {
await promise;
// Check that the favicons for the pages we added were removed.
await checkFaviconMissingForPage(TEST_PAGE_URI);
await checkFaviconMissingForPage(BOOKMARKED_PAGE_URI);
await promiseFaviconMissingForPage(TEST_PAGE_URI);
await promiseFaviconMissingForPage(BOOKMARKED_PAGE_URI);
});

View File

@@ -28,10 +28,10 @@ add_task(async function test_storing_a_normal_16x16_icon() {
return db.execute(`UPDATE moz_icons SET expire_ms = 0, data = "test"`);
});
let favicon = await PlacesTestUtils.getFaviconForPage(PAGE_URL);
Assert.equal(favicon.mimeType, "image/png");
let { data, mimeType } = await getFaviconDataForPage(PAGE_URL);
Assert.equal(mimeType, "image/png");
Assert.deepEqual(
favicon.rawData,
data,
"test".split("").map(c => c.charCodeAt(0))
);

View File

@@ -50,19 +50,19 @@ add_task(async function test_expire_associated() {
// Only the second and the third icons should have survived.
Assert.equal(
(await PlacesTestUtils.getFaviconForPage(TEST_URL, 16)).uri.spec,
await getFaviconUrlForPage(TEST_URL, 16),
TEST_URL + favicons[1].name,
"Should retrieve the 32px icon, not the 16px one."
);
Assert.equal(
(await PlacesTestUtils.getFaviconForPage(TEST_URL, 64)).uri.spec,
await getFaviconUrlForPage(TEST_URL, 64),
TEST_URL + favicons[2].name,
"Should retrieve the 64px icon"
);
// The expired icon for page 2 should have survived.
Assert.equal(
(await PlacesTestUtils.getFaviconForPage(TEST_URL2, 16)).uri.spec,
await getFaviconUrlForPage(TEST_URL2, 16),
TEST_URL + favicons[0].name,
"Should retrieve the expired 16px icon"
);
@@ -107,7 +107,7 @@ add_task(async function test_expire_root() {
// Only the root icon should have survived.
Assert.equal(
(await PlacesTestUtils.getFaviconForPage(pageURI, 16)).uri.spec,
await getFaviconUrlForPage(pageURI, 16),
rootIconURI.spec,
"Should retrieve the root icon."
);

View File

@@ -53,8 +53,9 @@ async function checkFaviconDataConversion(
fileDataURL
);
await new Promise(resolve => {
if (!aExpectConversion) {
await checkFaviconDataForPage(pageURI, aFileMimeType, fileData);
checkFaviconDataForPage(pageURI, aFileMimeType, fileData, resolve);
} else if (!aVaryOnWindows || !isWindows) {
let allowMissing = AppConstants.USE_LIBZ_RS;
let expectedFile = do_get_file(
@@ -67,11 +68,12 @@ async function checkFaviconDataConversion(
expectedFile = do_get_file("expected-" + aFileName + ".png");
}
let expectedData = readFileData(expectedFile);
await checkFaviconDataForPage(pageURI, "image/png", expectedData);
checkFaviconDataForPage(pageURI, "image/png", expectedData, resolve);
} else {
// Not check the favicon data.
await checkFaviconDataForPage(pageURI, "image/png", null);
checkFaviconDataForPage(pageURI, "image/png", null, resolve);
}
});
}
add_task(async function test_storing_a_normal_16x16_icon() {

View File

@@ -0,0 +1,457 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
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";
const ICON32_URL = "http://places.test/favicon-normal32.png";
const FAVICON16_DATA = readFileData(do_get_file("favicon-normal16.png"));
const ICON16_URL = "http://places.test/favicon-normal16.png";
const FAVICON64_DATA = readFileData(do_get_file("favicon-big64.png"));
const ICON64_URL = "http://places.test/favicon-big64.png";
const SVG_DATA = readFileData(do_get_file("favicon-svg.svg"));
const SVG_URL = "http://example.com/favicon.svg";
const MIMETYPE_PNG = "image/png";
const MIMETYPE_SVG = "image/svg+xml";
add_task(async function test_normal() {
Assert.equal(FAVICON_DATA.length, 344);
let pageURI = NetUtil.newURI("http://example.com/normal");
let dataURL = await PlacesTestUtils.fileDataToDataURL(
FAVICON_DATA,
"image/png"
);
await PlacesTestUtils.addVisits(pageURI);
await PlacesUtils.favicons.setFaviconForPage(pageURI, FAVICON_URI, dataURL);
await new Promise(resolve => {
PlacesUtils.favicons.getFaviconDataForPage(
pageURI,
function (aURI, aDataLen, aData, aMimeType) {
Assert.ok(aURI.equals(FAVICON_URI));
Assert.equal(FAVICON_DATA.length, aDataLen);
Assert.ok(compareArrays(FAVICON_DATA, aData));
Assert.equal(FAVICON_MIMETYPE, aMimeType);
resolve();
}
);
});
});
add_task(async function test_missing() {
let pageURI = NetUtil.newURI("http://example.com/missing");
await new Promise(resolve => {
PlacesUtils.favicons.getFaviconDataForPage(
pageURI,
function (aURI, aDataLen, aData, aMimeType) {
// Check also the expected data types.
Assert.ok(aURI === null);
Assert.ok(aDataLen === 0);
Assert.ok(aData.length === 0);
Assert.ok(aMimeType === "");
resolve();
}
);
});
});
add_task(async function test_fallback() {
const ROOT_URL = "https://www.example.com/";
const ROOT_ICON_URL = ROOT_URL + "favicon.ico";
const SUBPAGE_URL = ROOT_URL + "/missing";
info("Set icon for the root");
await PlacesTestUtils.addVisits(ROOT_URL);
let data = readFileData(do_get_file("favicon-normal16.png"));
let dataURL = await PlacesTestUtils.fileDataToDataURL(data, "image/png");
await PlacesTestUtils.setFaviconForPage(ROOT_URL, ROOT_ICON_URL, dataURL);
info("check fallback icons");
await 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();
}
);
});
await 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();
}
);
});
info("Now add a proper icon for the page");
await PlacesTestUtils.addVisits(SUBPAGE_URL);
let data32 = readFileData(do_get_file("favicon-normal32.png"));
let dataURL32 = await PlacesTestUtils.fileDataToDataURL(data32, "image/png");
await PlacesTestUtils.setFaviconForPage(SUBPAGE_URL, ICON32_URL, dataURL32);
info("check no fallback icons");
await 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();
}
);
});
await 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();
}
);
});
});
add_task(async function test_richIconPrioritizationBelowThreshold() {
await runFaviconTest(
"https://example.com/test_prioritization_below_threshold",
[
{
url: ICON16_URL,
data: FAVICON16_DATA,
isRich: false,
mimetype: MIMETYPE_PNG,
},
{
url: ICON32_URL,
data: FAVICON_DATA,
isRich: true,
mimetype: MIMETYPE_PNG,
},
],
ICON16_URL,
FAVICON16_DATA,
12
);
});
add_task(async function test_richIconPrioritizationAboveThreshold() {
await runFaviconTest(
"https://example.com/test_prioritization_below_threshold",
[
{
url: ICON16_URL,
data: FAVICON16_DATA,
isRich: false,
mimetype: MIMETYPE_PNG,
},
{
url: ICON32_URL,
data: FAVICON_DATA,
isRich: true,
mimetype: MIMETYPE_PNG,
},
],
ICON32_URL,
FAVICON_DATA,
72
);
});
add_task(async function test_sizeSelection() {
// Icons set:
// - 16px non-rich
// - 64px non-rich
// - 32px rich
const testCases = [
// Should select 16px icon, non-rich icons are prioritized for
// preferred size <= 64px, and (24 - 16) = 8 <= (64 - 24) / 4 = 10.
// Therefore smaller icon is selected.
{
expectedURI: ICON16_URL,
expectedData: FAVICON16_DATA,
preferredSize: 24,
},
// Should select 64px icon, non-rich icons are prioritized for
// preferred size <= 64px, and (64 - 32) / 4 = 8 < (32 - 16) = 16.
// Therefore, larger icon is selected.
{
expectedURI: ICON64_URL,
expectedData: FAVICON64_DATA,
preferredSize: 32,
},
// Should select 64px icon, no discrimination between rich/non-rich for
// preferred size > 64px.
{
expectedURI: ICON64_URL,
expectedData: FAVICON64_DATA,
preferredSize: 80,
},
];
for (const { expectedURI, expectedData, preferredSize } of testCases) {
await runFaviconTest(
"https://example.com/test_size_selection",
[
{
url: ICON16_URL,
data: FAVICON16_DATA,
isRich: false,
mimetype: MIMETYPE_PNG,
},
{
url: ICON64_URL,
data: FAVICON64_DATA,
isRich: false,
mimetype: MIMETYPE_PNG,
},
{
url: ICON32_URL,
data: FAVICON_DATA,
isRich: true,
mimetype: MIMETYPE_PNG,
},
],
expectedURI,
expectedData,
preferredSize
);
}
});
add_task(async function test_sizeSelectionRichOnly() {
// Should select 16px icon, since there are no non-rich icons found,
// we return the best-sized rich icon.
await runFaviconTest(
"https://example.com/test_size_selection_rich_only",
[
{
url: ICON16_URL,
data: FAVICON16_DATA,
isRich: true,
mimetype: MIMETYPE_PNG,
},
{
url: ICON64_URL,
data: FAVICON64_DATA,
isRich: true,
mimetype: MIMETYPE_PNG,
},
{
url: ICON32_URL,
data: FAVICON_DATA,
isRich: true,
mimetype: MIMETYPE_PNG,
},
],
ICON16_URL,
FAVICON16_DATA,
17
);
});
add_task(async function test_svg() {
// Selected non-svg is not a perfect fit, so we prefer SVG.
await runFaviconTest(
"https://example.com/test_icon_selection_svg",
[
{
url: SVG_URL,
data: SVG_DATA,
isRich: false,
mimetype: MIMETYPE_SVG,
},
{
url: ICON32_URL,
data: FAVICON_DATA,
isRich: false,
mimetype: MIMETYPE_PNG,
},
],
SVG_URL,
SVG_DATA,
31
);
// Selected non-svg is a perfect fit, so we prefer it.
await runFaviconTest(
"https://example.com/test_icon_selection_svg",
[
{
url: SVG_URL,
data: SVG_DATA,
isRich: false,
mimetype: MIMETYPE_SVG,
},
{
url: ICON32_URL,
data: FAVICON_DATA,
isRich: false,
mimetype: MIMETYPE_PNG,
},
],
ICON32_URL,
FAVICON_DATA,
32
);
// Selected non-svg is a perfect fit, but it is rich so we prefer SVG.
await runFaviconTest(
"https://example.com/test_icon_selection_svg",
[
{
url: SVG_URL,
data: SVG_DATA,
isRich: false,
mimetype: MIMETYPE_SVG,
},
{
url: ICON32_URL,
data: FAVICON_DATA,
isRich: true,
mimetype: MIMETYPE_PNG,
},
],
SVG_URL,
SVG_DATA,
32
);
// Selected non-svg is not a perfect fit, but SVG is rich, so we prefer non-SVG.
await runFaviconTest(
"https://example.com/test_icon_selection_svg",
[
{
url: SVG_URL,
data: SVG_DATA,
isRich: true,
mimetype: MIMETYPE_SVG,
},
{
url: ICON32_URL,
data: FAVICON_DATA,
isRich: false,
mimetype: MIMETYPE_PNG,
},
],
ICON32_URL,
FAVICON_DATA,
31
);
// Selected non-SVG is a perfect fit and it is rich, and SVG is also rich,
// so we prefer the original non-SVG selection.
await runFaviconTest(
"https://example.com/test_icon_selection_svg",
[
{
url: SVG_URL,
data: SVG_DATA,
isRich: true,
mimetype: MIMETYPE_SVG,
},
{
url: ICON32_URL,
data: FAVICON_DATA,
isRich: true,
mimetype: MIMETYPE_PNG,
},
],
ICON32_URL,
FAVICON_DATA,
32
);
// When requested size is above threshold we have no preference when it comes to richness.
await runFaviconTest(
"https://example.com/test_icon_selection_svg",
[
{
url: SVG_URL,
data: SVG_DATA,
isRich: true,
mimetype: MIMETYPE_SVG,
},
{
url: ICON64_URL,
data: FAVICON64_DATA,
isRich: false,
mimetype: MIMETYPE_PNG,
},
],
SVG_URL,
SVG_DATA,
65
);
// Prefer non-rich SVG when requested size is below threshold.
await runFaviconTest(
"https://example.com/test_icon_selection_svg",
[
{
url: SVG_URL + "#2",
data: SVG_DATA,
isRich: true,
mimetype: MIMETYPE_SVG,
},
{
url: SVG_URL,
data: SVG_DATA,
isRich: false,
mimetype: MIMETYPE_SVG,
},
],
SVG_URL,
SVG_DATA,
32
);
});
async function runFaviconTest(
PAGE_URL,
iconData,
expectedURI,
expectedData,
preferredSize
) {
await PlacesTestUtils.clearFavicons();
await PlacesTestUtils.addVisits(PAGE_URL);
for (const { url, data, isRich, mimetype } of iconData) {
const dataURL = await PlacesTestUtils.fileDataToDataURL(data, mimetype);
await PlacesTestUtils.setFaviconForPage(PAGE_URL, url, dataURL, 0, isRich);
}
await new Promise(resolve => {
PlacesUtils.favicons.getFaviconDataForPage(
NetUtil.newURI(PAGE_URL),
(aURI, aDataLen, aData) => {
Assert.equal(aURI.spec, expectedURI);
Assert.equal(aDataLen, expectedData.length);
Assert.deepEqual(aData, expectedData);
resolve();
},
preferredSize
);
});
}

View File

@@ -28,7 +28,7 @@ add_task(async function test_basic() {
await IOUtils.remove(favicon.file.path);
});
add_task(async function test_svg_basic() {
add_task(async function test_svg() {
const favicon = "<svg><rect width='1px' height='1px'/></svg>";
const pageURI = uri("http://example.com/");
@@ -96,157 +96,7 @@ async function doTestGetFaviconForPage({
await PlacesTestUtils.clearFavicons();
}
add_task(async function test_fallback() {
const rootPageURI = uri("http://example.com/");
const rootFaviconURI = uri("http://example.com/favicon.ico");
const favicon16File = do_get_file("favicon-normal16.png");
const favicon16DataURI = await createDataURLForFavicon({
data: readFileData(favicon16File),
mimeType: "image/png",
});
info("Set icon for the root");
await PlacesTestUtils.addVisits(rootPageURI);
await PlacesUtils.favicons.setFaviconForPage(
rootPageURI,
rootFaviconURI,
favicon16DataURI
);
info("Check fallback icons");
const subPageURI = uri("http://example.com/missing");
Assert.equal(
(await PlacesUtils.favicons.getFaviconForPage(subPageURI)).uri.spec,
rootFaviconURI.spec
);
info("Now add a new icon for the subpage");
await PlacesTestUtils.addVisits(subPageURI);
const subFaviconURI = uri("http://example.com/favicon.png");
const favicon32File = do_get_file("favicon-normal32.png");
const favicon32DataURI = await createDataURLForFavicon({
data: readFileData(favicon32File),
mimeType: "image/png",
});
await PlacesTestUtils.setFaviconForPage(
subPageURI,
subFaviconURI,
favicon32DataURI
);
info("Check no fallback icons");
Assert.equal(
(await PlacesUtils.favicons.getFaviconForPage(rootPageURI)).uri.spec,
rootFaviconURI.spec
);
Assert.equal(
(await PlacesUtils.favicons.getFaviconForPage(subPageURI)).uri.spec,
subFaviconURI.spec
);
await PlacesUtils.history.clear();
await PlacesTestUtils.clearFavicons();
});
add_task(async function test_fallback_no_root_icon() {
const rootPageURI = uri("http://example.com/");
const subPageURIs = [
uri("http://example.com/page"),
uri("http://example.com/about"),
uri("http://example.com/home"),
];
const favicon32File = do_get_file("favicon-normal32.png");
const favicon32DataURI = await createDataURLForFavicon({
data: readFileData(favicon32File),
mimeType: "image/png",
});
for (let i = 0; i < 10; i++) {
await PlacesTestUtils.addVisits(subPageURIs[0]);
}
await PlacesTestUtils.addVisits(subPageURIs[1]);
await PlacesTestUtils.addVisits(subPageURIs[2]);
for (let uri of subPageURIs) {
await PlacesTestUtils.setFaviconForPage(
uri,
uri.spec + "/favicon.ico",
favicon32DataURI
);
}
await PlacesTestUtils.addVisits(rootPageURI);
Assert.equal(
(await PlacesUtils.favicons.getFaviconForPage(rootPageURI)).uri.spec,
subPageURIs[0].spec + "/favicon.ico",
"No root icon, should use icon from most frecent subpage"
);
});
add_task(async function test_fallback_no_root_icon_with_port() {
const pageURI1 = uri("http://example.com:3000");
const subPageURI1 = uri("http://example.com:3000/subpage");
const subFaviconURI1 = uri("http://example.com:3000/subpage/favicon.ico");
const pageURI2 = uri("http://example.com:5000");
const subPageURI2 = uri("http://example.com:5000/subpage");
const subFaviconURI2 = uri("http://example.com:5000/subpage/favicon.ico");
const favicon32File = do_get_file("favicon-normal32.png");
const favicon32DataURI = await createDataURLForFavicon({
data: readFileData(favicon32File),
mimeType: "image/png",
});
await PlacesTestUtils.addVisits(subPageURI1);
await PlacesTestUtils.addVisits(Array(10).fill(subPageURI2));
await PlacesTestUtils.setFaviconForPage(
subPageURI1,
subFaviconURI1,
favicon32DataURI
);
await PlacesTestUtils.addVisits(subPageURI1);
await PlacesTestUtils.addVisits(subPageURI2);
Assert.equal(
(await PlacesUtils.favicons.getFaviconForPage(pageURI1)).uri.spec,
subFaviconURI1.spec,
"No root icon, should use icon from most frecent subpage"
);
Assert.equal(
await PlacesUtils.favicons.getFaviconForPage(pageURI2),
null,
"Should return null since no icons exist for root or its subpages"
);
await PlacesTestUtils.setFaviconForPage(
subPageURI2,
subFaviconURI2,
favicon32DataURI
);
await PlacesTestUtils.addVisits("http://localhost:5000/other_subpage");
await PlacesTestUtils.setFaviconForPage(
"http://localhost:5000/other_subpage",
"http://localhost:5000/other_subpage/favicon.ico",
favicon32DataURI
);
Assert.equal(
(await PlacesUtils.favicons.getFaviconForPage(subPageURI2)).uri.spec,
subFaviconURI2.spec,
"No root icon, should use icon from most frecent subpage"
);
await PlacesUtils.history.clear();
await PlacesTestUtils.clearFavicons();
});
add_task(async function test_rich_priority_below_threshold() {
add_task(async function test_rich_priority() {
const pageURI = uri("http://example.com/");
await PlacesTestUtils.addVisits(pageURI);
@@ -332,423 +182,6 @@ add_task(async function test_rich_priority_above_threshold() {
await PlacesTestUtils.clearFavicons();
});
add_task(async function test_size_selection() {
const pageURI = uri("http://example.com/");
await PlacesTestUtils.addVisits(pageURI);
// Icons set:
// - 16px non-rich
// - 32px rich
// - 64px non-rich
const favicon16File = do_get_file("favicon-normal16.png");
const favicon16URI = uri("http://example.com/favicon16.png");
const favicon16DataURI = await createDataURLForFavicon({
data: readFileData(favicon16File),
mimeType: "image/png",
});
const favicon32File = do_get_file("favicon-normal32.png");
const favicon32URI = uri("http://example.com/favicon32.png");
const favicon32DataURI = await createDataURLForFavicon({
data: readFileData(favicon32File),
mimeType: "image/png",
});
const favicon64File = do_get_file("favicon-big64.png");
const favicon64URI = uri("http://example.com/favicon64.png");
const favicon64DataURI = await createDataURLForFavicon({
data: readFileData(favicon64File),
mimeType: "image/png",
});
await PlacesUtils.favicons.setFaviconForPage(
pageURI,
favicon16URI,
favicon16DataURI,
0,
false // Non-rich
);
await PlacesUtils.favicons.setFaviconForPage(
pageURI,
favicon32URI,
favicon32DataURI,
0,
true // Rich
);
await PlacesUtils.favicons.setFaviconForPage(
pageURI,
favicon64URI,
favicon64DataURI,
0,
false // Non-rich
);
// Should select 16px icon, non-rich icons are prioritized for preferred
// size <= 64px, and (24 - 16) = 8 <= (64 - 24) / 4 = 10.
// Therefore smaller icon is selected.
const faviconFor24 = await PlacesUtils.favicons.getFaviconForPage(
pageURI,
24
);
Assert.equal(faviconFor24.uri.spec, favicon16URI.spec);
Assert.equal(faviconFor24.dataURI.spec, favicon16DataURI.spec);
Assert.equal(faviconFor24.mimeType, "image/png");
Assert.equal(faviconFor24.width, 16);
// Should select 64px icon, non-rich icons are prioritized for preferred
// size <= 64px, and (64 - 32) / 4 = 8 < (32 - 16) = 16.
// Therefore, larger icon is selected.
const faviconFor32 = await PlacesUtils.favicons.getFaviconForPage(
pageURI,
32
);
Assert.equal(faviconFor32.uri.spec, favicon64URI.spec);
Assert.equal(faviconFor32.dataURI.spec, favicon64DataURI.spec);
Assert.equal(faviconFor32.mimeType, "image/png");
Assert.equal(faviconFor32.width, 64);
// Should select 64px icon, no discrimination between rich/non-rich for
// preferred size > 64px.
const faviconFor80 = await PlacesUtils.favicons.getFaviconForPage(
pageURI,
80
);
Assert.equal(faviconFor80.uri.spec, favicon64URI.spec);
Assert.equal(faviconFor80.dataURI.spec, favicon64DataURI.spec);
Assert.equal(faviconFor80.mimeType, "image/png");
Assert.equal(faviconFor80.width, 64);
await PlacesUtils.history.clear();
await PlacesTestUtils.clearFavicons();
});
add_task(async function test_size_selection_rich_only() {
const pageURI = uri("http://example.com/");
await PlacesTestUtils.addVisits(pageURI);
const favicon16File = do_get_file("favicon-normal16.png");
const favicon16URI = uri("http://example.com/favicon16.png");
const favicon16DataURI = await createDataURLForFavicon({
data: readFileData(favicon16File),
mimeType: "image/png",
});
const favicon32File = do_get_file("favicon-normal32.png");
const favicon32URI = uri("http://example.com/favicon32.png");
const favicon32DataURI = await createDataURLForFavicon({
data: readFileData(favicon32File),
mimeType: "image/png",
});
const favicon64File = do_get_file("favicon-big64.png");
const favicon64URI = uri("http://example.com/favicon64.png");
const favicon64DataURI = await createDataURLForFavicon({
data: readFileData(favicon64File),
mimeType: "image/png",
});
await PlacesUtils.favicons.setFaviconForPage(
pageURI,
favicon16URI,
favicon16DataURI,
0,
true
);
await PlacesUtils.favicons.setFaviconForPage(
pageURI,
favicon32URI,
favicon32DataURI,
0,
true
);
await PlacesUtils.favicons.setFaviconForPage(
pageURI,
favicon64URI,
favicon64DataURI,
0,
true
);
// Should select 16px icon, since there are no non-rich icons found,
// we return the best-sized rich icon.
const faviconFor17 = await PlacesUtils.favicons.getFaviconForPage(
pageURI,
17
);
Assert.equal(faviconFor17.uri.spec, favicon16URI.spec);
Assert.equal(faviconFor17.dataURI.spec, favicon16DataURI.spec);
Assert.equal(faviconFor17.mimeType, "image/png");
Assert.equal(faviconFor17.width, 16);
await PlacesUtils.history.clear();
await PlacesTestUtils.clearFavicons();
});
add_task(async function test_svg_selection() {
const pageURI = uri("http://example.com/");
// SVG
const faviconSVG = "<svg><rect width='1px' height='1px'/></svg>";
const faviconSVGURI = uri("http://example.com/favicon.svg");
const faviconSVGDataURI = uri(`data:image/svg+xml;utf8,${faviconSVG}`);
const faviconSVGDataURIBase64 = uri(
`data:image/svg+xml;base64,${base64EncodeString(faviconSVG)}`
);
// 32px png
const favicon32File = do_get_file("favicon-normal32.png");
const favicon32URI = uri("http://example.com/favicon32.png");
const favicon32DataURI = await createDataURLForFavicon({
data: readFileData(favicon32File),
mimeType: "image/png",
});
// 64px png
const favicon64File = do_get_file("favicon-big64.png");
const favicon64URI = uri("http://example.com/favicon64.png");
const favicon64DataURI = await createDataURLForFavicon({
data: readFileData(favicon64File),
mimeType: "image/png",
});
info("Selected non-svg is not a perfect fit, so we prefer SVG");
await doTestSVGSelection({
pageURI,
favicon1: {
uri: faviconSVGURI,
dataURI: faviconSVGDataURI,
isRich: false,
},
favicon2: {
uri: favicon32URI,
dataURI: favicon32DataURI,
isRich: false,
},
preferredSize: 31,
expected: {
uri: faviconSVGURI,
dataURI: faviconSVGDataURIBase64,
mimeType: "image/svg+xml",
width: 65535,
},
});
info("Selected non-svg is a perfect fit, so we prefer it");
await doTestSVGSelection({
pageURI,
favicon1: {
uri: faviconSVGURI,
dataURI: faviconSVGDataURI,
isRich: false,
},
favicon2: {
uri: favicon32URI,
dataURI: favicon32DataURI,
isRich: false,
},
preferredSize: 32,
expected: {
uri: favicon32URI,
dataURI: favicon32DataURI,
mimeType: "image/png",
width: 32,
},
});
info("Selected non-svg is a perfect fit, but it is rich so we prefer SVG");
await doTestSVGSelection({
pageURI,
favicon1: {
uri: faviconSVGURI,
dataURI: faviconSVGDataURI,
isRich: false,
},
favicon2: {
uri: favicon32URI,
dataURI: favicon32DataURI,
isRich: true,
},
preferredSize: 32,
expected: {
uri: faviconSVGURI,
dataURI: faviconSVGDataURIBase64,
mimeType: "image/svg+xml",
width: 65535,
},
});
info(
"Selected non-svg is not a perfect fit, but SVG is rich, so we prefer non-SVG"
);
await doTestSVGSelection({
pageURI,
favicon1: {
uri: faviconSVGURI,
dataURI: faviconSVGDataURI,
isRich: true,
},
favicon2: {
uri: favicon32URI,
dataURI: favicon32DataURI,
isRich: false,
},
preferredSize: 31,
expected: {
uri: favicon32URI,
dataURI: favicon32DataURI,
mimeType: "image/png",
width: 32,
},
});
info(
"Selected non-SVG is a perfect fit and it is rich, and SVG is also rich, so we prefer the original non-SVG selection"
);
await doTestSVGSelection({
pageURI,
favicon1: {
uri: faviconSVGURI,
dataURI: faviconSVGDataURI,
isRich: true,
},
favicon2: {
uri: favicon32URI,
dataURI: favicon32DataURI,
isRich: true,
},
preferredSize: 32,
expected: {
uri: favicon32URI,
dataURI: favicon32DataURI,
mimeType: "image/png",
width: 32,
},
});
info(
"When requested size is above threshold we have no preference when it comes to richness"
);
await doTestSVGSelection({
pageURI,
favicon1: {
uri: faviconSVGURI,
dataURI: faviconSVGDataURI,
isRich: true,
},
favicon2: {
uri: favicon64URI,
dataURI: favicon64DataURI,
isRich: false,
},
preferredSize: 65,
expected: {
uri: faviconSVGURI,
dataURI: faviconSVGDataURIBase64,
mimeType: "image/svg+xml",
width: 65535,
},
});
info("Prefer non-rich SVG when requested size is below threshold");
await doTestSVGSelection({
pageURI,
favicon1: {
uri: uri("http://example.com/favicon.svg#2"),
dataURI: faviconSVGDataURI,
isRich: true,
},
favicon2: {
uri: faviconSVGURI,
dataURI: faviconSVGDataURI,
isRich: false,
},
preferredSize: 32,
expected: {
uri: faviconSVGURI,
dataURI: faviconSVGDataURIBase64,
mimeType: "image/svg+xml",
width: 65535,
},
});
});
async function doTestSVGSelection({
pageURI,
favicon1,
favicon2,
preferredSize,
expected,
}) {
await PlacesTestUtils.addVisits(pageURI);
await PlacesUtils.favicons.setFaviconForPage(
pageURI,
favicon1.uri,
favicon1.dataURI,
0,
favicon1.isRich
);
await PlacesUtils.favicons.setFaviconForPage(
pageURI,
favicon2.uri,
favicon2.dataURI,
0,
favicon2.isRich
);
const result = await PlacesUtils.favicons.getFaviconForPage(
pageURI,
preferredSize
);
Assert.equal(result.uri.spec, expected.uri.spec);
Assert.equal(result.dataURI.spec, expected.dataURI.spec);
Assert.equal(result.mimeType, expected.mimeType);
Assert.equal(result.width, expected.width);
await PlacesUtils.history.clear();
await PlacesTestUtils.clearFavicons();
}
add_task(async function test_port() {
const pageURI = uri("http://example.com/");
const faviconURI = uri("http://example.com/favicon.ico");
const faviconFile = do_get_file("favicon-normal32.png");
const faviconDataURI = await createDataURLForFavicon({
data: readFileData(faviconFile),
mimeType: "image/png",
});
const pageURIWithPort = uri("http://example.com:5000/");
const faviconURIWithPort = uri("http://example.com:5000/favicon.ico");
const faviconFileWithPort = do_get_file("favicon-normal16.png");
const faviconDataURIWithPort = await createDataURLForFavicon({
data: readFileData(faviconFileWithPort),
mimeType: "image/png",
});
info("Set icon for the URL with the port");
await PlacesTestUtils.addVisits(pageURIWithPort);
await PlacesTestUtils.setFaviconForPage(
pageURIWithPort,
faviconURIWithPort,
faviconDataURIWithPort
);
Assert.equal(
(await PlacesUtils.favicons.getFaviconForPage(pageURIWithPort)).uri.spec,
faviconURIWithPort.spec,
"The favicon of the URL with the port should be chosen"
);
info("Set icon for the URL without the port");
await PlacesTestUtils.addVisits(pageURI);
await PlacesTestUtils.setFaviconForPage(pageURI, faviconURI, faviconDataURI);
Assert.equal(
(await PlacesUtils.favicons.getFaviconForPage(pageURIWithPort)).uri.spec,
faviconURIWithPort.spec,
"The favicon of the URL with the port should still be chosen when both are defined"
);
Assert.equal(
(await PlacesUtils.favicons.getFaviconForPage(pageURI)).uri.spec,
faviconURI.spec,
"The favicon of the URL without the port should be chosen correctly when there is an icon defined for the url with a port"
);
await PlacesUtils.history.clear();
await PlacesTestUtils.clearFavicons();
});
add_task(async function test_no_favicon() {
const pageURI = uri("http://example.com/");
const result = await PlacesUtils.favicons.getFaviconForPage(pageURI);

View File

@@ -0,0 +1,237 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const ICON32_URL = "http://places.test/favicon-normal32.png";
add_task(async function test_normal() {
let pageURI = NetUtil.newURI("http://example.com/normal");
await PlacesTestUtils.addVisits(pageURI);
await PlacesTestUtils.setFaviconForPage(
pageURI,
SMALLPNG_DATA_URI,
SMALLPNG_DATA_URI
);
await new Promise(resolve => {
PlacesUtils.favicons.getFaviconDataForPage(
pageURI,
function (aURI, aDataLen, aData, aMimeType) {
Assert.ok(aURI.equals(SMALLPNG_DATA_URI));
// Check also the expected data types.
Assert.ok(aDataLen !== 0);
Assert.ok(aData.length !== 0);
Assert.ok(aMimeType === "image/png");
resolve();
}
);
});
});
add_task(async function test_missing() {
PlacesTestUtils.clearFavicons();
let pageURI = NetUtil.newURI("http://example.com/missing");
await new Promise(resolve => {
PlacesUtils.favicons.getFaviconURLForPage(
pageURI,
function (aURI, aDataLen, aData, aMimeType) {
// Check also the expected data types.
Assert.ok(aURI === null);
Assert.ok(aDataLen === 0);
Assert.ok(aData.length === 0);
Assert.ok(aMimeType === "");
resolve();
}
);
});
});
add_task(async function test_fallback() {
const ROOT_URL = "https://www.example.com/";
const ROOT_ICON_URL = ROOT_URL + "favicon.ico";
const SUBPAGE_URL = ROOT_URL + "/missing";
info("Set icon for the root");
await PlacesTestUtils.addVisits(ROOT_URL);
let dataURL = await readFileDataAsDataURL(
do_get_file("favicon-normal16.png"),
"image/png"
);
await PlacesTestUtils.setFaviconForPage(ROOT_URL, ROOT_ICON_URL, dataURL);
info("check fallback icons");
Assert.equal(
await getFaviconUrlForPage(ROOT_URL),
ROOT_ICON_URL,
"The root should have its favicon"
);
Assert.equal(
await getFaviconUrlForPage(SUBPAGE_URL),
ROOT_ICON_URL,
"The page should fallback to the root icon"
);
info("Now add a proper icon for the page");
await PlacesTestUtils.addVisits(SUBPAGE_URL);
let dataURL32 = await readFileDataAsDataURL(
do_get_file("favicon-normal32.png"),
"image/png"
);
await PlacesTestUtils.setFaviconForPage(SUBPAGE_URL, ICON32_URL, dataURL32);
info("check no fallback icons");
Assert.equal(
await getFaviconUrlForPage(ROOT_URL),
ROOT_ICON_URL,
"The root should still have its favicon"
);
Assert.equal(
await getFaviconUrlForPage(SUBPAGE_URL),
ICON32_URL,
"The page should also have its icon"
);
});
add_task(async function test_URIsWithPort() {
const URL_WITH_PORT = "https://www.example.com:5000/";
const ICON_URL = URL_WITH_PORT + "favicon.ico";
const URL_WITHOUT_PORT = "https://www.example.com/";
const ICON_URL_NO_PORT = URL_WITHOUT_PORT + "favicon.ico";
info("Set icon for the URL with the port");
await PlacesTestUtils.addVisits(URL_WITH_PORT);
let dataURL = await readFileDataAsDataURL(
do_get_file("favicon-normal16.png"),
"image/png"
);
await PlacesTestUtils.setFaviconForPage(URL_WITH_PORT, ICON_URL, dataURL);
Assert.equal(
await getFaviconUrlForPage(URL_WITH_PORT),
ICON_URL,
"The favicon of the URL with the port should be chosen"
);
info("Set icon for the URL without the port");
await PlacesTestUtils.addVisits(URL_WITHOUT_PORT);
let dataURL32 = await readFileDataAsDataURL(
do_get_file("favicon-normal32.png"),
"image/png"
);
await PlacesTestUtils.setFaviconForPage(
URL_WITHOUT_PORT,
ICON_URL_NO_PORT,
dataURL32
);
Assert.equal(
await getFaviconUrlForPage(URL_WITH_PORT),
ICON_URL,
"The favicon of the URL with the port should still be chosen when both are defined"
);
Assert.equal(
await getFaviconUrlForPage(URL_WITHOUT_PORT),
ICON_URL_NO_PORT,
"The favicon of the URL without the port should be chosen correctly when there is an icon defined for the url with a port"
);
});
add_task(async function test_noRootIconFallback() {
await PlacesTestUtils.clearFavicons();
const ROOT_URL = "http://test.com";
const SUBPAGE_URLS = [
ROOT_URL + "/page",
ROOT_URL + "/about",
ROOT_URL + "/home",
];
for (let i = 0; i < 10; i++) {
await PlacesTestUtils.addVisits(SUBPAGE_URLS[0]);
}
await PlacesTestUtils.addVisits(SUBPAGE_URLS[1]);
await PlacesTestUtils.addVisits(SUBPAGE_URLS[2]);
let dataURL32 = await readFileDataAsDataURL(
do_get_file("favicon-normal32.png"),
"image/png"
);
for (let url of SUBPAGE_URLS) {
await PlacesTestUtils.setFaviconForPage(
url,
url + "/favicon.ico",
dataURL32
);
}
await PlacesTestUtils.addVisits(ROOT_URL);
Assert.equal(
await getFaviconUrlForPage(ROOT_URL),
SUBPAGE_URLS[0] + "/favicon.ico",
"No root icon, should use icon from most frecent subpage"
);
});
add_task(async function test_noRootIconFallbackURIsWithPort() {
await PlacesTestUtils.clearFavicons();
const TEST_URI = "http://localhost:3000";
const TEST_URI_2 = "http://localhost:5000";
const TEST_URI_SUBPAGE = "http://localhost:3000/subpage";
const TEST_URI_2_SUBPAGE = "http://localhost:5000/subpage";
await PlacesTestUtils.addVisits(TEST_URI_SUBPAGE);
await PlacesTestUtils.addVisits(Array(10).fill(TEST_URI_2_SUBPAGE));
let dataURL32 = await readFileDataAsDataURL(
do_get_file("favicon-normal32.png"),
"image/png"
);
await PlacesTestUtils.setFaviconForPage(
TEST_URI_SUBPAGE,
TEST_URI_SUBPAGE + "/favicon.ico",
dataURL32
);
await PlacesTestUtils.addVisits(TEST_URI);
await PlacesTestUtils.addVisits(TEST_URI_2);
Assert.equal(
await getFaviconUrlForPage(TEST_URI),
TEST_URI_SUBPAGE + "/favicon.ico",
"No root icon, should use icon from most frecent subpage"
);
try {
await getFaviconUrlForPage(TEST_URI_2);
Assert.ok(false, "No root icon for root or root subpages");
} catch (error) {
Assert.ok(
true,
"Should return error since no icons exist for root or its subpages"
);
}
await PlacesTestUtils.setFaviconForPage(
TEST_URI_2_SUBPAGE,
TEST_URI_2_SUBPAGE + "/favicon.ico",
dataURL32
);
await PlacesTestUtils.addVisits("http://localhost:5000/other_subpage");
await PlacesTestUtils.setFaviconForPage(
"http://localhost:5000/other_subpage",
"http://localhost:5000/other_subpage/favicon.ico",
dataURL32
);
Assert.equal(
await getFaviconUrlForPage(TEST_URI_2),
TEST_URI_2_SUBPAGE + "/favicon.ico",
"No root icon, should use icon from most frecent subpage"
);
});

View File

@@ -32,7 +32,7 @@ add_task(async function () {
);
await PlacesTestUtils.setFaviconForPage(pageURI.spec, icon.uri.spec, dataURI);
Assert.equal(
(await PlacesTestUtils.getFaviconForPage(pageURI)).uri.spec,
await getFaviconUrlForPage(pageURI),
icon.uri.spec,
"A resampled version of the icon should be stored"
);

View File

@@ -38,9 +38,9 @@ add_task(async function () {
let data = readFileData(file);
info("Check getFaviconDataForPage");
let icon = await PlacesTestUtils.getFaviconForPage(pageURI, size);
let icon = await getFaviconDataForPage(pageURI, size);
Assert.equal(icon.mimeType, "image/png");
Assert.deepEqual(icon.rawData, data);
Assert.deepEqual(icon.data, data);
info("Check cached-favicon protocol");
await compareFavicons(

View File

@@ -16,16 +16,9 @@ add_task(async function () {
);
// Sanity checks.
Assert.equal(await getFaviconUrlForPage(pageURI), faviconURI.spec);
Assert.equal(
(await PlacesTestUtils.getFaviconForPage(pageURI)).uri.spec,
faviconURI.spec
);
Assert.equal(
(
await PlacesTestUtils.getFaviconForPage(
"https://places.test/somethingelse/"
)
).uri.spec,
await getFaviconUrlForPage("https://places.test/somethingelse/"),
faviconURI.spec
);
@@ -49,10 +42,7 @@ add_task(async function () {
await PlacesUtils.history.remove(pageURI);
// Still works since the icon has not been removed.
Assert.equal(
(await PlacesTestUtils.getFaviconForPage(pageURI)).uri.spec,
faviconURI.spec
);
Assert.equal(await getFaviconUrlForPage(pageURI), faviconURI.spec);
// Remove all the pages for the given domain.
await PlacesUtils.history.remove("http://places.test/page2/");
@@ -93,17 +83,17 @@ add_task(async function test_removePagesByTimeframe() {
// Sanity checks.
Assert.equal(
(await PlacesTestUtils.getFaviconForPage(pageURI)).uri.spec,
await getFaviconUrlForPage(pageURI),
faviconURI.spec,
"Should get the biggest icon"
);
Assert.equal(
(await PlacesTestUtils.getFaviconForPage(pageURI, 1)).uri.spec,
await getFaviconUrlForPage(pageURI, 1),
rootIconURI.spec,
"Should get the smallest icon"
);
Assert.equal(
(await PlacesTestUtils.getFaviconForPage(oldPageURI)).uri.spec,
await getFaviconUrlForPage(oldPageURI),
rootIconURI.spec,
"Should get the root icon"
);
@@ -152,7 +142,7 @@ add_task(async function test_different_host() {
);
Assert.equal(
(await PlacesTestUtils.getFaviconForPage(pageURI)).uri.spec,
await getFaviconUrlForPage(pageURI),
faviconURI.spec,
"Should get the png icon"
);
@@ -189,7 +179,7 @@ add_task(async function test_same_size() {
);
Assert.equal(
(await PlacesTestUtils.getFaviconForPage(pageURI, 20)).uri.spec,
await getFaviconUrlForPage(pageURI, 20),
faviconURI.spec,
"Should get the non-root icon"
);
@@ -221,7 +211,7 @@ add_task(async function test_root_on_different_host() {
await PlacesTestUtils.setFaviconForPage(pageURI1, iconURI, SMALLPNG_DATA_URI);
Assert.equal(await getRootValue(ICON_URL), 1, "Check root == 1");
Assert.equal(
(await PlacesTestUtils.getFaviconForPage(pageURI1, 16)).uri.spec,
await getFaviconUrlForPage(pageURI1, 16),
ICON_URL,
"The icon should been found"
);
@@ -230,7 +220,7 @@ add_task(async function test_root_on_different_host() {
await PlacesTestUtils.setFaviconForPage(pageURI2, iconURI, SMALLPNG_DATA_URI);
Assert.equal(await getRootValue(ICON_URL), 1, "Check root == 1");
Assert.equal(
(await PlacesTestUtils.getFaviconForPage(pageURI2, 16)).uri.spec,
await getFaviconUrlForPage(pageURI2, 16),
ICON_URL,
"The icon should be found"
);
@@ -238,7 +228,7 @@ add_task(async function test_root_on_different_host() {
await PlacesUtils.history.remove(pageURI1);
Assert.equal(
(await PlacesTestUtils.getFaviconForPage(pageURI2, 16)).uri.spec,
await getFaviconUrlForPage(pageURI2, 16),
ICON_URL,
"The icon should not have been removed"
);

View File

@@ -57,11 +57,21 @@ add_task(async function test_replaceExisting() {
firstFaviconDataURL
);
await checkFaviconDataForPage(
await new Promise(resolve => {
PlacesUtils.favicons.getFaviconDataForPage(
pageURI,
function (aURI, aDataLen, aData, aMimeType) {
Assert.equal(aMimeType, firstFavicon.mimeType);
Assert.ok(compareArrays(aData, firstFavicon.data));
checkFaviconDataForPage(
pageURI,
firstFavicon.mimeType,
firstFavicon.data
firstFavicon.data,
resolve
);
}
);
});
await doTestSetFaviconForPage({
pageURI,
@@ -312,14 +322,8 @@ add_task(async function test_sameHostRedirect() {
await promise;
// The favicon should be set also on the bookmarked url that redirected.
let favicon = await PlacesUtils.favicons.getFaviconForPage(
PlacesUtils.toURI(srcUrl)
);
Assert.equal(
favicon.rawData.length,
SMALLPNG_DATA_LEN,
"Check favicon dataLen"
);
let { dataLen } = await PlacesUtils.promiseFaviconData(srcUrl);
Assert.equal(dataLen, SMALLPNG_DATA_LEN, "Check favicon dataLen");
await PlacesUtils.bookmarks.eraseEverything();
await PlacesUtils.history.clear();
@@ -379,11 +383,14 @@ async function doTestSetFaviconForPage({
info("Check the result of setFaviconForPage");
Assert.equal(result, null, "If succeeded, the promise has no data");
await checkFaviconDataForPage(
await new Promise(resolve => {
checkFaviconDataForPage(
pageURI,
expectedFaviconMimeType,
expectedFaviconData
expectedFaviconData,
resolve
);
});
}
add_task(async function test_incorrectMimeTypeDataURI() {

View File

@@ -14,12 +14,24 @@ add_task(async function () {
SMALLSVG_DATA_URI
);
let favicon = await PlacesTestUtils.getFaviconForPage(PAGEURI);
await new Promise(resolve => {
PlacesUtils.favicons.getFaviconDataForPage(
PAGEURI,
function (aURI, aDataLen, aData, aMimeType) {
Assert.equal(
favicon.uri.spec,
aURI.spec,
SMALLSVG_DATA_URI.spec,
"setFavicon aURI check"
);
Assert.equal(favicon.rawData.length, 263, "setFavicon aDataLen check");
Assert.equal(favicon.mimeType, "image/svg+xml", "setFavicon aMimeType check");
Assert.equal(aDataLen, 263, "setFavicon aDataLen check");
Assert.equal(aMimeType, "image/svg+xml", "setFavicon aMimeType check");
resolve();
}
);
});
let data = await PlacesUtils.promiseFaviconData(PAGEURI.spec);
equal(data.uri.spec, SMALLSVG_DATA_URI.spec, "getFavicon aURI check");
equal(data.dataLen, 263, "getFavicon aDataLen check");
equal(data.mimeType, "image/svg+xml", "getFavicon aMimeType check");
});

View File

@@ -52,10 +52,14 @@ support-files = [
["test_favicons_protocols_ref.js"]
["test_getFaviconDataForPage.js"]
["test_getFaviconForPage.js"]
["test_getFaviconLinkForIcon.js"]
["test_getFaviconURLForPage.js"]
["test_heavy_favicon.js"]
["test_incremental_vacuum.js"]

View File

@@ -734,6 +734,38 @@ function sortBy(array, prop) {
return array.sort((a, b) => compareAscending(a[prop], b[prop]));
}
function getFaviconUrlForPage(page, width = 0) {
let pageURI =
page instanceof Ci.nsIURI ? page : NetUtil.newURI(new URL(page).href);
return new Promise((resolve, reject) => {
PlacesUtils.favicons.getFaviconURLForPage(
pageURI,
iconURI => {
if (iconURI) {
resolve(iconURI.spec);
} else {
reject("Unable to find an icon for " + pageURI.spec);
}
},
width
);
});
}
function getFaviconDataForPage(page, width = 0) {
let pageURI =
page instanceof Ci.nsIURI ? page : NetUtil.newURI(new URL(page).href);
return new Promise(resolve => {
PlacesUtils.favicons.getFaviconDataForPage(
pageURI,
(iconUri, len, data, mimeType) => {
resolve({ data, mimeType });
},
width
);
});
}
/**
* Asynchronously compares contents from 2 favicon urls.
*/

View File

@@ -2654,8 +2654,12 @@ tests.push({
});
Assert.equal(pageInfo.annotations.get("anno"), "anno");
let favicon = await PlacesTestUtils.getFaviconForPage(this._uri2);
Assert.equal(favicon.uri.spec, SMALLPNG_DATA_URI.spec);
await new Promise(resolve => {
PlacesUtils.favicons.getFaviconURLForPage(this._uri2, aFaviconURI => {
Assert.ok(aFaviconURI.equals(SMALLPNG_DATA_URI));
resolve();
});
});
},
});

View File

@@ -240,13 +240,21 @@ add_task(async function test_import_chromefavicon() {
dataURL
);
let { dataURI: base64Icon } =
await PlacesTestUtils.getFaviconForPage(PAGE_URI);
let data = await new Promise(resolve => {
PlacesUtils.favicons.getFaviconDataForPage(
PAGE_URI,
(uri, dataLen, faviconData) => resolve(faviconData)
);
});
let base64Icon =
"data:image/png;base64," +
base64EncodeString(String.fromCharCode.apply(String, data));
test_bookmarks.unfiled.push({
title: "Test",
url: PAGE_URI.spec,
icon: base64Icon.spec,
icon: base64Icon,
});
info("Export to html");
@@ -353,10 +361,11 @@ function checkItem(aExpected, aNode) {
Assert.equal(aNode.uri, aExpected.url);
break;
case "icon": {
let { dataURI: base64Icon } = await PlacesTestUtils.getFaviconForPage(
aExpected.url
);
Assert.ok(base64Icon.spec == aExpected.icon);
let { data } = await getFaviconDataForPage(aExpected.url);
let base64Icon =
"data:image/png;base64," +
base64EncodeString(String.fromCharCode.apply(String, data));
Assert.ok(base64Icon == aExpected.icon);
break;
}
case "keyword": {

View File

@@ -109,11 +109,18 @@ var database_check = async function () {
root.containerOpen = false;
// favicons
let favicon = await PlacesTestUtils.getFaviconForPage(TEST_FAVICON_PAGE_URL);
// aURI should never be null when rawData.length > 0.
Assert.notEqual(favicon.uri, null);
await new Promise(resolve => {
PlacesUtils.favicons.getFaviconDataForPage(
uri(TEST_FAVICON_PAGE_URL),
(aURI, aDataLen) => {
// aURI should never be null when aDataLen > 0.
Assert.notEqual(aURI, null);
// Favicon data is stored in the bookmarks file as a "data:" URI. For
// simplicity, instead of converting the data we receive to a "data:" URI
// and comparing it, we just check the data size.
Assert.equal(TEST_FAVICON_DATA_SIZE, favicon.rawData.length);
Assert.equal(TEST_FAVICON_DATA_SIZE, aDataLen);
resolve();
}
);
});
};

View File

@@ -313,10 +313,11 @@ async function checkItem(aExpected, aNode) {
Assert.equal(aNode.uri, aExpected.url);
break;
case "icon": {
let { dataURI: base64Icon } = await PlacesTestUtils.getFaviconForPage(
aExpected.url
);
Assert.equal(base64Icon.spec, aExpected.icon);
let { data } = await getFaviconDataForPage(aExpected.url);
let base64Icon =
"data:image/png;base64," +
base64EncodeString(String.fromCharCode.apply(String, data));
Assert.equal(base64Icon, aExpected.icon);
break;
}
case "keyword": {

View File

@@ -855,17 +855,17 @@ var ActivityStreamProvider = {
async _loadIcons(aUri, preferredFaviconWidth) {
let iconData = {};
// Fetch the largest icon available.
let faviconData;
try {
let faviconData = await lazy.PlacesUtils.favicons.getFaviconForPage(
faviconData = await lazy.PlacesUtils.promiseFaviconData(
aUri,
this.THUMB_FAVICON_SIZE
);
let rawData = faviconData.rawData;
Object.assign(iconData, {
favicon: rawData,
faviconLength: rawData.length,
favicon: faviconData.data,
faviconLength: faviconData.dataLen,
faviconRef: faviconData.uri.ref,
faviconSize: faviconData.width,
faviconSize: faviconData.size,
mimeType: faviconData.mimeType,
});
} catch (e) {
@@ -876,16 +876,15 @@ var ActivityStreamProvider = {
// Also fetch a smaller icon.
try {
let faviconData = await lazy.PlacesUtils.favicons.getFaviconForPage(
faviconData = await lazy.PlacesUtils.promiseFaviconData(
aUri,
preferredFaviconWidth
);
let rawData = faviconData.rawData;
Object.assign(iconData, {
smallFavicon: rawData,
smallFaviconLength: rawData.length,
smallFavicon: faviconData.data,
smallFaviconLength: faviconData.dataLen,
smallFaviconRef: faviconData.uri.ref,
smallFaviconSize: faviconData.width,
smallFaviconSize: faviconData.size,
smallFaviconMimeType: faviconData.mimeType,
});
} catch (e) {

View File

@@ -48,7 +48,7 @@
#include "nsNetCID.h"
#include "prtime.h"
#ifdef MOZ_PLACES
# include "mozilla/places/nsFaviconService.h"
# include "nsIFaviconService.h"
#endif
#include "nsIDownloader.h"
#include "nsIChannel.h"
@@ -74,6 +74,7 @@ namespace mozilla::widget {
#ifdef MOZ_PLACES
NS_IMPL_ISUPPORTS(myDownloadObserver, nsIDownloadObserver)
NS_IMPL_ISUPPORTS(AsyncFaviconDataReady, nsIFaviconDataCallback)
#endif
NS_IMPL_ISUPPORTS(AsyncEncodeAndWriteIcon, nsIRunnable)
NS_IMPL_ISUPPORTS(AsyncDeleteAllFaviconsFromDisk, nsIRunnable)
@@ -669,6 +670,31 @@ MSG WinUtils::InitMSG(UINT aMessage, WPARAM wParam, LPARAM lParam, HWND aWnd) {
}
#ifdef MOZ_PLACES
/************************************************************************
* Constructs as AsyncFaviconDataReady Object
* @param aIOThread : the thread which performs the action
* @param aURLShortcut : Differentiates between (false)Jumplistcache and
* (true)Shortcutcache
* @param aRunnable : Executed in the aIOThread when the favicon cache is
* avaiable
* @param [aPromiseHolder=null]: Optional PromiseHolder that will be forwarded
* to AsyncEncodeAndWriteIcon if getting the
* favicon from the favicon service succeeds. If
* it doesn't succeed, the held MozPromise will
* be rejected.
************************************************************************/
AsyncFaviconDataReady::AsyncFaviconDataReady(
nsIURI* aNewURI, RefPtr<nsISerialEventTarget> aIOThread,
const bool aURLShortcut, already_AddRefed<nsIRunnable> aRunnable,
UniquePtr<MozPromiseHolder<ObtainCachedIconFileAsyncPromise>>
aPromiseHolder)
: mNewURI(aNewURI),
mIOThread(aIOThread),
mRunnable(aRunnable),
mPromiseHolder(std::move(aPromiseHolder)),
mURLShortcut(aURLShortcut) {}
NS_IMETHODIMP
myDownloadObserver::OnDownloadComplete(nsIDownloader* downloader,
nsIRequest* request, nsresult status,
@@ -676,25 +702,28 @@ myDownloadObserver::OnDownloadComplete(nsIDownloader* downloader,
return NS_OK;
}
static nsresult MaybeDownloadFavicon(nsIURI* aNewURI, const bool aURLShortcut) {
if (!aURLShortcut) {
nsresult AsyncFaviconDataReady::OnFaviconDataNotAvailable(void) {
if (!mURLShortcut) {
return NS_OK;
}
nsCOMPtr<nsIFile> icoFile;
nsresult rv =
FaviconHelper::GetOutputIconPath(aNewURI, icoFile, aURLShortcut);
FaviconHelper::GetOutputIconPath(mNewURI, icoFile, mURLShortcut);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIURI> mozIconURI;
rv = NS_NewURI(getter_AddRefs(mozIconURI), "moz-icon://.html?size=32");
NS_ENSURE_SUCCESS(rv, rv);
if (NS_FAILED(rv)) {
return rv;
}
nsCOMPtr<nsIChannel> channel;
rv = NS_NewChannel(getter_AddRefs(channel), mozIconURI,
nsContentUtils::GetSystemPrincipal(),
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
nsIContentPolicy::TYPE_INTERNAL_IMAGE);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIDownloadObserver> downloadObserver = new myDownloadObserver;
@@ -705,36 +734,22 @@ static nsresult MaybeDownloadFavicon(nsIURI* aNewURI, const bool aURLShortcut) {
return channel->AsyncOpen(listener);
}
static nsresult CacheFavicon(
const places::FaviconPromise::ResolveOrRejectValue& aPromiseResult,
nsIURI* aNewURI, RefPtr<nsISerialEventTarget> aIOThread,
const bool aURLShortcut, nsCOMPtr<nsIRunnable> aRunnable,
UniquePtr<MozPromiseHolder<ObtainCachedIconFileAsyncPromise>>
aPromiseHolder) {
nsresult rv = NS_OK;
auto guard = MakeScopeExit([&]() {
if (NS_FAILED(rv) && aPromiseHolder) {
aPromiseHolder->RejectIfExists(rv, __func__);
}
});
nsCOMPtr<nsIFavicon> favicon =
aPromiseResult.IsResolve() ? aPromiseResult.ResolveValue() : nullptr;
if (!favicon) {
MaybeDownloadFavicon(std::move(aNewURI), aURLShortcut);
return (rv = NS_ERROR_FAILURE);
NS_IMETHODIMP
AsyncFaviconDataReady::OnComplete(nsIURI* aFaviconURI, uint32_t aDataLen,
const uint8_t* aData,
const nsACString& aMimeType,
uint16_t aWidth) {
if (!aDataLen || !aData) {
if (mURLShortcut) {
OnFaviconDataNotAvailable();
}
// Get favicon content.
nsTArray<uint8_t> rawData;
rv = favicon->GetRawData(rawData);
NS_ENSURE_SUCCESS(rv, rv);
nsAutoCString mimeType;
rv = favicon->GetMimeType(mimeType);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsCOMPtr<nsIFile> icoFile;
rv = FaviconHelper::GetOutputIconPath(aNewURI, icoFile, aURLShortcut);
nsresult rv =
FaviconHelper::GetOutputIconPath(mNewURI, icoFile, mURLShortcut);
NS_ENSURE_SUCCESS(rv, rv);
nsAutoString path;
@@ -744,9 +759,9 @@ static nsresult CacheFavicon(
// Decode the image from the format it was returned to us in (probably PNG)
nsCOMPtr<imgIContainer> container;
nsCOMPtr<imgITools> imgtool = do_CreateInstance("@mozilla.org/image/tools;1");
rv = imgtool->DecodeImageFromBuffer(
reinterpret_cast<const char*>(rawData.Elements()), rawData.Length(),
mimeType, getter_AddRefs(container));
rv = imgtool->DecodeImageFromBuffer(reinterpret_cast<const char*>(aData),
aDataLen, aMimeType,
getter_AddRefs(container));
NS_ENSURE_SUCCESS(rv, rv);
RefPtr<SourceSurface> surface = container->GetFrame(
@@ -757,7 +772,7 @@ static nsresult CacheFavicon(
RefPtr<DataSourceSurface> dataSurface;
IntSize size;
if (aURLShortcut &&
if (mURLShortcut &&
(surface->GetSize().width < 48 || surface->GetSize().height < 48)) {
// Create a 48x48 surface and paint the icon into the central rect.
size.width = std::max(surface->GetSize().width, 48);
@@ -775,7 +790,8 @@ static nsresult CacheFavicon(
BackendType::CAIRO, map.mData, dataSurface->GetSize(), map.mStride,
dataSurface->GetFormat());
if (!dt) {
gfxWarning() << "CreateDrawTargetForData failed in CacheFavicon";
gfxWarning() << "AsyncFaviconDataReady::OnComplete failed in "
"CreateDrawTargetForData";
return NS_ERROR_OUT_OF_MEMORY;
}
dt->FillRect(Rect(0, 0, size.width, size.height),
@@ -812,13 +828,11 @@ static nsresult CacheFavicon(
}
int32_t stride = 4 * size.width;
guard.release();
// AsyncEncodeAndWriteIcon takes ownership of the heap allocated buffer
nsCOMPtr<nsIRunnable> event = new AsyncEncodeAndWriteIcon(
path, std::move(data), stride, size.width, size.height,
aRunnable.forget(), std::move(aPromiseHolder));
aIOThread->Dispatch(event, NS_DISPATCH_NORMAL);
mRunnable.forget(), std::move(mPromiseHolder));
mIOThread->Dispatch(event, NS_DISPATCH_NORMAL);
return NS_OK;
}
@@ -842,18 +856,6 @@ AsyncEncodeAndWriteIcon::AsyncEncodeAndWriteIcon(
NS_IMETHODIMP AsyncEncodeAndWriteIcon::Run() {
MOZ_ASSERT(!NS_IsMainThread(), "Should not be called on the main thread.");
nsresult rv = NS_OK;
auto guard = MakeScopeExit([&]() {
if (!mPromiseHolder) {
return;
}
if (NS_SUCCEEDED(rv)) {
mPromiseHolder->ResolveIfExists(mIconPath, __func__);
} else {
mPromiseHolder->RejectIfExists(rv, __func__);
}
});
// Note that since we're off the main thread we can't use
// gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget()
RefPtr<DataSourceSurface> surface = Factory::CreateWrappingDataSourceSurface(
@@ -867,16 +869,16 @@ NS_IMETHODIMP AsyncEncodeAndWriteIcon::Run() {
MOZ_TRY(NS_NewLocalFile(mIconPath, getter_AddRefs(comFile)));
nsCOMPtr<nsIFile> dirPath;
MOZ_TRY(comFile->GetParent(getter_AddRefs(dirPath)));
rv = dirPath->Create(nsIFile::DIRECTORY_TYPE, 0777);
nsresult rv = dirPath->Create(nsIFile::DIRECTORY_TYPE, 0777);
if (NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS) {
return rv;
}
file = _wfopen(mIconPath.get(), L"wb");
if (!file) {
return (rv = NS_ERROR_FAILURE);
return NS_ERROR_FAILURE;
}
}
rv = gfxUtils::EncodeSourceSurface(surface, ImageType::ICO, u""_ns,
nsresult rv = gfxUtils::EncodeSourceSurface(surface, ImageType::ICO, u""_ns,
gfxUtils::eBinaryEncode, file);
fclose(file);
NS_ENSURE_SUCCESS(rv, rv);
@@ -884,10 +886,17 @@ NS_IMETHODIMP AsyncEncodeAndWriteIcon::Run() {
if (mRunnable) {
mRunnable->Run();
}
if (mPromiseHolder) {
mPromiseHolder->ResolveIfExists(mIconPath, __func__);
}
return rv;
}
AsyncEncodeAndWriteIcon::~AsyncEncodeAndWriteIcon() {}
AsyncEncodeAndWriteIcon::~AsyncEncodeAndWriteIcon() {
if (mPromiseHolder) {
mPromiseHolder->RejectIfExists(NS_ERROR_FAILURE, __func__);
}
}
AsyncDeleteAllFaviconsFromDisk::AsyncDeleteAllFaviconsFromDisk(
bool aIgnoreRecent)
@@ -1095,6 +1104,7 @@ auto FaviconHelper::ObtainCachedIconFileAsync(
// CacheIconFileFromFaviconURIAsync to request the favicon.
RefPtr<nsISerialEventTarget> currentThread =
GetCurrentSerialEventTarget();
return InvokeAsync(
GetMainThreadSerialEventTarget(),
"ObtainCachedIconFileAsync call to "
@@ -1194,17 +1204,16 @@ nsresult FaviconHelper::CacheIconFileFromFaviconURIAsync(
nsCOMPtr<nsIRunnable> runnable = aRunnable;
#ifdef MOZ_PLACES
// Obtain the favicon service and get the favicon for the specified page
auto* favIconSvc = nsFaviconService::GetFaviconService();
nsCOMPtr<nsIFaviconService> favIconSvc(
do_GetService("@mozilla.org/browser/favicon-service;1"));
NS_ENSURE_TRUE(favIconSvc, NS_ERROR_FAILURE);
favIconSvc->AsyncGetFaviconForPage(aFaviconPageURI)
->Then(GetMainThreadSerialEventTarget(), __func__,
[aFaviconPageURI, aIOThread, aURLShortcut, runnable,
promiseHolder = std::move(aPromiseHolder)](
const places::FaviconPromise::ResolveOrRejectValue&
aResult) mutable {
CacheFavicon(aResult, aFaviconPageURI, aIOThread, aURLShortcut,
runnable, std::move(promiseHolder));
});
nsCOMPtr<nsIFaviconDataCallback> callback =
new mozilla::widget::AsyncFaviconDataReady(
aFaviconPageURI, aIOThread, aURLShortcut, runnable.forget(),
std::move(aPromiseHolder));
favIconSvc->GetFaviconDataForPage(aFaviconPageURI, callback, 0);
#endif
return NS_OK;
}

View File

@@ -581,6 +581,34 @@ class WinUtils {
typedef MozPromise<nsString, nsresult, true> ObtainCachedIconFileAsyncPromise;
#ifdef MOZ_PLACES
class AsyncFaviconDataReady final : public nsIFaviconDataCallback {
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIFAVICONDATACALLBACK
AsyncFaviconDataReady(
nsIURI* aNewURI, RefPtr<nsISerialEventTarget> aIOThread,
const bool aURLShortcut, already_AddRefed<nsIRunnable> aRunnable,
UniquePtr<MozPromiseHolder<ObtainCachedIconFileAsyncPromise>>
aPromiseHolder = nullptr);
nsresult OnFaviconDataNotAvailable(void);
private:
~AsyncFaviconDataReady() {
if (mPromiseHolder) {
mPromiseHolder->RejectIfExists(NS_ERROR_FAILURE, __func__);
}
}
nsCOMPtr<nsIURI> mNewURI;
RefPtr<nsISerialEventTarget> mIOThread;
nsCOMPtr<nsIRunnable> mRunnable;
UniquePtr<MozPromiseHolder<ObtainCachedIconFileAsyncPromise>> mPromiseHolder;
const bool mURLShortcut;
};
#endif
/**
* Asynchronously tries add the list to the build
*/