1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/components/places/nsFaviconService.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,613 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +/** 1.10 + * This is the favicon service, which stores favicons for web pages with your 1.11 + * history as you browse. It is also used to save the favicons for bookmarks. 1.12 + * 1.13 + * DANGER: The history query system makes assumptions about the favicon storage 1.14 + * so that icons can be quickly generated for history/bookmark result sets. If 1.15 + * you change the database layout at all, you will have to update both services. 1.16 + */ 1.17 + 1.18 +#include "nsFaviconService.h" 1.19 + 1.20 +#include "nsNavHistory.h" 1.21 +#include "nsPlacesMacros.h" 1.22 +#include "Helpers.h" 1.23 +#include "AsyncFaviconHelpers.h" 1.24 + 1.25 +#include "nsNetUtil.h" 1.26 +#include "nsReadableUtils.h" 1.27 +#include "nsStreamUtils.h" 1.28 +#include "nsStringStream.h" 1.29 +#include "plbase64.h" 1.30 +#include "nsIClassInfoImpl.h" 1.31 +#include "mozilla/ArrayUtils.h" 1.32 +#include "mozilla/Preferences.h" 1.33 + 1.34 +// For large favicons optimization. 1.35 +#include "imgITools.h" 1.36 +#include "imgIContainer.h" 1.37 + 1.38 +// Default value for mOptimizedIconDimension 1.39 +#define OPTIMIZED_FAVICON_DIMENSION 16 1.40 + 1.41 +#define MAX_FAVICON_CACHE_SIZE 256 1.42 +#define FAVICON_CACHE_REDUCE_COUNT 64 1.43 + 1.44 +#define MAX_UNASSOCIATED_FAVICONS 64 1.45 + 1.46 +// When replaceFaviconData is called, we store the icons in an in-memory cache 1.47 +// instead of in storage. Icons in the cache are expired according to this 1.48 +// interval. 1.49 +#define UNASSOCIATED_ICON_EXPIRY_INTERVAL 60000 1.50 + 1.51 +// The MIME type of the default favicon and favicons created by 1.52 +// OptimizeFaviconImage. 1.53 +#define DEFAULT_MIME_TYPE "image/png" 1.54 + 1.55 +using namespace mozilla; 1.56 +using namespace mozilla::places; 1.57 + 1.58 +/** 1.59 + * Used to notify a topic to system observers on async execute completion. 1.60 + * Will throw on error. 1.61 + */ 1.62 +class ExpireFaviconsStatementCallbackNotifier : public AsyncStatementCallback 1.63 +{ 1.64 +public: 1.65 + ExpireFaviconsStatementCallbackNotifier(); 1.66 + NS_IMETHOD HandleCompletion(uint16_t aReason); 1.67 +}; 1.68 + 1.69 + 1.70 +PLACES_FACTORY_SINGLETON_IMPLEMENTATION(nsFaviconService, gFaviconService) 1.71 + 1.72 +NS_IMPL_CLASSINFO(nsFaviconService, nullptr, 0, NS_FAVICONSERVICE_CID) 1.73 +NS_IMPL_ISUPPORTS_CI( 1.74 + nsFaviconService 1.75 +, nsIFaviconService 1.76 +, mozIAsyncFavicons 1.77 +, nsITimerCallback 1.78 +) 1.79 + 1.80 +nsFaviconService::nsFaviconService() 1.81 + : mOptimizedIconDimension(OPTIMIZED_FAVICON_DIMENSION) 1.82 + , mFailedFaviconSerial(0) 1.83 + , mFailedFavicons(MAX_FAVICON_CACHE_SIZE) 1.84 + , mUnassociatedIcons(MAX_UNASSOCIATED_FAVICONS) 1.85 +{ 1.86 + NS_ASSERTION(!gFaviconService, 1.87 + "Attempting to create two instances of the service!"); 1.88 + gFaviconService = this; 1.89 +} 1.90 + 1.91 + 1.92 +nsFaviconService::~nsFaviconService() 1.93 +{ 1.94 + NS_ASSERTION(gFaviconService == this, 1.95 + "Deleting a non-singleton instance of the service"); 1.96 + if (gFaviconService == this) 1.97 + gFaviconService = nullptr; 1.98 +} 1.99 + 1.100 + 1.101 +nsresult 1.102 +nsFaviconService::Init() 1.103 +{ 1.104 + mDB = Database::GetDatabase(); 1.105 + NS_ENSURE_STATE(mDB); 1.106 + 1.107 + mOptimizedIconDimension = Preferences::GetInt( 1.108 + "places.favicons.optimizeToDimension", OPTIMIZED_FAVICON_DIMENSION 1.109 + ); 1.110 + 1.111 + mExpireUnassociatedIconsTimer = do_CreateInstance("@mozilla.org/timer;1"); 1.112 + NS_ENSURE_STATE(mExpireUnassociatedIconsTimer); 1.113 + 1.114 + return NS_OK; 1.115 +} 1.116 + 1.117 +NS_IMETHODIMP 1.118 +nsFaviconService::ExpireAllFavicons() 1.119 +{ 1.120 + nsCOMPtr<mozIStorageAsyncStatement> unlinkIconsStmt = mDB->GetAsyncStatement( 1.121 + "UPDATE moz_places " 1.122 + "SET favicon_id = NULL " 1.123 + "WHERE favicon_id NOT NULL" 1.124 + ); 1.125 + NS_ENSURE_STATE(unlinkIconsStmt); 1.126 + nsCOMPtr<mozIStorageAsyncStatement> removeIconsStmt = mDB->GetAsyncStatement( 1.127 + "DELETE FROM moz_favicons WHERE id NOT IN (" 1.128 + "SELECT favicon_id FROM moz_places WHERE favicon_id NOT NULL " 1.129 + ")" 1.130 + ); 1.131 + NS_ENSURE_STATE(removeIconsStmt); 1.132 + 1.133 + mozIStorageBaseStatement* stmts[] = { 1.134 + unlinkIconsStmt.get() 1.135 + , removeIconsStmt.get() 1.136 + }; 1.137 + nsCOMPtr<mozIStoragePendingStatement> ps; 1.138 + nsRefPtr<ExpireFaviconsStatementCallbackNotifier> callback = 1.139 + new ExpireFaviconsStatementCallbackNotifier(); 1.140 + nsresult rv = mDB->MainConn()->ExecuteAsync( 1.141 + stmts, ArrayLength(stmts), callback, getter_AddRefs(ps) 1.142 + ); 1.143 + NS_ENSURE_SUCCESS(rv, rv); 1.144 + 1.145 + return NS_OK; 1.146 +} 1.147 + 1.148 +//////////////////////////////////////////////////////////////////////////////// 1.149 +//// nsITimerCallback 1.150 + 1.151 +static PLDHashOperator 1.152 +ExpireNonrecentUnassociatedIconsEnumerator( 1.153 + UnassociatedIconHashKey* aIconKey, 1.154 + void* aNow) 1.155 +{ 1.156 + PRTime now = *(reinterpret_cast<PRTime*>(aNow)); 1.157 + if (now - aIconKey->created >= UNASSOCIATED_ICON_EXPIRY_INTERVAL) { 1.158 + return PL_DHASH_REMOVE; 1.159 + } 1.160 + return PL_DHASH_NEXT; 1.161 +} 1.162 + 1.163 +NS_IMETHODIMP 1.164 +nsFaviconService::Notify(nsITimer* timer) 1.165 +{ 1.166 + if (timer != mExpireUnassociatedIconsTimer.get()) { 1.167 + return NS_ERROR_INVALID_ARG; 1.168 + } 1.169 + 1.170 + PRTime now = PR_Now(); 1.171 + mUnassociatedIcons.EnumerateEntries( 1.172 + ExpireNonrecentUnassociatedIconsEnumerator, &now); 1.173 + // Re-init the expiry timer if the cache isn't empty. 1.174 + if (mUnassociatedIcons.Count() > 0) { 1.175 + mExpireUnassociatedIconsTimer->InitWithCallback( 1.176 + this, UNASSOCIATED_ICON_EXPIRY_INTERVAL, nsITimer::TYPE_ONE_SHOT); 1.177 + } 1.178 + 1.179 + return NS_OK; 1.180 +} 1.181 + 1.182 +//////////////////////////////////////////////////////////////////////////////// 1.183 +//// nsIFaviconService 1.184 + 1.185 +NS_IMETHODIMP 1.186 +nsFaviconService::GetDefaultFavicon(nsIURI** _retval) 1.187 +{ 1.188 + NS_ENSURE_ARG_POINTER(_retval); 1.189 + 1.190 + // not found, use default 1.191 + if (!mDefaultIcon) { 1.192 + nsresult rv = NS_NewURI(getter_AddRefs(mDefaultIcon), 1.193 + NS_LITERAL_CSTRING(FAVICON_DEFAULT_URL)); 1.194 + NS_ENSURE_SUCCESS(rv, rv); 1.195 + } 1.196 + return mDefaultIcon->Clone(_retval); 1.197 +} 1.198 + 1.199 +void 1.200 +nsFaviconService::SendFaviconNotifications(nsIURI* aPageURI, 1.201 + nsIURI* aFaviconURI, 1.202 + const nsACString& aGUID) 1.203 +{ 1.204 + nsAutoCString faviconSpec; 1.205 + nsNavHistory* history = nsNavHistory::GetHistoryService(); 1.206 + if (history && NS_SUCCEEDED(aFaviconURI->GetSpec(faviconSpec))) { 1.207 + history->SendPageChangedNotification(aPageURI, 1.208 + nsINavHistoryObserver::ATTRIBUTE_FAVICON, 1.209 + NS_ConvertUTF8toUTF16(faviconSpec), 1.210 + aGUID); 1.211 + } 1.212 +} 1.213 + 1.214 +NS_IMETHODIMP 1.215 +nsFaviconService::SetAndFetchFaviconForPage(nsIURI* aPageURI, 1.216 + nsIURI* aFaviconURI, 1.217 + bool aForceReload, 1.218 + uint32_t aFaviconLoadType, 1.219 + nsIFaviconDataCallback* aCallback) 1.220 +{ 1.221 + NS_ENSURE_ARG(aPageURI); 1.222 + NS_ENSURE_ARG(aFaviconURI); 1.223 + 1.224 + // If a favicon is in the failed cache, only load it during a forced reload. 1.225 + bool previouslyFailed; 1.226 + nsresult rv = IsFailedFavicon(aFaviconURI, &previouslyFailed); 1.227 + NS_ENSURE_SUCCESS(rv, rv); 1.228 + if (previouslyFailed) { 1.229 + if (aForceReload) 1.230 + RemoveFailedFavicon(aFaviconURI); 1.231 + else 1.232 + return NS_OK; 1.233 + } 1.234 + 1.235 + // Check if the icon already exists and fetch it from the network, if needed. 1.236 + // Finally associate the icon to the requested page if not yet associated. 1.237 + rv = AsyncFetchAndSetIconForPage::start( 1.238 + aFaviconURI, aPageURI, aForceReload ? FETCH_ALWAYS : FETCH_IF_MISSING, 1.239 + aFaviconLoadType, aCallback 1.240 + ); 1.241 + NS_ENSURE_SUCCESS(rv, rv); 1.242 + 1.243 + // DB will be updated and observers notified when data has finished loading. 1.244 + return NS_OK; 1.245 +} 1.246 + 1.247 +NS_IMETHODIMP 1.248 +nsFaviconService::ReplaceFaviconData(nsIURI* aFaviconURI, 1.249 + const uint8_t* aData, 1.250 + uint32_t aDataLen, 1.251 + const nsACString& aMimeType, 1.252 + PRTime aExpiration) 1.253 +{ 1.254 + NS_ENSURE_ARG(aFaviconURI); 1.255 + NS_ENSURE_ARG(aData); 1.256 + NS_ENSURE_TRUE(aDataLen > 0, NS_ERROR_INVALID_ARG); 1.257 + NS_ENSURE_TRUE(aMimeType.Length() > 0, NS_ERROR_INVALID_ARG); 1.258 + if (aExpiration == 0) { 1.259 + aExpiration = PR_Now() + MAX_FAVICON_EXPIRATION; 1.260 + } 1.261 + 1.262 + UnassociatedIconHashKey* iconKey = mUnassociatedIcons.PutEntry(aFaviconURI); 1.263 + if (!iconKey) { 1.264 + return NS_ERROR_OUT_OF_MEMORY; 1.265 + } 1.266 + 1.267 + iconKey->created = PR_Now(); 1.268 + 1.269 + // If the cache contains unassociated icons, an expiry timer should already exist, otherwise 1.270 + // there may be a timer left hanging around, so make sure we fire a new one. 1.271 + int32_t unassociatedCount = mUnassociatedIcons.Count(); 1.272 + if (unassociatedCount == 1) { 1.273 + mExpireUnassociatedIconsTimer->Cancel(); 1.274 + mExpireUnassociatedIconsTimer->InitWithCallback( 1.275 + this, UNASSOCIATED_ICON_EXPIRY_INTERVAL, nsITimer::TYPE_ONE_SHOT); 1.276 + } 1.277 + 1.278 + IconData* iconData = &(iconKey->iconData); 1.279 + iconData->expiration = aExpiration; 1.280 + iconData->status = ICON_STATUS_CACHED; 1.281 + iconData->fetchMode = FETCH_NEVER; 1.282 + nsresult rv = aFaviconURI->GetSpec(iconData->spec); 1.283 + NS_ENSURE_SUCCESS(rv, rv); 1.284 + 1.285 + // If the page provided a large image for the favicon (eg, a highres image 1.286 + // or a multiresolution .ico file), we don't want to store more data than 1.287 + // needed. 1.288 + if (aDataLen > MAX_ICON_FILESIZE(mOptimizedIconDimension)) { 1.289 + rv = OptimizeFaviconImage(aData, aDataLen, aMimeType, iconData->data, iconData->mimeType); 1.290 + NS_ENSURE_SUCCESS(rv, rv); 1.291 + 1.292 + if (iconData->data.Length() > MAX_FAVICON_SIZE) { 1.293 + // We cannot optimize this favicon size and we are over the maximum size 1.294 + // allowed, so we will not save data to the db to avoid bloating it. 1.295 + mUnassociatedIcons.RemoveEntry(aFaviconURI); 1.296 + return NS_ERROR_FAILURE; 1.297 + } 1.298 + } else { 1.299 + iconData->mimeType.Assign(aMimeType); 1.300 + iconData->data.Assign(TO_CHARBUFFER(aData), aDataLen); 1.301 + } 1.302 + 1.303 + // If the database contains an icon at the given url, we will update the 1.304 + // database immediately so that the associated pages are kept in sync. 1.305 + // Otherwise, do nothing and let the icon be picked up from the memory hash. 1.306 + rv = AsyncReplaceFaviconData::start(iconData); 1.307 + NS_ENSURE_SUCCESS(rv, rv); 1.308 + 1.309 + return NS_OK; 1.310 +} 1.311 + 1.312 +NS_IMETHODIMP 1.313 +nsFaviconService::ReplaceFaviconDataFromDataURL(nsIURI* aFaviconURI, 1.314 + const nsAString& aDataURL, 1.315 + PRTime aExpiration) 1.316 +{ 1.317 + NS_ENSURE_ARG(aFaviconURI); 1.318 + NS_ENSURE_TRUE(aDataURL.Length() > 0, NS_ERROR_INVALID_ARG); 1.319 + if (aExpiration == 0) { 1.320 + aExpiration = PR_Now() + MAX_FAVICON_EXPIRATION; 1.321 + } 1.322 + 1.323 + nsCOMPtr<nsIURI> dataURI; 1.324 + nsresult rv = NS_NewURI(getter_AddRefs(dataURI), aDataURL); 1.325 + NS_ENSURE_SUCCESS(rv, rv); 1.326 + 1.327 + // Use the data: protocol handler to convert the data. 1.328 + nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv); 1.329 + NS_ENSURE_SUCCESS(rv, rv); 1.330 + nsCOMPtr<nsIProtocolHandler> protocolHandler; 1.331 + rv = ioService->GetProtocolHandler("data", getter_AddRefs(protocolHandler)); 1.332 + NS_ENSURE_SUCCESS(rv, rv); 1.333 + 1.334 + nsCOMPtr<nsIChannel> channel; 1.335 + rv = protocolHandler->NewChannel(dataURI, getter_AddRefs(channel)); 1.336 + NS_ENSURE_SUCCESS(rv, rv); 1.337 + 1.338 + // Blocking stream is OK for data URIs. 1.339 + nsCOMPtr<nsIInputStream> stream; 1.340 + rv = channel->Open(getter_AddRefs(stream)); 1.341 + NS_ENSURE_SUCCESS(rv, rv); 1.342 + 1.343 + uint64_t available64; 1.344 + rv = stream->Available(&available64); 1.345 + NS_ENSURE_SUCCESS(rv, rv); 1.346 + if (available64 == 0 || available64 > UINT32_MAX / sizeof(uint8_t)) 1.347 + return NS_ERROR_FILE_TOO_BIG; 1.348 + uint32_t available = (uint32_t)available64; 1.349 + 1.350 + // Read all the decoded data. 1.351 + uint8_t* buffer = static_cast<uint8_t*> 1.352 + (nsMemory::Alloc(sizeof(uint8_t) * available)); 1.353 + if (!buffer) 1.354 + return NS_ERROR_OUT_OF_MEMORY; 1.355 + uint32_t numRead; 1.356 + rv = stream->Read(TO_CHARBUFFER(buffer), available, &numRead); 1.357 + if (NS_FAILED(rv) || numRead != available) { 1.358 + nsMemory::Free(buffer); 1.359 + return rv; 1.360 + } 1.361 + 1.362 + nsAutoCString mimeType; 1.363 + rv = channel->GetContentType(mimeType); 1.364 + if (NS_FAILED(rv)) { 1.365 + nsMemory::Free(buffer); 1.366 + return rv; 1.367 + } 1.368 + 1.369 + // ReplaceFaviconData can now do the dirty work. 1.370 + rv = ReplaceFaviconData(aFaviconURI, buffer, available, mimeType, aExpiration); 1.371 + nsMemory::Free(buffer); 1.372 + NS_ENSURE_SUCCESS(rv, rv); 1.373 + 1.374 + return NS_OK; 1.375 +} 1.376 + 1.377 +NS_IMETHODIMP 1.378 +nsFaviconService::GetFaviconURLForPage(nsIURI *aPageURI, 1.379 + nsIFaviconDataCallback* aCallback) 1.380 +{ 1.381 + NS_ENSURE_ARG(aPageURI); 1.382 + NS_ENSURE_ARG(aCallback); 1.383 + 1.384 + nsresult rv = AsyncGetFaviconURLForPage::start(aPageURI, aCallback); 1.385 + NS_ENSURE_SUCCESS(rv, rv); 1.386 + return NS_OK; 1.387 +} 1.388 + 1.389 +NS_IMETHODIMP 1.390 +nsFaviconService::GetFaviconDataForPage(nsIURI* aPageURI, 1.391 + nsIFaviconDataCallback* aCallback) 1.392 +{ 1.393 + NS_ENSURE_ARG(aPageURI); 1.394 + NS_ENSURE_ARG(aCallback); 1.395 + 1.396 + nsresult rv = AsyncGetFaviconDataForPage::start(aPageURI, aCallback); 1.397 + NS_ENSURE_SUCCESS(rv, rv); 1.398 + return NS_OK; 1.399 +} 1.400 + 1.401 +nsresult 1.402 +nsFaviconService::GetFaviconLinkForIcon(nsIURI* aFaviconURI, 1.403 + nsIURI** aOutputURI) 1.404 +{ 1.405 + NS_ENSURE_ARG(aFaviconURI); 1.406 + NS_ENSURE_ARG_POINTER(aOutputURI); 1.407 + 1.408 + nsAutoCString spec; 1.409 + if (aFaviconURI) { 1.410 + nsresult rv = aFaviconURI->GetSpec(spec); 1.411 + NS_ENSURE_SUCCESS(rv, rv); 1.412 + } 1.413 + return GetFaviconLinkForIconString(spec, aOutputURI); 1.414 +} 1.415 + 1.416 + 1.417 +static PLDHashOperator 1.418 +ExpireFailedFaviconsCallback(nsCStringHashKey::KeyType aKey, 1.419 + uint32_t& aData, 1.420 + void* userArg) 1.421 +{ 1.422 + uint32_t* threshold = reinterpret_cast<uint32_t*>(userArg); 1.423 + if (aData < *threshold) 1.424 + return PL_DHASH_REMOVE; 1.425 + return PL_DHASH_NEXT; 1.426 +} 1.427 + 1.428 + 1.429 +NS_IMETHODIMP 1.430 +nsFaviconService::AddFailedFavicon(nsIURI* aFaviconURI) 1.431 +{ 1.432 + NS_ENSURE_ARG(aFaviconURI); 1.433 + 1.434 + nsAutoCString spec; 1.435 + nsresult rv = aFaviconURI->GetSpec(spec); 1.436 + NS_ENSURE_SUCCESS(rv, rv); 1.437 + 1.438 + mFailedFavicons.Put(spec, mFailedFaviconSerial); 1.439 + mFailedFaviconSerial ++; 1.440 + 1.441 + if (mFailedFavicons.Count() > MAX_FAVICON_CACHE_SIZE) { 1.442 + // need to expire some entries, delete the FAVICON_CACHE_REDUCE_COUNT number 1.443 + // of items that are the oldest 1.444 + uint32_t threshold = mFailedFaviconSerial - 1.445 + MAX_FAVICON_CACHE_SIZE + FAVICON_CACHE_REDUCE_COUNT; 1.446 + mFailedFavicons.Enumerate(ExpireFailedFaviconsCallback, &threshold); 1.447 + } 1.448 + return NS_OK; 1.449 +} 1.450 + 1.451 + 1.452 +NS_IMETHODIMP 1.453 +nsFaviconService::RemoveFailedFavicon(nsIURI* aFaviconURI) 1.454 +{ 1.455 + NS_ENSURE_ARG(aFaviconURI); 1.456 + 1.457 + nsAutoCString spec; 1.458 + nsresult rv = aFaviconURI->GetSpec(spec); 1.459 + NS_ENSURE_SUCCESS(rv, rv); 1.460 + 1.461 + // we silently do nothing and succeed if the icon is not in the cache 1.462 + mFailedFavicons.Remove(spec); 1.463 + return NS_OK; 1.464 +} 1.465 + 1.466 + 1.467 +NS_IMETHODIMP 1.468 +nsFaviconService::IsFailedFavicon(nsIURI* aFaviconURI, bool* _retval) 1.469 +{ 1.470 + NS_ENSURE_ARG(aFaviconURI); 1.471 + nsAutoCString spec; 1.472 + nsresult rv = aFaviconURI->GetSpec(spec); 1.473 + NS_ENSURE_SUCCESS(rv, rv); 1.474 + 1.475 + uint32_t serial; 1.476 + *_retval = mFailedFavicons.Get(spec, &serial); 1.477 + return NS_OK; 1.478 +} 1.479 + 1.480 + 1.481 +// nsFaviconService::GetFaviconLinkForIconString 1.482 +// 1.483 +// This computes a favicon URL with string input and using the cached 1.484 +// default one to minimize parsing. 1.485 + 1.486 +nsresult 1.487 +nsFaviconService::GetFaviconLinkForIconString(const nsCString& aSpec, 1.488 + nsIURI** aOutput) 1.489 +{ 1.490 + if (aSpec.IsEmpty()) { 1.491 + // default icon for empty strings 1.492 + if (! mDefaultIcon) { 1.493 + nsresult rv = NS_NewURI(getter_AddRefs(mDefaultIcon), 1.494 + NS_LITERAL_CSTRING(FAVICON_DEFAULT_URL)); 1.495 + NS_ENSURE_SUCCESS(rv, rv); 1.496 + } 1.497 + return mDefaultIcon->Clone(aOutput); 1.498 + } 1.499 + 1.500 + if (StringBeginsWith(aSpec, NS_LITERAL_CSTRING("chrome:"))) { 1.501 + // pass through for chrome URLs, since they can be referenced without 1.502 + // this service 1.503 + return NS_NewURI(aOutput, aSpec); 1.504 + } 1.505 + 1.506 + nsAutoCString annoUri; 1.507 + annoUri.AssignLiteral("moz-anno:" FAVICON_ANNOTATION_NAME ":"); 1.508 + annoUri += aSpec; 1.509 + return NS_NewURI(aOutput, annoUri); 1.510 +} 1.511 + 1.512 + 1.513 +// nsFaviconService::GetFaviconSpecForIconString 1.514 +// 1.515 +// This computes a favicon spec for when you don't want a URI object (as in 1.516 +// the tree view implementation), sparing all parsing and normalization. 1.517 +void 1.518 +nsFaviconService::GetFaviconSpecForIconString(const nsCString& aSpec, 1.519 + nsACString& aOutput) 1.520 +{ 1.521 + if (aSpec.IsEmpty()) { 1.522 + aOutput.AssignLiteral(FAVICON_DEFAULT_URL); 1.523 + } else if (StringBeginsWith(aSpec, NS_LITERAL_CSTRING("chrome:"))) { 1.524 + aOutput = aSpec; 1.525 + } else { 1.526 + aOutput.AssignLiteral("moz-anno:" FAVICON_ANNOTATION_NAME ":"); 1.527 + aOutput += aSpec; 1.528 + } 1.529 +} 1.530 + 1.531 + 1.532 +// nsFaviconService::OptimizeFaviconImage 1.533 +// 1.534 +// Given a blob of data (a image file already read into a buffer), optimize 1.535 +// its size by recompressing it as a 16x16 PNG. 1.536 +nsresult 1.537 +nsFaviconService::OptimizeFaviconImage(const uint8_t* aData, uint32_t aDataLen, 1.538 + const nsACString& aMimeType, 1.539 + nsACString& aNewData, 1.540 + nsACString& aNewMimeType) 1.541 +{ 1.542 + nsresult rv; 1.543 + 1.544 + nsCOMPtr<imgITools> imgtool = do_CreateInstance("@mozilla.org/image/tools;1"); 1.545 + 1.546 + nsCOMPtr<nsIInputStream> stream; 1.547 + rv = NS_NewByteInputStream(getter_AddRefs(stream), 1.548 + reinterpret_cast<const char*>(aData), aDataLen, 1.549 + NS_ASSIGNMENT_DEPEND); 1.550 + NS_ENSURE_SUCCESS(rv, rv); 1.551 + 1.552 + // decode image 1.553 + nsCOMPtr<imgIContainer> container; 1.554 + rv = imgtool->DecodeImageData(stream, aMimeType, getter_AddRefs(container)); 1.555 + NS_ENSURE_SUCCESS(rv, rv); 1.556 + 1.557 + aNewMimeType.AssignLiteral(DEFAULT_MIME_TYPE); 1.558 + 1.559 + // scale and recompress 1.560 + nsCOMPtr<nsIInputStream> iconStream; 1.561 + rv = imgtool->EncodeScaledImage(container, aNewMimeType, 1.562 + mOptimizedIconDimension, 1.563 + mOptimizedIconDimension, 1.564 + EmptyString(), 1.565 + getter_AddRefs(iconStream)); 1.566 + NS_ENSURE_SUCCESS(rv, rv); 1.567 + 1.568 + // Read the stream into a new buffer. 1.569 + rv = NS_ConsumeStream(iconStream, UINT32_MAX, aNewData); 1.570 + NS_ENSURE_SUCCESS(rv, rv); 1.571 + 1.572 + return NS_OK; 1.573 +} 1.574 + 1.575 +nsresult 1.576 +nsFaviconService::GetFaviconDataAsync(nsIURI* aFaviconURI, 1.577 + mozIStorageStatementCallback *aCallback) 1.578 +{ 1.579 + NS_ASSERTION(aCallback, "Doesn't make sense to call this without a callback"); 1.580 + nsCOMPtr<mozIStorageAsyncStatement> stmt = mDB->GetAsyncStatement( 1.581 + "SELECT f.data, f.mime_type FROM moz_favicons f WHERE url = :icon_url" 1.582 + ); 1.583 + NS_ENSURE_STATE(stmt); 1.584 + 1.585 + nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("icon_url"), aFaviconURI); 1.586 + NS_ENSURE_SUCCESS(rv, rv); 1.587 + 1.588 + nsCOMPtr<mozIStoragePendingStatement> pendingStatement; 1.589 + return stmt->ExecuteAsync(aCallback, getter_AddRefs(pendingStatement)); 1.590 +} 1.591 + 1.592 +//////////////////////////////////////////////////////////////////////////////// 1.593 +//// ExpireFaviconsStatementCallbackNotifier 1.594 + 1.595 +ExpireFaviconsStatementCallbackNotifier::ExpireFaviconsStatementCallbackNotifier() 1.596 +{ 1.597 +} 1.598 + 1.599 + 1.600 +NS_IMETHODIMP 1.601 +ExpireFaviconsStatementCallbackNotifier::HandleCompletion(uint16_t aReason) 1.602 +{ 1.603 + // We should dispatch only if expiration has been successful. 1.604 + if (aReason != mozIStorageStatementCallback::REASON_FINISHED) 1.605 + return NS_OK; 1.606 + 1.607 + nsCOMPtr<nsIObserverService> observerService = 1.608 + mozilla::services::GetObserverService(); 1.609 + if (observerService) { 1.610 + (void)observerService->NotifyObservers(nullptr, 1.611 + NS_PLACES_FAVICONS_EXPIRED_TOPIC_ID, 1.612 + nullptr); 1.613 + } 1.614 + 1.615 + return NS_OK; 1.616 +}