michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /** michael@0: * This is the favicon service, which stores favicons for web pages with your michael@0: * history as you browse. It is also used to save the favicons for bookmarks. michael@0: * michael@0: * DANGER: The history query system makes assumptions about the favicon storage michael@0: * so that icons can be quickly generated for history/bookmark result sets. If michael@0: * you change the database layout at all, you will have to update both services. michael@0: */ michael@0: michael@0: #include "nsFaviconService.h" michael@0: michael@0: #include "nsNavHistory.h" michael@0: #include "nsPlacesMacros.h" michael@0: #include "Helpers.h" michael@0: #include "AsyncFaviconHelpers.h" michael@0: michael@0: #include "nsNetUtil.h" michael@0: #include "nsReadableUtils.h" michael@0: #include "nsStreamUtils.h" michael@0: #include "nsStringStream.h" michael@0: #include "plbase64.h" michael@0: #include "nsIClassInfoImpl.h" michael@0: #include "mozilla/ArrayUtils.h" michael@0: #include "mozilla/Preferences.h" michael@0: michael@0: // For large favicons optimization. michael@0: #include "imgITools.h" michael@0: #include "imgIContainer.h" michael@0: michael@0: // Default value for mOptimizedIconDimension michael@0: #define OPTIMIZED_FAVICON_DIMENSION 16 michael@0: michael@0: #define MAX_FAVICON_CACHE_SIZE 256 michael@0: #define FAVICON_CACHE_REDUCE_COUNT 64 michael@0: michael@0: #define MAX_UNASSOCIATED_FAVICONS 64 michael@0: michael@0: // When replaceFaviconData is called, we store the icons in an in-memory cache michael@0: // instead of in storage. Icons in the cache are expired according to this michael@0: // interval. michael@0: #define UNASSOCIATED_ICON_EXPIRY_INTERVAL 60000 michael@0: michael@0: // The MIME type of the default favicon and favicons created by michael@0: // OptimizeFaviconImage. michael@0: #define DEFAULT_MIME_TYPE "image/png" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::places; michael@0: michael@0: /** michael@0: * Used to notify a topic to system observers on async execute completion. michael@0: * Will throw on error. michael@0: */ michael@0: class ExpireFaviconsStatementCallbackNotifier : public AsyncStatementCallback michael@0: { michael@0: public: michael@0: ExpireFaviconsStatementCallbackNotifier(); michael@0: NS_IMETHOD HandleCompletion(uint16_t aReason); michael@0: }; michael@0: michael@0: michael@0: PLACES_FACTORY_SINGLETON_IMPLEMENTATION(nsFaviconService, gFaviconService) michael@0: michael@0: NS_IMPL_CLASSINFO(nsFaviconService, nullptr, 0, NS_FAVICONSERVICE_CID) michael@0: NS_IMPL_ISUPPORTS_CI( michael@0: nsFaviconService michael@0: , nsIFaviconService michael@0: , mozIAsyncFavicons michael@0: , nsITimerCallback michael@0: ) michael@0: michael@0: nsFaviconService::nsFaviconService() michael@0: : mOptimizedIconDimension(OPTIMIZED_FAVICON_DIMENSION) michael@0: , mFailedFaviconSerial(0) michael@0: , mFailedFavicons(MAX_FAVICON_CACHE_SIZE) michael@0: , mUnassociatedIcons(MAX_UNASSOCIATED_FAVICONS) michael@0: { michael@0: NS_ASSERTION(!gFaviconService, michael@0: "Attempting to create two instances of the service!"); michael@0: gFaviconService = this; michael@0: } michael@0: michael@0: michael@0: nsFaviconService::~nsFaviconService() michael@0: { michael@0: NS_ASSERTION(gFaviconService == this, michael@0: "Deleting a non-singleton instance of the service"); michael@0: if (gFaviconService == this) michael@0: gFaviconService = nullptr; michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: nsFaviconService::Init() michael@0: { michael@0: mDB = Database::GetDatabase(); michael@0: NS_ENSURE_STATE(mDB); michael@0: michael@0: mOptimizedIconDimension = Preferences::GetInt( michael@0: "places.favicons.optimizeToDimension", OPTIMIZED_FAVICON_DIMENSION michael@0: ); michael@0: michael@0: mExpireUnassociatedIconsTimer = do_CreateInstance("@mozilla.org/timer;1"); michael@0: NS_ENSURE_STATE(mExpireUnassociatedIconsTimer); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFaviconService::ExpireAllFavicons() michael@0: { michael@0: nsCOMPtr unlinkIconsStmt = mDB->GetAsyncStatement( michael@0: "UPDATE moz_places " michael@0: "SET favicon_id = NULL " michael@0: "WHERE favicon_id NOT NULL" michael@0: ); michael@0: NS_ENSURE_STATE(unlinkIconsStmt); michael@0: nsCOMPtr removeIconsStmt = mDB->GetAsyncStatement( michael@0: "DELETE FROM moz_favicons WHERE id NOT IN (" michael@0: "SELECT favicon_id FROM moz_places WHERE favicon_id NOT NULL " michael@0: ")" michael@0: ); michael@0: NS_ENSURE_STATE(removeIconsStmt); michael@0: michael@0: mozIStorageBaseStatement* stmts[] = { michael@0: unlinkIconsStmt.get() michael@0: , removeIconsStmt.get() michael@0: }; michael@0: nsCOMPtr ps; michael@0: nsRefPtr callback = michael@0: new ExpireFaviconsStatementCallbackNotifier(); michael@0: nsresult rv = mDB->MainConn()->ExecuteAsync( michael@0: stmts, ArrayLength(stmts), callback, getter_AddRefs(ps) michael@0: ); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: //// nsITimerCallback michael@0: michael@0: static PLDHashOperator michael@0: ExpireNonrecentUnassociatedIconsEnumerator( michael@0: UnassociatedIconHashKey* aIconKey, michael@0: void* aNow) michael@0: { michael@0: PRTime now = *(reinterpret_cast(aNow)); michael@0: if (now - aIconKey->created >= UNASSOCIATED_ICON_EXPIRY_INTERVAL) { michael@0: return PL_DHASH_REMOVE; michael@0: } michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFaviconService::Notify(nsITimer* timer) michael@0: { michael@0: if (timer != mExpireUnassociatedIconsTimer.get()) { michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: PRTime now = PR_Now(); michael@0: mUnassociatedIcons.EnumerateEntries( michael@0: ExpireNonrecentUnassociatedIconsEnumerator, &now); michael@0: // Re-init the expiry timer if the cache isn't empty. michael@0: if (mUnassociatedIcons.Count() > 0) { michael@0: mExpireUnassociatedIconsTimer->InitWithCallback( michael@0: this, UNASSOCIATED_ICON_EXPIRY_INTERVAL, nsITimer::TYPE_ONE_SHOT); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: //// nsIFaviconService michael@0: michael@0: NS_IMETHODIMP michael@0: nsFaviconService::GetDefaultFavicon(nsIURI** _retval) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(_retval); michael@0: michael@0: // not found, use default michael@0: if (!mDefaultIcon) { michael@0: nsresult rv = NS_NewURI(getter_AddRefs(mDefaultIcon), michael@0: NS_LITERAL_CSTRING(FAVICON_DEFAULT_URL)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: return mDefaultIcon->Clone(_retval); michael@0: } michael@0: michael@0: void michael@0: nsFaviconService::SendFaviconNotifications(nsIURI* aPageURI, michael@0: nsIURI* aFaviconURI, michael@0: const nsACString& aGUID) michael@0: { michael@0: nsAutoCString faviconSpec; michael@0: nsNavHistory* history = nsNavHistory::GetHistoryService(); michael@0: if (history && NS_SUCCEEDED(aFaviconURI->GetSpec(faviconSpec))) { michael@0: history->SendPageChangedNotification(aPageURI, michael@0: nsINavHistoryObserver::ATTRIBUTE_FAVICON, michael@0: NS_ConvertUTF8toUTF16(faviconSpec), michael@0: aGUID); michael@0: } michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFaviconService::SetAndFetchFaviconForPage(nsIURI* aPageURI, michael@0: nsIURI* aFaviconURI, michael@0: bool aForceReload, michael@0: uint32_t aFaviconLoadType, michael@0: nsIFaviconDataCallback* aCallback) michael@0: { michael@0: NS_ENSURE_ARG(aPageURI); michael@0: NS_ENSURE_ARG(aFaviconURI); michael@0: michael@0: // If a favicon is in the failed cache, only load it during a forced reload. michael@0: bool previouslyFailed; michael@0: nsresult rv = IsFailedFavicon(aFaviconURI, &previouslyFailed); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: if (previouslyFailed) { michael@0: if (aForceReload) michael@0: RemoveFailedFavicon(aFaviconURI); michael@0: else michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Check if the icon already exists and fetch it from the network, if needed. michael@0: // Finally associate the icon to the requested page if not yet associated. michael@0: rv = AsyncFetchAndSetIconForPage::start( michael@0: aFaviconURI, aPageURI, aForceReload ? FETCH_ALWAYS : FETCH_IF_MISSING, michael@0: aFaviconLoadType, aCallback michael@0: ); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // DB will be updated and observers notified when data has finished loading. michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFaviconService::ReplaceFaviconData(nsIURI* aFaviconURI, michael@0: const uint8_t* aData, michael@0: uint32_t aDataLen, michael@0: const nsACString& aMimeType, michael@0: PRTime aExpiration) michael@0: { michael@0: NS_ENSURE_ARG(aFaviconURI); michael@0: NS_ENSURE_ARG(aData); michael@0: NS_ENSURE_TRUE(aDataLen > 0, NS_ERROR_INVALID_ARG); michael@0: NS_ENSURE_TRUE(aMimeType.Length() > 0, NS_ERROR_INVALID_ARG); michael@0: if (aExpiration == 0) { michael@0: aExpiration = PR_Now() + MAX_FAVICON_EXPIRATION; michael@0: } michael@0: michael@0: UnassociatedIconHashKey* iconKey = mUnassociatedIcons.PutEntry(aFaviconURI); michael@0: if (!iconKey) { michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: iconKey->created = PR_Now(); michael@0: michael@0: // If the cache contains unassociated icons, an expiry timer should already exist, otherwise michael@0: // there may be a timer left hanging around, so make sure we fire a new one. michael@0: int32_t unassociatedCount = mUnassociatedIcons.Count(); michael@0: if (unassociatedCount == 1) { michael@0: mExpireUnassociatedIconsTimer->Cancel(); michael@0: mExpireUnassociatedIconsTimer->InitWithCallback( michael@0: this, UNASSOCIATED_ICON_EXPIRY_INTERVAL, nsITimer::TYPE_ONE_SHOT); michael@0: } michael@0: michael@0: IconData* iconData = &(iconKey->iconData); michael@0: iconData->expiration = aExpiration; michael@0: iconData->status = ICON_STATUS_CACHED; michael@0: iconData->fetchMode = FETCH_NEVER; michael@0: nsresult rv = aFaviconURI->GetSpec(iconData->spec); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // If the page provided a large image for the favicon (eg, a highres image michael@0: // or a multiresolution .ico file), we don't want to store more data than michael@0: // needed. michael@0: if (aDataLen > MAX_ICON_FILESIZE(mOptimizedIconDimension)) { michael@0: rv = OptimizeFaviconImage(aData, aDataLen, aMimeType, iconData->data, iconData->mimeType); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (iconData->data.Length() > MAX_FAVICON_SIZE) { michael@0: // We cannot optimize this favicon size and we are over the maximum size michael@0: // allowed, so we will not save data to the db to avoid bloating it. michael@0: mUnassociatedIcons.RemoveEntry(aFaviconURI); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: } else { michael@0: iconData->mimeType.Assign(aMimeType); michael@0: iconData->data.Assign(TO_CHARBUFFER(aData), aDataLen); michael@0: } michael@0: michael@0: // If the database contains an icon at the given url, we will update the michael@0: // database immediately so that the associated pages are kept in sync. michael@0: // Otherwise, do nothing and let the icon be picked up from the memory hash. michael@0: rv = AsyncReplaceFaviconData::start(iconData); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFaviconService::ReplaceFaviconDataFromDataURL(nsIURI* aFaviconURI, michael@0: const nsAString& aDataURL, michael@0: PRTime aExpiration) michael@0: { michael@0: NS_ENSURE_ARG(aFaviconURI); michael@0: NS_ENSURE_TRUE(aDataURL.Length() > 0, NS_ERROR_INVALID_ARG); michael@0: if (aExpiration == 0) { michael@0: aExpiration = PR_Now() + MAX_FAVICON_EXPIRATION; michael@0: } michael@0: michael@0: nsCOMPtr dataURI; michael@0: nsresult rv = NS_NewURI(getter_AddRefs(dataURI), aDataURL); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // Use the data: protocol handler to convert the data. michael@0: nsCOMPtr ioService = do_GetIOService(&rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: nsCOMPtr protocolHandler; michael@0: rv = ioService->GetProtocolHandler("data", getter_AddRefs(protocolHandler)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsCOMPtr channel; michael@0: rv = protocolHandler->NewChannel(dataURI, getter_AddRefs(channel)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // Blocking stream is OK for data URIs. michael@0: nsCOMPtr stream; michael@0: rv = channel->Open(getter_AddRefs(stream)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: uint64_t available64; michael@0: rv = stream->Available(&available64); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: if (available64 == 0 || available64 > UINT32_MAX / sizeof(uint8_t)) michael@0: return NS_ERROR_FILE_TOO_BIG; michael@0: uint32_t available = (uint32_t)available64; michael@0: michael@0: // Read all the decoded data. michael@0: uint8_t* buffer = static_cast michael@0: (nsMemory::Alloc(sizeof(uint8_t) * available)); michael@0: if (!buffer) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: uint32_t numRead; michael@0: rv = stream->Read(TO_CHARBUFFER(buffer), available, &numRead); michael@0: if (NS_FAILED(rv) || numRead != available) { michael@0: nsMemory::Free(buffer); michael@0: return rv; michael@0: } michael@0: michael@0: nsAutoCString mimeType; michael@0: rv = channel->GetContentType(mimeType); michael@0: if (NS_FAILED(rv)) { michael@0: nsMemory::Free(buffer); michael@0: return rv; michael@0: } michael@0: michael@0: // ReplaceFaviconData can now do the dirty work. michael@0: rv = ReplaceFaviconData(aFaviconURI, buffer, available, mimeType, aExpiration); michael@0: nsMemory::Free(buffer); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFaviconService::GetFaviconURLForPage(nsIURI *aPageURI, michael@0: nsIFaviconDataCallback* aCallback) michael@0: { michael@0: NS_ENSURE_ARG(aPageURI); michael@0: NS_ENSURE_ARG(aCallback); michael@0: michael@0: nsresult rv = AsyncGetFaviconURLForPage::start(aPageURI, aCallback); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFaviconService::GetFaviconDataForPage(nsIURI* aPageURI, michael@0: nsIFaviconDataCallback* aCallback) michael@0: { michael@0: NS_ENSURE_ARG(aPageURI); michael@0: NS_ENSURE_ARG(aCallback); michael@0: michael@0: nsresult rv = AsyncGetFaviconDataForPage::start(aPageURI, aCallback); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsFaviconService::GetFaviconLinkForIcon(nsIURI* aFaviconURI, michael@0: nsIURI** aOutputURI) michael@0: { michael@0: NS_ENSURE_ARG(aFaviconURI); michael@0: NS_ENSURE_ARG_POINTER(aOutputURI); michael@0: michael@0: nsAutoCString spec; michael@0: if (aFaviconURI) { michael@0: nsresult rv = aFaviconURI->GetSpec(spec); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: return GetFaviconLinkForIconString(spec, aOutputURI); michael@0: } michael@0: michael@0: michael@0: static PLDHashOperator michael@0: ExpireFailedFaviconsCallback(nsCStringHashKey::KeyType aKey, michael@0: uint32_t& aData, michael@0: void* userArg) michael@0: { michael@0: uint32_t* threshold = reinterpret_cast(userArg); michael@0: if (aData < *threshold) michael@0: return PL_DHASH_REMOVE; michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsFaviconService::AddFailedFavicon(nsIURI* aFaviconURI) michael@0: { michael@0: NS_ENSURE_ARG(aFaviconURI); michael@0: michael@0: nsAutoCString spec; michael@0: nsresult rv = aFaviconURI->GetSpec(spec); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: mFailedFavicons.Put(spec, mFailedFaviconSerial); michael@0: mFailedFaviconSerial ++; michael@0: michael@0: if (mFailedFavicons.Count() > MAX_FAVICON_CACHE_SIZE) { michael@0: // need to expire some entries, delete the FAVICON_CACHE_REDUCE_COUNT number michael@0: // of items that are the oldest michael@0: uint32_t threshold = mFailedFaviconSerial - michael@0: MAX_FAVICON_CACHE_SIZE + FAVICON_CACHE_REDUCE_COUNT; michael@0: mFailedFavicons.Enumerate(ExpireFailedFaviconsCallback, &threshold); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsFaviconService::RemoveFailedFavicon(nsIURI* aFaviconURI) michael@0: { michael@0: NS_ENSURE_ARG(aFaviconURI); michael@0: michael@0: nsAutoCString spec; michael@0: nsresult rv = aFaviconURI->GetSpec(spec); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // we silently do nothing and succeed if the icon is not in the cache michael@0: mFailedFavicons.Remove(spec); michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsFaviconService::IsFailedFavicon(nsIURI* aFaviconURI, bool* _retval) michael@0: { michael@0: NS_ENSURE_ARG(aFaviconURI); michael@0: nsAutoCString spec; michael@0: nsresult rv = aFaviconURI->GetSpec(spec); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: uint32_t serial; michael@0: *_retval = mFailedFavicons.Get(spec, &serial); michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: // nsFaviconService::GetFaviconLinkForIconString michael@0: // michael@0: // This computes a favicon URL with string input and using the cached michael@0: // default one to minimize parsing. michael@0: michael@0: nsresult michael@0: nsFaviconService::GetFaviconLinkForIconString(const nsCString& aSpec, michael@0: nsIURI** aOutput) michael@0: { michael@0: if (aSpec.IsEmpty()) { michael@0: // default icon for empty strings michael@0: if (! mDefaultIcon) { michael@0: nsresult rv = NS_NewURI(getter_AddRefs(mDefaultIcon), michael@0: NS_LITERAL_CSTRING(FAVICON_DEFAULT_URL)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: return mDefaultIcon->Clone(aOutput); michael@0: } michael@0: michael@0: if (StringBeginsWith(aSpec, NS_LITERAL_CSTRING("chrome:"))) { michael@0: // pass through for chrome URLs, since they can be referenced without michael@0: // this service michael@0: return NS_NewURI(aOutput, aSpec); michael@0: } michael@0: michael@0: nsAutoCString annoUri; michael@0: annoUri.AssignLiteral("moz-anno:" FAVICON_ANNOTATION_NAME ":"); michael@0: annoUri += aSpec; michael@0: return NS_NewURI(aOutput, annoUri); michael@0: } michael@0: michael@0: michael@0: // nsFaviconService::GetFaviconSpecForIconString michael@0: // michael@0: // This computes a favicon spec for when you don't want a URI object (as in michael@0: // the tree view implementation), sparing all parsing and normalization. michael@0: void michael@0: nsFaviconService::GetFaviconSpecForIconString(const nsCString& aSpec, michael@0: nsACString& aOutput) michael@0: { michael@0: if (aSpec.IsEmpty()) { michael@0: aOutput.AssignLiteral(FAVICON_DEFAULT_URL); michael@0: } else if (StringBeginsWith(aSpec, NS_LITERAL_CSTRING("chrome:"))) { michael@0: aOutput = aSpec; michael@0: } else { michael@0: aOutput.AssignLiteral("moz-anno:" FAVICON_ANNOTATION_NAME ":"); michael@0: aOutput += aSpec; michael@0: } michael@0: } michael@0: michael@0: michael@0: // nsFaviconService::OptimizeFaviconImage michael@0: // michael@0: // Given a blob of data (a image file already read into a buffer), optimize michael@0: // its size by recompressing it as a 16x16 PNG. michael@0: nsresult michael@0: nsFaviconService::OptimizeFaviconImage(const uint8_t* aData, uint32_t aDataLen, michael@0: const nsACString& aMimeType, michael@0: nsACString& aNewData, michael@0: nsACString& aNewMimeType) michael@0: { michael@0: nsresult rv; michael@0: michael@0: nsCOMPtr imgtool = do_CreateInstance("@mozilla.org/image/tools;1"); michael@0: michael@0: nsCOMPtr stream; michael@0: rv = NS_NewByteInputStream(getter_AddRefs(stream), michael@0: reinterpret_cast(aData), aDataLen, michael@0: NS_ASSIGNMENT_DEPEND); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // decode image michael@0: nsCOMPtr container; michael@0: rv = imgtool->DecodeImageData(stream, aMimeType, getter_AddRefs(container)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: aNewMimeType.AssignLiteral(DEFAULT_MIME_TYPE); michael@0: michael@0: // scale and recompress michael@0: nsCOMPtr iconStream; michael@0: rv = imgtool->EncodeScaledImage(container, aNewMimeType, michael@0: mOptimizedIconDimension, michael@0: mOptimizedIconDimension, michael@0: EmptyString(), michael@0: getter_AddRefs(iconStream)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // Read the stream into a new buffer. michael@0: rv = NS_ConsumeStream(iconStream, UINT32_MAX, aNewData); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsFaviconService::GetFaviconDataAsync(nsIURI* aFaviconURI, michael@0: mozIStorageStatementCallback *aCallback) michael@0: { michael@0: NS_ASSERTION(aCallback, "Doesn't make sense to call this without a callback"); michael@0: nsCOMPtr stmt = mDB->GetAsyncStatement( michael@0: "SELECT f.data, f.mime_type FROM moz_favicons f WHERE url = :icon_url" michael@0: ); michael@0: NS_ENSURE_STATE(stmt); michael@0: michael@0: nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("icon_url"), aFaviconURI); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsCOMPtr pendingStatement; michael@0: return stmt->ExecuteAsync(aCallback, getter_AddRefs(pendingStatement)); michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: //// ExpireFaviconsStatementCallbackNotifier michael@0: michael@0: ExpireFaviconsStatementCallbackNotifier::ExpireFaviconsStatementCallbackNotifier() michael@0: { michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: ExpireFaviconsStatementCallbackNotifier::HandleCompletion(uint16_t aReason) michael@0: { michael@0: // We should dispatch only if expiration has been successful. michael@0: if (aReason != mozIStorageStatementCallback::REASON_FINISHED) michael@0: return NS_OK; michael@0: michael@0: nsCOMPtr observerService = michael@0: mozilla::services::GetObserverService(); michael@0: if (observerService) { michael@0: (void)observerService->NotifyObservers(nullptr, michael@0: NS_PLACES_FAVICONS_EXPIRED_TOPIC_ID, michael@0: nullptr); michael@0: } michael@0: michael@0: return NS_OK; michael@0: }