LCOV - code coverage report
Current view: directory - toolkit/components/places - AsyncFaviconHelpers.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 487 439 90.1 %
Date: 2012-06-02 Functions: 54 52 96.3 %

       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                 :  * ***** BEGIN LICENSE BLOCK *****
       4                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       5                 :  *
       6                 :  * The contents of this file are subject to the Mozilla Public License Version
       7                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       8                 :  * the License. You may obtain a copy of the License at
       9                 :  * http://www.mozilla.org/MPL/
      10                 :  *
      11                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      12                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      13                 :  * for the specific language governing rights and limitations under the
      14                 :  * License.
      15                 :  *
      16                 :  * The Original Code is Places.
      17                 :  *
      18                 :  * The Initial Developer of the Original Code is the Mozilla Foundation.
      19                 :  * Portions created by the Initial Developer are Copyright (C) 2010
      20                 :  * the Initial Developer. All Rights Reserved.
      21                 :  *
      22                 :  * Contributor(s):
      23                 :  *   Marco Bonardo <mak77@bonardo.net> (original author)
      24                 :  *   Richard Newman <rnewman@mozilla.com>
      25                 :  *
      26                 :  * Alternatively, the contents of this file may be used under the terms of
      27                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      28                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      29                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      30                 :  * of those above. If you wish to allow use of your version of this file only
      31                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      32                 :  * use your version of this file under the terms of the MPL, indicate your
      33                 :  * decision by deleting the provisions above and replace them with the notice
      34                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      35                 :  * the provisions above, a recipient may use your version of this file under
      36                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      37                 :  *
      38                 :  * ***** END LICENSE BLOCK ***** */
      39                 : 
      40                 : #include "AsyncFaviconHelpers.h"
      41                 : 
      42                 : #include "nsIContentSniffer.h"
      43                 : #include "nsICacheService.h"
      44                 : #include "nsICacheVisitor.h"
      45                 : #include "nsICachingChannel.h"
      46                 : #include "nsIAsyncVerifyRedirectCallback.h"
      47                 : 
      48                 : #include "nsNavHistory.h"
      49                 : #include "nsFaviconService.h"
      50                 : #include "mozilla/storage.h"
      51                 : #include "nsNetUtil.h"
      52                 : #include "nsPrintfCString.h"
      53                 : #include "nsStreamUtils.h"
      54                 : 
      55                 : #define CONTENT_SNIFFING_SERVICES "content-sniffing-services"
      56                 : 
      57                 : using namespace mozilla::places;
      58                 : using namespace mozilla::storage;
      59                 : 
      60                 : namespace mozilla {
      61                 : namespace places {
      62                 : 
      63                 : namespace {
      64                 : 
      65                 : /**
      66                 :  * Fetches information on a page from the Places database.
      67                 :  *
      68                 :  * @param aDBConn
      69                 :  *        Database connection to history tables.
      70                 :  * @param _page
      71                 :  *        Page that should be fetched.
      72                 :  */
      73                 : nsresult
      74              25 : FetchPageInfo(nsRefPtr<Database>& aDB,
      75                 :               PageData& _page)
      76                 : {
      77              25 :   NS_PRECONDITION(_page.spec.Length(), "Must have a non-empty spec!");
      78              25 :   NS_PRECONDITION(!NS_IsMainThread(),
      79                 :                   "This should not be called on the main thread");
      80                 : 
      81                 :   // This query fragment finds the bookmarked uri we want to set the icon for,
      82                 :   // walking up to three redirect levels.
      83                 :   nsCString redirectedBookmarksFragment =
      84                 :     nsPrintfCString(1024,
      85                 :       "SELECT h.url "
      86                 :       "FROM moz_bookmarks b "
      87                 :       "WHERE b.fk = h.id "
      88                 :       "UNION ALL " // Union not directly bookmarked pages.
      89                 :       "SELECT (SELECT url FROM moz_places WHERE id = %s) "
      90                 :       "FROM moz_historyvisits self "
      91                 :       "JOIN moz_bookmarks b ON b.fk = %s "
      92                 :       "LEFT JOIN moz_historyvisits parent ON parent.id = self.from_visit "
      93                 :       "LEFT JOIN moz_historyvisits grandparent ON parent.from_visit = grandparent.id "
      94                 :         "AND parent.visit_type IN (%d, %d) "
      95                 :       "LEFT JOIN moz_historyvisits greatgrandparent ON grandparent.from_visit = greatgrandparent.id "
      96                 :         "AND grandparent.visit_type IN (%d, %d) "
      97                 :       "WHERE self.visit_type IN (%d, %d) "
      98                 :         "AND self.place_id = h.id "
      99                 :       "LIMIT 1 ",
     100              25 :       NS_LITERAL_CSTRING("COALESCE(greatgrandparent.place_id, grandparent.place_id, parent.place_id)").get(),
     101              25 :       NS_LITERAL_CSTRING("COALESCE(greatgrandparent.place_id, grandparent.place_id, parent.place_id)").get(),
     102                 :       nsINavHistoryService::TRANSITION_REDIRECT_PERMANENT,
     103                 :       nsINavHistoryService::TRANSITION_REDIRECT_TEMPORARY,
     104                 :       nsINavHistoryService::TRANSITION_REDIRECT_PERMANENT,
     105                 :       nsINavHistoryService::TRANSITION_REDIRECT_TEMPORARY,
     106                 :       nsINavHistoryService::TRANSITION_REDIRECT_PERMANENT,
     107                 :       nsINavHistoryService::TRANSITION_REDIRECT_TEMPORARY
     108              50 :     );
     109                 : 
     110              25 :   nsCOMPtr<mozIStorageStatement> stmt = aDB->GetStatement(NS_LITERAL_CSTRING(
     111                 :     "SELECT h.id, h.favicon_id, h.guid, "
     112              50 :            "(") + redirectedBookmarksFragment + NS_LITERAL_CSTRING(") "
     113                 :     "FROM moz_places h WHERE h.url = :page_url"
     114              50 :   ));
     115              25 :   NS_ENSURE_STATE(stmt);
     116              50 :   mozStorageStatementScoper scoper(stmt);
     117                 : 
     118              25 :   nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"),
     119              25 :                                 _page.spec);
     120              25 :   NS_ENSURE_SUCCESS(rv, rv);
     121                 : 
     122                 :   bool hasResult;
     123              25 :   rv = stmt->ExecuteStep(&hasResult);
     124              25 :   NS_ENSURE_SUCCESS(rv, rv);
     125              25 :   if (!hasResult) {
     126                 :     // The page does not exist.
     127               5 :     return NS_ERROR_NOT_AVAILABLE;
     128                 :   }
     129                 : 
     130              20 :   rv = stmt->GetInt64(0, &_page.id);
     131              20 :   NS_ENSURE_SUCCESS(rv, rv);
     132                 :   bool isNull;
     133              20 :   rv = stmt->GetIsNull(1, &isNull);
     134              20 :   NS_ENSURE_SUCCESS(rv, rv);
     135                 :   // favicon_id can be NULL.
     136              20 :   if (!isNull) {
     137               3 :     rv = stmt->GetInt64(1, &_page.iconId);
     138               3 :     NS_ENSURE_SUCCESS(rv, rv);
     139                 :   }
     140              20 :   rv = stmt->GetUTF8String(2, _page.guid);
     141              20 :   NS_ENSURE_SUCCESS(rv, rv);
     142              20 :   rv = stmt->GetIsNull(3, &isNull);
     143              20 :   NS_ENSURE_SUCCESS(rv, rv);
     144                 :   // The page could not be bookmarked.
     145              20 :   if (!isNull) {
     146               6 :     rv = stmt->GetUTF8String(3, _page.bookmarkedSpec);
     147               6 :     NS_ENSURE_SUCCESS(rv, rv);
     148                 :   }
     149                 : 
     150              20 :   if (!_page.canAddToHistory) {
     151                 :     // Either history is disabled or the scheme is not supported.  In such a
     152                 :     // case we want to update the icon only if the page is bookmarked.
     153                 : 
     154               3 :     if (_page.bookmarkedSpec.IsEmpty()) {
     155                 :       // The page is not bookmarked.  Since updating the icon with a disabled
     156                 :       // history would be a privacy leak, bail out as if the page did not exist.
     157               0 :       return NS_ERROR_NOT_AVAILABLE;
     158                 :     }
     159                 :     else {
     160                 :       // The page, or a redirect to it, is bookmarked.  If the bookmarked spec
     161                 :       // is different from the requested one, use it.
     162               3 :       if (!_page.bookmarkedSpec.Equals(_page.spec)) {
     163               0 :         _page.spec = _page.bookmarkedSpec;
     164               0 :         rv = FetchPageInfo(aDB, _page);
     165               0 :         NS_ENSURE_SUCCESS(rv, rv);
     166                 :       }
     167                 :     }
     168                 :   }
     169                 : 
     170              20 :   return NS_OK;
     171                 : }
     172                 : 
     173                 : /**
     174                 :  * Stores information on a icon in the database.
     175                 :  *
     176                 :  * @param aDBConn
     177                 :  *        Database connection to history tables.
     178                 :  * @param aIcon
     179                 :  *        Icon that should be stored.
     180                 :  */
     181                 : nsresult
     182              22 : SetIconInfo(nsRefPtr<Database>& aDB,
     183                 :             IconData& aIcon)
     184                 : {
     185              22 :   NS_PRECONDITION(!NS_IsMainThread(),
     186                 :                   "This should not be called on the main thread");
     187                 : 
     188                 :   // The 'multi-coalesce' here ensures that replacing a favicon without
     189                 :   // specifying a :guid parameter doesn't cause it to be allocated a new
     190                 :   // GUID.
     191                 :   nsCOMPtr<mozIStorageStatement> stmt = aDB->GetStatement(
     192                 :     "INSERT OR REPLACE INTO moz_favicons "
     193                 :       "(id, url, data, mime_type, expiration, guid) "
     194                 :     "VALUES ((SELECT id FROM moz_favicons WHERE url = :icon_url), "
     195                 :             ":icon_url, :data, :mime_type, :expiration, "
     196                 :             "COALESCE(:guid, "
     197                 :                      "(SELECT guid FROM moz_favicons "
     198                 :                       "WHERE url = :icon_url), "
     199                 :                      "GENERATE_GUID()))"
     200              44 :   );
     201              22 :   NS_ENSURE_STATE(stmt);
     202              44 :   mozStorageStatementScoper scoper(stmt);
     203              22 :   nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("icon_url"), aIcon.spec);
     204              22 :   NS_ENSURE_SUCCESS(rv, rv);
     205              44 :   rv = stmt->BindBlobByName(NS_LITERAL_CSTRING("data"),
     206              22 :                             TO_INTBUFFER(aIcon.data), aIcon.data.Length());
     207              22 :   NS_ENSURE_SUCCESS(rv, rv);
     208              22 :   rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("mime_type"), aIcon.mimeType);
     209              22 :   NS_ENSURE_SUCCESS(rv, rv);
     210              22 :   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("expiration"), aIcon.expiration);
     211              22 :   NS_ENSURE_SUCCESS(rv, rv);
     212                 : 
     213                 :   // Binding a GUID allows us to override the current (or generated) GUID.
     214              22 :   if (aIcon.guid.IsEmpty()) {
     215              22 :     rv = stmt->BindNullByName(NS_LITERAL_CSTRING("guid"));
     216                 :   }
     217                 :   else {
     218               0 :     rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"), aIcon.guid);
     219                 :   }
     220              22 :   NS_ENSURE_SUCCESS(rv, rv);
     221                 : 
     222              22 :   rv = stmt->Execute();
     223              22 :   NS_ENSURE_SUCCESS(rv, rv);
     224                 : 
     225              22 :   return NS_OK;
     226                 : }
     227                 : 
     228                 : /**
     229                 :  * Fetches information on a icon from the Places database.
     230                 :  *
     231                 :  * @param aDBConn
     232                 :  *        Database connection to history tables.
     233                 :  * @param _icon
     234                 :  *        Icon that should be fetched.
     235                 :  */
     236                 : nsresult
     237              60 : FetchIconInfo(nsRefPtr<Database>& aDB,
     238                 :               IconData& _icon)
     239                 : {
     240              60 :   NS_PRECONDITION(_icon.spec.Length(), "Must have a non-empty spec!");
     241              60 :   NS_PRECONDITION(!NS_IsMainThread(),
     242                 :                   "This should not be called on the main thread");
     243                 : 
     244              60 :   if (_icon.status & ICON_STATUS_CACHED) {
     245               8 :     return NS_OK;
     246                 :   }
     247                 : 
     248                 :   nsCOMPtr<mozIStorageStatement> stmt = aDB->GetStatement(
     249                 :     "SELECT id, expiration, data, mime_type "
     250                 :     "FROM moz_favicons WHERE url = :icon_url"
     251             104 :   );
     252              52 :   NS_ENSURE_STATE(stmt);
     253             104 :   mozStorageStatementScoper scoper(stmt);
     254                 : 
     255              52 :   nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("icon_url"),
     256              52 :                                 _icon.spec);
     257              52 :   NS_ENSURE_SUCCESS(rv, rv);
     258                 : 
     259                 :   bool hasResult;
     260              52 :   rv = stmt->ExecuteStep(&hasResult);
     261              52 :   NS_ENSURE_SUCCESS(rv, rv);
     262              52 :   if (!hasResult) {
     263                 :     // The icon does not exist yet, bail out.
     264              25 :     return NS_OK;
     265                 :   }
     266                 : 
     267              27 :   rv = stmt->GetInt64(0, &_icon.id);
     268              27 :   NS_ENSURE_SUCCESS(rv, rv);
     269                 : 
     270                 :   // Expiration can be NULL.
     271                 :   bool isNull;
     272              27 :   rv = stmt->GetIsNull(1, &isNull);
     273              27 :   NS_ENSURE_SUCCESS(rv, rv);
     274              27 :   if (!isNull) {
     275              27 :     rv = stmt->GetInt64(1, &_icon.expiration);
     276              27 :     NS_ENSURE_SUCCESS(rv, rv);
     277                 :   }
     278                 : 
     279                 :   // Data can be NULL.
     280              27 :   rv = stmt->GetIsNull(2, &isNull);
     281              27 :   NS_ENSURE_SUCCESS(rv, rv);
     282              27 :   if (!isNull) {
     283                 :     PRUint8* data;
     284              27 :     PRUint32 dataLen = 0;
     285              27 :     rv = stmt->GetBlob(2, &dataLen, &data);
     286              27 :     NS_ENSURE_SUCCESS(rv, rv);
     287              27 :     _icon.data.Adopt(TO_CHARBUFFER(data), dataLen);
     288                 :     // Read mime only if we have data.
     289              27 :     rv = stmt->GetUTF8String(3, _icon.mimeType);
     290              27 :     NS_ENSURE_SUCCESS(rv, rv);
     291                 :   }
     292                 : 
     293              27 :   return NS_OK;
     294                 : }
     295                 : 
     296                 : nsresult
     297               2 : FetchIconURL(nsRefPtr<Database>& aDB,
     298                 :              const nsACString& aPageSpec,
     299                 :              nsACString& aIconSpec)
     300                 : {
     301               2 :   NS_PRECONDITION(!aPageSpec.IsEmpty(), "Page spec must not be empty.");
     302               2 :   NS_PRECONDITION(!NS_IsMainThread(),
     303                 :                   "This should not be called on the main thread.");
     304                 : 
     305               2 :   aIconSpec.Truncate();
     306                 : 
     307                 :   nsCOMPtr<mozIStorageStatement> stmt = aDB->GetStatement(
     308                 :     "SELECT f.url "
     309                 :     "FROM moz_places h "
     310                 :     "JOIN moz_favicons f ON h.favicon_id = f.id "
     311                 :     "WHERE h.url = :page_url"
     312               4 :   );
     313               2 :   NS_ENSURE_STATE(stmt);
     314               4 :   mozStorageStatementScoper scoper(stmt);
     315                 : 
     316               2 :   nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"),
     317               2 :                                 aPageSpec);
     318               2 :   NS_ENSURE_SUCCESS(rv, rv);
     319                 : 
     320                 :   bool hasResult;
     321               2 :   if (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
     322               2 :     rv = stmt->GetUTF8String(0, aIconSpec);
     323               2 :     NS_ENSURE_SUCCESS(rv, rv);
     324                 :   }
     325                 : 
     326               2 :   return NS_OK;
     327                 : }
     328                 : 
     329                 : /**
     330                 :  * Tries to guess the mimeType from icon data.
     331                 :  *
     332                 :  * @param aRequest
     333                 :  *        The network request object.
     334                 :  * @param aData
     335                 :  *        Data for this icon.
     336                 :  * @param _mimeType
     337                 :  *        The guessed mime-type or empty string if a valid one can't be found.
     338                 :  */
     339                 : nsresult
     340              15 : SniffMimeTypeForIconData(nsIRequest* aRequest,
     341                 :                          const nsCString& aData,
     342                 :                          nsCString& _mimeType)
     343                 : {
     344              15 :   NS_PRECONDITION(NS_IsMainThread(),
     345                 :                   "This should be called on the main thread");
     346                 : 
     347                 :   nsCOMPtr<nsICategoryManager> categoryManager =
     348              30 :     do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
     349              15 :   NS_ENSURE_TRUE(categoryManager, NS_ERROR_OUT_OF_MEMORY);
     350              30 :   nsCOMPtr<nsISimpleEnumerator> sniffers;
     351              15 :   nsresult rv = categoryManager->EnumerateCategory(CONTENT_SNIFFING_SERVICES,
     352              15 :                                                    getter_AddRefs(sniffers));
     353              15 :   NS_ENSURE_SUCCESS(rv, rv);
     354                 : 
     355              15 :   bool hasMore = false;
     356              60 :   while (_mimeType.IsEmpty() &&
     357              15 :          NS_SUCCEEDED(sniffers->HasMoreElements(&hasMore)) &&
     358                 :          hasMore) {
     359              30 :     nsCOMPtr<nsISupports> snifferCIDSupports;
     360              15 :     rv = sniffers->GetNext(getter_AddRefs(snifferCIDSupports));
     361              15 :     NS_ENSURE_SUCCESS(rv, rv);
     362                 :     nsCOMPtr<nsISupportsCString> snifferCIDSupportsCString =
     363              30 :       do_QueryInterface(snifferCIDSupports);
     364              15 :     NS_ENSURE_STATE(snifferCIDSupports);
     365              30 :     nsCAutoString snifferCID;
     366              15 :     rv = snifferCIDSupportsCString->GetData(snifferCID);
     367              15 :     NS_ENSURE_SUCCESS(rv, rv);
     368              30 :     nsCOMPtr<nsIContentSniffer> sniffer = do_GetService(snifferCID.get());
     369              15 :     NS_ENSURE_STATE(sniffer);
     370                 : 
     371                 :      // Ignore errors: we'll try the next sniffer.
     372              30 :     (void)sniffer->GetMIMETypeFromContent(aRequest, TO_INTBUFFER(aData),
     373              45 :                                           aData.Length(), _mimeType);
     374                 :   }
     375              15 :   return NS_OK;
     376                 : }
     377                 : 
     378                 : /**
     379                 :  * Tries to compute the expiration time for a icon from the channel.
     380                 :  *
     381                 :  * @param aChannel
     382                 :  *        The network channel used to fetch the icon.
     383                 :  * @return a valid expiration value for the fetched icon.
     384                 :  */
     385                 : PRTime
     386              15 : GetExpirationTimeFromChannel(nsIChannel* aChannel)
     387                 : {
     388              15 :   NS_PRECONDITION(NS_IsMainThread(),
     389                 :                   "This should be called on the main thread");
     390                 : 
     391                 :   // Attempt to get an expiration time from the cache.  If this fails, we'll
     392                 :   // make one up.
     393              15 :   PRTime expiration = -1;
     394              30 :   nsCOMPtr<nsICachingChannel> cachingChannel = do_QueryInterface(aChannel);
     395              15 :   if (cachingChannel) {
     396               0 :     nsCOMPtr<nsISupports> cacheToken;
     397               0 :     nsresult rv = cachingChannel->GetCacheToken(getter_AddRefs(cacheToken));
     398               0 :     if (NS_SUCCEEDED(rv)) {
     399               0 :       nsCOMPtr<nsICacheEntryInfo> cacheEntry = do_QueryInterface(cacheToken);
     400                 :       PRUint32 seconds;
     401               0 :       rv = cacheEntry->GetExpirationTime(&seconds);
     402               0 :       if (NS_SUCCEEDED(rv)) {
     403                 :         // Set the expiration, but make sure we honor our cap.
     404               0 :         expiration = PR_Now() + NS_MIN((PRTime)seconds * PR_USEC_PER_SEC,
     405               0 :                                        MAX_FAVICON_EXPIRATION);
     406                 :       }
     407                 :     }
     408                 :   }
     409                 :   // If we did not obtain a time from the cache, use the cap value.
     410              15 :   return expiration < 0 ? PR_Now() + MAX_FAVICON_EXPIRATION
     411              30 :                         : expiration;
     412                 : }
     413                 : 
     414                 : /**
     415                 :  * Checks the icon and evaluates if it needs to be optimized.  In such a case it
     416                 :  * will try to reduce its size through OptimizeFaviconImage method of the
     417                 :  * favicons service.
     418                 :  *
     419                 :  * @param aIcon
     420                 :  *        The icon to be evaluated.
     421                 :  * @param aFaviconSvc
     422                 :  *        Pointer to the favicons service.
     423                 :  */
     424                 : nsresult
     425              15 : OptimizeIconSize(IconData& aIcon,
     426                 :                  nsFaviconService* aFaviconSvc)
     427                 : {
     428              15 :   NS_PRECONDITION(NS_IsMainThread(),
     429                 :                   "This should be called on the main thread");
     430                 : 
     431                 :   // Even if the page provides a large image for the favicon (eg, a highres
     432                 :   // image or a multiresolution .ico file), don't try to store more data than
     433                 :   // needed.
     434              30 :   nsCAutoString newData, newMimeType;
     435              15 :   if (aIcon.data.Length() > MAX_ICON_FILESIZE(aFaviconSvc->GetOptimizedIconDimension())) {
     436               0 :     nsresult rv = aFaviconSvc->OptimizeFaviconImage(TO_INTBUFFER(aIcon.data),
     437                 :                                                     aIcon.data.Length(),
     438                 :                                                     aIcon.mimeType,
     439                 :                                                     newData,
     440               0 :                                                     newMimeType);
     441               0 :     if (NS_SUCCEEDED(rv) && newData.Length() < aIcon.data.Length()) {
     442               0 :       aIcon.data = newData;
     443               0 :       aIcon.mimeType = newMimeType;
     444                 :     }
     445                 :   }
     446              15 :   return NS_OK;
     447                 : }
     448                 : 
     449                 : } // Anonymous namespace.
     450                 : 
     451                 : 
     452                 : ////////////////////////////////////////////////////////////////////////////////
     453                 : //// AsyncFaviconHelperBase
     454                 : 
     455             103 : AsyncFaviconHelperBase::AsyncFaviconHelperBase(
     456                 :   nsCOMPtr<nsIFaviconDataCallback>& aCallback
     457             103 : ) : mDB(Database::GetDatabase())
     458                 : {
     459                 :   // Don't AddRef or Release in runnables for thread-safety.
     460             103 :   mCallback.swap(aCallback);
     461             103 : }
     462                 : 
     463             206 : AsyncFaviconHelperBase::~AsyncFaviconHelperBase()
     464                 : {
     465             206 :   nsCOMPtr<nsIThread> thread;
     466             103 :   (void)NS_GetMainThread(getter_AddRefs(thread));
     467             103 :   if (mCallback) {
     468              16 :     (void)NS_ProxyRelease(thread, mCallback, true);
     469                 :   }
     470             206 : }
     471                 : 
     472                 : ////////////////////////////////////////////////////////////////////////////////
     473                 : //// AsyncFetchAndSetIconForPage
     474                 : 
     475                 : // static
     476                 : nsresult
     477              24 : AsyncFetchAndSetIconForPage::start(nsIURI* aFaviconURI,
     478                 :                                    nsIURI* aPageURI,
     479                 :                                    enum AsyncFaviconFetchMode aFetchMode,
     480                 :                                    nsIFaviconDataCallback* aCallback)
     481                 : {
     482              24 :   NS_PRECONDITION(NS_IsMainThread(),
     483                 :                   "This should be called on the main thread");
     484                 : 
     485              48 :   PageData page;
     486              24 :   nsresult rv = aPageURI->GetSpec(page.spec);
     487              24 :   NS_ENSURE_SUCCESS(rv, rv);
     488                 :   // URIs can arguably miss a host.
     489              24 :   (void)GetReversedHostname(aPageURI, page.revHost);
     490                 :   bool canAddToHistory;
     491              24 :   nsNavHistory* navHistory = nsNavHistory::GetHistoryService();
     492              24 :   NS_ENSURE_TRUE(navHistory, NS_ERROR_OUT_OF_MEMORY);
     493              24 :   rv = navHistory->CanAddURI(aPageURI, &canAddToHistory);
     494              24 :   NS_ENSURE_SUCCESS(rv, rv);
     495              24 :   page.canAddToHistory = !!canAddToHistory;
     496                 : 
     497              48 :   IconData icon;
     498                 : 
     499              24 :   nsFaviconService* favicons = nsFaviconService::GetFaviconService();
     500              24 :   NS_ENSURE_STATE(favicons);
     501                 : 
     502                 :   UnassociatedIconHashKey* iconKey =
     503              24 :     favicons->mUnassociatedIcons.GetEntry(aFaviconURI);
     504                 : 
     505              24 :   if (iconKey) {
     506               8 :     icon = iconKey->iconData;
     507               8 :     favicons->mUnassociatedIcons.RemoveEntry(aFaviconURI);
     508                 :   } else {
     509              16 :     icon.fetchMode = aFetchMode;
     510              16 :     rv = aFaviconURI->GetSpec(icon.spec);
     511              16 :     NS_ENSURE_SUCCESS(rv, rv);
     512                 :   }
     513                 : 
     514                 :   // If the page url points to an image, the icon's url will be the same.
     515                 :   // In future evaluate to store a resample of the image.  For now avoid that
     516                 :   // for database size concerns.
     517                 :   // Don't store favicons for error pages too.
     518              48 :   if (icon.spec.Equals(page.spec) ||
     519              24 :       icon.spec.Equals(FAVICON_ERRORPAGE_URL)) {
     520               1 :     return NS_OK;
     521                 :   }
     522                 : 
     523                 :   // The event will swap owning pointers, thus we need a new pointer.
     524              46 :   nsCOMPtr<nsIFaviconDataCallback> callback(aCallback);
     525                 :   nsRefPtr<AsyncFetchAndSetIconForPage> event =
     526              46 :     new AsyncFetchAndSetIconForPage(icon, page, callback);
     527                 : 
     528                 :   // Get the target thread and start the work.
     529              46 :   nsRefPtr<Database> DB = Database::GetDatabase();
     530              23 :   NS_ENSURE_STATE(DB);
     531              23 :   DB->DispatchToAsyncThread(event);
     532                 : 
     533              23 :   return NS_OK;
     534                 : }
     535                 : 
     536              23 : AsyncFetchAndSetIconForPage::AsyncFetchAndSetIconForPage(
     537                 :   IconData& aIcon
     538                 : , PageData& aPage
     539                 : , nsCOMPtr<nsIFaviconDataCallback>& aCallback
     540                 : ) : AsyncFaviconHelperBase(aCallback)
     541                 :   , mIcon(aIcon)
     542              23 :   , mPage(aPage)
     543                 : {
     544              23 : }
     545                 : 
     546              46 : AsyncFetchAndSetIconForPage::~AsyncFetchAndSetIconForPage()
     547                 : {
     548              92 : }
     549                 : 
     550                 : NS_IMETHODIMP
     551              23 : AsyncFetchAndSetIconForPage::Run()
     552                 : {
     553              23 :   NS_PRECONDITION(!NS_IsMainThread(),
     554                 :                   "This should not be called on the main thread");
     555                 : 
     556                 :   // Try to fetch the icon from the database.
     557              23 :   nsresult rv = FetchIconInfo(mDB, mIcon);
     558              23 :   NS_ENSURE_SUCCESS(rv, rv);
     559                 : 
     560              23 :   bool isInvalidIcon = mIcon.data.IsEmpty() ||
     561              23 :                        (mIcon.expiration && PR_Now() > mIcon.expiration);
     562                 :   bool fetchIconFromNetwork = mIcon.fetchMode == FETCH_ALWAYS ||
     563              23 :                               (mIcon.fetchMode == FETCH_IF_MISSING && isInvalidIcon);
     564                 : 
     565              23 :   if (!fetchIconFromNetwork) {
     566                 :     // There is already a valid icon or we don't want to fetch a new one,
     567                 :     // directly proceed with association.
     568                 :     nsRefPtr<AsyncAssociateIconToPage> event =
     569              16 :         new AsyncAssociateIconToPage(mIcon, mPage, mCallback);
     570               8 :     mDB->DispatchToAsyncThread(event);
     571                 : 
     572               8 :     return NS_OK;
     573                 :   }
     574                 :   else {
     575                 :     // Fetch the icon from network.  When done this will associate the
     576                 :     // icon to the page and notify.
     577                 :     nsRefPtr<AsyncFetchAndSetIconFromNetwork> event =
     578              30 :       new AsyncFetchAndSetIconFromNetwork(mIcon, mPage, mCallback);
     579                 : 
     580                 :     // Start the work on the main thread.
     581              15 :     rv = NS_DispatchToMainThread(event);
     582              15 :     NS_ENSURE_SUCCESS(rv, rv);
     583                 :   }
     584                 : 
     585              15 :   return NS_OK;
     586                 : }
     587                 : 
     588                 : ////////////////////////////////////////////////////////////////////////////////
     589                 : //// AsyncFetchAndSetIconFromNetwork
     590                 : 
     591             315 : NS_IMPL_ISUPPORTS_INHERITED3(
     592                 :   AsyncFetchAndSetIconFromNetwork
     593                 : , nsRunnable
     594                 : , nsIStreamListener
     595                 : , nsIInterfaceRequestor
     596                 : , nsIChannelEventSink
     597                 : )
     598                 : 
     599              15 : AsyncFetchAndSetIconFromNetwork::AsyncFetchAndSetIconFromNetwork(
     600                 :   IconData& aIcon
     601                 : , PageData& aPage
     602                 : , nsCOMPtr<nsIFaviconDataCallback>& aCallback
     603                 : )
     604                 : : AsyncFaviconHelperBase(aCallback)
     605                 : , mIcon(aIcon)
     606              15 : , mPage(aPage)
     607                 : {
     608              15 : }
     609                 : 
     610              45 : AsyncFetchAndSetIconFromNetwork::~AsyncFetchAndSetIconFromNetwork()
     611                 : {
     612              30 :   nsCOMPtr<nsIThread> thread;
     613              15 :   (void)NS_GetMainThread(getter_AddRefs(thread));
     614              15 :   if (mChannel) {
     615              15 :     (void)NS_ProxyRelease(thread, mChannel, true);
     616                 :   }
     617              60 : }
     618                 : 
     619                 : NS_IMETHODIMP
     620              15 : AsyncFetchAndSetIconFromNetwork::Run()
     621                 : {
     622              15 :   NS_PRECONDITION(NS_IsMainThread(),
     623                 :                   "This should be called on the main thread");
     624                 : 
     625                 :   // Ensure data is cleared, since it's going to be overwritten.
     626              15 :   if (mIcon.data.Length() > 0) {
     627               4 :     mIcon.data.Truncate(0);
     628               4 :     mIcon.mimeType.Truncate(0);
     629                 :   }
     630                 : 
     631              30 :   nsCOMPtr<nsIURI> iconURI;
     632              15 :   nsresult rv = NS_NewURI(getter_AddRefs(iconURI), mIcon.spec);
     633              15 :   NS_ENSURE_SUCCESS(rv, rv);
     634              15 :   rv = NS_NewChannel(getter_AddRefs(mChannel), iconURI);
     635              15 :   NS_ENSURE_SUCCESS(rv, rv);
     636                 :   nsCOMPtr<nsIInterfaceRequestor> listenerRequestor =
     637              30 :     do_QueryInterface(reinterpret_cast<nsISupports*>(this));
     638              15 :   NS_ENSURE_STATE(listenerRequestor);
     639              15 :   rv = mChannel->SetNotificationCallbacks(listenerRequestor);
     640              15 :   NS_ENSURE_SUCCESS(rv, rv);
     641              15 :   rv = mChannel->AsyncOpen(this, nsnull);
     642              15 :   NS_ENSURE_SUCCESS(rv, rv);
     643                 : 
     644              15 :   return NS_OK;
     645                 : }
     646                 : 
     647                 : NS_IMETHODIMP
     648              15 : AsyncFetchAndSetIconFromNetwork::OnStartRequest(nsIRequest* aRequest,
     649                 :                                                 nsISupports* aContext)
     650                 : {
     651              15 :   return NS_OK;
     652                 : }
     653                 : 
     654                 : NS_IMETHODIMP
     655              15 : AsyncFetchAndSetIconFromNetwork::OnDataAvailable(nsIRequest* aRequest,
     656                 :                                                  nsISupports* aContext,
     657                 :                                                  nsIInputStream* aInputStream,
     658                 :                                                  PRUint32 aOffset,
     659                 :                                                  PRUint32 aCount)
     660                 : {
     661              30 :   nsCAutoString buffer;
     662              15 :   nsresult rv = NS_ConsumeStream(aInputStream, aCount, buffer);
     663              15 :   if (rv != NS_BASE_STREAM_WOULD_BLOCK && NS_FAILED(rv)) {
     664               0 :     return rv;
     665                 :   }
     666                 : 
     667              15 :   mIcon.data.Append(buffer);
     668              15 :   return NS_OK;
     669                 : }
     670                 : 
     671                 : 
     672                 : NS_IMETHODIMP
     673              15 : AsyncFetchAndSetIconFromNetwork::GetInterface(const nsIID& uuid,
     674                 :                                               void** aResult)
     675                 : {
     676              15 :   return QueryInterface(uuid, aResult);
     677                 : }
     678                 : 
     679                 : 
     680                 : NS_IMETHODIMP
     681               0 : AsyncFetchAndSetIconFromNetwork::AsyncOnChannelRedirect(
     682                 :   nsIChannel* oldChannel
     683                 : , nsIChannel* newChannel
     684                 : , PRUint32 flags
     685                 : , nsIAsyncVerifyRedirectCallback *cb
     686                 : )
     687                 : {
     688               0 :   mChannel = newChannel;
     689               0 :   (void)cb->OnRedirectVerifyCallback(NS_OK);
     690               0 :   return NS_OK;
     691                 : }
     692                 : 
     693                 : NS_IMETHODIMP
     694              15 : AsyncFetchAndSetIconFromNetwork::OnStopRequest(nsIRequest* aRequest,
     695                 :                                                nsISupports* aContext,
     696                 :                                                nsresult aStatusCode)
     697                 : {
     698              15 :   MOZ_ASSERT(NS_IsMainThread());
     699                 : 
     700              15 :   nsFaviconService* favicons = nsFaviconService::GetFaviconService();
     701              15 :   NS_ENSURE_STATE(favicons);
     702                 : 
     703                 :   // If fetching the icon failed, add it to the failed cache.
     704              15 :   if (NS_FAILED(aStatusCode) || mIcon.data.Length() == 0) {
     705               0 :     nsCOMPtr<nsIURI> iconURI;
     706               0 :     nsresult rv = NS_NewURI(getter_AddRefs(iconURI), mIcon.spec);
     707               0 :     NS_ENSURE_SUCCESS(rv, rv);
     708               0 :     rv = favicons->AddFailedFavicon(iconURI);
     709               0 :     NS_ENSURE_SUCCESS(rv, rv);
     710               0 :     return NS_OK;
     711                 :   }
     712                 : 
     713              15 :   nsresult rv = SniffMimeTypeForIconData(aRequest, mIcon.data, mIcon.mimeType);
     714              15 :   NS_ENSURE_SUCCESS(rv, rv);
     715                 : 
     716                 :   // If the icon does not have a valid MIME type, add it to the failed cache.
     717              15 :   if (mIcon.mimeType.IsEmpty()) {
     718               0 :     nsCOMPtr<nsIURI> iconURI;
     719               0 :     rv = NS_NewURI(getter_AddRefs(iconURI), mIcon.spec);
     720               0 :     NS_ENSURE_SUCCESS(rv, rv);
     721               0 :     rv = favicons->AddFailedFavicon(iconURI);
     722               0 :     NS_ENSURE_SUCCESS(rv, rv);
     723               0 :     return NS_OK;
     724                 :   }
     725                 : 
     726              15 :   mIcon.expiration = GetExpirationTimeFromChannel(mChannel);
     727                 : 
     728              15 :   rv = OptimizeIconSize(mIcon, favicons);
     729              15 :   NS_ENSURE_SUCCESS(rv, rv);
     730                 : 
     731                 :   // If over the maximum size allowed, don't save data to the database to
     732                 :   // avoid bloating it.
     733              15 :   if (mIcon.data.Length() > MAX_FAVICON_SIZE) {
     734               0 :     return NS_OK;
     735                 :   }
     736                 : 
     737              15 :   mIcon.status = ICON_STATUS_CHANGED;
     738                 : 
     739                 :   nsRefPtr<AsyncAssociateIconToPage> event =
     740              30 :     new AsyncAssociateIconToPage(mIcon, mPage, mCallback);
     741              15 :   mDB->DispatchToAsyncThread(event);
     742                 : 
     743              15 :   return NS_OK;
     744                 : }
     745                 : 
     746                 : ////////////////////////////////////////////////////////////////////////////////
     747                 : //// AsyncAssociateIconToPage
     748                 : 
     749              23 : AsyncAssociateIconToPage::AsyncAssociateIconToPage(
     750                 :   IconData& aIcon
     751                 : , PageData& aPage
     752                 : , nsCOMPtr<nsIFaviconDataCallback>& aCallback
     753                 : ) : AsyncFaviconHelperBase(aCallback)
     754                 :   , mIcon(aIcon)
     755              23 :   , mPage(aPage)
     756                 : {
     757              23 : }
     758                 : 
     759              46 : AsyncAssociateIconToPage::~AsyncAssociateIconToPage()
     760                 : {
     761              92 : }
     762                 : 
     763                 : NS_IMETHODIMP
     764              23 : AsyncAssociateIconToPage::Run()
     765                 : {
     766              23 :   NS_PRECONDITION(!NS_IsMainThread(),
     767                 :                   "This should not be called on the main thread");
     768                 : 
     769              23 :   nsresult rv = FetchPageInfo(mDB, mPage);
     770              23 :   if (rv == NS_ERROR_NOT_AVAILABLE){
     771                 :     // We have never seen this page.  If we can add the page to history,
     772                 :     // we will try to do it later, otherwise just bail out.
     773               5 :     if (!mPage.canAddToHistory) {
     774               3 :       return NS_OK;
     775                 :     }
     776                 :   }
     777                 :   else {
     778              18 :     NS_ENSURE_SUCCESS(rv, rv);
     779                 :   }
     780                 : 
     781                 :   mozStorageTransaction transaction(mDB->MainConn(), false,
     782              40 :                                     mozIStorageConnection::TRANSACTION_IMMEDIATE);
     783                 : 
     784                 :   // If there is no entry for this icon, or the entry is obsolete, replace it.
     785              20 :   if (mIcon.id == 0 || (mIcon.status & ICON_STATUS_CHANGED)) {
     786              20 :     rv = SetIconInfo(mDB, mIcon);
     787              20 :     NS_ENSURE_SUCCESS(rv, rv);
     788                 : 
     789                 :     // Get the new icon id.  Do this regardless mIcon.id, since other code
     790                 :     // could have added a entry before us.  Indeed we interrupted the thread
     791                 :     // after the previous call to FetchIconInfo.
     792              20 :     mIcon.status = (mIcon.status & ~(ICON_STATUS_CACHED)) | ICON_STATUS_SAVED;
     793              20 :     rv = FetchIconInfo(mDB, mIcon);
     794              20 :     NS_ENSURE_SUCCESS(rv, rv);
     795                 :   }
     796                 : 
     797                 :   // If the page does not have an id, try to insert a new one.
     798              20 :   if (mPage.id == 0) {
     799                 :     nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
     800                 :       "INSERT INTO moz_places (url, rev_host, hidden, favicon_id, frecency, guid) "
     801                 :       "VALUES (:page_url, :rev_host, 1, :favicon_id, 0, GENERATE_GUID()) "
     802               4 :     );
     803               2 :     NS_ENSURE_STATE(stmt);
     804               4 :     mozStorageStatementScoper scoper(stmt);
     805               2 :     rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), mPage.spec);
     806               2 :     NS_ENSURE_SUCCESS(rv, rv);
     807                 :     // The rev_host can be null.
     808               2 :     if (mPage.revHost.IsEmpty()) {
     809               0 :       rv = stmt->BindNullByName(NS_LITERAL_CSTRING("rev_host"));
     810                 :     }
     811                 :     else {
     812               2 :       rv = stmt->BindStringByName(NS_LITERAL_CSTRING("rev_host"), mPage.revHost);
     813                 :     }
     814               2 :     NS_ENSURE_SUCCESS(rv, rv);
     815               2 :     rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("favicon_id"), mIcon.id);
     816               2 :     NS_ENSURE_SUCCESS(rv, rv);
     817               2 :     rv = stmt->Execute();
     818               2 :     NS_ENSURE_SUCCESS(rv, rv);
     819                 : 
     820                 :     // Get the new id and GUID.
     821               2 :     rv = FetchPageInfo(mDB, mPage);
     822               2 :     NS_ENSURE_SUCCESS(rv, rv);
     823                 : 
     824               4 :     mIcon.status |= ICON_STATUS_ASSOCIATED;
     825                 :   }
     826                 :   // Otherwise just associate the icon to the page, if needed.
     827              18 :   else if (mPage.iconId != mIcon.id) {
     828              34 :     nsCOMPtr<mozIStorageStatement> stmt;
     829              17 :     if (mPage.id) {
     830                 :       stmt = mDB->GetStatement(
     831                 :         "UPDATE moz_places SET favicon_id = :icon_id WHERE id = :page_id"
     832              17 :       );
     833              17 :       NS_ENSURE_STATE(stmt);
     834              17 :       rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), mPage.id);
     835              17 :       NS_ENSURE_SUCCESS(rv, rv);
     836                 :     }
     837                 :     else {
     838                 :       stmt = mDB->GetStatement(
     839                 :         "UPDATE moz_places SET favicon_id = :icon_id WHERE url = :page_url"
     840               0 :       );
     841               0 :       NS_ENSURE_STATE(stmt);
     842               0 :       rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), mPage.spec);
     843               0 :       NS_ENSURE_SUCCESS(rv, rv);
     844                 :     }
     845              17 :     rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("icon_id"), mIcon.id);
     846              17 :     NS_ENSURE_SUCCESS(rv, rv);
     847                 : 
     848              34 :     mozStorageStatementScoper scoper(stmt);
     849              17 :     rv = stmt->Execute();
     850              17 :     NS_ENSURE_SUCCESS(rv, rv);
     851                 : 
     852              34 :     mIcon.status |= ICON_STATUS_ASSOCIATED;
     853                 :   }
     854                 : 
     855              20 :   rv = transaction.Commit();
     856              20 :   NS_ENSURE_SUCCESS(rv, rv);
     857                 : 
     858                 :   // Finally, dispatch an event to the main thread to notify observers.
     859              40 :   nsCOMPtr<nsIRunnable> event = new NotifyIconObservers(mIcon, mPage, mCallback);
     860              20 :   rv = NS_DispatchToMainThread(event);
     861              20 :   NS_ENSURE_SUCCESS(rv, rv);
     862                 : 
     863              20 :   return NS_OK;
     864                 : }
     865                 : 
     866                 : ////////////////////////////////////////////////////////////////////////////////
     867                 : //// AsyncGetFaviconURLForPage
     868                 : 
     869                 : // static
     870                 : nsresult
     871               1 : AsyncGetFaviconURLForPage::start(nsIURI* aPageURI,
     872                 :                                  nsIFaviconDataCallback* aCallback)
     873                 : {
     874               1 :   NS_ENSURE_ARG(aCallback);
     875               1 :   NS_ENSURE_ARG(aPageURI);
     876               1 :   NS_PRECONDITION(NS_IsMainThread(),
     877                 :                   "This should be called on the main thread.");
     878                 : 
     879               2 :   nsCAutoString pageSpec;
     880               1 :   nsresult rv = aPageURI->GetSpec(pageSpec);
     881               1 :   NS_ENSURE_SUCCESS(rv, rv);
     882                 : 
     883               2 :   nsCOMPtr<nsIFaviconDataCallback> callback = aCallback;
     884                 :   nsRefPtr<AsyncGetFaviconURLForPage> event =
     885               2 :     new AsyncGetFaviconURLForPage(pageSpec, callback);
     886                 : 
     887               2 :   nsRefPtr<Database> DB = Database::GetDatabase();
     888               1 :   NS_ENSURE_STATE(DB);
     889               1 :   DB->DispatchToAsyncThread(event);
     890                 : 
     891               1 :   return NS_OK;
     892                 : }
     893                 : 
     894               1 : AsyncGetFaviconURLForPage::AsyncGetFaviconURLForPage(
     895                 :   const nsACString& aPageSpec
     896                 : , nsCOMPtr<nsIFaviconDataCallback>& aCallback
     897               1 : ) : AsyncFaviconHelperBase(aCallback)
     898                 : {
     899               1 :   mPageSpec.Assign(aPageSpec);
     900               1 : }
     901                 : 
     902               2 : AsyncGetFaviconURLForPage::~AsyncGetFaviconURLForPage()
     903                 : {
     904               4 : }
     905                 : 
     906                 : NS_IMETHODIMP
     907               1 : AsyncGetFaviconURLForPage::Run()
     908                 : {
     909               1 :   NS_PRECONDITION(!NS_IsMainThread(),
     910                 :                   "This should not be called on the main thread.");
     911                 : 
     912               2 :   nsCAutoString iconSpec;
     913               1 :   nsresult rv = FetchIconURL(mDB, mPageSpec, iconSpec);
     914               1 :   NS_ENSURE_SUCCESS(rv, rv);
     915                 : 
     916                 :   // No icon was found.
     917               1 :   if (iconSpec.IsEmpty())
     918               0 :     return NS_OK;
     919                 : 
     920                 :   // Now notify our callback of the icon spec we retrieved.
     921               2 :   IconData iconData;
     922               1 :   iconData.spec.Assign(iconSpec);
     923                 : 
     924               2 :   PageData pageData;
     925               1 :   pageData.spec.Assign(mPageSpec);
     926                 : 
     927                 :   nsCOMPtr<nsIRunnable> event =
     928               2 :     new NotifyIconObservers(iconData, pageData, mCallback);
     929               1 :   rv = NS_DispatchToMainThread(event);
     930               1 :   NS_ENSURE_SUCCESS(rv, rv);
     931                 : 
     932               1 :   return NS_OK;
     933                 : }
     934                 : 
     935                 : ////////////////////////////////////////////////////////////////////////////////
     936                 : //// AsyncGetFaviconDataForPage
     937                 : 
     938                 : // static
     939                 : nsresult
     940               1 : AsyncGetFaviconDataForPage::start(nsIURI* aPageURI,
     941                 :                                   nsIFaviconDataCallback* aCallback)
     942                 : {
     943               1 :   NS_ENSURE_ARG(aCallback);
     944               1 :   NS_ENSURE_ARG(aPageURI);
     945               1 :   NS_PRECONDITION(NS_IsMainThread(),
     946                 :                   "This should be called on the main thread.");
     947                 : 
     948               2 :   nsCAutoString pageSpec;
     949               1 :   nsresult rv = aPageURI->GetSpec(pageSpec);
     950               1 :   NS_ENSURE_SUCCESS(rv, rv);
     951                 : 
     952               2 :   nsCOMPtr<nsIFaviconDataCallback> callback = aCallback;
     953                 :   nsRefPtr<AsyncGetFaviconDataForPage> event =
     954               2 :     new AsyncGetFaviconDataForPage(pageSpec, callback);
     955                 : 
     956               2 :   nsRefPtr<Database> DB = Database::GetDatabase();
     957               1 :   NS_ENSURE_STATE(DB);
     958               1 :   DB->DispatchToAsyncThread(event);
     959                 : 
     960               1 :   return NS_OK;
     961                 : }
     962                 : 
     963               1 : AsyncGetFaviconDataForPage::AsyncGetFaviconDataForPage(
     964                 :   const nsACString& aPageSpec
     965                 : , nsCOMPtr<nsIFaviconDataCallback>& aCallback
     966               1 : ) : AsyncFaviconHelperBase(aCallback)
     967                 : {
     968               1 :   mPageSpec.Assign(aPageSpec);
     969               1 : }
     970                 : 
     971               2 : AsyncGetFaviconDataForPage::~AsyncGetFaviconDataForPage()
     972                 : {
     973               4 : }
     974                 : 
     975                 : NS_IMETHODIMP
     976               1 : AsyncGetFaviconDataForPage::Run()
     977                 : {
     978               1 :   NS_PRECONDITION(!NS_IsMainThread(),
     979                 :                   "This should not be called on the main thread.");
     980                 : 
     981               2 :   nsCAutoString iconSpec;
     982               1 :   nsresult rv = FetchIconURL(mDB, mPageSpec, iconSpec);
     983               1 :   NS_ENSURE_SUCCESS(rv, rv);
     984                 : 
     985               1 :   if (!iconSpec.Length()) {
     986               0 :     return NS_ERROR_NOT_AVAILABLE;
     987                 :   }
     988                 : 
     989               2 :   IconData iconData;
     990               1 :   iconData.spec.Assign(iconSpec);
     991                 : 
     992               2 :   PageData pageData;
     993               1 :   pageData.spec.Assign(mPageSpec);
     994                 : 
     995               1 :   rv = FetchIconInfo(mDB, iconData);
     996               1 :   NS_ENSURE_SUCCESS(rv, rv);
     997                 : 
     998                 :   nsCOMPtr<nsIRunnable> event =
     999               2 :     new NotifyIconObservers(iconData, pageData, mCallback);
    1000               1 :   rv = NS_DispatchToMainThread(event);
    1001               1 :   NS_ENSURE_SUCCESS(rv, rv);
    1002               1 :   return NS_OK;
    1003                 : }
    1004                 : 
    1005                 : ////////////////////////////////////////////////////////////////////////////////
    1006                 : //// AsyncReplaceFaviconData
    1007                 : 
    1008                 : // static
    1009                 : nsresult
    1010              16 : AsyncReplaceFaviconData::start(IconData *aIcon)
    1011                 : {
    1012              16 :   NS_ENSURE_ARG(aIcon);
    1013              16 :   NS_PRECONDITION(NS_IsMainThread(),
    1014                 :                   "This should be called on the main thread.");
    1015                 : 
    1016              32 :   nsCOMPtr<nsIFaviconDataCallback> callback;
    1017                 :   nsRefPtr<AsyncReplaceFaviconData> event =
    1018              32 :     new AsyncReplaceFaviconData(*aIcon, callback);
    1019                 : 
    1020              32 :   nsRefPtr<Database> DB = Database::GetDatabase();
    1021              16 :   NS_ENSURE_STATE(DB);
    1022              16 :   DB->DispatchToAsyncThread(event);
    1023                 : 
    1024              16 :   return NS_OK;
    1025                 : }
    1026                 : 
    1027              16 : AsyncReplaceFaviconData::AsyncReplaceFaviconData(
    1028                 :   IconData &aIcon
    1029                 : , nsCOMPtr<nsIFaviconDataCallback>& aCallback
    1030                 : ) : AsyncFaviconHelperBase(aCallback)
    1031              16 :   , mIcon(aIcon)
    1032                 : {
    1033              16 : }
    1034                 : 
    1035              32 : AsyncReplaceFaviconData::~AsyncReplaceFaviconData()
    1036                 : {
    1037              64 : }
    1038                 : 
    1039                 : NS_IMETHODIMP
    1040              16 : AsyncReplaceFaviconData::Run()
    1041                 : {
    1042              16 :   NS_PRECONDITION(!NS_IsMainThread(),
    1043                 :                   "This should not be called on the main thread");
    1044                 : 
    1045              32 :   IconData dbIcon;
    1046              16 :   dbIcon.spec.Assign(mIcon.spec);
    1047              16 :   nsresult rv = FetchIconInfo(mDB, dbIcon);
    1048              16 :   NS_ENSURE_SUCCESS(rv, rv);
    1049                 : 
    1050              16 :   if (!dbIcon.id) {
    1051              14 :     return NS_OK;
    1052                 :   }
    1053                 : 
    1054               2 :   rv = SetIconInfo(mDB, mIcon);
    1055               2 :   NS_ENSURE_SUCCESS(rv, rv);
    1056                 : 
    1057                 :   // We can invalidate the cache version since we now persist the icon.
    1058               4 :   nsCOMPtr<nsIRunnable> event = new RemoveIconDataCacheEntry(mIcon, mCallback);
    1059               2 :   rv = NS_DispatchToMainThread(event);
    1060               2 :   NS_ENSURE_SUCCESS(rv, rv);
    1061                 : 
    1062               2 :   return NS_OK;
    1063                 : }
    1064                 : 
    1065                 : 
    1066                 : ////////////////////////////////////////////////////////////////////////////////
    1067                 : //// RemoveIconDataCacheEntry
    1068                 : 
    1069               2 : RemoveIconDataCacheEntry::RemoveIconDataCacheEntry(
    1070                 :   IconData& aIcon
    1071                 : , nsCOMPtr<nsIFaviconDataCallback>& aCallback
    1072                 : )
    1073                 : : AsyncFaviconHelperBase(aCallback)
    1074               2 : , mIcon(aIcon)
    1075                 : {
    1076               2 : }
    1077                 : 
    1078               4 : RemoveIconDataCacheEntry::~RemoveIconDataCacheEntry()
    1079                 : {
    1080               8 : }
    1081                 : 
    1082                 : NS_IMETHODIMP
    1083               2 : RemoveIconDataCacheEntry::Run()
    1084                 : {
    1085               2 :   NS_PRECONDITION(NS_IsMainThread(),
    1086                 :                   "This should be called on the main thread");
    1087                 : 
    1088               4 :   nsCOMPtr<nsIURI> iconURI;
    1089               2 :   nsresult rv = NS_NewURI(getter_AddRefs(iconURI), mIcon.spec);
    1090               2 :   NS_ENSURE_SUCCESS(rv, rv);
    1091                 : 
    1092               2 :   nsFaviconService* favicons = nsFaviconService::GetFaviconService();
    1093               2 :   NS_ENSURE_STATE(favicons);
    1094               2 :   favicons->mUnassociatedIcons.RemoveEntry(iconURI);
    1095                 : 
    1096               2 :   return NS_OK;
    1097                 : }
    1098                 : 
    1099                 : 
    1100                 : ////////////////////////////////////////////////////////////////////////////////
    1101                 : //// NotifyIconObservers
    1102                 : 
    1103              22 : NotifyIconObservers::NotifyIconObservers(
    1104                 :   IconData& aIcon
    1105                 : , PageData& aPage
    1106                 : , nsCOMPtr<nsIFaviconDataCallback>& aCallback
    1107                 : )
    1108                 : : AsyncFaviconHelperBase(aCallback)
    1109                 : , mIcon(aIcon)
    1110              22 : , mPage(aPage)
    1111                 : {
    1112              22 : }
    1113                 : 
    1114              44 : NotifyIconObservers::~NotifyIconObservers()
    1115                 : {
    1116              88 : }
    1117                 : 
    1118                 : NS_IMETHODIMP
    1119              22 : NotifyIconObservers::Run()
    1120                 : {
    1121              22 :   NS_PRECONDITION(NS_IsMainThread(),
    1122                 :                   "This should be called on the main thread");
    1123                 : 
    1124              44 :   nsCOMPtr<nsIURI> iconURI;
    1125              22 :   nsresult rv = NS_NewURI(getter_AddRefs(iconURI), mIcon.spec);
    1126              22 :   NS_ENSURE_SUCCESS(rv, rv);
    1127                 : 
    1128                 :   // Notify observers only if something changed.
    1129              22 :   if (mIcon.status & ICON_STATUS_SAVED ||
    1130                 :       mIcon.status & ICON_STATUS_ASSOCIATED) {
    1131              40 :     nsCOMPtr<nsIURI> pageURI;
    1132              20 :     rv = NS_NewURI(getter_AddRefs(pageURI), mPage.spec);
    1133              20 :     NS_ENSURE_SUCCESS(rv, rv);
    1134                 : 
    1135              20 :     nsFaviconService* favicons = nsFaviconService::GetFaviconService();
    1136              20 :     NS_ENSURE_STATE(favicons);
    1137              20 :     (void)favicons->SendFaviconNotifications(pageURI, iconURI, mPage.guid);
    1138                 : 
    1139                 :     // If the page is bookmarked and the bookmarked url is different from the
    1140                 :     // updated one, start a new task to update its icon as well.
    1141              26 :     if (!mPage.bookmarkedSpec.IsEmpty() &&
    1142               6 :         !mPage.bookmarkedSpec.Equals(mPage.spec)) {
    1143                 :       // Create a new page struct to avoid polluting it with old data.
    1144               0 :       PageData bookmarkedPage;
    1145               0 :       bookmarkedPage.spec = mPage.bookmarkedSpec;
    1146                 : 
    1147                 :       // This will be silent, so be sure to not pass in the current callback.
    1148               0 :       nsCOMPtr<nsIFaviconDataCallback> nullCallback;
    1149                 :       nsRefPtr<AsyncAssociateIconToPage> event =
    1150               0 :           new AsyncAssociateIconToPage(mIcon, bookmarkedPage, nullCallback);
    1151               0 :       mDB->DispatchToAsyncThread(event);
    1152                 :     }
    1153                 :   }
    1154                 : 
    1155              22 :   if (mCallback) {
    1156              16 :     (void)mCallback->OnFaviconDataAvailable(iconURI,
    1157                 :                                             mIcon.data.Length(),
    1158              16 :                                             TO_INTBUFFER(mIcon.data),
    1159              32 :                                             mIcon.mimeType);
    1160                 :   }
    1161                 : 
    1162              22 :   return NS_OK;
    1163                 : }
    1164                 : 
    1165                 : } // namespace places
    1166                 : } // namespace mozilla

Generated by: LCOV version 1.7