LCOV - code coverage report
Current view: top level - toolkit/components/places - FaviconHelpers.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 102 700 14.6 %
Date: 2017-07-14 16:53:18 Functions: 11 36 30.6 %
Legend: Lines: hit not hit

          Line data    Source code
       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/. */
       6             : 
       7             : #include "FaviconHelpers.h"
       8             : 
       9             : #include "nsICacheEntry.h"
      10             : #include "nsICachingChannel.h"
      11             : #include "nsIAsyncVerifyRedirectCallback.h"
      12             : #include "nsIPrincipal.h"
      13             : 
      14             : #include "nsNavHistory.h"
      15             : #include "nsFaviconService.h"
      16             : #include "mozilla/storage.h"
      17             : #include "mozilla/Telemetry.h"
      18             : #include "nsNetUtil.h"
      19             : #include "nsPrintfCString.h"
      20             : #include "nsStreamUtils.h"
      21             : #include "nsStringStream.h"
      22             : #include "nsIPrivateBrowsingChannel.h"
      23             : #include "nsISupportsPriority.h"
      24             : #include "nsContentUtils.h"
      25             : #include <algorithm>
      26             : #include <deque>
      27             : #include "mozilla/gfx/2D.h"
      28             : #include "imgIContainer.h"
      29             : #include "ImageOps.h"
      30             : #include "imgIEncoder.h"
      31             : 
      32             : using namespace mozilla::places;
      33             : using namespace mozilla::storage;
      34             : 
      35             : namespace mozilla {
      36             : namespace places {
      37             : 
      38             : namespace {
      39             : 
      40             : /**
      41             :  * Fetches information about a page from the database.
      42             :  *
      43             :  * @param aDB
      44             :  *        Database connection to history tables.
      45             :  * @param _page
      46             :  *        Page that should be fetched.
      47             :  */
      48             : nsresult
      49           0 : FetchPageInfo(const RefPtr<Database>& aDB,
      50             :               PageData& _page)
      51             : {
      52           0 :   MOZ_ASSERT(_page.spec.Length(), "Must have a non-empty spec!");
      53           0 :   MOZ_ASSERT(!NS_IsMainThread());
      54             : 
      55             :   // This query finds the bookmarked uri we want to set the icon for,
      56             :   // walking up to two redirect levels.
      57           0 :   nsCString query = nsPrintfCString(
      58             :     "SELECT h.id, pi.id, h.guid, ( "
      59             :       "SELECT h.url FROM moz_bookmarks b WHERE b.fk = h.id "
      60             :       "UNION ALL " // Union not directly bookmarked pages.
      61             :       "SELECT url FROM moz_places WHERE id = ( "
      62             :         "SELECT COALESCE(grandparent.place_id, parent.place_id) as r_place_id "
      63             :         "FROM moz_historyvisits dest "
      64             :         "LEFT JOIN moz_historyvisits parent ON parent.id = dest.from_visit "
      65             :                                           "AND dest.visit_type IN (%d, %d) "
      66             :         "LEFT JOIN moz_historyvisits grandparent ON parent.from_visit = grandparent.id "
      67             :           "AND parent.visit_type IN (%d, %d) "
      68             :         "WHERE dest.place_id = h.id "
      69             :         "AND EXISTS(SELECT 1 FROM moz_bookmarks b WHERE b.fk = r_place_id) "
      70             :         "LIMIT 1 "
      71             :       ") "
      72             :     "), fixup_url(get_unreversed_host(h.rev_host)) AS host "
      73             :     "FROM moz_places h "
      74             :     "LEFT JOIN moz_pages_w_icons pi ON page_url_hash = hash(:page_url) AND page_url = :page_url "
      75             :     "WHERE h.url_hash = hash(:page_url) AND h.url = :page_url",
      76             :     nsINavHistoryService::TRANSITION_REDIRECT_PERMANENT,
      77             :     nsINavHistoryService::TRANSITION_REDIRECT_TEMPORARY,
      78             :     nsINavHistoryService::TRANSITION_REDIRECT_PERMANENT,
      79             :     nsINavHistoryService::TRANSITION_REDIRECT_TEMPORARY
      80           0 :   );
      81             : 
      82           0 :   nsCOMPtr<mozIStorageStatement> stmt = aDB->GetStatement(query);
      83           0 :   NS_ENSURE_STATE(stmt);
      84           0 :   mozStorageStatementScoper scoper(stmt);
      85             : 
      86           0 :   nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"),
      87           0 :                                 _page.spec);
      88           0 :   NS_ENSURE_SUCCESS(rv, rv);
      89             : 
      90             :   bool hasResult;
      91           0 :   rv = stmt->ExecuteStep(&hasResult);
      92           0 :   NS_ENSURE_SUCCESS(rv, rv);
      93           0 :   if (!hasResult) {
      94             :     // The page does not exist.
      95           0 :     return NS_ERROR_NOT_AVAILABLE;
      96             :   }
      97             : 
      98           0 :   rv = stmt->GetInt64(0, &_page.placeId);
      99           0 :   NS_ENSURE_SUCCESS(rv, rv);
     100             :   // May be null, and in such a case this will be 0.
     101           0 :   _page.id = stmt->AsInt64(1);
     102           0 :   rv = stmt->GetUTF8String(2, _page.guid);
     103           0 :   NS_ENSURE_SUCCESS(rv, rv);
     104             :   // Bookmarked url can be nullptr.
     105             :   bool isNull;
     106           0 :   rv = stmt->GetIsNull(3, &isNull);
     107           0 :   NS_ENSURE_SUCCESS(rv, rv);
     108             :   // The page could not be bookmarked.
     109           0 :   if (!isNull) {
     110           0 :     rv = stmt->GetUTF8String(3, _page.bookmarkedSpec);
     111           0 :     NS_ENSURE_SUCCESS(rv, rv);
     112             :   }
     113             : 
     114           0 :   if (_page.host.IsEmpty()) {
     115           0 :     rv = stmt->GetUTF8String(4, _page.host);
     116           0 :     NS_ENSURE_SUCCESS(rv, rv);
     117             :   }
     118             : 
     119           0 :   if (!_page.canAddToHistory) {
     120             :     // Either history is disabled or the scheme is not supported.  In such a
     121             :     // case we want to update the icon only if the page is bookmarked.
     122             : 
     123           0 :     if (_page.bookmarkedSpec.IsEmpty()) {
     124             :       // The page is not bookmarked.  Since updating the icon with a disabled
     125             :       // history would be a privacy leak, bail out as if the page did not exist.
     126           0 :       return NS_ERROR_NOT_AVAILABLE;
     127             :     }
     128             :     else {
     129             :       // The page, or a redirect to it, is bookmarked.  If the bookmarked spec
     130             :       // is different from the requested one, use it.
     131           0 :       if (!_page.bookmarkedSpec.Equals(_page.spec)) {
     132           0 :         _page.spec = _page.bookmarkedSpec;
     133           0 :         rv = FetchPageInfo(aDB, _page);
     134           0 :         NS_ENSURE_SUCCESS(rv, rv);
     135             :       }
     136             :     }
     137             :   }
     138             : 
     139           0 :   return NS_OK;
     140             : }
     141             : 
     142             : /**
     143             :  * Stores information about an icon in the database.
     144             :  *
     145             :  * @param aDB
     146             :  *        Database connection to history tables.
     147             :  * @param aIcon
     148             :  *        Icon that should be stored.
     149             :  * @param aMustReplace
     150             :  *        If set to true, the function will bail out with NS_ERROR_NOT_AVAILABLE
     151             :  *        if it can't find a previous stored icon to replace.
     152             :  * @note Should be wrapped in a transaction.
     153             :  */
     154             : nsresult
     155           0 : SetIconInfo(const RefPtr<Database>& aDB,
     156             :             IconData& aIcon,
     157             :             bool aMustReplace = false)
     158             : {
     159           0 :   MOZ_ASSERT(!NS_IsMainThread());
     160           0 :   MOZ_ASSERT(aIcon.payloads.Length() > 0);
     161           0 :   MOZ_ASSERT(!aIcon.spec.IsEmpty());
     162           0 :   MOZ_ASSERT(aIcon.expiration > 0);
     163             : 
     164             :   // There are multiple cases possible at this point:
     165             :   //   1. We must insert some payloads and no payloads exist in the table. This
     166             :   //      would be a straight INSERT.
     167             :   //   2. The table contains the same number of payloads we are inserting. This
     168             :   //      would be a straight UPDATE.
     169             :   //   3. The table contains more payloads than we are inserting. This would be
     170             :   //      an UPDATE and a DELETE.
     171             :   //   4. The table contains less payloads than we are inserting. This would be
     172             :   //      an UPDATE and an INSERT.
     173             :   // We can't just remove all the old entries and insert the new ones, cause
     174             :   // we'd lose the referential integrity with pages.  For the same reason we
     175             :   // cannot use INSERT OR REPLACE, since it's implemented as DELETE AND INSERT.
     176             :   // Thus, we follow this strategy:
     177             :   //   * SELECT all existing icon ids
     178             :   //   * For each payload, either UPDATE OR INSERT reusing icon ids.
     179             :   //   * If any previous icon ids is leftover, DELETE it.
     180             : 
     181           0 :   nsCOMPtr<mozIStorageStatement> selectStmt = aDB->GetStatement(
     182             :     "SELECT id FROM moz_icons "
     183             :     "WHERE fixed_icon_url_hash = hash(fixup_url(:url)) "
     184             :       "AND icon_url = :url "
     185           0 :   );
     186           0 :   NS_ENSURE_STATE(selectStmt);
     187           0 :   mozStorageStatementScoper scoper(selectStmt);
     188           0 :   nsresult rv = URIBinder::Bind(selectStmt, NS_LITERAL_CSTRING("url"), aIcon.spec);
     189           0 :   NS_ENSURE_SUCCESS(rv, rv);
     190           0 :   std::deque<int64_t> ids;
     191           0 :   bool hasResult = false;
     192           0 :   while (NS_SUCCEEDED(selectStmt->ExecuteStep(&hasResult)) && hasResult) {
     193           0 :     int64_t id = selectStmt->AsInt64(0);
     194           0 :     MOZ_ASSERT(id > 0);
     195           0 :     ids.push_back(id);
     196             :   }
     197           0 :   if (aMustReplace && ids.empty()) {
     198           0 :     return NS_ERROR_NOT_AVAILABLE;
     199             :   }
     200             : 
     201           0 :   nsCOMPtr<mozIStorageStatement> insertStmt = aDB->GetStatement(
     202             :     "INSERT INTO moz_icons "
     203             :       "(icon_url, fixed_icon_url_hash, width, root, expire_ms, data) "
     204             :     "VALUES (:url, hash(fixup_url(:url)), :width, :root, :expire, :data) "
     205           0 :   );
     206           0 :   NS_ENSURE_STATE(insertStmt);
     207           0 :   nsCOMPtr<mozIStorageStatement> updateStmt = aDB->GetStatement(
     208             :     "UPDATE moz_icons SET width = :width, "
     209             :                          "expire_ms = :expire, "
     210             :                          "data = :data, "
     211             :                          "root = :root "
     212             :     "WHERE id = :id "
     213           0 :   );
     214           0 :   NS_ENSURE_STATE(updateStmt);
     215             : 
     216           0 :   for (auto& payload : aIcon.payloads) {
     217             :     // Sanity checks.
     218           0 :     MOZ_ASSERT(payload.mimeType.EqualsLiteral(PNG_MIME_TYPE) ||
     219             :               payload.mimeType.EqualsLiteral(SVG_MIME_TYPE),
     220             :               "Only png and svg payloads are supported");
     221           0 :     MOZ_ASSERT(!payload.mimeType.EqualsLiteral(SVG_MIME_TYPE) ||
     222             :                payload.width == UINT16_MAX,
     223             :               "SVG payloads should have max width");
     224           0 :     MOZ_ASSERT(payload.width > 0, "Payload should have a width");
     225             : #ifdef DEBUG
     226             :     // Done to ensure we fetch the id. See the MOZ_ASSERT below.
     227           0 :     payload.id = 0;
     228             : #endif
     229           0 :     if (!ids.empty()) {
     230             :       // Pop the first existing id for reuse.
     231           0 :       int64_t id = ids.front();
     232           0 :       ids.pop_front();
     233           0 :       mozStorageStatementScoper scoper(updateStmt);
     234           0 :       rv = updateStmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), id);
     235           0 :       NS_ENSURE_SUCCESS(rv, rv);
     236           0 :       rv = updateStmt->BindInt32ByName(NS_LITERAL_CSTRING("width"),
     237           0 :                                        payload.width);
     238           0 :       NS_ENSURE_SUCCESS(rv, rv);
     239           0 :       rv = updateStmt->BindInt64ByName(NS_LITERAL_CSTRING("expire"),
     240           0 :                                        aIcon.expiration / 1000);
     241           0 :       NS_ENSURE_SUCCESS(rv, rv);
     242           0 :       rv = updateStmt->BindInt32ByName(NS_LITERAL_CSTRING("root"),
     243           0 :                                        aIcon.rootIcon);
     244           0 :       NS_ENSURE_SUCCESS(rv, rv);
     245           0 :       rv = updateStmt->BindBlobByName(NS_LITERAL_CSTRING("data"),
     246           0 :                                 TO_INTBUFFER(payload.data),
     247           0 :                                 payload.data.Length());
     248           0 :       NS_ENSURE_SUCCESS(rv, rv);
     249           0 :       rv = updateStmt->Execute();
     250           0 :       NS_ENSURE_SUCCESS(rv, rv);
     251             :       // Set the new payload id.
     252           0 :       payload.id = id;
     253             :     } else {
     254             :       // Insert a new entry.
     255           0 :       mozStorageStatementScoper scoper(insertStmt);
     256           0 :       rv = URIBinder::Bind(insertStmt, NS_LITERAL_CSTRING("url"), aIcon.spec);
     257           0 :       NS_ENSURE_SUCCESS(rv, rv);
     258           0 :       rv = insertStmt->BindInt32ByName(NS_LITERAL_CSTRING("width"),
     259           0 :                                        payload.width);
     260           0 :       NS_ENSURE_SUCCESS(rv, rv);
     261             : 
     262           0 :       rv = insertStmt->BindInt32ByName(NS_LITERAL_CSTRING("root"),
     263           0 :                                        aIcon.rootIcon);
     264           0 :       NS_ENSURE_SUCCESS(rv, rv);
     265           0 :       rv = insertStmt->BindInt64ByName(NS_LITERAL_CSTRING("expire"),
     266           0 :                                        aIcon.expiration / 1000);
     267           0 :       NS_ENSURE_SUCCESS(rv, rv);
     268           0 :       rv = insertStmt->BindBlobByName(NS_LITERAL_CSTRING("data"),
     269           0 :                                 TO_INTBUFFER(payload.data),
     270           0 :                                 payload.data.Length());
     271           0 :       NS_ENSURE_SUCCESS(rv, rv);
     272           0 :       rv = insertStmt->Execute();
     273           0 :       NS_ENSURE_SUCCESS(rv, rv);
     274             :       // Set the new payload id.
     275           0 :       payload.id = nsFaviconService::sLastInsertedIconId;
     276             :     }
     277           0 :     MOZ_ASSERT(payload.id > 0, "Payload should have an id");
     278             :   }
     279             : 
     280           0 :   if (!ids.empty()) {
     281             :     // Remove any old leftover payload.
     282           0 :     nsAutoCString sql("DELETE FROM moz_icons WHERE id IN (");
     283           0 :     for (int64_t id : ids) {
     284           0 :       sql.AppendInt(id);
     285           0 :       sql.AppendLiteral(",");
     286             :     }
     287           0 :     sql.AppendLiteral(" 0)"); // Non-existing id to match the trailing comma.
     288           0 :     nsCOMPtr<mozIStorageStatement> stmt = aDB->GetStatement(sql);
     289           0 :     NS_ENSURE_STATE(stmt);
     290           0 :     mozStorageStatementScoper scoper(stmt);
     291           0 :     rv = stmt->Execute();
     292           0 :     NS_ENSURE_SUCCESS(rv, rv);
     293             :   }
     294             : 
     295           0 :   return NS_OK;
     296             : }
     297             : 
     298             : /**
     299             :  * Fetches information on a icon url from the database.
     300             :  *
     301             :  * @param aDBConn
     302             :  *        Database connection to history tables.
     303             :  * @param aPreferredWidth
     304             :  *        The preferred size to fetch.
     305             :  * @param _icon
     306             :  *        Icon that should be fetched.
     307             :  */
     308             : nsresult
     309           1 : FetchIconInfo(const RefPtr<Database>& aDB,
     310             :               uint16_t aPreferredWidth,
     311             :               IconData& _icon
     312             : )
     313             : {
     314           1 :   MOZ_ASSERT(_icon.spec.Length(), "Must have a non-empty spec!");
     315           1 :   MOZ_ASSERT(!NS_IsMainThread());
     316             : 
     317           1 :   if (_icon.status & ICON_STATUS_CACHED) {
     318             :     // The icon data has already been set by ReplaceFaviconData.
     319           0 :     return NS_OK;
     320             :   }
     321             : 
     322           2 :   nsCOMPtr<mozIStorageStatement> stmt = aDB->GetStatement(
     323             :     "/* do not warn (bug no: not worth having a compound index) */ "
     324             :     "SELECT id, expire_ms, data, width, root "
     325             :     "FROM moz_icons "
     326             :     "WHERE fixed_icon_url_hash = hash(fixup_url(:url)) "
     327             :       "AND icon_url = :url "
     328             :     "ORDER BY width DESC "
     329           2 :   );
     330           1 :   NS_ENSURE_STATE(stmt);
     331           2 :   mozStorageStatementScoper scoper(stmt);
     332             : 
     333           3 :   DebugOnly<nsresult> rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("url"),
     334           3 :                                            _icon.spec);
     335           1 :   MOZ_ASSERT(NS_SUCCEEDED(rv));
     336             : 
     337           1 :   bool hasResult = false;
     338           1 :   while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
     339           0 :     IconPayload payload;
     340           0 :     rv = stmt->GetInt64(0, &payload.id);
     341           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
     342             : 
     343             :     // Expiration can be nullptr.
     344             :     bool isNull;
     345           0 :     rv = stmt->GetIsNull(1, &isNull);
     346           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
     347           0 :     if (!isNull) {
     348             :       int64_t expire_ms;
     349           0 :       rv = stmt->GetInt64(1, &expire_ms);
     350           0 :       MOZ_ASSERT(NS_SUCCEEDED(rv));
     351           0 :       _icon.expiration = expire_ms * 1000;
     352             :     }
     353             : 
     354             :     uint8_t* data;
     355           0 :     uint32_t dataLen = 0;
     356           0 :     rv = stmt->GetBlob(2, &dataLen, &data);
     357           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
     358           0 :     payload.data.Adopt(TO_CHARBUFFER(data), dataLen);
     359             : 
     360             :     int32_t width;
     361           0 :     rv = stmt->GetInt32(3, &width);
     362           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
     363           0 :     payload.width = width;
     364           0 :     if (payload.width == UINT16_MAX) {
     365           0 :       payload.mimeType.AssignLiteral(SVG_MIME_TYPE);
     366             :     } else {
     367           0 :       payload.mimeType.AssignLiteral(PNG_MIME_TYPE);
     368             :     }
     369             : 
     370             :     int32_t rootIcon;
     371           0 :     rv = stmt->GetInt32(4, &rootIcon);
     372           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
     373           0 :     _icon.rootIcon = rootIcon;
     374             : 
     375           0 :     if (aPreferredWidth == 0 || _icon.payloads.Length() == 0) {
     376           0 :       _icon.payloads.AppendElement(payload);
     377           0 :     } else if (payload.width >= aPreferredWidth) {
     378             :       // Only retain the best matching payload.
     379           0 :       _icon.payloads.ReplaceElementAt(0, payload);
     380             :     } else {
     381           0 :       break;
     382             :     }
     383             :   }
     384             : 
     385           1 :   return NS_OK;
     386             : }
     387             : 
     388             : nsresult
     389           0 : FetchIconPerSpec(const RefPtr<Database>& aDB,
     390             :                  const nsACString& aPageSpec,
     391             :                  const nsACString& aPageHost,
     392             :                  IconData& aIconData,
     393             :                  uint16_t aPreferredWidth)
     394             : {
     395           0 :   MOZ_ASSERT(!aPageSpec.IsEmpty(), "Page spec must not be empty.");
     396           0 :   MOZ_ASSERT(!NS_IsMainThread());
     397             : 
     398             :   // This selects both associated and root domain icons, ordered by width,
     399             :   // where an associated icon has priority over a root domain icon.
     400             :   // Regardless, note that while this way we are far more efficient, we lost
     401             :   // associations with root domain icons, so it's possible we'll return one
     402             :   // for a specific size when an associated icon for that size doesn't exist.
     403           0 :   nsCOMPtr<mozIStorageStatement> stmt = aDB->GetStatement(
     404             :     "/* do not warn (bug no: not worth having a compound index) */ "
     405             :     "SELECT width, icon_url, root "
     406             :     "FROM moz_icons i "
     407             :     "JOIN moz_icons_to_pages ON i.id = icon_id "
     408             :     "JOIN moz_pages_w_icons p ON p.id = page_id "
     409             :     "WHERE page_url_hash = hash(:url) AND page_url = :url "
     410             :        "OR (:hash_idx AND page_url_hash = hash(substr(:url, 0, :hash_idx)) "
     411             :                      "AND page_url = substr(:url, 0, :hash_idx)) "
     412             :     "UNION ALL "
     413             :     "SELECT width, icon_url, root "
     414             :     "FROM moz_icons i "
     415             :     "WHERE fixed_icon_url_hash = hash(fixup_url(:root_icon_url)) "
     416             :     "ORDER BY width DESC, root ASC "
     417           0 :   );
     418           0 :   NS_ENSURE_STATE(stmt);
     419           0 :   mozStorageStatementScoper scoper(stmt);
     420             : 
     421           0 :   nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("url"), aPageSpec);
     422           0 :   NS_ENSURE_SUCCESS(rv, rv);
     423           0 :   nsAutoCString rootIconFixedUrl(aPageHost);
     424           0 :   if (!rootIconFixedUrl.IsEmpty()) {
     425           0 :     rootIconFixedUrl.AppendLiteral("/favicon.ico");
     426             :   }
     427           0 :   rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("root_icon_url"),
     428           0 :                                   rootIconFixedUrl);
     429           0 :   NS_ENSURE_SUCCESS(rv, rv);
     430           0 :   int32_t hashIdx = PromiseFlatCString(aPageSpec).RFind("#");
     431           0 :   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("hash_idx"), hashIdx + 1);
     432           0 :   NS_ENSURE_SUCCESS(rv, rv);
     433             : 
     434             :   // Return the biggest icon close to the preferred width. It may be bigger
     435             :   // or smaller if the preferred width isn't found.
     436             :   bool hasResult;
     437           0 :   while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
     438             :     int32_t width;
     439           0 :     rv = stmt->GetInt32(0, &width);
     440           0 :     if (!aIconData.spec.IsEmpty() && width < aPreferredWidth) {
     441             :       // We found the best match, or we already found a match so we don't need
     442             :       // to fallback to the root domain icon.
     443           0 :       break;
     444             :     }
     445           0 :     rv = stmt->GetUTF8String(1, aIconData.spec);
     446           0 :     NS_ENSURE_SUCCESS(rv, rv);
     447             :   }
     448             : 
     449           0 :   return NS_OK;
     450             : }
     451             : 
     452             : /**
     453             :  * Tries to compute the expiration time for a icon from the channel.
     454             :  *
     455             :  * @param aChannel
     456             :  *        The network channel used to fetch the icon.
     457             :  * @return a valid expiration value for the fetched icon.
     458             :  */
     459             : PRTime
     460           0 : GetExpirationTimeFromChannel(nsIChannel* aChannel)
     461             : {
     462           0 :   MOZ_ASSERT(NS_IsMainThread());
     463             : 
     464             :   // Attempt to get an expiration time from the cache.  If this fails, we'll
     465             :   // make one up.
     466           0 :   PRTime expiration = -1;
     467           0 :   nsCOMPtr<nsICachingChannel> cachingChannel = do_QueryInterface(aChannel);
     468           0 :   if (cachingChannel) {
     469           0 :     nsCOMPtr<nsISupports> cacheToken;
     470           0 :     nsresult rv = cachingChannel->GetCacheToken(getter_AddRefs(cacheToken));
     471           0 :     if (NS_SUCCEEDED(rv)) {
     472           0 :       nsCOMPtr<nsICacheEntry> cacheEntry = do_QueryInterface(cacheToken);
     473             :       uint32_t seconds;
     474           0 :       rv = cacheEntry->GetExpirationTime(&seconds);
     475           0 :       if (NS_SUCCEEDED(rv)) {
     476             :         // Set the expiration, but make sure we honor our cap.
     477           0 :         expiration = PR_Now() + std::min((PRTime)seconds * PR_USEC_PER_SEC,
     478           0 :                                        MAX_FAVICON_EXPIRATION);
     479             :       }
     480             :     }
     481             :   }
     482             :   // If we did not obtain a time from the cache, use the cap value.
     483           0 :   return expiration < 0 ? PR_Now() + MAX_FAVICON_EXPIRATION
     484           0 :                         : expiration;
     485             : }
     486             : 
     487             : } // namespace
     488             : 
     489             : ////////////////////////////////////////////////////////////////////////////////
     490             : //// AsyncFetchAndSetIconForPage
     491             : 
     492         123 : NS_IMPL_ISUPPORTS_INHERITED(
     493             :   AsyncFetchAndSetIconForPage
     494             : , Runnable
     495             : , nsIStreamListener
     496             : , nsIInterfaceRequestor
     497             : , nsIChannelEventSink
     498             : , mozIPlacesPendingOperation
     499             : )
     500             : 
     501           1 : AsyncFetchAndSetIconForPage::AsyncFetchAndSetIconForPage(
     502             :   IconData& aIcon
     503             : , PageData& aPage
     504             : , bool aFaviconLoadPrivate
     505             : , nsIFaviconDataCallback* aCallback
     506             : , nsIPrincipal* aLoadingPrincipal
     507           1 : ) : Runnable("places::AsyncFetchAndSetIconForPage")
     508             :   , mCallback(new nsMainThreadPtrHolder<nsIFaviconDataCallback>(
     509           1 :       "AsyncFetchAndSetIconForPage::mCallback", aCallback))
     510             :   , mIcon(aIcon)
     511             :   , mPage(aPage)
     512             :   , mFaviconLoadPrivate(aFaviconLoadPrivate)
     513             :   , mLoadingPrincipal(new nsMainThreadPtrHolder<nsIPrincipal>(
     514           1 :       "AsyncFetchAndSetIconForPage::mLoadingPrincipal", aLoadingPrincipal))
     515           3 :   , mCanceled(false)
     516             : {
     517           1 :   MOZ_ASSERT(NS_IsMainThread());
     518           1 : }
     519             : 
     520             : NS_IMETHODIMP
     521           1 : AsyncFetchAndSetIconForPage::Run()
     522             : {
     523           1 :   MOZ_ASSERT(!NS_IsMainThread());
     524             : 
     525             :   // Try to fetch the icon from the database.
     526           2 :   RefPtr<Database> DB = Database::GetDatabase();
     527           1 :   NS_ENSURE_STATE(DB);
     528           1 :   nsresult rv = FetchIconInfo(DB, 0, mIcon);
     529           1 :   NS_ENSURE_SUCCESS(rv, rv);
     530             : 
     531           1 :   bool isInvalidIcon = !mIcon.payloads.Length() || PR_Now() > mIcon.expiration;
     532           3 :   bool fetchIconFromNetwork = mIcon.fetchMode == FETCH_ALWAYS ||
     533           3 :                               (mIcon.fetchMode == FETCH_IF_MISSING && isInvalidIcon);
     534             : 
     535           1 :   if (!fetchIconFromNetwork) {
     536             :     // There is already a valid icon or we don't want to fetch a new one,
     537             :     // directly proceed with association.
     538             :     RefPtr<AsyncAssociateIconToPage> event =
     539           0 :         new AsyncAssociateIconToPage(mIcon, mPage, mCallback);
     540             :     // We're already on the async thread.
     541           0 :     return event->Run();
     542             :   }
     543             : 
     544             :   // Fetch the icon from the network, the request starts from the main-thread.
     545             :   // When done this will associate the icon to the page and notify.
     546             :   nsCOMPtr<nsIRunnable> event =
     547           2 :     NewRunnableMethod("places::AsyncFetchAndSetIconForPage::FetchFromNetwork",
     548             :                       this,
     549           2 :                       &AsyncFetchAndSetIconForPage::FetchFromNetwork);
     550           1 :   return NS_DispatchToMainThread(event);
     551             : }
     552             : 
     553             : nsresult
     554           1 : AsyncFetchAndSetIconForPage::FetchFromNetwork() {
     555           1 :   MOZ_ASSERT(NS_IsMainThread());
     556             : 
     557           1 :   if (mCanceled) {
     558           0 :     return NS_OK;
     559             :   }
     560             : 
     561             :   // Ensure data is cleared, since it's going to be overwritten.
     562           1 :   mIcon.payloads.Clear();
     563             : 
     564           2 :   IconPayload payload;
     565           1 :   mIcon.payloads.AppendElement(payload);
     566             : 
     567           2 :   nsCOMPtr<nsIURI> iconURI;
     568           1 :   nsresult rv = NS_NewURI(getter_AddRefs(iconURI), mIcon.spec);
     569           1 :   NS_ENSURE_SUCCESS(rv, rv);
     570           2 :   nsCOMPtr<nsIChannel> channel;
     571           2 :   rv = NS_NewChannel(getter_AddRefs(channel),
     572             :                      iconURI,
     573             :                      mLoadingPrincipal,
     574             :                      nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS |
     575             :                      nsILoadInfo::SEC_ALLOW_CHROME |
     576             :                      nsILoadInfo::SEC_DISALLOW_SCRIPT,
     577           1 :                      nsIContentPolicy::TYPE_INTERNAL_IMAGE_FAVICON);
     578             : 
     579           1 :   NS_ENSURE_SUCCESS(rv, rv);
     580             :   nsCOMPtr<nsIInterfaceRequestor> listenerRequestor =
     581           2 :     do_QueryInterface(reinterpret_cast<nsISupports*>(this));
     582           1 :   NS_ENSURE_STATE(listenerRequestor);
     583           1 :   rv = channel->SetNotificationCallbacks(listenerRequestor);
     584           1 :   NS_ENSURE_SUCCESS(rv, rv);
     585           2 :   nsCOMPtr<nsIPrivateBrowsingChannel> pbChannel = do_QueryInterface(channel);
     586           1 :   if (pbChannel) {
     587           1 :     rv = pbChannel->SetPrivate(mFaviconLoadPrivate);
     588           1 :     NS_ENSURE_SUCCESS(rv, rv);
     589             :   }
     590             : 
     591           2 :   nsCOMPtr<nsISupportsPriority> priorityChannel = do_QueryInterface(channel);
     592           1 :   if (priorityChannel) {
     593           1 :     priorityChannel->AdjustPriority(nsISupportsPriority::PRIORITY_LOWEST);
     594             :   }
     595             : 
     596           1 :   rv = channel->AsyncOpen2(this);
     597           1 :   if (NS_SUCCEEDED(rv)) {
     598           1 :     mRequest = channel;
     599             :   }
     600           1 :   return rv;
     601             : }
     602             : 
     603             : NS_IMETHODIMP
     604           0 : AsyncFetchAndSetIconForPage::Cancel()
     605             : {
     606           0 :   MOZ_ASSERT(NS_IsMainThread());
     607           0 :   if (mCanceled) {
     608           0 :     return NS_ERROR_UNEXPECTED;
     609             :   }
     610           0 :   mCanceled = true;
     611           0 :   if (mRequest) {
     612           0 :     mRequest->Cancel(NS_BINDING_ABORTED);
     613             :   }
     614           0 :   return NS_OK;
     615             : }
     616             : 
     617             : NS_IMETHODIMP
     618           1 : AsyncFetchAndSetIconForPage::OnStartRequest(nsIRequest* aRequest,
     619             :                                             nsISupports* aContext)
     620             : {
     621             :   // mRequest should already be set from ::FetchFromNetwork, but in the case of
     622             :   // a redirect we might get a new request, and we should make sure we keep a
     623             :   // reference to the most current request.
     624           1 :   mRequest = aRequest;
     625           1 :   if (mCanceled) {
     626           0 :     mRequest->Cancel(NS_BINDING_ABORTED);
     627             :   }
     628           1 :   return NS_OK;
     629             : }
     630             : 
     631             : NS_IMETHODIMP
     632           1 : AsyncFetchAndSetIconForPage::OnDataAvailable(nsIRequest* aRequest,
     633             :                                              nsISupports* aContext,
     634             :                                              nsIInputStream* aInputStream,
     635             :                                              uint64_t aOffset,
     636             :                                              uint32_t aCount)
     637             : {
     638           1 :   MOZ_ASSERT(mIcon.payloads.Length() == 1);
     639             :   // Limit downloads to 500KB.
     640           1 :   const size_t kMaxDownloadSize = 500 * 1024;
     641           1 :   if (mIcon.payloads[0].data.Length() + aCount > kMaxDownloadSize) {
     642           0 :     mIcon.payloads.Clear();
     643           0 :     return NS_ERROR_FILE_TOO_BIG;
     644             :   }
     645             : 
     646           2 :   nsAutoCString buffer;
     647           1 :   nsresult rv = NS_ConsumeStream(aInputStream, aCount, buffer);
     648           1 :   if (rv != NS_BASE_STREAM_WOULD_BLOCK && NS_FAILED(rv)) {
     649           0 :     return rv;
     650             :   }
     651             : 
     652           1 :   if (!mIcon.payloads[0].data.Append(buffer, fallible)) {
     653           0 :     mIcon.payloads.Clear();
     654           0 :     return NS_ERROR_OUT_OF_MEMORY;
     655             :   }
     656             : 
     657           1 :   return NS_OK;
     658             : }
     659             : 
     660             : 
     661             : NS_IMETHODIMP
     662          20 : AsyncFetchAndSetIconForPage::GetInterface(const nsIID& uuid,
     663             :                                           void** aResult)
     664             : {
     665          20 :   return QueryInterface(uuid, aResult);
     666             : }
     667             : 
     668             : 
     669             : NS_IMETHODIMP
     670           0 : AsyncFetchAndSetIconForPage::AsyncOnChannelRedirect(
     671             :   nsIChannel* oldChannel
     672             : , nsIChannel* newChannel
     673             : , uint32_t flags
     674             : , nsIAsyncVerifyRedirectCallback *cb
     675             : )
     676             : {
     677             :   // If we've been canceled, stop the redirect with NS_BINDING_ABORTED, and
     678             :   // handle the cancel on the original channel.
     679           0 :   (void)cb->OnRedirectVerifyCallback(mCanceled ? NS_BINDING_ABORTED : NS_OK);
     680           0 :   return NS_OK;
     681             : }
     682             : 
     683             : NS_IMETHODIMP
     684           1 : AsyncFetchAndSetIconForPage::OnStopRequest(nsIRequest* aRequest,
     685             :                                            nsISupports* aContext,
     686             :                                            nsresult aStatusCode)
     687             : {
     688           1 :   MOZ_ASSERT(NS_IsMainThread());
     689             : 
     690             :   // Don't need to track this anymore.
     691           1 :   mRequest = nullptr;
     692           1 :   if (mCanceled) {
     693           0 :     return NS_OK;
     694             :   }
     695             : 
     696           1 :   nsFaviconService* favicons = nsFaviconService::GetFaviconService();
     697           1 :   NS_ENSURE_STATE(favicons);
     698             : 
     699             :   nsresult rv;
     700             : 
     701             :   // If fetching the icon failed, add it to the failed cache.
     702           1 :   if (NS_FAILED(aStatusCode) || mIcon.payloads.Length() == 0) {
     703           0 :     nsCOMPtr<nsIURI> iconURI;
     704           0 :     rv = NS_NewURI(getter_AddRefs(iconURI), mIcon.spec);
     705           0 :     NS_ENSURE_SUCCESS(rv, rv);
     706           0 :     rv = favicons->AddFailedFavicon(iconURI);
     707           0 :     NS_ENSURE_SUCCESS(rv, rv);
     708           0 :     return NS_OK;
     709             :   }
     710             : 
     711           2 :   nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
     712             :   // aRequest should always QI to nsIChannel.
     713           1 :   MOZ_ASSERT(channel);
     714             : 
     715           1 :   MOZ_ASSERT(mIcon.payloads.Length() == 1);
     716           1 :   IconPayload& payload = mIcon.payloads[0];
     717             : 
     718           2 :   nsAutoCString contentType;
     719           1 :   channel->GetContentType(contentType);
     720             :   // Bug 366324 - We don't want to sniff for SVG, so rely on server-specified type.
     721           1 :   if (contentType.EqualsLiteral(SVG_MIME_TYPE)) {
     722           0 :     payload.mimeType.AssignLiteral(SVG_MIME_TYPE);
     723           0 :     payload.width = UINT16_MAX;
     724             :   } else {
     725           1 :     NS_SniffContent(NS_DATA_SNIFFER_CATEGORY, aRequest,
     726           1 :                     TO_INTBUFFER(payload.data), payload.data.Length(),
     727           1 :                     payload.mimeType);
     728             :   }
     729             : 
     730             :   // If the icon does not have a valid MIME type, add it to the failed cache.
     731           1 :   if (payload.mimeType.IsEmpty()) {
     732           2 :     nsCOMPtr<nsIURI> iconURI;
     733           1 :     rv = NS_NewURI(getter_AddRefs(iconURI), mIcon.spec);
     734           1 :     NS_ENSURE_SUCCESS(rv, rv);
     735           1 :     rv = favicons->AddFailedFavicon(iconURI);
     736           1 :     NS_ENSURE_SUCCESS(rv, rv);
     737           1 :     return NS_OK;
     738             :   }
     739             : 
     740           0 :   mIcon.expiration = GetExpirationTimeFromChannel(channel);
     741             : 
     742             :   // Telemetry probes to measure the favicon file sizes for each different file type.
     743             :   // This allow us to measure common file sizes while also observing each type popularity.
     744           0 :   if (payload.mimeType.EqualsLiteral(PNG_MIME_TYPE)) {
     745           0 :     mozilla::Telemetry::Accumulate(mozilla::Telemetry::PLACES_FAVICON_PNG_SIZES, payload.data.Length());
     746             :   }
     747           0 :   else if (payload.mimeType.EqualsLiteral("image/x-icon") ||
     748           0 :            payload.mimeType.EqualsLiteral("image/vnd.microsoft.icon")) {
     749           0 :     mozilla::Telemetry::Accumulate(mozilla::Telemetry::PLACES_FAVICON_ICO_SIZES, payload.data.Length());
     750             :   }
     751           0 :   else if (payload.mimeType.EqualsLiteral("image/jpeg") ||
     752           0 :            payload.mimeType.EqualsLiteral("image/pjpeg")) {
     753           0 :     mozilla::Telemetry::Accumulate(mozilla::Telemetry::PLACES_FAVICON_JPEG_SIZES, payload.data.Length());
     754             :   }
     755           0 :   else if (payload.mimeType.EqualsLiteral("image/gif")) {
     756           0 :     mozilla::Telemetry::Accumulate(mozilla::Telemetry::PLACES_FAVICON_GIF_SIZES, payload.data.Length());
     757             :   }
     758           0 :   else if (payload.mimeType.EqualsLiteral("image/bmp") ||
     759           0 :            payload.mimeType.EqualsLiteral("image/x-windows-bmp")) {
     760           0 :     mozilla::Telemetry::Accumulate(mozilla::Telemetry::PLACES_FAVICON_BMP_SIZES, payload.data.Length());
     761             :   }
     762           0 :   else if (payload.mimeType.EqualsLiteral(SVG_MIME_TYPE)) {
     763           0 :     mozilla::Telemetry::Accumulate(mozilla::Telemetry::PLACES_FAVICON_SVG_SIZES, payload.data.Length());
     764             :   }
     765             :   else {
     766           0 :     mozilla::Telemetry::Accumulate(mozilla::Telemetry::PLACES_FAVICON_OTHER_SIZES, payload.data.Length());
     767             :   }
     768             : 
     769           0 :   rv = favicons->OptimizeIconSizes(mIcon);
     770           0 :   NS_ENSURE_SUCCESS(rv, rv);
     771             : 
     772             :   // If there's not valid payload, don't store the icon into to the database.
     773           0 :   if (mIcon.payloads.Length() == 0) {
     774           0 :     return NS_OK;
     775             :   }
     776             : 
     777           0 :   mIcon.status = ICON_STATUS_CHANGED;
     778             : 
     779           0 :   RefPtr<Database> DB = Database::GetDatabase();
     780           0 :   NS_ENSURE_STATE(DB);
     781             :   RefPtr<AsyncAssociateIconToPage> event =
     782           0 :     new AsyncAssociateIconToPage(mIcon, mPage, mCallback);
     783           0 :   DB->DispatchToAsyncThread(event);
     784             : 
     785           0 :   return NS_OK;
     786             : }
     787             : 
     788             : ////////////////////////////////////////////////////////////////////////////////
     789             : //// AsyncAssociateIconToPage
     790             : 
     791           0 : AsyncAssociateIconToPage::AsyncAssociateIconToPage(
     792             :   const IconData& aIcon,
     793             :   const PageData& aPage,
     794           0 :   const nsMainThreadPtrHandle<nsIFaviconDataCallback>& aCallback)
     795             :   : Runnable("places::AsyncAssociateIconToPage")
     796             :   , mCallback(aCallback)
     797             :   , mIcon(aIcon)
     798           0 :   , mPage(aPage)
     799             : {
     800             :   // May be created in both threads.
     801           0 : }
     802             : 
     803             : NS_IMETHODIMP
     804           0 : AsyncAssociateIconToPage::Run()
     805             : {
     806           0 :   MOZ_ASSERT(!NS_IsMainThread());
     807             : 
     808           0 :   RefPtr<Database> DB = Database::GetDatabase();
     809           0 :   NS_ENSURE_STATE(DB);
     810           0 :   nsresult rv = FetchPageInfo(DB, mPage);
     811           0 :   if (rv == NS_ERROR_NOT_AVAILABLE){
     812             :     // We have never seen this page.  If we can add the page to history,
     813             :     // we will try to do it later, otherwise just bail out.
     814           0 :     if (!mPage.canAddToHistory) {
     815           0 :       return NS_OK;
     816             :     }
     817             :   }
     818             :   else {
     819           0 :     NS_ENSURE_SUCCESS(rv, rv);
     820             :   }
     821             : 
     822           0 :   bool shouldUpdateIcon = mIcon.status & ICON_STATUS_CHANGED;
     823           0 :   if (!shouldUpdateIcon) {
     824           0 :     for (const auto& payload : mIcon.payloads) {
     825             :       // If the entry is missing from the database, we should add it.
     826           0 :       if (payload.id == 0) {
     827           0 :         shouldUpdateIcon = true;
     828           0 :         break;
     829             :       }
     830             :     }
     831             :   }
     832             : 
     833             :   mozStorageTransaction transaction(DB->MainConn(), false,
     834           0 :                                     mozIStorageConnection::TRANSACTION_IMMEDIATE);
     835             : 
     836           0 :   if (shouldUpdateIcon) {
     837           0 :     rv = SetIconInfo(DB, mIcon);
     838           0 :     NS_ENSURE_SUCCESS(rv, rv);
     839             : 
     840           0 :     mIcon.status = (mIcon.status & ~(ICON_STATUS_CACHED)) | ICON_STATUS_SAVED;
     841             :   }
     842             : 
     843             :   // If the page does not have an id, don't try to insert a new one, cause we
     844             :   // don't know where the page comes from.  Not doing so we may end adding
     845             :   // a page that otherwise we'd explicitly ignore, like a POST or an error page.
     846           0 :   if (mPage.placeId == 0) {
     847           0 :     rv = transaction.Commit();
     848           0 :     NS_ENSURE_SUCCESS(rv, rv);
     849           0 :     return NS_OK;
     850             :   }
     851             : 
     852             :   // Don't associate pages to root domain icons, since those will be returned
     853             :   // regardless.  This saves a lot of work and database space since we don't
     854             :   // need to store urls and relations.
     855             :   // Though, this is possible only if both the page and the icon have the same
     856             :   // host, otherwise we couldn't relate them.
     857           0 :   if (!mIcon.rootIcon || !mIcon.host.Equals(mPage.host)) {
     858             :     // The page may have associated payloads already, and those could have to be
     859             :     // expired. For example at a certain point a page could decide to stop serving
     860             :     // its usual 16px and 32px pngs, and use an svg instead.
     861             :     // On the other side, we could also be in the process of adding more payloads
     862             :     // to this page, and we should not expire the payloads we just added.
     863             :     // For this, we use the expiration field as an indicator and remove relations
     864             :     // based on it being elapsed. We don't remove orphan icons at this time since
     865             :     // it would have a cost. The privacy hit is limited since history removal
     866             :     // methods already expire orphan icons.
     867           0 :     if (mPage.id != 0)  {
     868           0 :       nsCOMPtr<mozIStorageStatement> stmt;
     869           0 :       stmt = DB->GetStatement(
     870             :         "DELETE FROM moz_icons_to_pages "
     871             :         "WHERE icon_id IN ( "
     872             :           "SELECT icon_id FROM moz_icons_to_pages "
     873             :           "JOIN moz_icons i ON icon_id = i.id "
     874             :           "WHERE page_id = :page_id "
     875             :             "AND expire_ms < strftime('%s','now','localtime','start of day','-7 days','utc') * 1000 "
     876             :         ") AND page_id = :page_id "
     877           0 :       );
     878           0 :       NS_ENSURE_STATE(stmt);
     879           0 :       mozStorageStatementScoper scoper(stmt);
     880           0 :       rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), mPage.id);
     881           0 :       NS_ENSURE_SUCCESS(rv, rv);
     882           0 :       rv = stmt->Execute();
     883           0 :       NS_ENSURE_SUCCESS(rv, rv);
     884             :     } else {
     885             :       // We need to create the page entry.
     886           0 :       nsCOMPtr<mozIStorageStatement> stmt;
     887           0 :       stmt = DB->GetStatement(
     888             :         "INSERT OR IGNORE INTO moz_pages_w_icons (page_url, page_url_hash) "
     889             :         "VALUES (:page_url, hash(:page_url)) "
     890           0 :       );
     891           0 :       NS_ENSURE_STATE(stmt);
     892           0 :       mozStorageStatementScoper scoper(stmt);
     893           0 :       rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), mPage.spec);
     894           0 :       NS_ENSURE_SUCCESS(rv, rv);
     895           0 :       rv = stmt->Execute();
     896           0 :       NS_ENSURE_SUCCESS(rv, rv);
     897             :     }
     898             : 
     899             :     // Then we can create the relations.
     900           0 :     nsCOMPtr<mozIStorageStatement> stmt;
     901           0 :     stmt = DB->GetStatement(
     902             :       "INSERT OR IGNORE INTO moz_icons_to_pages (page_id, icon_id) "
     903             :       "VALUES ((SELECT id from moz_pages_w_icons WHERE page_url_hash = hash(:page_url) AND page_url = :page_url), "
     904             :               ":icon_id) "
     905           0 :     );
     906           0 :     NS_ENSURE_STATE(stmt);
     907             : 
     908             :     // For some reason using BindingParamsArray here fails execution, so we must
     909             :     // execute the statements one by one.
     910             :     // In the future we may want to investigate the reasons, sounds like related
     911             :     // to contraints.
     912           0 :     for (const auto& payload : mIcon.payloads) {
     913           0 :       mozStorageStatementScoper scoper(stmt);
     914           0 :       nsCOMPtr<mozIStorageBindingParams> params;
     915           0 :       rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), mPage.spec);
     916           0 :       NS_ENSURE_SUCCESS(rv, rv);
     917           0 :       rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("icon_id"), payload.id);
     918           0 :       NS_ENSURE_SUCCESS(rv, rv);
     919           0 :       rv = stmt->Execute();
     920           0 :       NS_ENSURE_SUCCESS(rv, rv);
     921             :     }
     922             :   }
     923             : 
     924           0 :   mIcon.status |= ICON_STATUS_ASSOCIATED;
     925             : 
     926           0 :   rv = transaction.Commit();
     927           0 :   NS_ENSURE_SUCCESS(rv, rv);
     928             : 
     929             :   // Finally, dispatch an event to the main thread to notify observers.
     930           0 :   nsCOMPtr<nsIRunnable> event = new NotifyIconObservers(mIcon, mPage, mCallback);
     931           0 :   rv = NS_DispatchToMainThread(event);
     932           0 :   NS_ENSURE_SUCCESS(rv, rv);
     933             : 
     934           0 :   return NS_OK;
     935             : }
     936             : 
     937             : ////////////////////////////////////////////////////////////////////////////////
     938             : //// AsyncGetFaviconURLForPage
     939             : 
     940           0 : AsyncGetFaviconURLForPage::AsyncGetFaviconURLForPage(
     941             :   const nsACString& aPageSpec
     942             : , const nsACString& aPageHost
     943             : , uint16_t aPreferredWidth
     944             : , nsIFaviconDataCallback* aCallback
     945           0 : ) : Runnable("places::AsyncGetFaviconURLForPage")
     946             :   , mPreferredWidth(aPreferredWidth == 0 ? UINT16_MAX : aPreferredWidth)
     947             :   , mCallback(new nsMainThreadPtrHolder<nsIFaviconDataCallback>(
     948           0 :       "AsyncGetFaviconURLForPage::mCallback", aCallback))
     949             : {
     950           0 :   MOZ_ASSERT(NS_IsMainThread());
     951           0 :   mPageSpec.Assign(aPageSpec);
     952           0 :   mPageHost.Assign(aPageHost);
     953           0 : }
     954             : 
     955             : NS_IMETHODIMP
     956           0 : AsyncGetFaviconURLForPage::Run()
     957             : {
     958           0 :   MOZ_ASSERT(!NS_IsMainThread());
     959             : 
     960           0 :   RefPtr<Database> DB = Database::GetDatabase();
     961           0 :   NS_ENSURE_STATE(DB);
     962           0 :   IconData iconData;
     963           0 :   nsresult rv = FetchIconPerSpec(DB, mPageSpec, mPageHost, iconData, mPreferredWidth);
     964           0 :   NS_ENSURE_SUCCESS(rv, rv);
     965             : 
     966             :   // Now notify our callback of the icon spec we retrieved, even if empty.
     967           0 :   PageData pageData;
     968           0 :   pageData.spec.Assign(mPageSpec);
     969             : 
     970             :   nsCOMPtr<nsIRunnable> event =
     971           0 :     new NotifyIconObservers(iconData, pageData, mCallback);
     972           0 :   rv = NS_DispatchToMainThread(event);
     973           0 :   NS_ENSURE_SUCCESS(rv, rv);
     974             : 
     975           0 :   return NS_OK;
     976             : }
     977             : 
     978             : ////////////////////////////////////////////////////////////////////////////////
     979             : //// AsyncGetFaviconDataForPage
     980             : 
     981           0 : AsyncGetFaviconDataForPage::AsyncGetFaviconDataForPage(
     982             :   const nsACString& aPageSpec
     983             : , const nsACString& aPageHost
     984             : ,  uint16_t aPreferredWidth
     985             : , nsIFaviconDataCallback* aCallback
     986           0 : ) : Runnable("places::AsyncGetFaviconDataForPage")
     987             :   , mPreferredWidth(aPreferredWidth == 0 ? UINT16_MAX : aPreferredWidth)
     988             :   , mCallback(new nsMainThreadPtrHolder<nsIFaviconDataCallback>(
     989           0 :       "AsyncGetFaviconDataForPage::mCallback", aCallback))
     990             :  {
     991           0 :   MOZ_ASSERT(NS_IsMainThread());
     992           0 :   mPageSpec.Assign(aPageSpec);
     993           0 :   mPageHost.Assign(aPageHost);
     994           0 : }
     995             : 
     996             : NS_IMETHODIMP
     997           0 : AsyncGetFaviconDataForPage::Run()
     998             : {
     999           0 :   MOZ_ASSERT(!NS_IsMainThread());
    1000             : 
    1001           0 :   RefPtr<Database> DB = Database::GetDatabase();
    1002           0 :   NS_ENSURE_STATE(DB);
    1003           0 :   IconData iconData;
    1004           0 :   nsresult rv = FetchIconPerSpec(DB, mPageSpec, mPageHost, iconData, mPreferredWidth);
    1005           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1006             : 
    1007           0 :   if (!iconData.spec.IsEmpty()) {
    1008           0 :     rv = FetchIconInfo(DB, mPreferredWidth, iconData);
    1009           0 :     if (NS_FAILED(rv)) {
    1010           0 :       iconData.spec.Truncate();
    1011             :     }
    1012             :   }
    1013             : 
    1014           0 :   PageData pageData;
    1015           0 :   pageData.spec.Assign(mPageSpec);
    1016             : 
    1017             :   nsCOMPtr<nsIRunnable> event =
    1018           0 :     new NotifyIconObservers(iconData, pageData, mCallback);
    1019           0 :   rv = NS_DispatchToMainThread(event);
    1020           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1021           0 :   return NS_OK;
    1022             : }
    1023             : 
    1024             : ////////////////////////////////////////////////////////////////////////////////
    1025             : //// AsyncReplaceFaviconData
    1026             : 
    1027           0 : AsyncReplaceFaviconData::AsyncReplaceFaviconData(const IconData& aIcon)
    1028             :   : Runnable("places::AsyncReplaceFaviconData")
    1029           0 :   , mIcon(aIcon)
    1030             : {
    1031           0 :   MOZ_ASSERT(NS_IsMainThread());
    1032           0 : }
    1033             : 
    1034             : NS_IMETHODIMP
    1035           0 : AsyncReplaceFaviconData::Run()
    1036             : {
    1037           0 :   MOZ_ASSERT(!NS_IsMainThread());
    1038             : 
    1039           0 :   RefPtr<Database> DB = Database::GetDatabase();
    1040           0 :   NS_ENSURE_STATE(DB);
    1041             : 
    1042             :   mozStorageTransaction transaction(DB->MainConn(), false,
    1043           0 :                                     mozIStorageConnection::TRANSACTION_IMMEDIATE);
    1044           0 :   nsresult rv = SetIconInfo(DB, mIcon, true);
    1045           0 :   if (rv == NS_ERROR_NOT_AVAILABLE) {
    1046             :     // There's no previous icon to replace, we don't need to do anything.
    1047           0 :     return NS_OK;
    1048             :   }
    1049           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1050           0 :   rv = transaction.Commit();
    1051           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1052             : 
    1053             :   // We can invalidate the cache version since we now persist the icon.
    1054           0 :   nsCOMPtr<nsIRunnable> event = NewRunnableMethod(
    1055             :     "places::AsyncReplaceFaviconData::RemoveIconDataCacheEntry",
    1056             :     this,
    1057           0 :     &AsyncReplaceFaviconData::RemoveIconDataCacheEntry);
    1058           0 :   rv = NS_DispatchToMainThread(event);
    1059           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1060             : 
    1061           0 :   return NS_OK;
    1062             : }
    1063             : 
    1064             : nsresult
    1065           0 : AsyncReplaceFaviconData::RemoveIconDataCacheEntry()
    1066             : {
    1067           0 :   MOZ_ASSERT(NS_IsMainThread());
    1068             : 
    1069           0 :   nsCOMPtr<nsIURI> iconURI;
    1070           0 :   nsresult rv = NS_NewURI(getter_AddRefs(iconURI), mIcon.spec);
    1071           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1072             : 
    1073           0 :   nsFaviconService* favicons = nsFaviconService::GetFaviconService();
    1074           0 :   NS_ENSURE_STATE(favicons);
    1075           0 :   favicons->mUnassociatedIcons.RemoveEntry(iconURI);
    1076             : 
    1077           0 :   return NS_OK;
    1078             : }
    1079             : 
    1080             : 
    1081             : ////////////////////////////////////////////////////////////////////////////////
    1082             : //// NotifyIconObservers
    1083             : 
    1084           0 : NotifyIconObservers::NotifyIconObservers(
    1085             :   const IconData& aIcon,
    1086             :   const PageData& aPage,
    1087           0 :   const nsMainThreadPtrHandle<nsIFaviconDataCallback>& aCallback)
    1088             :   : Runnable("places::NotifyIconObservers")
    1089             :   , mCallback(aCallback)
    1090             :   , mIcon(aIcon)
    1091           0 :   , mPage(aPage)
    1092             : {
    1093           0 : }
    1094             : 
    1095             : NS_IMETHODIMP
    1096           0 : NotifyIconObservers::Run()
    1097             : {
    1098           0 :   MOZ_ASSERT(NS_IsMainThread());
    1099             : 
    1100           0 :   nsCOMPtr<nsIURI> iconURI;
    1101           0 :   if (!mIcon.spec.IsEmpty()) {
    1102           0 :     MOZ_ALWAYS_SUCCEEDS(NS_NewURI(getter_AddRefs(iconURI), mIcon.spec));
    1103           0 :     if (iconURI)
    1104             :     {
    1105             :       // Notify observers only if something changed.
    1106           0 :       if (mIcon.status & ICON_STATUS_SAVED ||
    1107           0 :           mIcon.status & ICON_STATUS_ASSOCIATED) {
    1108           0 :         SendGlobalNotifications(iconURI);
    1109             :       }
    1110             :     }
    1111             :   }
    1112             : 
    1113           0 :   if (!mCallback) {
    1114           0 :     return NS_OK;
    1115             :   }
    1116             : 
    1117           0 :   if (mIcon.payloads.Length() > 0) {
    1118           0 :     IconPayload& payload = mIcon.payloads[0];
    1119           0 :     return mCallback->OnComplete(iconURI, payload.data.Length(),
    1120           0 :                                  TO_INTBUFFER(payload.data), payload.mimeType,
    1121           0 :                                  payload.width);
    1122             :   }
    1123           0 :   return mCallback->OnComplete(iconURI, 0, TO_INTBUFFER(EmptyCString()),
    1124           0 :                                EmptyCString(), 0);
    1125             : }
    1126             : 
    1127             : void
    1128           0 : NotifyIconObservers::SendGlobalNotifications(nsIURI* aIconURI)
    1129             : {
    1130           0 :   nsCOMPtr<nsIURI> pageURI;
    1131           0 :   MOZ_ALWAYS_SUCCEEDS(NS_NewURI(getter_AddRefs(pageURI), mPage.spec));
    1132           0 :   if (pageURI) {
    1133           0 :     nsFaviconService* favicons = nsFaviconService::GetFaviconService();
    1134           0 :     MOZ_ASSERT(favicons);
    1135           0 :     if (favicons) {
    1136           0 :       (void)favicons->SendFaviconNotifications(pageURI, aIconURI, mPage.guid);
    1137             :     }
    1138             :   }
    1139             : 
    1140             :   // If the page is bookmarked and the bookmarked url is different from the
    1141             :   // updated one, start a new task to update its icon as well.
    1142           0 :   if (!mPage.bookmarkedSpec.IsEmpty() &&
    1143           0 :       !mPage.bookmarkedSpec.Equals(mPage.spec)) {
    1144             :     // Create a new page struct to avoid polluting it with old data.
    1145           0 :     PageData bookmarkedPage;
    1146           0 :     bookmarkedPage.spec = mPage.bookmarkedSpec;
    1147             : 
    1148           0 :     RefPtr<Database> DB = Database::GetDatabase();
    1149           0 :     if (!DB)
    1150           0 :       return;
    1151             :     // This will be silent, so be sure to not pass in the current callback.
    1152           0 :     nsMainThreadPtrHandle<nsIFaviconDataCallback> nullCallback;
    1153             :     RefPtr<AsyncAssociateIconToPage> event =
    1154           0 :         new AsyncAssociateIconToPage(mIcon, bookmarkedPage, nullCallback);
    1155           0 :     DB->DispatchToAsyncThread(event);
    1156             :   }
    1157             : }
    1158             : 
    1159             : ////////////////////////////////////////////////////////////////////////////////
    1160             : //// FetchAndConvertUnsupportedPayloads
    1161             : 
    1162           0 : FetchAndConvertUnsupportedPayloads::FetchAndConvertUnsupportedPayloads(
    1163           0 :   mozIStorageConnection* aDBConn)
    1164             :   : Runnable("places::FetchAndConvertUnsupportedPayloads")
    1165           0 :   , mDB(aDBConn)
    1166             : {
    1167             : 
    1168           0 : }
    1169             : 
    1170             : NS_IMETHODIMP
    1171           0 : FetchAndConvertUnsupportedPayloads::Run()
    1172             : {
    1173           0 :   if (NS_IsMainThread()) {
    1174           0 :     Preferences::ClearUser(PREF_CONVERT_PAYLOADS);
    1175           0 :     return NS_OK;
    1176             :   }
    1177             : 
    1178           0 :   MOZ_ASSERT(!NS_IsMainThread());
    1179           0 :   NS_ENSURE_STATE(mDB);
    1180             : 
    1181           0 :   nsCOMPtr<mozIStorageStatement> stmt;
    1182           0 :   nsresult rv = mDB->CreateStatement(NS_LITERAL_CSTRING(
    1183             :     "SELECT id, width, data FROM moz_icons WHERE typeof(width) = 'text' "
    1184             :     "ORDER BY id ASC "
    1185             :     "LIMIT 200 "
    1186           0 :   ), getter_AddRefs(stmt));
    1187           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1188             : 
    1189             :   mozStorageTransaction transaction(mDB, false,
    1190           0 :                                     mozIStorageConnection::TRANSACTION_IMMEDIATE);
    1191             : 
    1192             :   // We should do the work in chunks, or the wal journal may grow too much.
    1193           0 :   uint8_t count = 0;
    1194             :   bool hasResult;
    1195           0 :   while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
    1196           0 :     ++count;
    1197           0 :     int64_t id = stmt->AsInt64(0);
    1198           0 :     MOZ_ASSERT(id > 0);
    1199           0 :     nsAutoCString mimeType;
    1200           0 :     rv = stmt->GetUTF8String(1, mimeType);
    1201           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    1202           0 :       continue;
    1203             :     }
    1204             :     uint8_t* data;
    1205           0 :     uint32_t dataLen = 0;
    1206           0 :     rv = stmt->GetBlob(2, &dataLen, &data);
    1207           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    1208           0 :       continue;
    1209             :     }
    1210           0 :     nsCString buf;
    1211           0 :     buf.Adopt(TO_CHARBUFFER(data), dataLen);
    1212             : 
    1213           0 :     int32_t width = 0;
    1214           0 :     rv = ConvertPayload(id, mimeType, buf, &width);
    1215           0 :     Unused << NS_WARN_IF(NS_FAILED(rv));
    1216           0 :     if (NS_SUCCEEDED(rv)) {
    1217           0 :       rv = StorePayload(id, width, buf);
    1218           0 :       if (NS_WARN_IF(NS_FAILED(rv))) {
    1219           0 :         continue;
    1220             :       }
    1221             :     }
    1222             :   }
    1223             : 
    1224           0 :   rv = transaction.Commit();
    1225           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1226             : 
    1227           0 :   if (count == 200) {
    1228             :     // There are more results to handle. Re-dispatch to the same thread for the
    1229             :     // next chunk.
    1230           0 :     return NS_DispatchToCurrentThread(this);
    1231             :   }
    1232             : 
    1233             :   // We're done. Remove any leftovers.
    1234           0 :   rv = mDB->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1235             :     "DELETE FROM moz_icons WHERE typeof(width) = 'text'"
    1236           0 :   ));
    1237           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1238             :   // Run a one-time VACUUM of places.sqlite, since we removed a lot from it.
    1239             :   // It may cause jank, but not doing it could cause dataloss due to expiration.
    1240           0 :   rv = mDB->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1241             :     "VACUUM"
    1242           0 :   ));
    1243           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1244             : 
    1245             :   // Re-dispatch to the main-thread to flip the conversion pref.
    1246           0 :   return NS_DispatchToMainThread(this);
    1247             : }
    1248             : 
    1249             : nsresult
    1250           0 : FetchAndConvertUnsupportedPayloads::ConvertPayload(int64_t aId,
    1251             :                                                    const nsACString& aMimeType,
    1252             :                                                    nsCString& aPayload,
    1253             :                                                    int32_t* aWidth)
    1254             : {
    1255             :   // TODO (bug 1346139): this should probably be unified with the function that
    1256             :   // will handle additions optimization off the main thread.
    1257           0 :   MOZ_ASSERT(!NS_IsMainThread());
    1258           0 :   *aWidth = 0;
    1259             : 
    1260             :   // Exclude invalid mime types.
    1261           0 :   if (aPayload.Length() == 0 ||
    1262           0 :       !imgLoader::SupportImageWithMimeType(PromiseFlatCString(aMimeType).get(),
    1263             :                                            AcceptedMimeTypes::IMAGES_AND_DOCUMENTS)) {
    1264           0 :     return NS_ERROR_FAILURE;
    1265             :   }
    1266             : 
    1267             :   // If it's an SVG, there's nothing to optimize or convert.
    1268           0 :   if (aMimeType.EqualsLiteral(SVG_MIME_TYPE)) {
    1269           0 :     *aWidth = UINT16_MAX;
    1270           0 :     return NS_OK;
    1271             :   }
    1272             : 
    1273             :   // Convert the payload to an input stream.
    1274           0 :   nsCOMPtr<nsIInputStream> stream;
    1275           0 :   nsresult rv = NS_NewByteInputStream(getter_AddRefs(stream),
    1276           0 :                 aPayload.get(), aPayload.Length(),
    1277           0 :                 NS_ASSIGNMENT_DEPEND);
    1278           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1279             : 
    1280             :   // Decode the input stream to a surface.
    1281             :   RefPtr<gfx::SourceSurface> surface =
    1282           0 :       image::ImageOps::DecodeToSurface(stream,
    1283             :                                        aMimeType,
    1284           0 :                                        imgIContainer::DECODE_FLAGS_DEFAULT);
    1285           0 :   NS_ENSURE_STATE(surface);
    1286           0 :   RefPtr<gfx::DataSourceSurface> dataSurface = surface->GetDataSurface();
    1287           0 :   NS_ENSURE_STATE(dataSurface);
    1288             : 
    1289             :   // Read the current size and set an appropriate final width.
    1290           0 :   int32_t width = dataSurface->GetSize().width;
    1291           0 :   int32_t height = dataSurface->GetSize().height;
    1292             :   // For non-square images, pick the largest side.
    1293           0 :   int32_t originalSize = std::max(width, height);
    1294           0 :   int32_t size = originalSize;
    1295           0 :   for (uint16_t supportedSize : sFaviconSizes) {
    1296           0 :     if (supportedSize <= originalSize) {
    1297           0 :       size = supportedSize;
    1298           0 :       break;
    1299             :     }
    1300             :   }
    1301           0 :   *aWidth = size;
    1302             : 
    1303             :   // If the original payload is png and the size is the same, no reason to
    1304             :   // rescale the image.
    1305           0 :   if (aMimeType.EqualsLiteral(PNG_MIME_TYPE) && size == originalSize) {
    1306           0 :     return NS_OK;
    1307             :   }
    1308             : 
    1309             :   // Rescale when needed.
    1310             :   RefPtr<gfx::DataSourceSurface> targetDataSurface =
    1311           0 :     gfx::Factory::CreateDataSourceSurface(gfx::IntSize(size, size),
    1312             :                                           gfx::SurfaceFormat::B8G8R8A8,
    1313           0 :                                           true);
    1314           0 :   NS_ENSURE_STATE(targetDataSurface);
    1315             : 
    1316             :   { // Block scope for map.
    1317             :     gfx::DataSourceSurface::MappedSurface map;
    1318           0 :     if (!targetDataSurface->Map(gfx::DataSourceSurface::MapType::WRITE, &map)) {
    1319           0 :       return NS_ERROR_FAILURE;
    1320             :     }
    1321             : 
    1322             :     RefPtr<gfx::DrawTarget> dt =
    1323           0 :       gfx::Factory::CreateDrawTargetForData(gfx::BackendType::CAIRO,
    1324             :                                             map.mData,
    1325           0 :                                             targetDataSurface->GetSize(),
    1326             :                                             map.mStride,
    1327           0 :                                             gfx::SurfaceFormat::B8G8R8A8);
    1328           0 :     NS_ENSURE_STATE(dt);
    1329             : 
    1330           0 :     gfx::IntSize frameSize = dataSurface->GetSize();
    1331           0 :     dt->DrawSurface(dataSurface,
    1332           0 :                     gfx::Rect(0, 0, size, size),
    1333           0 :                     gfx::Rect(0, 0, frameSize.width, frameSize.height),
    1334           0 :                     gfx::DrawSurfaceOptions(),
    1335           0 :                     gfx::DrawOptions(1.0f, gfx::CompositionOp::OP_SOURCE));
    1336           0 :     targetDataSurface->Unmap();
    1337             :   }
    1338             : 
    1339             :   // Finally Encode.
    1340             :   nsCOMPtr<imgIEncoder> encoder =
    1341           0 :     do_CreateInstance("@mozilla.org/image/encoder;2?type=image/png");
    1342           0 :   NS_ENSURE_STATE(encoder);
    1343             : 
    1344             :   gfx::DataSourceSurface::MappedSurface map;
    1345           0 :   if (!targetDataSurface->Map(gfx::DataSourceSurface::MapType::READ, &map)) {
    1346           0 :     return NS_ERROR_FAILURE;
    1347             :   }
    1348           0 :   rv = encoder->InitFromData(map.mData, map.mStride * size, size, size,
    1349           0 :                              map.mStride, imgIEncoder::INPUT_FORMAT_HOSTARGB,
    1350           0 :                              EmptyString());
    1351           0 :   targetDataSurface->Unmap();
    1352           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1353             : 
    1354             :   // Read the stream into a new buffer.
    1355           0 :   nsCOMPtr<nsIInputStream> iconStream = do_QueryInterface(encoder);
    1356           0 :   NS_ENSURE_STATE(iconStream);
    1357           0 :   rv = NS_ConsumeStream(iconStream, UINT32_MAX, aPayload);
    1358           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1359             : 
    1360           0 :   return NS_OK;
    1361             : }
    1362             : 
    1363             : nsresult
    1364           0 : FetchAndConvertUnsupportedPayloads::StorePayload(int64_t aId,
    1365             :                                                  int32_t aWidth,
    1366             :                                                  const nsCString& aPayload)
    1367             : {
    1368           0 :   MOZ_ASSERT(!NS_IsMainThread());
    1369             : 
    1370           0 :   NS_ENSURE_STATE(mDB);
    1371           0 :   nsCOMPtr<mozIStorageStatement> stmt;
    1372           0 :   nsresult rv = mDB->CreateStatement(NS_LITERAL_CSTRING(
    1373             :     "UPDATE moz_icons SET data = :data, width = :width WHERE id = :id"
    1374           0 :   ), getter_AddRefs(stmt));
    1375           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1376             : 
    1377           0 :   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), aId);
    1378           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1379           0 :   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("width"), aWidth);
    1380           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1381           0 :   rv = stmt->BindBlobByName(NS_LITERAL_CSTRING("data"),
    1382           0 :                             TO_INTBUFFER(aPayload), aPayload.Length());
    1383           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1384             : 
    1385           0 :   rv = stmt->Execute();
    1386           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1387             : 
    1388           0 :   return NS_OK;
    1389             : }
    1390             : 
    1391             : ////////////////////////////////////////////////////////////////////////////////
    1392             : //// AsyncCopyFavicons
    1393             : 
    1394           0 : AsyncCopyFavicons::AsyncCopyFavicons(PageData& aFromPage,
    1395             :                                      PageData& aToPage,
    1396           0 :                                      nsIFaviconDataCallback* aCallback)
    1397             :   : Runnable("places::AsyncCopyFavicons")
    1398             :   , mFromPage(aFromPage)
    1399             :   , mToPage(aToPage)
    1400             :   , mCallback(new nsMainThreadPtrHolder<nsIFaviconDataCallback>(
    1401           0 :       "AsyncCopyFavicons::mCallback", aCallback))
    1402             : {
    1403           0 :   MOZ_ASSERT(NS_IsMainThread());
    1404           0 : }
    1405             : 
    1406             : NS_IMETHODIMP
    1407           0 : AsyncCopyFavicons::Run()
    1408             : {
    1409           0 :   MOZ_ASSERT(!NS_IsMainThread());
    1410             : 
    1411           0 :   IconData icon;
    1412             : 
    1413             :   // Ensure we'll callback and dispatch notifications to the main-thread.
    1414           0 :   auto cleanup = MakeScopeExit([&] () {
    1415             :     // If we bailed out early, just return a null icon uri, since we didn't
    1416             :     // copy anything.
    1417           0 :     if (!(icon.status & ICON_STATUS_ASSOCIATED)) {
    1418           0 :       icon.spec.Truncate();
    1419             :     }
    1420           0 :     nsCOMPtr<nsIRunnable> event = new NotifyIconObservers(icon, mToPage, mCallback);
    1421           0 :     NS_DispatchToMainThread(event);
    1422           0 :   });
    1423             : 
    1424           0 :   RefPtr<Database> DB = Database::GetDatabase();
    1425           0 :   NS_ENSURE_STATE(DB);
    1426             : 
    1427           0 :   nsresult rv = FetchPageInfo(DB, mToPage);
    1428           0 :   if (rv == NS_ERROR_NOT_AVAILABLE || !mToPage.placeId) {
    1429             :     // We have never seen this page, or we can't add this page to history and
    1430             :     // and it's not a bookmark. We won't add the page.
    1431           0 :     return NS_OK;
    1432             :   }
    1433           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1434             : 
    1435             :   // Get just one icon, to check whether the page has any, and to notify later.
    1436           0 :   rv = FetchIconPerSpec(DB, mFromPage.spec, EmptyCString(), icon, UINT16_MAX);
    1437           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1438             : 
    1439           0 :   if (icon.spec.IsEmpty()) {
    1440             :     // There's nothing to copy.
    1441           0 :     return NS_OK;
    1442             :   }
    1443             : 
    1444             :   // Insert an entry in moz_pages_w_icons if needed.
    1445           0 :   if (!mToPage.id) {
    1446             :     // We need to create the page entry.
    1447           0 :     nsCOMPtr<mozIStorageStatement> stmt;
    1448           0 :     stmt = DB->GetStatement(
    1449             :       "INSERT OR IGNORE INTO moz_pages_w_icons (page_url, page_url_hash) "
    1450             :       "VALUES (:page_url, hash(:page_url)) "
    1451           0 :     );
    1452           0 :     NS_ENSURE_STATE(stmt);
    1453           0 :     mozStorageStatementScoper scoper(stmt);
    1454           0 :     rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), mToPage.spec);
    1455           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1456           0 :     rv = stmt->Execute();
    1457           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1458             :     // Required to to fetch the id and the guid.
    1459           0 :     rv = FetchPageInfo(DB, mToPage);
    1460           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1461             :   }
    1462             : 
    1463             :   // Create the relations.
    1464           0 :   nsCOMPtr<mozIStorageStatement> stmt = DB->GetStatement(
    1465             :     "INSERT OR IGNORE INTO moz_icons_to_pages (page_id, icon_id) "
    1466             :       "SELECT :id, icon_id "
    1467             :       "FROM moz_icons_to_pages "
    1468             :       "WHERE page_id = (SELECT id FROM moz_pages_w_icons WHERE page_url_hash = hash(:url) AND page_url = :url) "
    1469           0 :   );
    1470           0 :   NS_ENSURE_STATE(stmt);
    1471           0 :   mozStorageStatementScoper scoper(stmt);
    1472           0 :   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), mToPage.id);
    1473           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1474           0 :   rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("url"), mFromPage.spec);
    1475           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1476           0 :   rv = stmt->Execute();
    1477           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1478             : 
    1479             :   // Setting this will make us send pageChanged notifications.
    1480             :   // The scope exit will take care of the callback and notifications.
    1481           0 :   icon.status |= ICON_STATUS_ASSOCIATED;
    1482             : 
    1483           0 :   return NS_OK;
    1484             : }
    1485             : 
    1486             : } // namespace places
    1487             : } // namespace mozilla

Generated by: LCOV version 1.13