Bug 429832 - Add API to nsIFaviconService to handle data URIs (for manish@flock.com, r=marco, r=dietrich)

This commit is contained in:
Dietrich Ayala 2008-07-03 11:07:56 -07:00
parent 320876b222
commit b6f0608f6d
5 changed files with 165 additions and 84 deletions

View File

@ -95,7 +95,6 @@
#include "nsParserCIID.h"
#include "nsStringAPI.h"
#include "nsUnicharUtils.h"
#include "plbase64.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsDirectoryServiceUtils.h"
#include "nsIPrefService.h"
@ -424,7 +423,7 @@ protected:
nsresult PopFrame();
nsresult SetFaviconForURI(nsIURI* aPageURI, nsIURI* aFaviconURI,
const nsCString& aData);
const nsString& aData);
PRInt64 ConvertImportedIdToInternalId(const nsCString& aId);
PRTime ConvertImportedDateToInternalDate(const nsACString& aDate);
@ -950,8 +949,7 @@ BookmarkContentSink::HandleLinkBegin(const nsIParserNode& node)
nsCOMPtr<nsIURI> iconUriObject;
NS_NewURI(getter_AddRefs(iconUriObject), iconUri);
if (!icon.IsEmpty() || iconUriObject) {
rv = SetFaviconForURI(frame.mPreviousLink, iconUriObject,
NS_ConvertUTF16toUTF8(icon));
rv = SetFaviconForURI(frame.mPreviousLink, iconUriObject, icon);
}
}
@ -1275,7 +1273,7 @@ BookmarkContentSink::PopFrame()
nsresult
BookmarkContentSink::SetFaviconForURI(nsIURI* aPageURI, nsIURI* aIconURI,
const nsCString& aData)
const nsString& aData)
{
nsresult rv;
static PRUint32 serialNumber = 0; // for made-up favicon URIs
@ -1315,53 +1313,13 @@ BookmarkContentSink::SetFaviconForURI(nsIURI* aPageURI, nsIURI* aIconURI,
serialNumber++;
}
nsCOMPtr<nsIURI> dataURI;
rv = NS_NewURI(getter_AddRefs(dataURI), aData);
NS_ENSURE_SUCCESS(rv, rv);
// use the data: protocol handler to convert the data
nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIProtocolHandler> protocolHandler;
rv = ioService->GetProtocolHandler("data", getter_AddRefs(protocolHandler));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIChannel> channel;
rv = protocolHandler->NewChannel(dataURI, getter_AddRefs(channel));
NS_ENSURE_SUCCESS(rv, rv);
// blocking stream is OK for data URIs
nsCOMPtr<nsIInputStream> stream;
rv = channel->Open(getter_AddRefs(stream));
NS_ENSURE_SUCCESS(rv, rv);
PRUint32 available;
rv = stream->Available(&available);
NS_ENSURE_SUCCESS(rv, rv);
if (available == 0)
return NS_ERROR_FAILURE;
// read all the decoded data
PRUint8* buffer = static_cast<PRUint8*>
(nsMemory::Alloc(sizeof(PRUint8) * available));
if (!buffer)
return NS_ERROR_OUT_OF_MEMORY;
PRUint32 numRead;
rv = stream->Read(reinterpret_cast<char*>(buffer), available, &numRead);
if (NS_FAILED(rv) || numRead != available) {
nsMemory::Free(buffer);
return rv;
}
nsCAutoString mimeType;
rv = channel->GetContentType(mimeType);
NS_ENSURE_SUCCESS(rv, rv);
// save in service
rv = faviconService->SetFaviconData(faviconURI, buffer, available, mimeType, 0);
nsMemory::Free(buffer);
rv = faviconService->SetFaviconDataFromDataURL(faviconURI, aData, 0);
NS_ENSURE_SUCCESS(rv, rv);
rv = faviconService->SetFaviconUrlForPage(aPageURI, faviconURI);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
@ -1496,27 +1454,6 @@ WriteContainerEpilogue(const nsACString& aIndent, nsIOutputStream* aOutput)
}
// DataToDataURI
static nsresult
DataToDataURI(PRUint8* aData, PRUint32 aDataLen, const nsACString& aMimeType,
nsACString& aDataURI)
{
char* encoded = PL_Base64Encode(reinterpret_cast<const char*>(aData),
aDataLen, nsnull);
if (!encoded)
return NS_ERROR_OUT_OF_MEMORY;
aDataURI.AssignLiteral("data:");
aDataURI.Append(aMimeType);
aDataURI.AppendLiteral(";base64,");
aDataURI.Append(encoded);
nsMemory::Free(encoded);
return NS_OK;
}
// WriteFaviconAttribute
//
// This writes the 'ICON="data:asdlfkjas;ldkfja;skdljfasdf"' attribute for
@ -1560,22 +1497,14 @@ WriteFaviconAttribute(const nsACString& aURI, nsIOutputStream* aOutput)
if (!faviconScheme.EqualsLiteral("chrome")) {
// only store data for non-chrome URIs
// get the data - BE SURE TO FREE
nsCAutoString mimeType;
PRUint32 dataLen;
PRUint8* data;
rv = faviconService->GetFaviconData(faviconURI, mimeType, &dataLen, &data);
nsAutoString faviconContents;
rv = faviconService->GetFaviconDataAsDataURL(faviconURI, faviconContents);
NS_ENSURE_SUCCESS(rv, rv);
if (dataLen > 0) {
// convert to URI
nsCString faviconContents;
rv = DataToDataURI(data, dataLen, mimeType, faviconContents);
nsMemory::Free(data);
NS_ENSURE_SUCCESS(rv, rv);
if (faviconContents.Length() > 0) {
rv = aOutput->Write(kIconAttribute, sizeof(kIconAttribute)-1, &dummy);
NS_ENSURE_SUCCESS(rv, rv);
rv = aOutput->Write(faviconContents.get(), faviconContents.Length(), &dummy);
NS_ConvertUTF16toUTF8 utf8Favicon(faviconContents);
rv = aOutput->Write(utf8Favicon.get(), utf8Favicon.Length(), &dummy);
NS_ENSURE_SUCCESS(rv, rv);
rv = aOutput->Write(kQuoteStr, sizeof(kQuoteStr)-1, &dummy);
NS_ENSURE_SUCCESS(rv, rv);

View File

@ -64,6 +64,13 @@ try {
do_throw("Could not get livemark service\n");
}
// Get favicon service
try {
var iconsvc = Cc["@mozilla.org/browser/favicon-service;1"].getService(Ci.nsIFaviconService);
} catch(ex) {
do_throw("Could not get favicon service\n");
}
// Get microsummary service
try {
var mssvc = Cc["@mozilla.org/microsummary/service;1"].getService(Ci.nsIMicrosummaryService);
@ -82,6 +89,9 @@ const DESCRIPTION_ANNO = "bookmarkProperties/description";
const LOAD_IN_SIDEBAR_ANNO = "bookmarkProperties/loadInSidebar";
const POST_DATA_ANNO = "bookmarkProperties/POSTData";
const TEST_FAVICON_PAGE_URL = "http://en-US.www.mozilla.com/en-US/firefox/central/";
const TEST_FAVICON_DATA_URL = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHWSURBVHjaYvz//z8DJQAggJiQOe/fv2fv7Oz8rays/N+VkfG/iYnJfyD/1+rVq7ffu3dPFpsBAAHEAHIBCJ85c8bN2Nj4vwsDw/8zQLwKiO8CcRoQu0DxqlWrdsHUwzBAAIGJmTNnPgYa9j8UqhFElwPxf2MIDeIrKSn9FwSJoRkAEEAM0DD4DzMAyPi/G+QKY4hh5WAXGf8PDQ0FGwJ22d27CjADAAIIrLmjo+MXA9R2kAHvGBA2wwx6B8W7od6CeQcggKCmCEL8bgwxYCbUIGTDVkHDBia+CuotgACCueD3TDQN75D4xmAvCoK9ARMHBzAw0AECiBHkAlC0Mdy7x9ABNA3obAZXIAa6iKEcGlMVQHwWyjYuL2d4v2cPg8vZswx7gHyAAAK7AOif7SAbOqCmn4Ha3AHFsIDtgPq/vLz8P4MSkJ2W9h8ggBjevXvHDo4FQUQg/kdypqCg4H8lUIACnQ/SOBMYI8bAsAJFPcj1AAEEjwVQqLpAbXmH5BJjqI0gi9DTAAgDBBCcAVLkgmQ7yKCZxpCQxqUZhAECCJ4XgMl493ug21ZD+aDAXH0WLM4A9MZPXJkJIIAwTAR5pQMalaCABQUULttBGCCAGCnNzgABBgAMJ5THwGvJLAAAAABJRU5ErkJggg==";
// main
function run_test() {
// get places import/export service
@ -306,4 +316,9 @@ function testCanonicalBookmarks(aFolder) {
unfiledBookmarks.containerOpen = true;
do_check_eq(unfiledBookmarks.childCount, 1);
unfiledBookmarks.containerOpen = false;
// favicons
var faviconURI = iconsvc.getFaviconForPage(uri(TEST_FAVICON_PAGE_URL));
var dataURL = iconsvc.getFaviconDataAsDataURL(faviconURI);
do_check_eq(TEST_FAVICON_DATA_URL, dataURL);
}

View File

@ -40,7 +40,7 @@
interface nsIURI;
[scriptable, uuid(91f635a4-2531-4f3d-89ef-81403a685f44)]
[scriptable, uuid(fafe10e0-194f-4e89-aab9-a5849e97287c)]
interface nsIFaviconService : nsISupports
{
/**
@ -148,6 +148,24 @@ interface nsIFaviconService : nsISupports
in unsigned long aDataLen, in AUTF8String aMimeType,
in PRTime aExpiration);
/**
* Stores the data of a given favicon. The data is provided by a string
* containing a data URL.
*
* @see setFaviconData
*
* @param aFaviconURI
* URI of the favicon whose data is being set.
* @param aDataURL
* string containing a data URL that represents the contents of
* the favicon to save
* @param aExpiration
* Time in microseconds since the epoch when this favicon expires.
* Until this time, we won't try to load it again.
*/
void setFaviconDataFromDataURL(in nsIURI aFaviconURI, in AString aDataURL,
in PRTime aExpiration);
/**
* Retrieves the given favicon data. Throws if we don't have data.
*
@ -172,6 +190,24 @@ interface nsIFaviconService : nsISupports
out unsigned long aDataLen,
[array,retval,size_is(aDataLen)] out octet aData);
/**
* Retrieves the given favicon data as a data URL. Throws if we don't
* have data.
*
* If there is no data but we have an entry for this favicon, the return
* value will be NULL.
*
* @see getFaviconData
*
* @param aFaviconURI
* URI of the favicon whose data is being read
* @returns A data URL containing the data of the favicon. This will be
* null if we have this URL but have no data associated with it.
* @throws NS_ERROR_NOT_AVAILABLE
* Thrown when we have never heard of this favicon URL.
*/
AString getFaviconDataAsDataURL(in nsIURI aFaviconURI);
/**
* Retrieves the URI of the favicon for the given page.
*

View File

@ -62,6 +62,7 @@
#include "nsStreamUtils.h"
#include "nsStringStream.h"
#include "mozStorageHelper.h"
#include "plbase64.h"
// For favicon optimization
#include "imgITools.h"
@ -636,6 +637,66 @@ nsFaviconService::SetFaviconData(nsIURI* aFaviconURI, const PRUint8* aData,
}
// nsFaviconService::SetFaviconDataFromDataURL
NS_IMETHODIMP
nsFaviconService::SetFaviconDataFromDataURL(nsIURI* aFaviconURI,
const nsAString& aDataURL,
PRTime aExpiration)
{
nsresult rv;
nsCOMPtr<nsIURI> dataURI;
rv = NS_NewURI(getter_AddRefs(dataURI), aDataURL);
NS_ENSURE_SUCCESS(rv, rv);
// use the data: protocol handler to convert the data
nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIProtocolHandler> protocolHandler;
rv = ioService->GetProtocolHandler("data", getter_AddRefs(protocolHandler));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIChannel> channel;
rv = protocolHandler->NewChannel(dataURI, getter_AddRefs(channel));
NS_ENSURE_SUCCESS(rv, rv);
// blocking stream is OK for data URIs
nsCOMPtr<nsIInputStream> stream;
rv = channel->Open(getter_AddRefs(stream));
NS_ENSURE_SUCCESS(rv, rv);
PRUint32 available;
rv = stream->Available(&available);
NS_ENSURE_SUCCESS(rv, rv);
if (available == 0)
return NS_ERROR_FAILURE;
// read all the decoded data
PRUint8* buffer = static_cast<PRUint8*>
(nsMemory::Alloc(sizeof(PRUint8) * available));
if (!buffer)
return NS_ERROR_OUT_OF_MEMORY;
PRUint32 numRead;
rv = stream->Read(reinterpret_cast<char*>(buffer), available, &numRead);
if (NS_FAILED(rv) || numRead != available) {
nsMemory::Free(buffer);
return rv;
}
nsCAutoString mimeType;
rv = channel->GetContentType(mimeType);
NS_ENSURE_SUCCESS(rv, rv);
// SetFaviconData can now do the dirty work
rv = SetFaviconData(aFaviconURI, buffer, available, mimeType, aExpiration);
nsMemory::Free(buffer);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
// nsFaviconService::GetFaviconData
NS_IMETHODIMP
@ -657,6 +718,43 @@ nsFaviconService::GetFaviconData(nsIURI* aFaviconURI, nsACString& aMimeType,
}
// nsFaviconService::GetFaviconDataAsDataURL
NS_IMETHODIMP
nsFaviconService::GetFaviconDataAsDataURL(nsIURI* aFaviconURI,
nsAString& aDataURL)
{
nsresult rv;
PRUint8* data;
PRUint32 dataLen;
nsCAutoString mimeType;
rv = GetFaviconData(aFaviconURI, mimeType, &dataLen, &data);
NS_ENSURE_SUCCESS(rv, rv);
if (!data) {
aDataURL.SetIsVoid(PR_TRUE);
return NS_OK;
}
char* encoded = PL_Base64Encode(reinterpret_cast<const char*>(data),
dataLen, nsnull);
nsMemory::Free(data);
if (!encoded)
return NS_ERROR_OUT_OF_MEMORY;
aDataURL.AssignLiteral("data:");
AppendUTF8toUTF16(mimeType, aDataURL);
aDataURL.AppendLiteral(";base64,");
AppendUTF8toUTF16(encoded, aDataURL);
nsMemory::Free(encoded);
return NS_OK;
}
// nsFaviconService::GetFaviconForPage
NS_IMETHODIMP

View File

@ -72,6 +72,9 @@ function setAndGetFaviconData(aFilename, aData, aMimeType) {
aData, aData.length, aMimeType,
Number.MAX_VALUE);
var dataURL = iconsvc.getFaviconDataAsDataURL(iconURI);
iconsvc.setFaviconDataFromDataURL(iconURI, dataURL, Number.MAX_VALUE);
var mimeTypeOutparam = {};
var outData = iconsvc.getFaviconData(iconURI,