From b6f0608f6d570522d25053a7ffc2fd0e896c5c8a Mon Sep 17 00:00:00 2001 From: Dietrich Ayala Date: Thu, 3 Jul 2008 11:07:56 -0700 Subject: [PATCH] Bug 429832 - Add API to nsIFaviconService to handle data URIs (for manish@flock.com, r=marco, r=dietrich) --- .../src/nsPlacesImportExportService.cpp | 95 +++--------------- .../places/tests/unit/test_bookmarks_html.js | 15 +++ .../places/public/nsIFaviconService.idl | 38 ++++++- .../places/src/nsFaviconService.cpp | 98 +++++++++++++++++++ .../places/tests/unit/test_favicons.js | 3 + 5 files changed, 165 insertions(+), 84 deletions(-) diff --git a/browser/components/places/src/nsPlacesImportExportService.cpp b/browser/components/places/src/nsPlacesImportExportService.cpp index 9f101e2227b2..55bf999f0248 100644 --- a/browser/components/places/src/nsPlacesImportExportService.cpp +++ b/browser/components/places/src/nsPlacesImportExportService.cpp @@ -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 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 dataURI; - rv = NS_NewURI(getter_AddRefs(dataURI), aData); - NS_ENSURE_SUCCESS(rv, rv); - - // use the data: protocol handler to convert the data - nsCOMPtr ioService = do_GetIOService(&rv); - NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr protocolHandler; - rv = ioService->GetProtocolHandler("data", getter_AddRefs(protocolHandler)); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr channel; - rv = protocolHandler->NewChannel(dataURI, getter_AddRefs(channel)); - NS_ENSURE_SUCCESS(rv, rv); - - // blocking stream is OK for data URIs - nsCOMPtr 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 - (nsMemory::Alloc(sizeof(PRUint8) * available)); - if (!buffer) - return NS_ERROR_OUT_OF_MEMORY; - PRUint32 numRead; - rv = stream->Read(reinterpret_cast(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(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); diff --git a/browser/components/places/tests/unit/test_bookmarks_html.js b/browser/components/places/tests/unit/test_bookmarks_html.js index 81903d00f823..909cb92ea4cb 100644 --- a/browser/components/places/tests/unit/test_bookmarks_html.js +++ b/browser/components/places/tests/unit/test_bookmarks_html.js @@ -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); } diff --git a/toolkit/components/places/public/nsIFaviconService.idl b/toolkit/components/places/public/nsIFaviconService.idl index 616054ed2e20..c22a6de060d4 100644 --- a/toolkit/components/places/public/nsIFaviconService.idl +++ b/toolkit/components/places/public/nsIFaviconService.idl @@ -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. * diff --git a/toolkit/components/places/src/nsFaviconService.cpp b/toolkit/components/places/src/nsFaviconService.cpp index 059478e53e8a..d6c52540af7a 100644 --- a/toolkit/components/places/src/nsFaviconService.cpp +++ b/toolkit/components/places/src/nsFaviconService.cpp @@ -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 dataURI; + rv = NS_NewURI(getter_AddRefs(dataURI), aDataURL); + NS_ENSURE_SUCCESS(rv, rv); + + // use the data: protocol handler to convert the data + nsCOMPtr ioService = do_GetIOService(&rv); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr protocolHandler; + rv = ioService->GetProtocolHandler("data", getter_AddRefs(protocolHandler)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr channel; + rv = protocolHandler->NewChannel(dataURI, getter_AddRefs(channel)); + NS_ENSURE_SUCCESS(rv, rv); + + // blocking stream is OK for data URIs + nsCOMPtr 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 + (nsMemory::Alloc(sizeof(PRUint8) * available)); + if (!buffer) + return NS_ERROR_OUT_OF_MEMORY; + PRUint32 numRead; + rv = stream->Read(reinterpret_cast(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(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 diff --git a/toolkit/components/places/tests/unit/test_favicons.js b/toolkit/components/places/tests/unit/test_favicons.js index 5332e8ed2ad2..9d70b46fb21b 100644 --- a/toolkit/components/places/tests/unit/test_favicons.js +++ b/toolkit/components/places/tests/unit/test_favicons.js @@ -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,