toolkit/components/places/AsyncFaviconHelpers.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
     2  * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
     3  * This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include "AsyncFaviconHelpers.h"
     9 #include "nsICacheService.h"
    10 #include "nsICacheEntry.h"
    11 #include "nsICachingChannel.h"
    12 #include "nsIAsyncVerifyRedirectCallback.h"
    14 #include "nsNavHistory.h"
    15 #include "nsFaviconService.h"
    16 #include "mozilla/storage.h"
    17 #include "nsNetUtil.h"
    18 #include "nsPrintfCString.h"
    19 #include "nsStreamUtils.h"
    20 #include "nsIPrivateBrowsingChannel.h"
    21 #include "nsISupportsPriority.h"
    22 #include <algorithm>
    24 using namespace mozilla::places;
    25 using namespace mozilla::storage;
    27 namespace mozilla {
    28 namespace places {
    30 namespace {
    32 /**
    33  * Fetches information on a page from the Places database.
    34  *
    35  * @param aDBConn
    36  *        Database connection to history tables.
    37  * @param _page
    38  *        Page that should be fetched.
    39  */
    40 nsresult
    41 FetchPageInfo(nsRefPtr<Database>& aDB,
    42               PageData& _page)
    43 {
    44   NS_PRECONDITION(_page.spec.Length(), "Must have a non-empty spec!");
    45   NS_PRECONDITION(!NS_IsMainThread(),
    46                   "This should not be called on the main thread");
    48   // This query finds the bookmarked uri we want to set the icon for,
    49   // walking up to two redirect levels.
    50   nsCString query = nsPrintfCString(
    51     "SELECT h.id, h.favicon_id, h.guid, ( "
    52       "SELECT h.url FROM moz_bookmarks b WHERE b.fk = h.id "
    53       "UNION ALL " // Union not directly bookmarked pages.
    54       "SELECT url FROM moz_places WHERE id = ( "
    55         "SELECT COALESCE(grandparent.place_id, parent.place_id) as r_place_id "
    56         "FROM moz_historyvisits dest "
    57         "LEFT JOIN moz_historyvisits parent ON parent.id = dest.from_visit "
    58                                           "AND dest.visit_type IN (%d, %d) "
    59         "LEFT JOIN moz_historyvisits grandparent ON parent.from_visit = grandparent.id "
    60           "AND parent.visit_type IN (%d, %d) "
    61         "WHERE dest.place_id = h.id "
    62         "AND EXISTS(SELECT 1 FROM moz_bookmarks b WHERE b.fk = r_place_id) "
    63         "LIMIT 1 "
    64       ") "
    65     ") FROM moz_places h WHERE h.url = :page_url",
    66     nsINavHistoryService::TRANSITION_REDIRECT_PERMANENT,
    67     nsINavHistoryService::TRANSITION_REDIRECT_TEMPORARY,
    68     nsINavHistoryService::TRANSITION_REDIRECT_PERMANENT,
    69     nsINavHistoryService::TRANSITION_REDIRECT_TEMPORARY
    70   );
    72   nsCOMPtr<mozIStorageStatement> stmt = aDB->GetStatement(query);
    73   NS_ENSURE_STATE(stmt);
    74   mozStorageStatementScoper scoper(stmt);
    76   nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"),
    77                                 _page.spec);
    78   NS_ENSURE_SUCCESS(rv, rv);
    80   bool hasResult;
    81   rv = stmt->ExecuteStep(&hasResult);
    82   NS_ENSURE_SUCCESS(rv, rv);
    83   if (!hasResult) {
    84     // The page does not exist.
    85     return NS_ERROR_NOT_AVAILABLE;
    86   }
    88   rv = stmt->GetInt64(0, &_page.id);
    89   NS_ENSURE_SUCCESS(rv, rv);
    90   bool isNull;
    91   rv = stmt->GetIsNull(1, &isNull);
    92   NS_ENSURE_SUCCESS(rv, rv);
    93   // favicon_id can be nullptr.
    94   if (!isNull) {
    95     rv = stmt->GetInt64(1, &_page.iconId);
    96     NS_ENSURE_SUCCESS(rv, rv);
    97   }
    98   rv = stmt->GetUTF8String(2, _page.guid);
    99   NS_ENSURE_SUCCESS(rv, rv);
   100   rv = stmt->GetIsNull(3, &isNull);
   101   NS_ENSURE_SUCCESS(rv, rv);
   102   // The page could not be bookmarked.
   103   if (!isNull) {
   104     rv = stmt->GetUTF8String(3, _page.bookmarkedSpec);
   105     NS_ENSURE_SUCCESS(rv, rv);
   106   }
   108   if (!_page.canAddToHistory) {
   109     // Either history is disabled or the scheme is not supported.  In such a
   110     // case we want to update the icon only if the page is bookmarked.
   112     if (_page.bookmarkedSpec.IsEmpty()) {
   113       // The page is not bookmarked.  Since updating the icon with a disabled
   114       // history would be a privacy leak, bail out as if the page did not exist.
   115       return NS_ERROR_NOT_AVAILABLE;
   116     }
   117     else {
   118       // The page, or a redirect to it, is bookmarked.  If the bookmarked spec
   119       // is different from the requested one, use it.
   120       if (!_page.bookmarkedSpec.Equals(_page.spec)) {
   121         _page.spec = _page.bookmarkedSpec;
   122         rv = FetchPageInfo(aDB, _page);
   123         NS_ENSURE_SUCCESS(rv, rv);
   124       }
   125     }
   126   }
   128   return NS_OK;
   129 }
   131 /**
   132  * Stores information on a icon in the database.
   133  *
   134  * @param aDBConn
   135  *        Database connection to history tables.
   136  * @param aIcon
   137  *        Icon that should be stored.
   138  */
   139 nsresult
   140 SetIconInfo(nsRefPtr<Database>& aDB,
   141             IconData& aIcon)
   142 {
   143   NS_PRECONDITION(!NS_IsMainThread(),
   144                   "This should not be called on the main thread");
   146   // The 'multi-coalesce' here ensures that replacing a favicon without
   147   // specifying a :guid parameter doesn't cause it to be allocated a new
   148   // GUID.
   149   nsCOMPtr<mozIStorageStatement> stmt = aDB->GetStatement(
   150     "INSERT OR REPLACE INTO moz_favicons "
   151       "(id, url, data, mime_type, expiration, guid) "
   152     "VALUES ((SELECT id FROM moz_favicons WHERE url = :icon_url), "
   153             ":icon_url, :data, :mime_type, :expiration, "
   154             "COALESCE(:guid, "
   155                      "(SELECT guid FROM moz_favicons "
   156                       "WHERE url = :icon_url), "
   157                      "GENERATE_GUID()))"
   158   );
   159   NS_ENSURE_STATE(stmt);
   160   mozStorageStatementScoper scoper(stmt);
   161   nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("icon_url"), aIcon.spec);
   162   NS_ENSURE_SUCCESS(rv, rv);
   163   rv = stmt->BindBlobByName(NS_LITERAL_CSTRING("data"),
   164                             TO_INTBUFFER(aIcon.data), aIcon.data.Length());
   165   NS_ENSURE_SUCCESS(rv, rv);
   166   rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("mime_type"), aIcon.mimeType);
   167   NS_ENSURE_SUCCESS(rv, rv);
   168   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("expiration"), aIcon.expiration);
   169   NS_ENSURE_SUCCESS(rv, rv);
   171   // Binding a GUID allows us to override the current (or generated) GUID.
   172   if (aIcon.guid.IsEmpty()) {
   173     rv = stmt->BindNullByName(NS_LITERAL_CSTRING("guid"));
   174   }
   175   else {
   176     rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"), aIcon.guid);
   177   }
   178   NS_ENSURE_SUCCESS(rv, rv);
   180   rv = stmt->Execute();
   181   NS_ENSURE_SUCCESS(rv, rv);
   183   return NS_OK;
   184 }
   186 /**
   187  * Fetches information on a icon from the Places database.
   188  *
   189  * @param aDBConn
   190  *        Database connection to history tables.
   191  * @param _icon
   192  *        Icon that should be fetched.
   193  */
   194 nsresult
   195 FetchIconInfo(nsRefPtr<Database>& aDB,
   196               IconData& _icon)
   197 {
   198   NS_PRECONDITION(_icon.spec.Length(), "Must have a non-empty spec!");
   199   NS_PRECONDITION(!NS_IsMainThread(),
   200                   "This should not be called on the main thread");
   202   if (_icon.status & ICON_STATUS_CACHED) {
   203     return NS_OK;
   204   }
   206   nsCOMPtr<mozIStorageStatement> stmt = aDB->GetStatement(
   207     "SELECT id, expiration, data, mime_type "
   208     "FROM moz_favicons WHERE url = :icon_url"
   209   );
   210   NS_ENSURE_STATE(stmt);
   211   mozStorageStatementScoper scoper(stmt);
   213   DebugOnly<nsresult> rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("icon_url"),
   214                                            _icon.spec);
   215   MOZ_ASSERT(NS_SUCCEEDED(rv));
   217   bool hasResult;
   218   rv = stmt->ExecuteStep(&hasResult);
   219   MOZ_ASSERT(NS_SUCCEEDED(rv));
   220   if (!hasResult) {
   221     // The icon does not exist yet, bail out.
   222     return NS_OK;
   223   }
   225   rv = stmt->GetInt64(0, &_icon.id);
   226   MOZ_ASSERT(NS_SUCCEEDED(rv));
   228   // Expiration can be nullptr.
   229   bool isNull;
   230   rv = stmt->GetIsNull(1, &isNull);
   231   MOZ_ASSERT(NS_SUCCEEDED(rv));
   232   if (!isNull) {
   233     rv = stmt->GetInt64(1, reinterpret_cast<int64_t*>(&_icon.expiration));
   234     MOZ_ASSERT(NS_SUCCEEDED(rv));
   235   }
   237   // Data can be nullptr.
   238   rv = stmt->GetIsNull(2, &isNull);
   239   MOZ_ASSERT(NS_SUCCEEDED(rv));
   240   if (!isNull) {
   241     uint8_t* data;
   242     uint32_t dataLen = 0;
   243     rv = stmt->GetBlob(2, &dataLen, &data);
   244     MOZ_ASSERT(NS_SUCCEEDED(rv));
   245     _icon.data.Adopt(TO_CHARBUFFER(data), dataLen);
   246     // Read mime only if we have data.
   247     rv = stmt->GetUTF8String(3, _icon.mimeType);
   248     MOZ_ASSERT(NS_SUCCEEDED(rv));
   249   }
   251   return NS_OK;
   252 }
   254 nsresult
   255 FetchIconURL(nsRefPtr<Database>& aDB,
   256              const nsACString& aPageSpec,
   257              nsACString& aIconSpec)
   258 {
   259   NS_PRECONDITION(!aPageSpec.IsEmpty(), "Page spec must not be empty.");
   260   NS_PRECONDITION(!NS_IsMainThread(),
   261                   "This should not be called on the main thread.");
   263   aIconSpec.Truncate();
   265   nsCOMPtr<mozIStorageStatement> stmt = aDB->GetStatement(
   266     "SELECT f.url "
   267     "FROM moz_places h "
   268     "JOIN moz_favicons f ON h.favicon_id = f.id "
   269     "WHERE h.url = :page_url"
   270   );
   271   NS_ENSURE_STATE(stmt);
   272   mozStorageStatementScoper scoper(stmt);
   274   nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"),
   275                                 aPageSpec);
   276   NS_ENSURE_SUCCESS(rv, rv);
   278   bool hasResult;
   279   if (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
   280     rv = stmt->GetUTF8String(0, aIconSpec);
   281     NS_ENSURE_SUCCESS(rv, rv);
   282   }
   284   return NS_OK;
   285 }
   287 /**
   288  * Tries to compute the expiration time for a icon from the channel.
   289  *
   290  * @param aChannel
   291  *        The network channel used to fetch the icon.
   292  * @return a valid expiration value for the fetched icon.
   293  */
   294 PRTime
   295 GetExpirationTimeFromChannel(nsIChannel* aChannel)
   296 {
   297   NS_PRECONDITION(NS_IsMainThread(),
   298                   "This should be called on the main thread");
   300   // Attempt to get an expiration time from the cache.  If this fails, we'll
   301   // make one up.
   302   PRTime expiration = -1;
   303   nsCOMPtr<nsICachingChannel> cachingChannel = do_QueryInterface(aChannel);
   304   if (cachingChannel) {
   305     nsCOMPtr<nsISupports> cacheToken;
   306     nsresult rv = cachingChannel->GetCacheToken(getter_AddRefs(cacheToken));
   307     if (NS_SUCCEEDED(rv)) {
   308       nsCOMPtr<nsICacheEntry> cacheEntry = do_QueryInterface(cacheToken);
   309       uint32_t seconds;
   310       rv = cacheEntry->GetExpirationTime(&seconds);
   311       if (NS_SUCCEEDED(rv)) {
   312         // Set the expiration, but make sure we honor our cap.
   313         expiration = PR_Now() + std::min((PRTime)seconds * PR_USEC_PER_SEC,
   314                                        MAX_FAVICON_EXPIRATION);
   315       }
   316     }
   317   }
   318   // If we did not obtain a time from the cache, use the cap value.
   319   return expiration < 0 ? PR_Now() + MAX_FAVICON_EXPIRATION
   320                         : expiration;
   321 }
   323 /**
   324  * Checks the icon and evaluates if it needs to be optimized.  In such a case it
   325  * will try to reduce its size through OptimizeFaviconImage method of the
   326  * favicons service.
   327  *
   328  * @param aIcon
   329  *        The icon to be evaluated.
   330  * @param aFaviconSvc
   331  *        Pointer to the favicons service.
   332  */
   333 nsresult
   334 OptimizeIconSize(IconData& aIcon,
   335                  nsFaviconService* aFaviconSvc)
   336 {
   337   NS_PRECONDITION(NS_IsMainThread(),
   338                   "This should be called on the main thread");
   340   // Even if the page provides a large image for the favicon (eg, a highres
   341   // image or a multiresolution .ico file), don't try to store more data than
   342   // needed.
   343   nsAutoCString newData, newMimeType;
   344   if (aIcon.data.Length() > MAX_ICON_FILESIZE(aFaviconSvc->GetOptimizedIconDimension())) {
   345     nsresult rv = aFaviconSvc->OptimizeFaviconImage(TO_INTBUFFER(aIcon.data),
   346                                                     aIcon.data.Length(),
   347                                                     aIcon.mimeType,
   348                                                     newData,
   349                                                     newMimeType);
   350     if (NS_SUCCEEDED(rv) && newData.Length() < aIcon.data.Length()) {
   351       aIcon.data = newData;
   352       aIcon.mimeType = newMimeType;
   353     }
   354   }
   355   return NS_OK;
   356 }
   358 } // Anonymous namespace.
   361 ////////////////////////////////////////////////////////////////////////////////
   362 //// AsyncFaviconHelperBase
   364 AsyncFaviconHelperBase::AsyncFaviconHelperBase(
   365   nsCOMPtr<nsIFaviconDataCallback>& aCallback
   366 ) : mDB(Database::GetDatabase())
   367 {
   368   // Don't AddRef or Release in runnables for thread-safety.
   369   mCallback.swap(aCallback);
   370 }
   372 AsyncFaviconHelperBase::~AsyncFaviconHelperBase()
   373 {
   374   nsCOMPtr<nsIThread> thread;
   375   (void)NS_GetMainThread(getter_AddRefs(thread));
   376   if (mCallback) {
   377     (void)NS_ProxyRelease(thread, mCallback, true);
   378   }
   379 }
   381 ////////////////////////////////////////////////////////////////////////////////
   382 //// AsyncFetchAndSetIconForPage
   384 // static
   385 nsresult
   386 AsyncFetchAndSetIconForPage::start(nsIURI* aFaviconURI,
   387                                    nsIURI* aPageURI,
   388                                    enum AsyncFaviconFetchMode aFetchMode,
   389                                    uint32_t aFaviconLoadType,
   390                                    nsIFaviconDataCallback* aCallback)
   391 {
   392   NS_PRECONDITION(NS_IsMainThread(),
   393                   "This should be called on the main thread");
   395   PageData page;
   396   nsresult rv = aPageURI->GetSpec(page.spec);
   397   NS_ENSURE_SUCCESS(rv, rv);
   398   // URIs can arguably miss a host.
   399   (void)GetReversedHostname(aPageURI, page.revHost);
   400   bool canAddToHistory;
   401   nsNavHistory* navHistory = nsNavHistory::GetHistoryService();
   402   NS_ENSURE_TRUE(navHistory, NS_ERROR_OUT_OF_MEMORY);
   403   rv = navHistory->CanAddURI(aPageURI, &canAddToHistory);
   404   NS_ENSURE_SUCCESS(rv, rv);
   405   page.canAddToHistory = !!canAddToHistory && aFaviconLoadType != nsIFaviconService::FAVICON_LOAD_PRIVATE;
   407   IconData icon;
   409   nsFaviconService* favicons = nsFaviconService::GetFaviconService();
   410   NS_ENSURE_STATE(favicons);
   412   UnassociatedIconHashKey* iconKey =
   413     favicons->mUnassociatedIcons.GetEntry(aFaviconURI);
   415   if (iconKey) {
   416     icon = iconKey->iconData;
   417     favicons->mUnassociatedIcons.RemoveEntry(aFaviconURI);
   418   } else {
   419     icon.fetchMode = aFetchMode;
   420     rv = aFaviconURI->GetSpec(icon.spec);
   421     NS_ENSURE_SUCCESS(rv, rv);
   422   }
   424   // If the page url points to an image, the icon's url will be the same.
   425   // In future evaluate to store a resample of the image.  For now avoid that
   426   // for database size concerns.
   427   // Don't store favicons for error pages too.
   428   if (icon.spec.Equals(page.spec) ||
   429       icon.spec.Equals(FAVICON_ERRORPAGE_URL)) {
   430     return NS_OK;
   431   }
   433   // The event will swap owning pointers, thus we need a new pointer.
   434   nsCOMPtr<nsIFaviconDataCallback> callback(aCallback);
   435   nsRefPtr<AsyncFetchAndSetIconForPage> event =
   436     new AsyncFetchAndSetIconForPage(icon, page, aFaviconLoadType, callback);
   438   // Get the target thread and start the work.
   439   nsRefPtr<Database> DB = Database::GetDatabase();
   440   NS_ENSURE_STATE(DB);
   441   DB->DispatchToAsyncThread(event);
   443   return NS_OK;
   444 }
   446 AsyncFetchAndSetIconForPage::AsyncFetchAndSetIconForPage(
   447   IconData& aIcon
   448 , PageData& aPage
   449 , uint32_t aFaviconLoadType
   450 , nsCOMPtr<nsIFaviconDataCallback>& aCallback
   451 ) : AsyncFaviconHelperBase(aCallback)
   452   , mIcon(aIcon)
   453   , mPage(aPage)
   454   , mFaviconLoadPrivate(aFaviconLoadType == nsIFaviconService::FAVICON_LOAD_PRIVATE)
   455 {
   456 }
   458 AsyncFetchAndSetIconForPage::~AsyncFetchAndSetIconForPage()
   459 {
   460 }
   462 NS_IMETHODIMP
   463 AsyncFetchAndSetIconForPage::Run()
   464 {
   465   NS_PRECONDITION(!NS_IsMainThread(),
   466                   "This should not be called on the main thread");
   468   // Try to fetch the icon from the database.
   469   nsresult rv = FetchIconInfo(mDB, mIcon);
   470   NS_ENSURE_SUCCESS(rv, rv);
   472   bool isInvalidIcon = mIcon.data.IsEmpty() ||
   473                        (mIcon.expiration && PR_Now() > mIcon.expiration);
   474   bool fetchIconFromNetwork = mIcon.fetchMode == FETCH_ALWAYS ||
   475                               (mIcon.fetchMode == FETCH_IF_MISSING && isInvalidIcon);
   477   if (!fetchIconFromNetwork) {
   478     // There is already a valid icon or we don't want to fetch a new one,
   479     // directly proceed with association.
   480     nsRefPtr<AsyncAssociateIconToPage> event =
   481         new AsyncAssociateIconToPage(mIcon, mPage, mCallback);
   482     mDB->DispatchToAsyncThread(event);
   484     return NS_OK;
   485   }
   486   else {
   487     // Fetch the icon from network.  When done this will associate the
   488     // icon to the page and notify.
   489     nsRefPtr<AsyncFetchAndSetIconFromNetwork> event =
   490       new AsyncFetchAndSetIconFromNetwork(mIcon, mPage, mFaviconLoadPrivate, mCallback);
   492     // Start the work on the main thread.
   493     rv = NS_DispatchToMainThread(event);
   494     NS_ENSURE_SUCCESS(rv, rv);
   495   }
   497   return NS_OK;
   498 }
   500 ////////////////////////////////////////////////////////////////////////////////
   501 //// AsyncFetchAndSetIconFromNetwork
   503 NS_IMPL_ISUPPORTS_INHERITED(
   504   AsyncFetchAndSetIconFromNetwork
   505 , nsRunnable
   506 , nsIStreamListener
   507 , nsIInterfaceRequestor
   508 , nsIChannelEventSink
   509 )
   511 AsyncFetchAndSetIconFromNetwork::AsyncFetchAndSetIconFromNetwork(
   512   IconData& aIcon
   513 , PageData& aPage
   514 , bool aFaviconLoadPrivate
   515 , nsCOMPtr<nsIFaviconDataCallback>& aCallback
   516 )
   517 : AsyncFaviconHelperBase(aCallback)
   518 , mIcon(aIcon)
   519 , mPage(aPage)
   520 , mFaviconLoadPrivate(aFaviconLoadPrivate)
   521 {
   522 }
   524 AsyncFetchAndSetIconFromNetwork::~AsyncFetchAndSetIconFromNetwork()
   525 {
   526 }
   528 NS_IMETHODIMP
   529 AsyncFetchAndSetIconFromNetwork::Run()
   530 {
   531   NS_PRECONDITION(NS_IsMainThread(),
   532                   "This should be called on the main thread");
   534   // Ensure data is cleared, since it's going to be overwritten.
   535   if (mIcon.data.Length() > 0) {
   536     mIcon.data.Truncate(0);
   537     mIcon.mimeType.Truncate(0);
   538   }
   540   nsCOMPtr<nsIURI> iconURI;
   541   nsresult rv = NS_NewURI(getter_AddRefs(iconURI), mIcon.spec);
   542   NS_ENSURE_SUCCESS(rv, rv);
   543   nsCOMPtr<nsIChannel> channel;
   544   rv = NS_NewChannel(getter_AddRefs(channel), iconURI);
   545   NS_ENSURE_SUCCESS(rv, rv);
   546   nsCOMPtr<nsIInterfaceRequestor> listenerRequestor =
   547     do_QueryInterface(reinterpret_cast<nsISupports*>(this));
   548   NS_ENSURE_STATE(listenerRequestor);
   549   rv = channel->SetNotificationCallbacks(listenerRequestor);
   550   NS_ENSURE_SUCCESS(rv, rv);
   551   nsCOMPtr<nsIPrivateBrowsingChannel> pbChannel = do_QueryInterface(channel);
   552   if (pbChannel) {
   553     rv = pbChannel->SetPrivate(mFaviconLoadPrivate);
   554     NS_ENSURE_SUCCESS(rv, rv);
   555   }
   557   nsCOMPtr<nsISupportsPriority> priorityChannel = do_QueryInterface(channel);
   558   if (priorityChannel) {
   559     priorityChannel->AdjustPriority(nsISupportsPriority::PRIORITY_LOWEST);
   560   }
   562   return channel->AsyncOpen(this, nullptr);
   563 }
   565 NS_IMETHODIMP
   566 AsyncFetchAndSetIconFromNetwork::OnStartRequest(nsIRequest* aRequest,
   567                                                 nsISupports* aContext)
   568 {
   569   return NS_OK;
   570 }
   572 NS_IMETHODIMP
   573 AsyncFetchAndSetIconFromNetwork::OnDataAvailable(nsIRequest* aRequest,
   574                                                  nsISupports* aContext,
   575                                                  nsIInputStream* aInputStream,
   576                                                  uint64_t aOffset,
   577                                                  uint32_t aCount)
   578 {
   579   nsAutoCString buffer;
   580   nsresult rv = NS_ConsumeStream(aInputStream, aCount, buffer);
   581   if (rv != NS_BASE_STREAM_WOULD_BLOCK && NS_FAILED(rv)) {
   582     return rv;
   583   }
   585   mIcon.data.Append(buffer);
   586   return NS_OK;
   587 }
   590 NS_IMETHODIMP
   591 AsyncFetchAndSetIconFromNetwork::GetInterface(const nsIID& uuid,
   592                                               void** aResult)
   593 {
   594   return QueryInterface(uuid, aResult);
   595 }
   598 NS_IMETHODIMP
   599 AsyncFetchAndSetIconFromNetwork::AsyncOnChannelRedirect(
   600   nsIChannel* oldChannel
   601 , nsIChannel* newChannel
   602 , uint32_t flags
   603 , nsIAsyncVerifyRedirectCallback *cb
   604 )
   605 {
   606   (void)cb->OnRedirectVerifyCallback(NS_OK);
   607   return NS_OK;
   608 }
   610 NS_IMETHODIMP
   611 AsyncFetchAndSetIconFromNetwork::OnStopRequest(nsIRequest* aRequest,
   612                                                nsISupports* aContext,
   613                                                nsresult aStatusCode)
   614 {
   615   MOZ_ASSERT(NS_IsMainThread());
   617   nsFaviconService* favicons = nsFaviconService::GetFaviconService();
   618   NS_ENSURE_STATE(favicons);
   620   nsresult rv;
   622   // If fetching the icon failed, add it to the failed cache.
   623   if (NS_FAILED(aStatusCode) || mIcon.data.Length() == 0) {
   624     nsCOMPtr<nsIURI> iconURI;
   625     rv = NS_NewURI(getter_AddRefs(iconURI), mIcon.spec);
   626     NS_ENSURE_SUCCESS(rv, rv);
   627     rv = favicons->AddFailedFavicon(iconURI);
   628     NS_ENSURE_SUCCESS(rv, rv);
   629     return NS_OK;
   630   }
   632   NS_SniffContent(NS_DATA_SNIFFER_CATEGORY, aRequest,
   633                   TO_INTBUFFER(mIcon.data), mIcon.data.Length(),
   634                   mIcon.mimeType);
   636   // If the icon does not have a valid MIME type, add it to the failed cache.
   637   if (mIcon.mimeType.IsEmpty()) {
   638     nsCOMPtr<nsIURI> iconURI;
   639     rv = NS_NewURI(getter_AddRefs(iconURI), mIcon.spec);
   640     NS_ENSURE_SUCCESS(rv, rv);
   641     rv = favicons->AddFailedFavicon(iconURI);
   642     NS_ENSURE_SUCCESS(rv, rv);
   643     return NS_OK;
   644   }
   646   nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
   647   // aRequest should always QI to nsIChannel.
   648   // See AsyncFetchAndSetIconFromNetwork::Run()
   649   MOZ_ASSERT(channel);
   650   mIcon.expiration = GetExpirationTimeFromChannel(channel);
   652   rv = OptimizeIconSize(mIcon, favicons);
   653   NS_ENSURE_SUCCESS(rv, rv);
   655   // If over the maximum size allowed, don't save data to the database to
   656   // avoid bloating it.
   657   if (mIcon.data.Length() > MAX_FAVICON_SIZE) {
   658     return NS_OK;
   659   }
   661   mIcon.status = ICON_STATUS_CHANGED;
   663   nsRefPtr<AsyncAssociateIconToPage> event =
   664     new AsyncAssociateIconToPage(mIcon, mPage, mCallback);
   665   mDB->DispatchToAsyncThread(event);
   667   return NS_OK;
   668 }
   670 ////////////////////////////////////////////////////////////////////////////////
   671 //// AsyncAssociateIconToPage
   673 AsyncAssociateIconToPage::AsyncAssociateIconToPage(
   674   IconData& aIcon
   675 , PageData& aPage
   676 , nsCOMPtr<nsIFaviconDataCallback>& aCallback
   677 ) : AsyncFaviconHelperBase(aCallback)
   678   , mIcon(aIcon)
   679   , mPage(aPage)
   680 {
   681 }
   683 AsyncAssociateIconToPage::~AsyncAssociateIconToPage()
   684 {
   685 }
   687 NS_IMETHODIMP
   688 AsyncAssociateIconToPage::Run()
   689 {
   690   NS_PRECONDITION(!NS_IsMainThread(),
   691                   "This should not be called on the main thread");
   693   nsresult rv = FetchPageInfo(mDB, mPage);
   694   if (rv == NS_ERROR_NOT_AVAILABLE){
   695     // We have never seen this page.  If we can add the page to history,
   696     // we will try to do it later, otherwise just bail out.
   697     if (!mPage.canAddToHistory) {
   698       return NS_OK;
   699     }
   700   }
   701   else {
   702     NS_ENSURE_SUCCESS(rv, rv);
   703   }
   705   mozStorageTransaction transaction(mDB->MainConn(), false,
   706                                     mozIStorageConnection::TRANSACTION_IMMEDIATE);
   708   // If there is no entry for this icon, or the entry is obsolete, replace it.
   709   if (mIcon.id == 0 || (mIcon.status & ICON_STATUS_CHANGED)) {
   710     rv = SetIconInfo(mDB, mIcon);
   711     NS_ENSURE_SUCCESS(rv, rv);
   713     // Get the new icon id.  Do this regardless mIcon.id, since other code
   714     // could have added a entry before us.  Indeed we interrupted the thread
   715     // after the previous call to FetchIconInfo.
   716     mIcon.status = (mIcon.status & ~(ICON_STATUS_CACHED)) | ICON_STATUS_SAVED;
   717     rv = FetchIconInfo(mDB, mIcon);
   718     NS_ENSURE_SUCCESS(rv, rv);
   719   }
   721   // If the page does not have an id, don't try to insert a new one, cause we
   722   // don't know where the page comes from.  Not doing so we may end adding
   723   // a page that otherwise we'd explicitly ignore, like a POST or an error page.
   724   if (mPage.id == 0) {
   725     return NS_OK;
   726   }
   728   // Otherwise just associate the icon to the page, if needed.
   729   if (mPage.iconId != mIcon.id) {
   730     nsCOMPtr<mozIStorageStatement> stmt;
   731     if (mPage.id) {
   732       stmt = mDB->GetStatement(
   733         "UPDATE moz_places SET favicon_id = :icon_id WHERE id = :page_id"
   734       );
   735       NS_ENSURE_STATE(stmt);
   736       rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), mPage.id);
   737       NS_ENSURE_SUCCESS(rv, rv);
   738     }
   739     else {
   740       stmt = mDB->GetStatement(
   741         "UPDATE moz_places SET favicon_id = :icon_id WHERE url = :page_url"
   742       );
   743       NS_ENSURE_STATE(stmt);
   744       rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), mPage.spec);
   745       NS_ENSURE_SUCCESS(rv, rv);
   746     }
   747     rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("icon_id"), mIcon.id);
   748     NS_ENSURE_SUCCESS(rv, rv);
   750     mozStorageStatementScoper scoper(stmt);
   751     rv = stmt->Execute();
   752     NS_ENSURE_SUCCESS(rv, rv);
   754     mIcon.status |= ICON_STATUS_ASSOCIATED;
   755   }
   757   rv = transaction.Commit();
   758   NS_ENSURE_SUCCESS(rv, rv);
   760   // Finally, dispatch an event to the main thread to notify observers.
   761   nsCOMPtr<nsIRunnable> event = new NotifyIconObservers(mIcon, mPage, mCallback);
   762   rv = NS_DispatchToMainThread(event);
   763   NS_ENSURE_SUCCESS(rv, rv);
   765   return NS_OK;
   766 }
   768 ////////////////////////////////////////////////////////////////////////////////
   769 //// AsyncGetFaviconURLForPage
   771 // static
   772 nsresult
   773 AsyncGetFaviconURLForPage::start(nsIURI* aPageURI,
   774                                  nsIFaviconDataCallback* aCallback)
   775 {
   776   NS_ENSURE_ARG(aCallback);
   777   NS_ENSURE_ARG(aPageURI);
   778   NS_PRECONDITION(NS_IsMainThread(),
   779                   "This should be called on the main thread.");
   781   nsAutoCString pageSpec;
   782   nsresult rv = aPageURI->GetSpec(pageSpec);
   783   NS_ENSURE_SUCCESS(rv, rv);
   785   nsCOMPtr<nsIFaviconDataCallback> callback = aCallback;
   786   nsRefPtr<AsyncGetFaviconURLForPage> event =
   787     new AsyncGetFaviconURLForPage(pageSpec, callback);
   789   nsRefPtr<Database> DB = Database::GetDatabase();
   790   NS_ENSURE_STATE(DB);
   791   DB->DispatchToAsyncThread(event);
   793   return NS_OK;
   794 }
   796 AsyncGetFaviconURLForPage::AsyncGetFaviconURLForPage(
   797   const nsACString& aPageSpec
   798 , nsCOMPtr<nsIFaviconDataCallback>& aCallback
   799 ) : AsyncFaviconHelperBase(aCallback)
   800 {
   801   mPageSpec.Assign(aPageSpec);
   802 }
   804 AsyncGetFaviconURLForPage::~AsyncGetFaviconURLForPage()
   805 {
   806 }
   808 NS_IMETHODIMP
   809 AsyncGetFaviconURLForPage::Run()
   810 {
   811   NS_PRECONDITION(!NS_IsMainThread(),
   812                   "This should not be called on the main thread.");
   814   nsAutoCString iconSpec;
   815   nsresult rv = FetchIconURL(mDB, mPageSpec, iconSpec);
   816   NS_ENSURE_SUCCESS(rv, rv);
   818   // Now notify our callback of the icon spec we retrieved, even if empty.
   819   IconData iconData;
   820   iconData.spec.Assign(iconSpec);
   822   PageData pageData;
   823   pageData.spec.Assign(mPageSpec);
   825   nsCOMPtr<nsIRunnable> event =
   826     new NotifyIconObservers(iconData, pageData, mCallback);
   827   rv = NS_DispatchToMainThread(event);
   828   NS_ENSURE_SUCCESS(rv, rv);
   830   return NS_OK;
   831 }
   833 ////////////////////////////////////////////////////////////////////////////////
   834 //// AsyncGetFaviconDataForPage
   836 // static
   837 nsresult
   838 AsyncGetFaviconDataForPage::start(nsIURI* aPageURI,
   839                                   nsIFaviconDataCallback* aCallback)
   840 {
   841   NS_ENSURE_ARG(aCallback);
   842   NS_ENSURE_ARG(aPageURI);
   843   NS_PRECONDITION(NS_IsMainThread(),
   844                   "This should be called on the main thread.");
   846   nsAutoCString pageSpec;
   847   nsresult rv = aPageURI->GetSpec(pageSpec);
   848   NS_ENSURE_SUCCESS(rv, rv);
   850   nsCOMPtr<nsIFaviconDataCallback> callback = aCallback;
   851   nsRefPtr<AsyncGetFaviconDataForPage> event =
   852     new AsyncGetFaviconDataForPage(pageSpec, callback);
   854   nsRefPtr<Database> DB = Database::GetDatabase();
   855   NS_ENSURE_STATE(DB);
   856   DB->DispatchToAsyncThread(event);
   858   return NS_OK;
   859 }
   861 AsyncGetFaviconDataForPage::AsyncGetFaviconDataForPage(
   862   const nsACString& aPageSpec
   863 , nsCOMPtr<nsIFaviconDataCallback>& aCallback
   864 ) : AsyncFaviconHelperBase(aCallback)
   865 {
   866   mPageSpec.Assign(aPageSpec);
   867 }
   869 AsyncGetFaviconDataForPage::~AsyncGetFaviconDataForPage()
   870 {
   871 }
   873 NS_IMETHODIMP
   874 AsyncGetFaviconDataForPage::Run()
   875 {
   876   NS_PRECONDITION(!NS_IsMainThread(),
   877                   "This should not be called on the main thread.");
   879   nsAutoCString iconSpec;
   880   nsresult rv = FetchIconURL(mDB, mPageSpec, iconSpec);
   881   NS_ENSURE_SUCCESS(rv, rv);
   883   IconData iconData;
   884   iconData.spec.Assign(iconSpec);
   886   PageData pageData;
   887   pageData.spec.Assign(mPageSpec);
   889   if (!iconSpec.IsEmpty()) {
   890     rv = FetchIconInfo(mDB, iconData);
   891     if (NS_FAILED(rv)) {
   892       iconData.spec.Truncate();
   893     }
   894   }
   896   nsCOMPtr<nsIRunnable> event =
   897     new NotifyIconObservers(iconData, pageData, mCallback);
   898   rv = NS_DispatchToMainThread(event);
   899   NS_ENSURE_SUCCESS(rv, rv);
   900   return NS_OK;
   901 }
   903 ////////////////////////////////////////////////////////////////////////////////
   904 //// AsyncReplaceFaviconData
   906 // static
   907 nsresult
   908 AsyncReplaceFaviconData::start(IconData *aIcon)
   909 {
   910   NS_ENSURE_ARG(aIcon);
   911   NS_PRECONDITION(NS_IsMainThread(),
   912                   "This should be called on the main thread.");
   914   nsCOMPtr<nsIFaviconDataCallback> callback;
   915   nsRefPtr<AsyncReplaceFaviconData> event =
   916     new AsyncReplaceFaviconData(*aIcon, callback);
   918   nsRefPtr<Database> DB = Database::GetDatabase();
   919   NS_ENSURE_STATE(DB);
   920   DB->DispatchToAsyncThread(event);
   922   return NS_OK;
   923 }
   925 AsyncReplaceFaviconData::AsyncReplaceFaviconData(
   926   IconData &aIcon
   927 , nsCOMPtr<nsIFaviconDataCallback>& aCallback
   928 ) : AsyncFaviconHelperBase(aCallback)
   929   , mIcon(aIcon)
   930 {
   931 }
   933 AsyncReplaceFaviconData::~AsyncReplaceFaviconData()
   934 {
   935 }
   937 NS_IMETHODIMP
   938 AsyncReplaceFaviconData::Run()
   939 {
   940   NS_PRECONDITION(!NS_IsMainThread(),
   941                   "This should not be called on the main thread");
   943   IconData dbIcon;
   944   dbIcon.spec.Assign(mIcon.spec);
   945   nsresult rv = FetchIconInfo(mDB, dbIcon);
   946   NS_ENSURE_SUCCESS(rv, rv);
   948   if (!dbIcon.id) {
   949     return NS_OK;
   950   }
   952   rv = SetIconInfo(mDB, mIcon);
   953   NS_ENSURE_SUCCESS(rv, rv);
   955   // We can invalidate the cache version since we now persist the icon.
   956   nsCOMPtr<nsIRunnable> event = new RemoveIconDataCacheEntry(mIcon, mCallback);
   957   rv = NS_DispatchToMainThread(event);
   958   NS_ENSURE_SUCCESS(rv, rv);
   960   return NS_OK;
   961 }
   964 ////////////////////////////////////////////////////////////////////////////////
   965 //// RemoveIconDataCacheEntry
   967 RemoveIconDataCacheEntry::RemoveIconDataCacheEntry(
   968   IconData& aIcon
   969 , nsCOMPtr<nsIFaviconDataCallback>& aCallback
   970 )
   971 : AsyncFaviconHelperBase(aCallback)
   972 , mIcon(aIcon)
   973 {
   974 }
   976 RemoveIconDataCacheEntry::~RemoveIconDataCacheEntry()
   977 {
   978 }
   980 NS_IMETHODIMP
   981 RemoveIconDataCacheEntry::Run()
   982 {
   983   NS_PRECONDITION(NS_IsMainThread(),
   984                   "This should be called on the main thread");
   986   nsCOMPtr<nsIURI> iconURI;
   987   nsresult rv = NS_NewURI(getter_AddRefs(iconURI), mIcon.spec);
   988   NS_ENSURE_SUCCESS(rv, rv);
   990   nsFaviconService* favicons = nsFaviconService::GetFaviconService();
   991   NS_ENSURE_STATE(favicons);
   992   favicons->mUnassociatedIcons.RemoveEntry(iconURI);
   994   return NS_OK;
   995 }
   998 ////////////////////////////////////////////////////////////////////////////////
   999 //// NotifyIconObservers
  1001 NotifyIconObservers::NotifyIconObservers(
  1002   IconData& aIcon
  1003 , PageData& aPage
  1004 , nsCOMPtr<nsIFaviconDataCallback>& aCallback
  1006 : AsyncFaviconHelperBase(aCallback)
  1007 , mIcon(aIcon)
  1008 , mPage(aPage)
  1012 NotifyIconObservers::~NotifyIconObservers()
  1016 NS_IMETHODIMP
  1017 NotifyIconObservers::Run()
  1019   NS_PRECONDITION(NS_IsMainThread(),
  1020                   "This should be called on the main thread");
  1022   nsCOMPtr<nsIURI> iconURI;
  1023   if (!mIcon.spec.IsEmpty()) {
  1024     MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_NewURI(getter_AddRefs(iconURI), mIcon.spec)));
  1025     if (iconURI)
  1027       // Notify observers only if something changed.
  1028       if (mIcon.status & ICON_STATUS_SAVED ||
  1029           mIcon.status & ICON_STATUS_ASSOCIATED) {
  1030         SendGlobalNotifications(iconURI);
  1035   if (mCallback) {
  1036     (void)mCallback->OnComplete(iconURI, mIcon.data.Length(),
  1037                                 TO_INTBUFFER(mIcon.data), mIcon.mimeType);
  1040   return NS_OK;
  1043 void
  1044 NotifyIconObservers::SendGlobalNotifications(nsIURI* aIconURI)
  1046   nsCOMPtr<nsIURI> pageURI;
  1047   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_NewURI(getter_AddRefs(pageURI), mPage.spec)));
  1048   if (pageURI) {
  1049     nsFaviconService* favicons = nsFaviconService::GetFaviconService();
  1050     MOZ_ASSERT(favicons);
  1051     if (favicons) {
  1052       (void)favicons->SendFaviconNotifications(pageURI, aIconURI, mPage.guid);
  1056   // If the page is bookmarked and the bookmarked url is different from the
  1057   // updated one, start a new task to update its icon as well.
  1058   if (!mPage.bookmarkedSpec.IsEmpty() &&
  1059       !mPage.bookmarkedSpec.Equals(mPage.spec)) {
  1060     // Create a new page struct to avoid polluting it with old data.
  1061     PageData bookmarkedPage;
  1062     bookmarkedPage.spec = mPage.bookmarkedSpec;
  1064     // This will be silent, so be sure to not pass in the current callback.
  1065     nsCOMPtr<nsIFaviconDataCallback> nullCallback;
  1066     nsRefPtr<AsyncAssociateIconToPage> event =
  1067         new AsyncAssociateIconToPage(mIcon, bookmarkedPage, nullCallback);
  1068     mDB->DispatchToAsyncThread(event);
  1072 } // namespace places
  1073 } // namespace mozilla

mercurial