toolkit/components/places/nsFaviconService.cpp

changeset 0
6474c204b198
     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 +}

mercurial