Bug 977177 - Update favicons API consumers. r=adw
authorMarco Bonardo <mbonardo@mozilla.com>
Wed, 28 Sep 2016 16:14:30 +0200
changeset 352673 90d755bcbb92
parent 352672 942aa1533e08
child 352674 4a0770d810dc
push id31643
push userkwierso@gmail.com
push date2017-04-12 22:13 +0000
treeherdermozilla-central@ff7729561efd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersadw
bugs977177, 1347532, 1346139, 1337402
milestone55.0a1
Bug 977177 - Update favicons API consumers. r=adw

Updates consumers to the new behavior.
Some consumers are changed to use the "page-icon:" protocol, since it's not
trivial to join the icons table and get a single result out of it. In most cases
the join would return multiple results since a page can have multiple icon payloads.
These consumers for now will return the biggest payload, bug 1347532 will fix
some of them to properly pass a #size=NN fragment.
Note that, even before, these were just "moz-anno:favicon:" uris, and the
payload had to be fetched from the database.
Some other consumers for now just fallback to the largest payload, by passing 0
to GetFaviconURLForPage.

The favicon optimization still happens on the main-thread, bug 1346139 will
handle that problem.

Most of the changes involve handling the modified IconData objects, that now
retain an array of payloads, rather than just one. But note that .ico files are
not yet split into single frames, due to imagelib missing APIs that will be handled
in bug 1337402.
The other changes involve fixing queries to properly join with the new tables.

Finally, note that thanks to the FOREIGN KEYS support, removing from moz_icons or
moz_pages_w_icons will also remove relations from moz_icons_to_pages.

The system only supports square icons, so icons are resized based on their larger side.

This doesn't include new tests, those will be in a following changeset.

MozReview-Commit-ID: JUkpquhpS8y
browser/components/customizableui/CustomizableWidgets.jsm
browser/components/places/content/browserPlacesViews.js
docshell/base/nsDocShell.cpp
testing/firefox-ui/tests/functional/locationbar/manifest.ini
testing/firefox-ui/tests/functional/locationbar/test_favicon_in_autocomplete.py
toolkit/components/alerts/nsAlertsService.cpp
toolkit/components/places/FaviconHelpers.cpp
toolkit/components/places/FaviconHelpers.h
toolkit/components/places/History.jsm
toolkit/components/places/PageIconProtocolHandler.js
toolkit/components/places/PlacesDBUtils.jsm
toolkit/components/places/PlacesUtils.jsm
toolkit/components/places/UnifiedComplete.js
toolkit/components/places/mozIAsyncFavicons.idl
toolkit/components/places/nsAnnoProtocolHandler.cpp
toolkit/components/places/nsFaviconService.cpp
toolkit/components/places/nsFaviconService.h
toolkit/components/places/nsIFaviconService.idl
toolkit/components/places/nsNavBookmarks.cpp
toolkit/components/places/nsNavHistory.cpp
toolkit/components/places/nsNavHistory.h
toolkit/components/places/nsNavHistoryResult.cpp
toolkit/components/places/nsNavHistoryResult.h
toolkit/components/places/nsPlacesExpiration.js
toolkit/components/places/tests/browser/browser_favicon_setAndFetchFaviconForPage_failures.js
toolkit/components/places/tests/chrome/chrome.ini
toolkit/components/places/tests/chrome/head.js
toolkit/components/places/tests/chrome/test_favicon_annotations.xul
toolkit/components/places/tests/favicons/expected-favicon-big16.ico.png
toolkit/components/places/tests/favicons/expected-favicon-big4.jpg.png
toolkit/components/places/tests/favicons/expected-favicon-big48.ico.png
toolkit/components/places/tests/favicons/expected-favicon-big64.png.png
toolkit/components/places/tests/favicons/expected-favicon-scale160x3.jpg.png
toolkit/components/places/tests/favicons/expected-favicon-scale3x160.jpg.png
toolkit/components/places/tests/favicons/test_expireAllFavicons.js
toolkit/components/places/tests/favicons/test_favicons_conversions.js
toolkit/components/places/tests/favicons/test_replaceFaviconData.js
toolkit/components/places/tests/favicons/test_svg_favicon.js
toolkit/components/places/tests/favicons/xpcshell.ini
toolkit/components/places/tests/head_common.js
toolkit/components/places/tests/history/test_remove.js
toolkit/components/places/tests/history/test_removeVisitsByFilter.js
toolkit/components/places/tests/unifiedcomplete/head_autocomplete.js
toolkit/components/places/tests/unifiedcomplete/test_autofill_default_behavior.js
toolkit/components/places/tests/unifiedcomplete/test_typed.js
toolkit/components/places/tests/unit/test_history_clear.js
toolkit/components/places/tests/unit/test_preventive_maintenance.js
toolkit/components/places/tests/unit/test_promiseBookmarksTree.js
toolkit/components/places/tests/unit/test_svg_favicon.js
toolkit/components/places/tests/unit/xpcshell.ini
widget/windows/WinUtils.cpp
--- a/browser/components/customizableui/CustomizableWidgets.jsm
+++ b/browser/components/customizableui/CustomizableWidgets.jsm
@@ -213,28 +213,24 @@ const CustomizableWidgets = [
             win.openUILink(item.getAttribute("targetURI"), aItemCommandEvent);
             CustomizableUI.hidePanelForNode(item);
           };
           let fragment = doc.createDocumentFragment();
           let row;
           while ((row = aResultSet.getNextRow())) {
             let uri = row.getResultByIndex(1);
             let title = row.getResultByIndex(2);
-            let icon = row.getResultByIndex(6);
 
             let item = doc.createElementNS(kNSXUL, "toolbarbutton");
             item.setAttribute("label", title || uri);
             item.setAttribute("targetURI", uri);
             item.setAttribute("class", "subviewbutton");
             item.addEventListener("command", onItemCommand);
             item.addEventListener("click", onItemCommand);
-            if (icon) {
-              let iconURL = "moz-anno:favicon:" + icon;
-              item.setAttribute("image", iconURL);
-            }
+            item.setAttribute("image", "page-icon:" + uri);
             fragment.appendChild(item);
           }
           items.appendChild(fragment);
         },
         handleError(aError) {
           log.debug("History view tried to show but had an error: " + aError);
         },
         handleCompletion(aReason) {
--- a/browser/components/places/content/browserPlacesViews.js
+++ b/browser/components/places/content/browserPlacesViews.js
@@ -503,24 +503,20 @@ PlacesViewBase.prototype = {
     let elt = this._getDOMNodeForPlacesNode(aPlacesNode);
 
     // There's no UI representation for the root node, thus there's nothing to
     // be done when the icon changes.
     if (elt == this._rootElt)
       return;
 
     // Here we need the <menu>.
-    if (elt.localName == "menupopup")
+    if (elt.localName == "menupopup") {
       elt = elt.parentNode;
-
-    let icon = aPlacesNode.icon;
-    if (!icon)
-      elt.removeAttribute("image");
-    else if (icon != elt.getAttribute("image"))
-      elt.setAttribute("image", icon);
+    }
+    elt.setAttribute("image", aPlacesNode.icon);
   },
 
   nodeAnnotationChanged:
   function PVB_nodeAnnotationChanged(aPlacesNode, aAnno) {
     let elt = this._getDOMNodeForPlacesNode(aPlacesNode);
 
     // All livemarks have a feedURI, so use it as our indicator of a livemark
     // being modified.
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -9598,17 +9598,17 @@ public:
     , mNewURI(aNewURI)
     , mLoadingPrincipal(aLoadingPrincipal)
     , mInPrivateBrowsing(aInPrivateBrowsing)
   {
   }
 
   NS_IMETHOD
   OnComplete(nsIURI* aFaviconURI, uint32_t aDataLen,
-             const uint8_t* aData, const nsACString& aMimeType) override
+             const uint8_t* aData, const nsACString& aMimeType, uint16_t aWidth) override
   {
     // Continue only if there is an associated favicon.
     if (!aFaviconURI) {
       return NS_OK;
     }
 
     MOZ_ASSERT(aDataLen == 0,
                "We weren't expecting the callback to deliver data.");
@@ -9657,17 +9657,17 @@ nsDocShell::CopyFavicon(nsIURI* aOldURI,
 #ifdef MOZ_PLACES
   nsCOMPtr<mozIAsyncFavicons> favSvc =
     do_GetService("@mozilla.org/browser/favicon-service;1");
   if (favSvc) {
     nsCOMPtr<nsIFaviconDataCallback> callback =
       new nsCopyFaviconCallback(favSvc, aNewURI,
                                 aLoadingPrincipal,
                                 aInPrivateBrowsing);
-    favSvc->GetFaviconURLForPage(aOldURI, callback);
+    favSvc->GetFaviconURLForPage(aOldURI, callback, 0);
   }
 #endif
 }
 
 class InternalLoadEvent : public Runnable
 {
 public:
   InternalLoadEvent(nsDocShell* aDocShell, nsIURI* aURI,
--- a/testing/firefox-ui/tests/functional/locationbar/manifest.ini
+++ b/testing/firefox-ui/tests/functional/locationbar/manifest.ini
@@ -1,9 +1,8 @@
 [DEFAULT]
 tags = local
 
 [test_access_locationbar.py]
 disabled = Bug 1168727 - Timeout when opening auto-complete popup
 [test_escape_autocomplete.py]
-[test_favicon_in_autocomplete.py]
 [test_suggest_bookmarks.py]
 
deleted file mode 100644
--- a/testing/firefox-ui/tests/functional/locationbar/test_favicon_in_autocomplete.py
+++ /dev/null
@@ -1,62 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-from firefox_puppeteer import PuppeteerMixin
-from marionette_driver import Wait
-from marionette_harness import MarionetteTestCase
-
-
-class TestFaviconInAutocomplete(PuppeteerMixin, MarionetteTestCase):
-
-    PREF_SUGGEST_SEARCHES = 'browser.urlbar.suggest.searches'
-    PREF_SUGGEST_BOOKMARK = 'browser.urlbar.suggest.bookmark'
-
-    def setUp(self):
-        super(TestFaviconInAutocomplete, self).setUp()
-
-        # Disable suggestions for searches and bookmarks to get results only for history
-        self.marionette.set_pref(self.PREF_SUGGEST_SEARCHES, False)
-        self.marionette.set_pref(self.PREF_SUGGEST_BOOKMARK, False)
-
-        self.puppeteer.places.remove_all_history()
-
-        self.test_urls = [self.marionette.absolute_url('layout/mozilla.html')]
-
-        self.test_string = 'mozilla'
-        self.test_favicon = 'mozilla_favicon.ico'
-
-        self.autocomplete_results = self.browser.navbar.locationbar.autocomplete_results
-
-    def tearDown(self):
-        try:
-            self.autocomplete_results.close(force=True)
-            self.marionette.clear_pref(self.PREF_SUGGEST_SEARCHES)
-            self.marionette.clear_pref(self.PREF_SUGGEST_BOOKMARK)
-        finally:
-            super(TestFaviconInAutocomplete, self).tearDown()
-
-    def test_favicon_in_autocomplete(self):
-        # Open the test page
-        def load_urls():
-            with self.marionette.using_context('content'):
-                self.marionette.navigate(self.test_urls[0])
-        self.puppeteer.places.wait_for_visited(self.test_urls, load_urls)
-
-        locationbar = self.browser.navbar.locationbar
-
-        # Clear the location bar, type the test string, check that autocomplete list opens
-        locationbar.clear()
-        locationbar.urlbar.send_keys(self.test_string)
-        self.assertEqual(locationbar.value, self.test_string)
-        Wait(self.marionette).until(lambda _: self.autocomplete_results.is_complete)
-
-        result = self.autocomplete_results.visible_results[1]
-
-        result_icon = self.marionette.execute_script("""
-          return arguments[0].image;
-        """, script_args=[result])
-
-        self.assertIn(self.test_favicon, result_icon)
-
-        self.autocomplete_results.close()
--- a/toolkit/components/alerts/nsAlertsService.cpp
+++ b/toolkit/components/alerts/nsAlertsService.cpp
@@ -44,17 +44,17 @@ public:
                nsIObserver* aAlertListener)
     : mBackend(aBackend)
     , mAlert(aAlert)
     , mAlertListener(aAlertListener)
   {}
 
   NS_IMETHOD
   OnComplete(nsIURI *aIconURI, uint32_t aIconSize, const uint8_t *aIconData,
-             const nsACString &aMimeType) override
+             const nsACString &aMimeType, uint16_t aWidth) override
   {
     nsresult rv = NS_ERROR_FAILURE;
     if (aIconSize > 0) {
       nsCOMPtr<nsIAlertsIconData> alertsIconData(do_QueryInterface(mBackend));
       if (alertsIconData) {
         rv = alertsIconData->ShowAlertWithIconData(mAlert, mAlertListener,
                                                    aIconSize, aIconData);
       }
@@ -106,19 +106,19 @@ ShowWithIconBackend(nsIAlertsService* aB
 
   nsCOMPtr<mozIAsyncFavicons> favicons(do_GetService(
     "@mozilla.org/browser/favicon-service;1"));
   NS_ENSURE_TRUE(favicons, NS_ERROR_FAILURE);
 
   nsCOMPtr<nsIFaviconDataCallback> callback =
     new IconCallback(aBackend, aAlert, aAlertListener);
   if (alertsIconData) {
-    return favicons->GetFaviconDataForPage(uri, callback);
+    return favicons->GetFaviconDataForPage(uri, callback, 0);
   }
-  return favicons->GetFaviconURLForPage(uri, callback);
+  return favicons->GetFaviconURLForPage(uri, callback, 0);
 #else
   return NS_ERROR_NOT_IMPLEMENTED;
 #endif // !MOZ_PLACES
 }
 
 nsresult
 ShowWithBackend(nsIAlertsService* aBackend, nsIAlertNotification* aAlert,
                 nsIObserver* aAlertListener, const nsAString& aPersistentData)
--- a/toolkit/components/places/FaviconHelpers.cpp
+++ b/toolkit/components/places/FaviconHelpers.cpp
@@ -18,63 +18,65 @@
 #include "nsNetUtil.h"
 #include "nsPrintfCString.h"
 #include "nsStreamUtils.h"
 #include "nsStringStream.h"
 #include "nsIPrivateBrowsingChannel.h"
 #include "nsISupportsPriority.h"
 #include "nsContentUtils.h"
 #include <algorithm>
+#include <deque>
 #include "mozilla/gfx/2D.h"
 #include "imgIContainer.h"
 #include "ImageOps.h"
-#include "imgLoader.h"
 #include "imgIEncoder.h"
 
 using namespace mozilla::places;
 using namespace mozilla::storage;
 
 namespace mozilla {
 namespace places {
 
 namespace {
 
 /**
- * Fetches information on a page from the Places database.
+ * Fetches information about a page from the database.
  *
- * @param aDBConn
+ * @param aDB
  *        Database connection to history tables.
  * @param _page
  *        Page that should be fetched.
  */
 nsresult
 FetchPageInfo(const RefPtr<Database>& aDB,
               PageData& _page)
 {
   MOZ_ASSERT(_page.spec.Length(), "Must have a non-empty spec!");
   MOZ_ASSERT(!NS_IsMainThread());
 
   // This query finds the bookmarked uri we want to set the icon for,
   // walking up to two redirect levels.
   nsCString query = nsPrintfCString(
-    "SELECT h.id, h.favicon_id, h.guid, ( "
+    "SELECT h.id, h.guid, ( "
       "SELECT h.url FROM moz_bookmarks b WHERE b.fk = h.id "
       "UNION ALL " // Union not directly bookmarked pages.
       "SELECT url FROM moz_places WHERE id = ( "
         "SELECT COALESCE(grandparent.place_id, parent.place_id) as r_place_id "
         "FROM moz_historyvisits dest "
         "LEFT JOIN moz_historyvisits parent ON parent.id = dest.from_visit "
                                           "AND dest.visit_type IN (%d, %d) "
         "LEFT JOIN moz_historyvisits grandparent ON parent.from_visit = grandparent.id "
           "AND parent.visit_type IN (%d, %d) "
         "WHERE dest.place_id = h.id "
         "AND EXISTS(SELECT 1 FROM moz_bookmarks b WHERE b.fk = r_place_id) "
         "LIMIT 1 "
       ") "
-    ") FROM moz_places h WHERE h.url_hash = hash(:page_url) AND h.url = :page_url",
+    ") "
+    "FROM moz_places h "
+    "WHERE h.url_hash = hash(:page_url) AND h.url = :page_url",
     nsINavHistoryService::TRANSITION_REDIRECT_PERMANENT,
     nsINavHistoryService::TRANSITION_REDIRECT_TEMPORARY,
     nsINavHistoryService::TRANSITION_REDIRECT_PERMANENT,
     nsINavHistoryService::TRANSITION_REDIRECT_TEMPORARY
   );
 
   nsCOMPtr<mozIStorageStatement> stmt = aDB->GetStatement(query);
   NS_ENSURE_STATE(stmt);
@@ -89,31 +91,25 @@ FetchPageInfo(const RefPtr<Database>& aD
   NS_ENSURE_SUCCESS(rv, rv);
   if (!hasResult) {
     // The page does not exist.
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   rv = stmt->GetInt64(0, &_page.id);
   NS_ENSURE_SUCCESS(rv, rv);
-  bool isNull;
-  rv = stmt->GetIsNull(1, &isNull);
+  rv = stmt->GetUTF8String(1, _page.guid);
   NS_ENSURE_SUCCESS(rv, rv);
-  // favicon_id can be nullptr.
-  if (!isNull) {
-    rv = stmt->GetInt64(1, &_page.iconId);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-  rv = stmt->GetUTF8String(2, _page.guid);
-  NS_ENSURE_SUCCESS(rv, rv);
-  rv = stmt->GetIsNull(3, &isNull);
+  // Bookmarked url can be nullptr.
+  bool isNull;
+  rv = stmt->GetIsNull(2, &isNull);
   NS_ENSURE_SUCCESS(rv, rv);
   // The page could not be bookmarked.
   if (!isNull) {
-    rv = stmt->GetUTF8String(3, _page.bookmarkedSpec);
+    rv = stmt->GetUTF8String(2, _page.bookmarkedSpec);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   if (!_page.canAddToHistory) {
     // Either history is disabled or the scheme is not supported.  In such a
     // case we want to update the icon only if the page is bookmarked.
 
     if (_page.bookmarkedSpec.IsEmpty()) {
@@ -131,146 +127,283 @@ FetchPageInfo(const RefPtr<Database>& aD
       }
     }
   }
 
   return NS_OK;
 }
 
 /**
- * Stores information on a icon in the database.
+ * Stores information about an icon in the database.
  *
- * @param aDBConn
+ * @param aDB
  *        Database connection to history tables.
  * @param aIcon
  *        Icon that should be stored.
+ * @param aMustReplace
+ *        If set to true, the function will bail out with NS_ERROR_NOT_AVAILABLE
+ *        if it can't find a previous stored icon to replace.
+ * @note Should be wrapped in a transaction.
  */
 nsresult
 SetIconInfo(const RefPtr<Database>& aDB,
-            const IconData& aIcon)
+            IconData& aIcon,
+            bool aMustReplace = false)
 {
   MOZ_ASSERT(!NS_IsMainThread());
+  MOZ_ASSERT(aIcon.payloads.Length() > 0);
+  MOZ_ASSERT(!aIcon.spec.IsEmpty());
 
-  nsCOMPtr<mozIStorageStatement> stmt = aDB->GetStatement(
-    "INSERT OR REPLACE INTO moz_favicons "
-      "(id, url, data, mime_type, expiration) "
-    "VALUES ((SELECT id FROM moz_favicons WHERE url = :icon_url), "
-            ":icon_url, :data, :mime_type, :expiration) "
+  // There are multiple cases possible at this point:
+  //   1. We must insert some payloads and no payloads exist in the table. This
+  //      would be a straight INSERT.
+  //   2. The table contains the same number of payloads we are inserting. This
+  //      would be a straight UPDATE.
+  //   3. The table contains more payloads than we are inserting. This would be
+  //      an UPDATE and a DELETE.
+  //   4. The table contains less payloads than we are inserting. This would be
+  //      an UPDATE and an INSERT.
+  // We can't just remove all the old entries and insert the new ones, cause
+  // we'd lose the referential integrity with pages.  For the same reason we
+  // cannot use INSERT OR REPLACE, since it's implemented as DELETE AND INSERT.
+  // Thus, we follow this strategy:
+  //   * SELECT all existing icon ids
+  //   * For each payload, either UPDATE OR INSERT reusing icon ids.
+  //   * If any previous icon ids is leftover, DELETE it.
+
+  nsCOMPtr<mozIStorageStatement> selectStmt = aDB->GetStatement(
+    "SELECT id FROM moz_icons "
+    "WHERE fixed_icon_url_hash = hash(fixup_url(:url)) "
+      "AND icon_url = :url "
   );
-  NS_ENSURE_STATE(stmt);
-  mozStorageStatementScoper scoper(stmt);
-  nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("icon_url"), aIcon.spec);
+  NS_ENSURE_STATE(selectStmt);
+  mozStorageStatementScoper scoper(selectStmt);
+  nsresult rv = URIBinder::Bind(selectStmt, NS_LITERAL_CSTRING("url"), aIcon.spec);
   NS_ENSURE_SUCCESS(rv, rv);
-  rv = stmt->BindBlobByName(NS_LITERAL_CSTRING("data"),
-                            TO_INTBUFFER(aIcon.data), aIcon.data.Length());
-  NS_ENSURE_SUCCESS(rv, rv);
-  rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("mime_type"), aIcon.mimeType);
-  NS_ENSURE_SUCCESS(rv, rv);
-  rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("expiration"), aIcon.expiration);
-  NS_ENSURE_SUCCESS(rv, rv);
+  std::deque<int64_t> ids;
+  bool hasResult = false;
+  while (NS_SUCCEEDED(selectStmt->ExecuteStep(&hasResult)) && hasResult) {
+    int64_t id = selectStmt->AsInt64(0);
+    MOZ_ASSERT(id > 0);
+    ids.push_back(id);
+  }
+  if (aMustReplace && ids.empty()) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  nsCOMPtr<mozIStorageStatement> insertStmt = aDB->GetStatement(
+    "INSERT INTO moz_icons "
+      "(icon_url, fixed_icon_url_hash, width, expire_ms, data) "
+    "VALUES (:url, hash(fixup_url(:url)), :width, :expire, :data) "
+  );
+  NS_ENSURE_STATE(insertStmt);
+  nsCOMPtr<mozIStorageStatement> updateStmt = aDB->GetStatement(
+    "UPDATE moz_icons SET width = :width, "
+                         "expire_ms = :expire, "
+                         "data = :data "
+    "WHERE id = :id "
+  );
+  NS_ENSURE_STATE(updateStmt);
 
-  rv = stmt->Execute();
-  NS_ENSURE_SUCCESS(rv, rv);
+  for (auto& payload : aIcon.payloads) {
+    // Sanity checks.
+    MOZ_ASSERT(payload.mimeType.EqualsLiteral(PNG_MIME_TYPE) ||
+              payload.mimeType.EqualsLiteral(SVG_MIME_TYPE),
+              "Only png and svg payloads are supported");
+    MOZ_ASSERT(!payload.mimeType.EqualsLiteral(SVG_MIME_TYPE) ||
+               payload.width == UINT16_MAX,
+              "SVG payloads should have max width");
+    MOZ_ASSERT(payload.width > 0, "Payload should have a width");
+#ifdef DEBUG
+    // Done to ensure we fetch the id. See the MOZ_ASSERT below.
+    payload.id = 0;
+#endif
+    if (!ids.empty()) {
+      // Pop the first existing id for reuse.
+      int64_t id = ids.front();
+      ids.pop_front();
+      mozStorageStatementScoper scoper(updateStmt);
+      rv = updateStmt->BindInt64ByName(NS_LITERAL_CSTRING("id"), id);
+      NS_ENSURE_SUCCESS(rv, rv);
+      rv = updateStmt->BindInt32ByName(NS_LITERAL_CSTRING("width"),
+                                       payload.width);
+      NS_ENSURE_SUCCESS(rv, rv);
+      rv = updateStmt->BindInt64ByName(NS_LITERAL_CSTRING("expire"),
+                                       aIcon.expiration / 1000);
+      NS_ENSURE_SUCCESS(rv, rv);
+      rv = updateStmt->BindBlobByName(NS_LITERAL_CSTRING("data"),
+                                TO_INTBUFFER(payload.data),
+                                payload.data.Length());
+      rv = updateStmt->Execute();
+      NS_ENSURE_SUCCESS(rv, rv);
+      // Set the new payload id.
+      payload.id = id;
+    } else {
+      // Insert a new entry.
+      mozStorageStatementScoper scoper(insertStmt);
+      rv = URIBinder::Bind(insertStmt, NS_LITERAL_CSTRING("url"), aIcon.spec);
+      NS_ENSURE_SUCCESS(rv, rv);
+      rv = insertStmt->BindInt32ByName(NS_LITERAL_CSTRING("width"),
+                                       payload.width);
+      NS_ENSURE_SUCCESS(rv, rv);
+      rv = insertStmt->BindInt64ByName(NS_LITERAL_CSTRING("expire"),
+                                       aIcon.expiration / 1000);
+      NS_ENSURE_SUCCESS(rv, rv);
+      rv = insertStmt->BindBlobByName(NS_LITERAL_CSTRING("data"),
+                                TO_INTBUFFER(payload.data),
+                                payload.data.Length());
+      NS_ENSURE_SUCCESS(rv, rv);
+      rv = insertStmt->Execute();
+      NS_ENSURE_SUCCESS(rv, rv);
+      // Set the new payload id.
+      payload.id = nsFaviconService::sLastInsertedIconId;
+    }
+    MOZ_ASSERT(payload.id > 0, "Payload should have an id");
+  }
+
+  if (!ids.empty()) {
+    // Remove any old leftover payload.
+    nsAutoCString sql("DELETE FROM moz_icons WHERE id IN (");
+    for (int64_t id : ids) {
+      sql.AppendInt(id);
+      sql.AppendLiteral(",");
+    }
+    sql.AppendLiteral(" 0)"); // Non-existing id to match the trailing comma.
+    nsCOMPtr<mozIStorageStatement> stmt = aDB->GetStatement(sql);
+    NS_ENSURE_STATE(stmt);
+    mozStorageStatementScoper scoper(stmt);
+    rv = stmt->Execute();
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
 
   return NS_OK;
 }
 
 /**
- * Fetches information on a icon from the Places database.
+ * Fetches information on a icon url from the database.
  *
  * @param aDBConn
  *        Database connection to history tables.
+ * @param aPreferredWidth
+ *        The preferred size to fetch.
  * @param _icon
  *        Icon that should be fetched.
  */
 nsresult
 FetchIconInfo(const RefPtr<Database>& aDB,
-              IconData& _icon)
+              uint16_t aPreferredWidth,
+              IconData& _icon
+)
 {
   MOZ_ASSERT(_icon.spec.Length(), "Must have a non-empty spec!");
   MOZ_ASSERT(!NS_IsMainThread());
 
   if (_icon.status & ICON_STATUS_CACHED) {
+    // The icon data has already been set by ReplaceFaviconData.
     return NS_OK;
   }
 
   nsCOMPtr<mozIStorageStatement> stmt = aDB->GetStatement(
-    "SELECT id, expiration, data, mime_type "
-    "FROM moz_favicons WHERE url = :icon_url"
+    "/* do not warn (bug no: not worth having a compound index) */ "
+    "SELECT id, expire_ms, data, width "
+    "FROM moz_icons "
+    "WHERE fixed_icon_url_hash = hash(fixup_url(:url)) "
+      "AND icon_url = :url "
+    "ORDER BY width ASC "
   );
   NS_ENSURE_STATE(stmt);
   mozStorageStatementScoper scoper(stmt);
 
-  DebugOnly<nsresult> rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("icon_url"),
+  DebugOnly<nsresult> rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("url"),
                                            _icon.spec);
   MOZ_ASSERT(NS_SUCCEEDED(rv));
 
-  bool hasResult;
-  rv = stmt->ExecuteStep(&hasResult);
-  MOZ_ASSERT(NS_SUCCEEDED(rv));
-  if (!hasResult) {
-    // The icon does not exist yet, bail out.
-    return NS_OK;
-  }
-
-  rv = stmt->GetInt64(0, &_icon.id);
-  MOZ_ASSERT(NS_SUCCEEDED(rv));
+  bool hasResult = false;
+  while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
+    IconPayload payload;
+    rv = stmt->GetInt64(0, &payload.id);
+    MOZ_ASSERT(NS_SUCCEEDED(rv));
 
-  // Expiration can be nullptr.
-  bool isNull;
-  rv = stmt->GetIsNull(1, &isNull);
-  MOZ_ASSERT(NS_SUCCEEDED(rv));
-  if (!isNull) {
-    rv = stmt->GetInt64(1, reinterpret_cast<int64_t*>(&_icon.expiration));
+    // Expiration can be nullptr.
+    bool isNull;
+    rv = stmt->GetIsNull(1, &isNull);
     MOZ_ASSERT(NS_SUCCEEDED(rv));
-  }
+    if (!isNull) {
+      int64_t expire_ms;
+      rv = stmt->GetInt64(1, &expire_ms);
+      MOZ_ASSERT(NS_SUCCEEDED(rv));
+      _icon.expiration = expire_ms * 1000;
+    }
 
-  // Data can be nullptr.
-  rv = stmt->GetIsNull(2, &isNull);
-  MOZ_ASSERT(NS_SUCCEEDED(rv));
-  if (!isNull) {
     uint8_t* data;
     uint32_t dataLen = 0;
     rv = stmt->GetBlob(2, &dataLen, &data);
     MOZ_ASSERT(NS_SUCCEEDED(rv));
-    _icon.data.Adopt(TO_CHARBUFFER(data), dataLen);
-    // Read mime only if we have data.
-    rv = stmt->GetUTF8String(3, _icon.mimeType);
+
+    payload.data.Adopt(TO_CHARBUFFER(data), dataLen);
+    int32_t width;
+    rv = stmt->GetInt32(3, &width);
     MOZ_ASSERT(NS_SUCCEEDED(rv));
+    payload.width = width;
+
+    if (payload.width == UINT16_MAX) {
+      payload.mimeType.AssignLiteral(SVG_MIME_TYPE);
+    } else {
+      payload.mimeType.AssignLiteral(PNG_MIME_TYPE);
+    }
+
+    if (aPreferredWidth == 0 || _icon.payloads.Length() == 0) {
+      _icon.payloads.AppendElement(payload);
+    } else if (payload.width >= aPreferredWidth) {
+      // Only retain the best matching payload.
+      _icon.payloads.ReplaceElementAt(0, payload);
+    } else {
+      break;
+    }
   }
 
   return NS_OK;
 }
 
 nsresult
-FetchIconURL(const RefPtr<Database>& aDB,
-             const nsACString& aPageSpec,
-             nsACString& aIconSpec)
+FetchIconPerSpec(const RefPtr<Database>& aDB,
+                 const nsACString& aPageSpec,
+                 IconData& aIconData,
+                 uint16_t aPreferredWidth)
 {
   MOZ_ASSERT(!aPageSpec.IsEmpty(), "Page spec must not be empty.");
   MOZ_ASSERT(!NS_IsMainThread());
 
-  aIconSpec.Truncate();
-
   nsCOMPtr<mozIStorageStatement> stmt = aDB->GetStatement(
-    "SELECT f.url "
-    "FROM moz_places h "
-    "JOIN moz_favicons f ON h.favicon_id = f.id "
-    "WHERE h.url_hash = hash(:page_url) AND h.url = :page_url"
+    "/* do not warn (bug no: not worth having a compound index) */ "
+    "SELECT width, icon_url "
+    "FROM moz_icons i "
+    "JOIN moz_icons_to_pages ON i.id = icon_id "
+    "JOIN moz_pages_w_icons p ON p.id = page_id "
+    "WHERE page_url_hash = hash(:url) AND page_url = :url "
+    "ORDER BY width DESC "
   );
   NS_ENSURE_STATE(stmt);
   mozStorageStatementScoper scoper(stmt);
 
-  nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"),
+  nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("url"),
                                 aPageSpec);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  // Return the biggest icon close to the preferred width. It may be bigger
+  // or smaller if the preferred width isn't found.
   bool hasResult;
-  if (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
-    rv = stmt->GetUTF8String(0, aIconSpec);
+  while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
+    int32_t width;
+    rv = stmt->GetInt32(0, &width);
+    NS_ENSURE_SUCCESS(rv, rv);
+    if (width < aPreferredWidth && !aIconData.spec.IsEmpty()) {
+      break;
+    }
+    rv = stmt->GetUTF8String(1, aIconData.spec);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return NS_OK;
 }
 
 /**
  * Tries to compute the expiration time for a icon from the channel.
@@ -302,50 +435,16 @@ GetExpirationTimeFromChannel(nsIChannel*
       }
     }
   }
   // If we did not obtain a time from the cache, use the cap value.
   return expiration < 0 ? PR_Now() + MAX_FAVICON_EXPIRATION
                         : expiration;
 }
 
-/**
- * Checks the icon and evaluates if it needs to be optimized.  In such a case it
- * will try to reduce its size through OptimizeFaviconImage method of the
- * favicons service.
- *
- * @param aIcon
- *        The icon to be evaluated.
- * @param aFaviconSvc
- *        Pointer to the favicons service.
- */
-nsresult
-OptimizeIconSize(IconData& aIcon,
-                 nsFaviconService* aFaviconSvc)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  // Even if the page provides a large image for the favicon (eg, a highres
-  // image or a multiresolution .ico file), don't try to store more data than
-  // needed.
-  nsAutoCString newData, newMimeType;
-  if (aIcon.data.Length() > MAX_FAVICON_FILESIZE) {
-    nsresult rv = aFaviconSvc->OptimizeFaviconImage(TO_INTBUFFER(aIcon.data),
-                                                    aIcon.data.Length(),
-                                                    aIcon.mimeType,
-                                                    newData,
-                                                    newMimeType);
-    if (NS_SUCCEEDED(rv) && newData.Length() < aIcon.data.Length()) {
-      aIcon.data = newData;
-      aIcon.mimeType = newMimeType;
-    }
-  }
-  return NS_OK;
-}
-
 } // namespace
 
 ////////////////////////////////////////////////////////////////////////////////
 //// AsyncFetchAndSetIconForPage
 
 NS_IMPL_ISUPPORTS_INHERITED(
   AsyncFetchAndSetIconForPage
 , Runnable
@@ -374,32 +473,31 @@ AsyncFetchAndSetIconForPage::AsyncFetchA
 NS_IMETHODIMP
 AsyncFetchAndSetIconForPage::Run()
 {
   MOZ_ASSERT(!NS_IsMainThread());
 
   // Try to fetch the icon from the database.
   RefPtr<Database> DB = Database::GetDatabase();
   NS_ENSURE_STATE(DB);
-  nsresult rv = FetchIconInfo(DB, mIcon);
+  nsresult rv = FetchIconInfo(DB, 0, mIcon);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  bool isInvalidIcon = mIcon.data.IsEmpty() ||
+  bool isInvalidIcon = !mIcon.payloads.Length() ||
                        (mIcon.expiration && PR_Now() > mIcon.expiration);
   bool fetchIconFromNetwork = mIcon.fetchMode == FETCH_ALWAYS ||
                               (mIcon.fetchMode == FETCH_IF_MISSING && isInvalidIcon);
 
   if (!fetchIconFromNetwork) {
     // There is already a valid icon or we don't want to fetch a new one,
     // directly proceed with association.
     RefPtr<AsyncAssociateIconToPage> event =
         new AsyncAssociateIconToPage(mIcon, mPage, mCallback);
-    DB->DispatchToAsyncThread(event);
-
-    return NS_OK;
+    // We're already on the async thread.
+    return event->Run();
   }
 
   // Fetch the icon from the network, the request starts from the main-thread.
   // When done this will associate the icon to the page and notify.
   nsCOMPtr<nsIRunnable> event =
     NewRunnableMethod(this, &AsyncFetchAndSetIconForPage::FetchFromNetwork);
   return NS_DispatchToMainThread(event);
 }
@@ -408,20 +506,20 @@ nsresult
 AsyncFetchAndSetIconForPage::FetchFromNetwork() {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (mCanceled) {
     return NS_OK;
   }
 
   // Ensure data is cleared, since it's going to be overwritten.
-  if (mIcon.data.Length() > 0) {
-    mIcon.data.Truncate(0);
-    mIcon.mimeType.Truncate(0);
-  }
+  mIcon.payloads.Clear();
+
+  IconPayload payload;
+  mIcon.payloads.AppendElement(payload);
 
   nsCOMPtr<nsIURI> iconURI;
   nsresult rv = NS_NewURI(getter_AddRefs(iconURI), mIcon.spec);
   NS_ENSURE_SUCCESS(rv, rv);
   nsCOMPtr<nsIChannel> channel;
   rv = NS_NewChannel(getter_AddRefs(channel),
                      iconURI,
                      mLoadingPrincipal,
@@ -484,30 +582,32 @@ AsyncFetchAndSetIconForPage::OnStartRequ
 
 NS_IMETHODIMP
 AsyncFetchAndSetIconForPage::OnDataAvailable(nsIRequest* aRequest,
                                              nsISupports* aContext,
                                              nsIInputStream* aInputStream,
                                              uint64_t aOffset,
                                              uint32_t aCount)
 {
-  const size_t kMaxFaviconDownloadSize = 1 * 1024 * 1024;
-  if (mIcon.data.Length() + aCount > kMaxFaviconDownloadSize) {
-    mIcon.data.Truncate();
+  MOZ_ASSERT(mIcon.payloads.Length() == 1);
+  // Limit downloads to 500KB.
+  const size_t kMaxDownloadSize = 500 * 1024;
+  if (mIcon.payloads[0].data.Length() + aCount > kMaxDownloadSize) {
+    mIcon.payloads.Clear();
     return NS_ERROR_FILE_TOO_BIG;
   }
 
   nsAutoCString buffer;
   nsresult rv = NS_ConsumeStream(aInputStream, aCount, buffer);
   if (rv != NS_BASE_STREAM_WOULD_BLOCK && NS_FAILED(rv)) {
     return rv;
   }
 
-  if (!mIcon.data.Append(buffer, fallible)) {
-    mIcon.data.Truncate();
+  if (!mIcon.payloads[0].data.Append(buffer, fallible)) {
+    mIcon.payloads.Clear();
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   return NS_OK;
 }
 
 
 NS_IMETHODIMP
@@ -546,85 +646,88 @@ AsyncFetchAndSetIconForPage::OnStopReque
   }
 
   nsFaviconService* favicons = nsFaviconService::GetFaviconService();
   NS_ENSURE_STATE(favicons);
 
   nsresult rv;
 
   // If fetching the icon failed, add it to the failed cache.
-  if (NS_FAILED(aStatusCode) || mIcon.data.Length() == 0) {
+  if (NS_FAILED(aStatusCode) || mIcon.payloads.Length() == 0) {
     nsCOMPtr<nsIURI> iconURI;
     rv = NS_NewURI(getter_AddRefs(iconURI), mIcon.spec);
     NS_ENSURE_SUCCESS(rv, rv);
     rv = favicons->AddFailedFavicon(iconURI);
     NS_ENSURE_SUCCESS(rv, rv);
     return NS_OK;
   }
 
   nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
   // aRequest should always QI to nsIChannel.
   MOZ_ASSERT(channel);
 
+  MOZ_ASSERT(mIcon.payloads.Length() == 1);
+  IconPayload& payload = mIcon.payloads[0];
+
   nsAutoCString contentType;
   channel->GetContentType(contentType);
-  // Bug 366324 - can't sniff SVG yet, so rely on server-specified type
-  if (contentType.EqualsLiteral("image/svg+xml")) {
-    mIcon.mimeType.AssignLiteral("image/svg+xml");
+  // Bug 366324 - We don't want to sniff for SVG, so rely on server-specified type.
+  if (contentType.EqualsLiteral(SVG_MIME_TYPE)) {
+    payload.mimeType.AssignLiteral(SVG_MIME_TYPE);
+    payload.width = UINT16_MAX;
   } else {
     NS_SniffContent(NS_DATA_SNIFFER_CATEGORY, aRequest,
-                    TO_INTBUFFER(mIcon.data), mIcon.data.Length(),
-                    mIcon.mimeType);
+                    TO_INTBUFFER(payload.data), payload.data.Length(),
+                    payload.mimeType);
   }
 
   // If the icon does not have a valid MIME type, add it to the failed cache.
-  if (mIcon.mimeType.IsEmpty()) {
+  if (payload.mimeType.IsEmpty()) {
     nsCOMPtr<nsIURI> iconURI;
     rv = NS_NewURI(getter_AddRefs(iconURI), mIcon.spec);
     NS_ENSURE_SUCCESS(rv, rv);
     rv = favicons->AddFailedFavicon(iconURI);
     NS_ENSURE_SUCCESS(rv, rv);
     return NS_OK;
   }
 
   mIcon.expiration = GetExpirationTimeFromChannel(channel);
 
   // Telemetry probes to measure the favicon file sizes for each different file type.
   // This allow us to measure common file sizes while also observing each type popularity.
-  if (mIcon.mimeType.EqualsLiteral("image/png")) {
-    mozilla::Telemetry::Accumulate(mozilla::Telemetry::PLACES_FAVICON_PNG_SIZES, mIcon.data.Length());
+  if (payload.mimeType.EqualsLiteral(PNG_MIME_TYPE)) {
+    mozilla::Telemetry::Accumulate(mozilla::Telemetry::PLACES_FAVICON_PNG_SIZES, payload.data.Length());
   }
-  else if (mIcon.mimeType.EqualsLiteral("image/x-icon") ||
-           mIcon.mimeType.EqualsLiteral("image/vnd.microsoft.icon")) {
-    mozilla::Telemetry::Accumulate(mozilla::Telemetry::PLACES_FAVICON_ICO_SIZES, mIcon.data.Length());
+  else if (payload.mimeType.EqualsLiteral("image/x-icon") ||
+           payload.mimeType.EqualsLiteral("image/vnd.microsoft.icon")) {
+    mozilla::Telemetry::Accumulate(mozilla::Telemetry::PLACES_FAVICON_ICO_SIZES, payload.data.Length());
   }
-  else if (mIcon.mimeType.EqualsLiteral("image/jpeg") ||
-           mIcon.mimeType.EqualsLiteral("image/pjpeg")) {
-    mozilla::Telemetry::Accumulate(mozilla::Telemetry::PLACES_FAVICON_JPEG_SIZES, mIcon.data.Length());
+  else if (payload.mimeType.EqualsLiteral("image/jpeg") ||
+           payload.mimeType.EqualsLiteral("image/pjpeg")) {
+    mozilla::Telemetry::Accumulate(mozilla::Telemetry::PLACES_FAVICON_JPEG_SIZES, payload.data.Length());
   }
-  else if (mIcon.mimeType.EqualsLiteral("image/gif")) {
-    mozilla::Telemetry::Accumulate(mozilla::Telemetry::PLACES_FAVICON_GIF_SIZES, mIcon.data.Length());
+  else if (payload.mimeType.EqualsLiteral("image/gif")) {
+    mozilla::Telemetry::Accumulate(mozilla::Telemetry::PLACES_FAVICON_GIF_SIZES, payload.data.Length());
   }
-  else if (mIcon.mimeType.EqualsLiteral("image/bmp") ||
-           mIcon.mimeType.EqualsLiteral("image/x-windows-bmp")) {
-    mozilla::Telemetry::Accumulate(mozilla::Telemetry::PLACES_FAVICON_BMP_SIZES, mIcon.data.Length());
+  else if (payload.mimeType.EqualsLiteral("image/bmp") ||
+           payload.mimeType.EqualsLiteral("image/x-windows-bmp")) {
+    mozilla::Telemetry::Accumulate(mozilla::Telemetry::PLACES_FAVICON_BMP_SIZES, payload.data.Length());
   }
-  else if (mIcon.mimeType.EqualsLiteral("image/svg+xml")) {
-    mozilla::Telemetry::Accumulate(mozilla::Telemetry::PLACES_FAVICON_SVG_SIZES, mIcon.data.Length());
+  else if (payload.mimeType.EqualsLiteral(SVG_MIME_TYPE)) {
+    mozilla::Telemetry::Accumulate(mozilla::Telemetry::PLACES_FAVICON_SVG_SIZES, payload.data.Length());
   }
   else {
-    mozilla::Telemetry::Accumulate(mozilla::Telemetry::PLACES_FAVICON_OTHER_SIZES, mIcon.data.Length());
+    mozilla::Telemetry::Accumulate(mozilla::Telemetry::PLACES_FAVICON_OTHER_SIZES, payload.data.Length());
   }
 
-  rv = OptimizeIconSize(mIcon, favicons);
+  rv = favicons->OptimizeIconSizes(mIcon);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  // If over the maximum size allowed, don't save data to the database to
-  // avoid bloating it.
-  if (mIcon.data.Length() > nsIFaviconService::MAX_FAVICON_BUFFER_SIZE) {
+  // If there's not valid payload, don't store the icon into to the database.
+  if (mIcon.payloads.Length() == 0) {
     return NS_OK;
   }
 
   mIcon.status = ICON_STATUS_CHANGED;
 
   RefPtr<Database> DB = Database::GetDatabase();
   NS_ENSURE_STATE(DB);
   RefPtr<AsyncAssociateIconToPage> event =
@@ -640,16 +743,17 @@ AsyncFetchAndSetIconForPage::OnStopReque
 AsyncAssociateIconToPage::AsyncAssociateIconToPage(
   const IconData& aIcon
 , const PageData& aPage
 , const nsMainThreadPtrHandle<nsIFaviconDataCallback>& aCallback
 ) : mCallback(aCallback)
   , mIcon(aIcon)
   , mPage(aPage)
 {
+  // May be created in both threads.
 }
 
 NS_IMETHODIMP
 AsyncAssociateIconToPage::Run()
 {
   MOZ_ASSERT(!NS_IsMainThread());
 
   RefPtr<Database> DB = Database::GetDatabase();
@@ -661,68 +765,91 @@ AsyncAssociateIconToPage::Run()
     if (!mPage.canAddToHistory) {
       return NS_OK;
     }
   }
   else {
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
+  bool shouldUpdateIcon = mIcon.status & ICON_STATUS_CHANGED;
+  if (!shouldUpdateIcon) {
+    for (const auto& payload : mIcon.payloads) {
+      // If the entry is missing from the database, we should add it.
+      if (payload.id == 0) {
+        shouldUpdateIcon = true;
+        break;
+      }
+    }
+  }
+
   mozStorageTransaction transaction(DB->MainConn(), false,
                                     mozIStorageConnection::TRANSACTION_IMMEDIATE);
 
-  // If there is no entry for this icon, or the entry is obsolete, replace it.
-  if (mIcon.id == 0 || (mIcon.status & ICON_STATUS_CHANGED)) {
+  if (shouldUpdateIcon) {
     rv = SetIconInfo(DB, mIcon);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    // Get the new icon id.  Do this regardless mIcon.id, since other code
-    // could have added a entry before us.  Indeed we interrupted the thread
-    // after the previous call to FetchIconInfo.
     mIcon.status = (mIcon.status & ~(ICON_STATUS_CACHED)) | ICON_STATUS_SAVED;
-    rv = FetchIconInfo(DB, mIcon);
-    NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // If the page does not have an id, don't try to insert a new one, cause we
   // don't know where the page comes from.  Not doing so we may end adding
   // a page that otherwise we'd explicitly ignore, like a POST or an error page.
   if (mPage.id == 0) {
+    rv = transaction.Commit();
+    NS_ENSURE_SUCCESS(rv, rv);
     return NS_OK;
   }
 
-  // Otherwise just associate the icon to the page, if needed.
-  if (mPage.iconId != mIcon.id) {
+  // First we need to create the page entry.
+  {
     nsCOMPtr<mozIStorageStatement> stmt;
-    if (mPage.id) {
-      stmt = DB->GetStatement(
-        "UPDATE moz_places SET favicon_id = :icon_id WHERE id = :page_id"
-      );
-      NS_ENSURE_STATE(stmt);
-      rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), mPage.id);
-      NS_ENSURE_SUCCESS(rv, rv);
-    }
-    else {
-      stmt = DB->GetStatement(
-        "UPDATE moz_places SET favicon_id = :icon_id "
-        "WHERE url_hash = hash(:page_url) AND url = :page_url"
-      );
-      NS_ENSURE_STATE(stmt);
-      rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), mPage.spec);
-      NS_ENSURE_SUCCESS(rv, rv);
-    }
-    rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("icon_id"), mIcon.id);
+    stmt = DB->GetStatement(
+      "INSERT OR IGNORE INTO moz_pages_w_icons (id, page_url, page_url_hash) "
+      "VALUES (:page_id, :page_url, hash(:page_url)) "
+    );
+    NS_ENSURE_STATE(stmt);
+    mozStorageStatementScoper scoper(stmt);
+    rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), mPage.id);
     NS_ENSURE_SUCCESS(rv, rv);
-
-    mozStorageStatementScoper scoper(stmt);
+    rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), mPage.spec);
+    NS_ENSURE_SUCCESS(rv, rv);
     rv = stmt->Execute();
     NS_ENSURE_SUCCESS(rv, rv);
+  }
 
-    mIcon.status |= ICON_STATUS_ASSOCIATED;
+  // Then we can create the relations.
+  nsCOMPtr<mozIStorageStatement> stmt;
+  stmt = DB->GetStatement(
+    "INSERT OR IGNORE INTO moz_icons_to_pages (page_id, icon_id) "
+    "VALUES (:page_id, :icon_id) "
+  );
+  NS_ENSURE_STATE(stmt);
+  mozStorageStatementScoper scoper(stmt);
+  nsCOMPtr<mozIStorageBindingParamsArray> paramsArray;
+  rv = stmt->NewBindingParamsArray(getter_AddRefs(paramsArray));
+  NS_ENSURE_SUCCESS(rv, rv);
+  for (const auto& payload : mIcon.payloads) {
+    nsCOMPtr<mozIStorageBindingParams> params;
+    rv = paramsArray->NewBindingParams(getter_AddRefs(params));
+    NS_ENSURE_SUCCESS(rv, rv);
+    rv = params->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), mPage.id);
+    NS_ENSURE_SUCCESS(rv, rv);
+    rv = params->BindInt64ByName(NS_LITERAL_CSTRING("icon_id"), payload.id);
+    NS_ENSURE_SUCCESS(rv, rv);
+    rv = paramsArray->AddParams(params);
+    NS_ENSURE_SUCCESS(rv, rv);
   }
+  rv = stmt->BindParameters(paramsArray);
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = stmt->Execute();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  mIcon.status |= ICON_STATUS_ASSOCIATED;
 
   rv = transaction.Commit();
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Finally, dispatch an event to the main thread to notify observers.
   nsCOMPtr<nsIRunnable> event = new NotifyIconObservers(mIcon, mPage, mCallback);
   rv = NS_DispatchToMainThread(event);
   NS_ENSURE_SUCCESS(rv, rv);
@@ -730,117 +857,116 @@ AsyncAssociateIconToPage::Run()
   return NS_OK;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 //// AsyncGetFaviconURLForPage
 
 AsyncGetFaviconURLForPage::AsyncGetFaviconURLForPage(
   const nsACString& aPageSpec
+, uint16_t aPreferredWidth
 , nsIFaviconDataCallback* aCallback
-) : mCallback(new nsMainThreadPtrHolder<nsIFaviconDataCallback>(aCallback))
+) : mPreferredWidth(aPreferredWidth == 0 ? UINT16_MAX : aPreferredWidth)
+  , mCallback(new nsMainThreadPtrHolder<nsIFaviconDataCallback>(aCallback))
 {
   MOZ_ASSERT(NS_IsMainThread());
   mPageSpec.Assign(aPageSpec);
 }
 
 NS_IMETHODIMP
 AsyncGetFaviconURLForPage::Run()
 {
   MOZ_ASSERT(!NS_IsMainThread());
 
   RefPtr<Database> DB = Database::GetDatabase();
   NS_ENSURE_STATE(DB);
-  nsAutoCString iconSpec;
-  nsresult rv = FetchIconURL(DB, mPageSpec, iconSpec);
+  IconData iconData;
+  nsresult rv = FetchIconPerSpec(DB, mPageSpec, iconData, mPreferredWidth);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Now notify our callback of the icon spec we retrieved, even if empty.
-  IconData iconData;
-  iconData.spec.Assign(iconSpec);
-
   PageData pageData;
   pageData.spec.Assign(mPageSpec);
 
   nsCOMPtr<nsIRunnable> event =
     new NotifyIconObservers(iconData, pageData, mCallback);
   rv = NS_DispatchToMainThread(event);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 //// AsyncGetFaviconDataForPage
 
 AsyncGetFaviconDataForPage::AsyncGetFaviconDataForPage(
   const nsACString& aPageSpec
+,  uint16_t aPreferredWidth
 , nsIFaviconDataCallback* aCallback
-) : mCallback(new nsMainThreadPtrHolder<nsIFaviconDataCallback>(aCallback))
+) : mPreferredWidth(aPreferredWidth == 0 ? UINT16_MAX : aPreferredWidth)
+  , mCallback(new nsMainThreadPtrHolder<nsIFaviconDataCallback>(aCallback))
  {
   MOZ_ASSERT(NS_IsMainThread());
   mPageSpec.Assign(aPageSpec);
 }
 
 NS_IMETHODIMP
 AsyncGetFaviconDataForPage::Run()
 {
   MOZ_ASSERT(!NS_IsMainThread());
 
   RefPtr<Database> DB = Database::GetDatabase();
   NS_ENSURE_STATE(DB);
-  nsAutoCString iconSpec;
-  nsresult rv = FetchIconURL(DB, mPageSpec, iconSpec);
+  IconData iconData;
+  nsresult rv = FetchIconPerSpec(DB, mPageSpec, iconData, mPreferredWidth);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  IconData iconData;
-  iconData.spec.Assign(iconSpec);
-
-  PageData pageData;
-  pageData.spec.Assign(mPageSpec);
-
-  if (!iconSpec.IsEmpty()) {
-    rv = FetchIconInfo(DB, iconData);
+  if (!iconData.spec.IsEmpty()) {
+    rv = FetchIconInfo(DB, mPreferredWidth, iconData);
     if (NS_FAILED(rv)) {
       iconData.spec.Truncate();
     }
   }
 
+  PageData pageData;
+  pageData.spec.Assign(mPageSpec);
+
   nsCOMPtr<nsIRunnable> event =
     new NotifyIconObservers(iconData, pageData, mCallback);
   rv = NS_DispatchToMainThread(event);
   NS_ENSURE_SUCCESS(rv, rv);
   return NS_OK;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 //// AsyncReplaceFaviconData
 
 AsyncReplaceFaviconData::AsyncReplaceFaviconData(const IconData &aIcon)
   : mIcon(aIcon)
 {
+  MOZ_ASSERT(NS_IsMainThread());
 }
 
 NS_IMETHODIMP
 AsyncReplaceFaviconData::Run()
 {
   MOZ_ASSERT(!NS_IsMainThread());
 
   RefPtr<Database> DB = Database::GetDatabase();
   NS_ENSURE_STATE(DB);
-  IconData dbIcon;
-  dbIcon.spec.Assign(mIcon.spec);
-  nsresult rv = FetchIconInfo(DB, dbIcon);
-  NS_ENSURE_SUCCESS(rv, rv);
 
-  if (!dbIcon.id) {
+  mozStorageTransaction transaction(DB->MainConn(), false,
+                                    mozIStorageConnection::TRANSACTION_IMMEDIATE);
+  nsresult rv = SetIconInfo(DB, mIcon, true);
+  if (rv == NS_ERROR_NOT_AVAILABLE) {
+    // There's no previous icon to replace, we don't need to do anything.
     return NS_OK;
   }
-
-  rv = SetIconInfo(DB, mIcon);
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = transaction.Commit();
   NS_ENSURE_SUCCESS(rv, rv);
 
   // We can invalidate the cache version since we now persist the icon.
   nsCOMPtr<nsIRunnable> event =
     NewRunnableMethod(this, &AsyncReplaceFaviconData::RemoveIconDataCacheEntry);
   rv = NS_DispatchToMainThread(event);
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -891,22 +1017,28 @@ NotifyIconObservers::Run()
       // Notify observers only if something changed.
       if (mIcon.status & ICON_STATUS_SAVED ||
           mIcon.status & ICON_STATUS_ASSOCIATED) {
         SendGlobalNotifications(iconURI);
       }
     }
   }
 
-  if (mCallback) {
-    (void)mCallback->OnComplete(iconURI, mIcon.data.Length(),
-                                TO_INTBUFFER(mIcon.data), mIcon.mimeType);
+  if (!mCallback) {
+    return NS_OK;
   }
 
-  return NS_OK;
+  if (mIcon.payloads.Length() > 0) {
+    IconPayload& payload = mIcon.payloads[0];
+    return mCallback->OnComplete(iconURI, payload.data.Length(),
+                                 TO_INTBUFFER(payload.data), payload.mimeType,
+                                 payload.width);
+  }
+  return mCallback->OnComplete(iconURI, 0, TO_INTBUFFER(EmptyCString()),
+                               EmptyCString(), 0);
 }
 
 void
 NotifyIconObservers::SendGlobalNotifications(nsIURI* aIconURI)
 {
   nsCOMPtr<nsIURI> pageURI;
   MOZ_ALWAYS_SUCCEEDS(NS_NewURI(getter_AddRefs(pageURI), mPage.spec));
   if (pageURI) {
--- a/toolkit/components/places/FaviconHelpers.h
+++ b/toolkit/components/places/FaviconHelpers.h
@@ -10,16 +10,17 @@
 #include "nsIChannelEventSink.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIStreamListener.h"
 #include "mozIPlacesPendingOperation.h"
 #include "nsThreadUtils.h"
 #include "nsProxyRelease.h"
 #include "imgITools.h"
 #include "imgIContainer.h"
+#include "imgLoader.h"
 
 class nsIPrincipal;
 
 #include "Database.h"
 #include "mozilla/storage.h"
 
 #define ICON_STATUS_UNKNOWN 0
 #define ICON_STATUS_CHANGED 1 << 0
@@ -54,56 +55,70 @@ namespace places {
  */
 enum AsyncFaviconFetchMode {
   FETCH_NEVER = 0
 , FETCH_IF_MISSING
 , FETCH_ALWAYS
 };
 
 /**
- * Data cache for a icon entry.
+ * Represents one of the payloads (frames) of an icon entry.
+ */
+struct IconPayload
+{
+  IconPayload()
+  : id(0)
+  , width(0)
+  {
+    data.SetIsVoid(true);
+    mimeType.SetIsVoid(true);
+  }
+
+  int64_t id;
+  uint16_t width;
+  nsCString data;
+  nsCString mimeType;
+};
+
+/**
+ * Represents an icon entry.
  */
 struct IconData
 {
   IconData()
-  : id(0)
-  , expiration(0)
+  : expiration(0)
   , fetchMode(FETCH_NEVER)
   , status(ICON_STATUS_UNKNOWN)
   {
   }
 
-  int64_t id;
   nsCString spec;
-  nsCString data;
-  nsCString mimeType;
   PRTime expiration;
   enum AsyncFaviconFetchMode fetchMode;
   uint16_t status; // This is a bitset, see ICON_STATUS_* defines above.
+  nsTArray<IconPayload> payloads;
 };
 
 /**
  * Data cache for a page entry.
  */
 struct PageData
 {
   PageData()
   : id(0)
   , canAddToHistory(true)
-  , iconId(0)
   {
     guid.SetIsVoid(true);
   }
 
   int64_t id;
   nsCString spec;
   nsCString bookmarkedSpec;
   nsString revHost;
   bool canAddToHistory; // False for disabled history and unsupported schemas.
-  int64_t iconId;
   nsCString guid;
 };
 
 /**
  * Async fetches icon from database or network, associates it with the required
  * page and finally notifies the change.
  */
 class AsyncFetchAndSetIconForPage final : public Runnable
@@ -194,21 +209,25 @@ public:
 
   /**
    * Constructor.
    *
    * @param aPageSpec
    *        URL of the page whose favicon's URL we're fetching
    * @param aCallback
    *        function to be called once finished
+   * @param aPreferredWidth
+   *        The preferred size for the icon
    */
   AsyncGetFaviconURLForPage(const nsACString& aPageSpec,
+                            uint16_t aPreferredWidth,
                             nsIFaviconDataCallback* aCallback);
 
 private:
+  uint16_t mPreferredWidth;
   nsMainThreadPtrHandle<nsIFaviconDataCallback> mCallback;
   nsCString mPageSpec;
 };
 
 
 /**
  * Asynchronously tries to get the URL and data of a page's favicon, then
  * notifies the given observer.
@@ -218,23 +237,28 @@ class AsyncGetFaviconDataForPage final :
 public:
   NS_DECL_NSIRUNNABLE
 
   /**
    * Constructor.
    *
    * @param aPageSpec
    *        URL of the page whose favicon URL and data we're fetching
+   * @param aPreferredWidth
+   *        The preferred size of the icon.  We will try to return an icon close
+   *        to this size.
    * @param aCallback
    *        function to be called once finished
    */
   AsyncGetFaviconDataForPage(const nsACString& aPageSpec,
+                             uint16_t aPreferredWidth,
                              nsIFaviconDataCallback* aCallback);
 
 private:
+  uint16_t mPreferredWidth;
   nsMainThreadPtrHandle<nsIFaviconDataCallback> mCallback;
   nsCString mPageSpec;
 };
 
 class AsyncReplaceFaviconData final : public Runnable
 {
 public:
   NS_DECL_NSIRUNNABLE
--- a/toolkit/components/places/History.jsm
+++ b/toolkit/components/places/History.jsm
@@ -155,19 +155,17 @@ this.History = Object.freeze({
    *      A promise resolved once the operation is complete.
    * @resolves (PageInfo)
    *      A PageInfo object populated with data after the insert is complete.
    * @rejects (Error)
    *      Rejects if the insert was unsuccessful.
    *
    * @throws (Error)
    *      If the `url` specified was for a protocol that should not be
-   *      stored (e.g. "chrome:", "mailbox:", "about:", "imap:", "news:",
-   *      "moz-anno:", "view-source:", "resource:", "data:", "wyciwyg:",
-   *      "javascript:", "blob:").
+   *      stored (@see nsNavHistory::CanAddURI).
    * @throws (Error)
    *      If `pageInfo` has an unexpected type.
    * @throws (Error)
    *      If `pageInfo` does not have a `url`.
    * @throws (Error)
    *      If `pageInfo` does not have a `visits` property or if the
    *      value of `visits` is ill-typed or is an empty array.
    * @throws (Error)
@@ -211,19 +209,17 @@ this.History = Object.freeze({
    * @return (Promise)
    *      A promise resolved once the operation is complete.
    * @resolves (null)
    * @rejects (Error)
    *      Rejects if all of the inserts were unsuccessful.
    *
    * @throws (Error)
    *      If the `url` specified was for a protocol that should not be
-   *      stored (e.g. "chrome:", "mailbox:", "about:", "imap:", "news:",
-   *      "moz-anno:", "view-source:", "resource:", "data:", "wyciwyg:",
-   *      "javascript:", "blob:").
+   *      stored (@see nsNavHistory::CanAddURI).
    * @throws (Error)
    *      If `pageInfos` has an unexpected type.
    * @throws (Error)
    *      If a `pageInfo` does not have a `url`.
    * @throws (Error)
    *      If a `PageInfo` does not have a `visits` property or if the
    *      value of `visits` is ill-typed or is an empty array.
    * @throws (Error)
@@ -701,19 +697,20 @@ var cleanupPages = Task.async(function*(
     // async race conditions.
     yield db.execute(`DELETE FROM moz_places WHERE id IN ( ${ idsList } )
                       AND foreign_count = 0 AND last_visit_date ISNULL`);
     // Hosts accumulated during the places delete are updated through a trigger
     // (see nsPlacesTriggers.h).
     yield db.executeCached(`DELETE FROM moz_updatehosts_temp`);
 
     // Expire orphans.
-    yield db.executeCached(`
-      DELETE FROM moz_favicons WHERE NOT EXISTS
-        (SELECT 1 FROM moz_places WHERE favicon_id = moz_favicons.id)`);
+    yield db.executeCached(`DELETE FROM moz_pages_w_icons
+                            WHERE page_url_hash NOT IN (SELECT url_hash FROM moz_places)`);
+    yield db.executeCached(`DELETE FROM moz_icons
+                            WHERE id NOT IN (SELECT icon_id FROM moz_icons_to_pages)`);
     yield db.execute(`DELETE FROM moz_annos
                       WHERE place_id IN ( ${ idsList } )`);
     yield db.execute(`DELETE FROM moz_inputhistory
                       WHERE place_id IN ( ${ idsList } )`);
   }
 });
 
 /**
--- a/toolkit/components/places/PageIconProtocolHandler.js
+++ b/toolkit/components/places/PageIconProtocolHandler.js
@@ -36,16 +36,26 @@ function streamDefaultFavicon(uri, loadI
     let defaultIconChannel = makeDefaultFaviconChannel(uri, loadInfo);
     defaultIconChannel.asyncOpen2(listener);
   } catch (ex) {
     Cu.reportError(ex);
     outputStream.close();
   }
 }
 
+function serveIcon(pipe, data, len) {
+  // Pass the icon data to the output stream.
+  let stream = Cc["@mozilla.org/binaryoutputstream;1"]
+                 .createInstance(Ci.nsIBinaryOutputStream);
+  stream.setOutputStream(pipe.outputStream);
+  stream.writeByteArray(data, len);
+  stream.close();
+  pipe.outputStream.close();
+}
+
 function PageIconProtocolHandler() {
 }
 
 PageIconProtocolHandler.prototype = {
   get scheme() {
     return "page-icon";
   },
 
@@ -78,35 +88,26 @@ PageIconProtocolHandler.prototype = {
       let channel = Cc["@mozilla.org/network/input-stream-channel;1"]
                       .createInstance(Ci.nsIInputStreamChannel);
       channel.QueryInterface(Ci.nsIChannel);
       channel.setURI(uri);
       channel.contentStream = pipe.inputStream;
       channel.loadInfo = loadInfo;
 
       let pageURI = NetUtil.newURI(uri.path);
-      PlacesUtils.favicons.getFaviconDataForPage(pageURI, (iconuri, len, data, mime) => {
+      PlacesUtils.favicons.getFaviconDataForPage(pageURI, (iconURI, len, data, mimeType) => {
+        channel.contentType = mimeType;
         if (len == 0) {
-          channel.contentType = "image/png";
           streamDefaultFavicon(uri, loadInfo, pipe.outputStream);
-          return;
-        }
-
-        try {
-          channel.contentType = mime;
-          // Pass the icon data to the output stream.
-          let stream = Cc["@mozilla.org/binaryoutputstream;1"]
-                         .createInstance(Ci.nsIBinaryOutputStream);
-          stream.setOutputStream(pipe.outputStream);
-          stream.writeByteArray(data, len);
-          stream.close();
-          pipe.outputStream.close();
-        } catch (ex) {
-          channel.contentType = "image/png";
-          streamDefaultFavicon(uri, loadInfo, pipe.outputStream);
+        } else {
+          try {
+            serveIcon(pipe, data, len);
+          } catch (ex) {
+            streamDefaultFavicon(uri, loadInfo, pipe.outputStream);
+          }
         }
       });
 
       return channel;
     } catch (ex) {
       return makeDefaultFaviconChannel(uri, loadInfo);
     }
   },
--- a/toolkit/components/places/PlacesDBUtils.jsm
+++ b/toolkit/components/places/PlacesDBUtils.jsm
@@ -588,23 +588,27 @@ this.PlacesDBUtils = {
        WHERE length(title) = 0 AND type = :folder_type
          AND parent = :tags_folder`
     );
     fixEmptyNamedTags.params["empty_title"] = "(notitle)";
     fixEmptyNamedTags.params["folder_type"] = PlacesUtils.bookmarks.TYPE_FOLDER;
     fixEmptyNamedTags.params["tags_folder"] = PlacesUtils.tagsFolderId;
     cleanupStatements.push(fixEmptyNamedTags);
 
-    // MOZ_FAVICONS
-    // E.1 remove orphan icons
+    // MOZ_ICONS
+    // E.1 remove orphan icon entries.
+    let deleteOrphanIconPages = DBConn.createAsyncStatement(
+      `DELETE FROM moz_pages_w_icons WHERE page_url_hash NOT IN (
+         SELECT url_hash FROM moz_places
+       )`);
+    cleanupStatements.push(deleteOrphanIconPages);
+
     let deleteOrphanIcons = DBConn.createAsyncStatement(
-      `DELETE FROM moz_favicons WHERE id IN (
-         SELECT id FROM moz_favicons f
-         WHERE NOT EXISTS
-           (SELECT id FROM moz_places WHERE favicon_id = f.id LIMIT 1)
+      `DELETE FROM moz_icons WHERE id NOT IN (
+         SELECT icon_id FROM moz_icons_to_pages
        )`);
     cleanupStatements.push(deleteOrphanIcons);
 
     // MOZ_HISTORYVISITS
     // F.1 remove orphan visits
     let deleteOrphanVisits = DBConn.createAsyncStatement(
       `DELETE FROM moz_historyvisits WHERE id IN (
          SELECT id FROM moz_historyvisits v
@@ -649,26 +653,16 @@ this.PlacesDBUtils = {
       `DELETE FROM moz_keywords WHERE id IN (
          SELECT id FROM moz_keywords k
          WHERE NOT EXISTS
            (SELECT 1 FROM moz_places h WHERE k.place_id = h.id)
        )`);
     cleanupStatements.push(deleteUnusedKeywords);
 
     // MOZ_PLACES
-    // L.1 fix wrong favicon ids
-    let fixInvalidFaviconIds = DBConn.createAsyncStatement(
-      `UPDATE moz_places SET favicon_id = NULL WHERE id IN (
-         SELECT id FROM moz_places h
-         WHERE favicon_id NOT NULL
-           AND NOT EXISTS
-             (SELECT id FROM moz_favicons WHERE id = h.favicon_id LIMIT 1)
-       )`);
-    cleanupStatements.push(fixInvalidFaviconIds);
-
     // L.2 recalculate visit_count and last_visit_date
     let fixVisitStats = DBConn.createAsyncStatement(
       `UPDATE moz_places
        SET visit_count = (SELECT count(*) FROM moz_historyvisits
                           WHERE place_id = moz_places.id AND visit_type NOT IN (0,4,7,8,9)),
            last_visit_date = (SELECT MAX(visit_date) FROM moz_historyvisits
                               WHERE place_id = moz_places.id)
        WHERE id IN (
--- a/toolkit/components/places/PlacesUtils.jsm
+++ b/toolkit/components/places/PlacesUtils.jsm
@@ -1864,32 +1864,35 @@ this.PlacesUtils = {
          UNION ALL
          SELECT b2.fk, level + 1, b2.type, b2.id, b2.guid, b2.parent,
                 descendants.guid, b2.position, b2.title, b2.dateAdded,
                 b2.lastModified
          FROM moz_bookmarks b2
          JOIN descendants ON b2.parent = descendants.id AND b2.id <> :tags_folder)
        SELECT d.level, d.id, d.guid, d.parent, d.parentGuid, d.type,
               d.position AS [index], d.title, d.dateAdded, d.lastModified,
-              h.url, f.url AS iconuri,
+              h.url, (SELECT icon_url FROM moz_icons i
+                      JOIN moz_icons_to_pages ON icon_id = i.id
+                      JOIN moz_pages_w_icons pi ON page_id = pi.id
+                      WHERE pi.page_url_hash = hash(h.url) AND pi.page_url = h.url
+                      ORDER BY width DESC LIMIT 1) AS iconuri,
               (SELECT GROUP_CONCAT(t.title, ',')
                FROM moz_bookmarks b2
                JOIN moz_bookmarks t ON t.id = +b2.parent AND t.parent = :tags_folder
                WHERE b2.fk = h.id
               ) AS tags,
               EXISTS (SELECT 1 FROM moz_items_annos
                       WHERE item_id = d.id LIMIT 1) AS has_annos,
               (SELECT a.content FROM moz_annos a
                JOIN moz_anno_attributes n ON a.anno_attribute_id = n.id
                WHERE place_id = h.id AND n.name = :charset_anno
               ) AS charset
        FROM descendants d
        LEFT JOIN moz_bookmarks b3 ON b3.id = d.parent
        LEFT JOIN moz_places h ON h.id = d.fk
-       LEFT JOIN moz_favicons f ON f.id = h.favicon_id
        ORDER BY d.level, d.parent, d.position`;
 
 
     if (!aItemGuid)
       aItemGuid = this.bookmarks.rootGuid;
 
     let hasExcludeItemsCallback =
       aOptions.hasOwnProperty("excludeItemsCallback");
--- a/toolkit/components/places/UnifiedComplete.js
+++ b/toolkit/components/places/UnifiedComplete.js
@@ -89,25 +89,24 @@ const REGEXP_USER_CONTEXT_ID = /(?:^| )u
 
 // Regex used to match one or more whitespace.
 const REGEXP_SPACES = /\s+/;
 
 // Sqlite result row index constants.
 const QUERYINDEX_QUERYTYPE     = 0;
 const QUERYINDEX_URL           = 1;
 const QUERYINDEX_TITLE         = 2;
-const QUERYINDEX_ICONURL       = 3;
-const QUERYINDEX_BOOKMARKED    = 4;
-const QUERYINDEX_BOOKMARKTITLE = 5;
-const QUERYINDEX_TAGS          = 6;
-const QUERYINDEX_VISITCOUNT    = 7;
-const QUERYINDEX_TYPED         = 8;
-const QUERYINDEX_PLACEID       = 9;
-const QUERYINDEX_SWITCHTAB     = 10;
-const QUERYINDEX_FRECENCY      = 11;
+const QUERYINDEX_BOOKMARKED    = 3;
+const QUERYINDEX_BOOKMARKTITLE = 4;
+const QUERYINDEX_TAGS          = 5;
+const QUERYINDEX_VISITCOUNT    = 6;
+const QUERYINDEX_TYPED         = 7;
+const QUERYINDEX_PLACEID       = 8;
+const QUERYINDEX_SWITCHTAB     = 9;
+const QUERYINDEX_FRECENCY      = 10;
 
 // This SQL query fragment provides the following:
 //   - whether the entry is bookmarked (QUERYINDEX_BOOKMARKED)
 //   - the bookmark title, if it is a bookmark (QUERYINDEX_BOOKMARKTITLE)
 //   - the tags associated with a bookmarked entry (QUERYINDEX_TAGS)
 const SQL_BOOKMARK_TAGS_FRAGMENT =
   `EXISTS(SELECT 1 FROM moz_bookmarks WHERE fk = h.id) AS bookmarked,
    ( SELECT title FROM moz_bookmarks WHERE fk = h.id AND title NOTNULL
@@ -120,20 +119,19 @@ const SQL_BOOKMARK_TAGS_FRAGMENT =
    ) AS tags`;
 
 // TODO bug 412736: in case of a frecency tie, we might break it with h.typed
 // and h.visit_count.  That is slower though, so not doing it yet...
 // NB: as a slight performance optimization, we only evaluate the "btitle"
 // and "tags" queries for bookmarked entries.
 function defaultQuery(conditions = "") {
   let query =
-    `SELECT :query_type, h.url, h.title, f.url, ${SQL_BOOKMARK_TAGS_FRAGMENT},
+    `SELECT :query_type, h.url, h.title, ${SQL_BOOKMARK_TAGS_FRAGMENT},
             h.visit_count, h.typed, h.id, t.open_count, h.frecency
      FROM moz_places h
-     LEFT JOIN moz_favicons f ON f.id = h.favicon_id
      LEFT JOIN moz_openpages_temp t
             ON t.url = h.url
            AND t.userContextId = :userContextId
      WHERE h.frecency <> 0
        AND AUTOCOMPLETE_MATCH(:searchString, h.url,
                               CASE WHEN bookmarked THEN
                                 IFNULL(btitle, h.title)
                               ELSE h.title END,
@@ -145,61 +143,55 @@ function defaultQuery(conditions = "") {
                               :matchBehavior, :searchBehavior)
        ${conditions}
      ORDER BY h.frecency DESC, h.id DESC
      LIMIT :maxResults`;
   return query;
 }
 
 const SQL_SWITCHTAB_QUERY =
-  `SELECT :query_type, t.url, t.url, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+  `SELECT :query_type, t.url, t.url, NULL, NULL, NULL, NULL, NULL, NULL,
           t.open_count, NULL
    FROM moz_openpages_temp t
    LEFT JOIN moz_places h ON h.url_hash = hash(t.url) AND h.url = t.url
    WHERE h.id IS NULL
      AND t.userContextId = :userContextId
      AND AUTOCOMPLETE_MATCH(:searchString, t.url, t.url, NULL,
                             NULL, NULL, NULL, t.open_count,
                             :matchBehavior, :searchBehavior)
    ORDER BY t.ROWID DESC
    LIMIT :maxResults`;
 
 const SQL_ADAPTIVE_QUERY =
   `/* do not warn (bug 487789) */
-   SELECT :query_type, h.url, h.title, f.url, ${SQL_BOOKMARK_TAGS_FRAGMENT},
+   SELECT :query_type, h.url, h.title, ${SQL_BOOKMARK_TAGS_FRAGMENT},
           h.visit_count, h.typed, h.id, t.open_count, h.frecency
    FROM (
      SELECT ROUND(MAX(use_count) * (1 + (input = :search_string)), 1) AS rank,
             place_id
      FROM moz_inputhistory
      WHERE input BETWEEN :search_string AND :search_string || X'FFFF'
      GROUP BY place_id
    ) AS i
    JOIN moz_places h ON h.id = i.place_id
-   LEFT JOIN moz_favicons f ON f.id = h.favicon_id
    LEFT JOIN moz_openpages_temp t
           ON t.url = h.url
          AND t.userContextId = :userContextId
    WHERE AUTOCOMPLETE_MATCH(NULL, h.url,
                             IFNULL(btitle, h.title), tags,
                             h.visit_count, h.typed, bookmarked,
                             t.open_count,
                             :matchBehavior, :searchBehavior)
    ORDER BY rank DESC, h.frecency DESC`;
 
 
 function hostQuery(conditions = "") {
   let query =
     `/* do not warn (bug NA): not worth to index on (typed, frecency) */
-     SELECT :query_type, host || '/', IFNULL(prefix, '') || host || '/',
-            ( SELECT f.url FROM moz_favicons f
-              JOIN moz_places h ON h.favicon_id = f.id
-              WHERE rev_host = get_unreversed_host(host || '.') || '.'
-                 OR rev_host = get_unreversed_host(host || '.') || '.www.'
-            ) AS favicon_url,
+     SELECT :query_type, host || '/', IFNULL(prefix, 'http://') || host || '/',
             NULL, NULL, NULL, NULL, NULL, NULL, NULL, frecency
      FROM moz_hosts
      WHERE host BETWEEN :searchString AND :searchString || X'FFFF'
      AND frecency <> 0
      ${conditions}
      ORDER BY frecency DESC
      LIMIT 1`;
   return query;
@@ -207,22 +199,17 @@ function hostQuery(conditions = "") {
 
 const SQL_HOST_QUERY = hostQuery();
 
 const SQL_TYPED_HOST_QUERY = hostQuery("AND typed = 1");
 
 function bookmarkedHostQuery(conditions = "") {
   let query =
     `/* do not warn (bug NA): not worth to index on (typed, frecency) */
-     SELECT :query_type, host || '/', IFNULL(prefix, '') || host || '/',
-            ( SELECT f.url FROM moz_favicons f
-              JOIN moz_places h ON h.favicon_id = f.id
-              WHERE rev_host = get_unreversed_host(host || '.') || '.'
-                 OR rev_host = get_unreversed_host(host || '.') || '.www.'
-            ) AS favicon_url,
+     SELECT :query_type, host || '/', IFNULL(prefix, 'http://') || host || '/',
             ( SELECT foreign_count > 0 FROM moz_places
               WHERE rev_host = get_unreversed_host(host || '.') || '.'
                  OR rev_host = get_unreversed_host(host || '.') || '.www.'
             ) AS bookmarked, NULL, NULL, NULL, NULL, NULL, NULL, frecency
      FROM moz_hosts
      WHERE host BETWEEN :searchString AND :searchString || X'FFFF'
      AND bookmarked
      AND frecency <> 0
@@ -233,21 +220,20 @@ function bookmarkedHostQuery(conditions 
 }
 
 const SQL_BOOKMARKED_HOST_QUERY = bookmarkedHostQuery();
 
 const SQL_BOOKMARKED_TYPED_HOST_QUERY = bookmarkedHostQuery("AND typed = 1");
 
 function urlQuery(conditions = "") {
   return `/* do not warn (bug no): cannot use an index to sort */
-          SELECT :query_type, h.url, NULL, f.url AS favicon_url,
+          SELECT :query_type, h.url, NULL,
             foreign_count > 0 AS bookmarked,
             NULL, NULL, NULL, NULL, NULL, NULL, h.frecency
           FROM moz_places h
-          LEFT JOIN moz_favicons f ON h.favicon_id = f.id
           WHERE (rev_host = :revHost OR rev_host = :revHost || "www.")
           AND h.frecency <> 0
           AND fixup_url(h.url) BETWEEN :searchString AND :searchString || X'FFFF'
           ${conditions}
           ORDER BY h.frecency DESC, h.id DESC
           LIMIT 1`;
 }
 
@@ -1387,17 +1373,22 @@ Search.prototype = {
       url,
       input: this._originalSearchString,
       postData,
     });
     let value = this._enableActions ? actionURL : url;
     // The title will end up being "host: queryString"
     let comment = entry.url.host;
 
-    this._addMatch({ value, comment, style, frecency: FRECENCY_DEFAULT });
+    this._addMatch({
+      value,
+      comment,
+      icon: "page-icon:" + url,
+      style,
+      frecency: FRECENCY_DEFAULT });
     return true;
   },
 
   *_matchSearchEngineUrl() {
     if (!Prefs.autofillSearchEngines)
       return false;
 
     let match = yield PlacesSearchAutocompleteProvider.findMatchByToken(
@@ -1528,22 +1519,17 @@ Search.prototype = {
   },
 
   *_matchRemoteTabs() {
     let matches = yield PlacesRemoteTabsAutocompleteProvider.getMatches(this._originalSearchString);
     for (let {url, title, icon, deviceName} of matches) {
       // It's rare that Sync supplies the icon for the page (but if it does, it
       // is a string URL)
       if (!icon) {
-        try {
-          let favicon = yield PlacesUtils.promiseFaviconLinkUrl(url);
-          if (favicon) {
-            icon = favicon.spec;
-          }
-        } catch (ex) {} // no favicon for this URL.
+        icon = "page-icon:" + url;
       } else {
         icon = PlacesUtils.favicons
                           .getFaviconLinkForIcon(NetUtil.newURI(icon)).spec;
       }
 
       let match = {
         // We include the deviceName in the action URL so we can render it in
         // the URLBar.
@@ -1616,26 +1602,19 @@ Search.prototype = {
       input: this._originalSearchString,
     });
 
     let match = {
       value,
       comment: displayURL,
       style: "action visiturl",
       frecency: 0,
+      icon: "page-icon:" + escapedURL
     };
 
-    try {
-      let favicon = yield PlacesUtils.promiseFaviconLinkUrl(uri);
-      if (favicon)
-        match.icon = favicon.spec;
-    } catch (e) {
-      // It's possible we don't have a favicon for this - and that's ok.
-    }
-
     this._addMatch(match);
     return true;
   },
 
   _onResultRow(row) {
     if (this._localMatchesCount == 0) {
       TelemetryStopwatch.finish(TELEMETRY_1ST_RESULT, this);
     }
@@ -1779,46 +1758,43 @@ Search.prototype = {
       this._localMatchesCount++;
     }
     return index;
   },
 
   _processHostRow(row) {
     let match = {};
     let strippedHost = row.getResultByIndex(QUERYINDEX_URL);
-    let unstrippedHost = row.getResultByIndex(QUERYINDEX_TITLE);
+    let url = row.getResultByIndex(QUERYINDEX_TITLE);
+    let unstrippedHost = stripHttpAndTrim(url, false);
     let frecency = row.getResultByIndex(QUERYINDEX_FRECENCY);
-    let faviconUrl = row.getResultByIndex(QUERYINDEX_ICONURL);
 
     // If the unfixup value doesn't preserve the user's input just
     // ignore it and complete to the found host.
     if (!unstrippedHost.toLowerCase().includes(this._trimmedOriginalSearchString.toLowerCase())) {
       unstrippedHost = null;
     }
 
     match.value = this._strippedPrefix + strippedHost;
     match.finalCompleteValue = unstrippedHost;
 
-    if (faviconUrl) {
-      match.icon = PlacesUtils.favicons
-                              .getFaviconLinkForIcon(NetUtil.newURI(faviconUrl)).spec;
-    }
+    match.icon = "page-icon:" + url;
+
     // Although this has a frecency, this query is executed before any other
     // queries that would result in frecency matches.
     match.frecency = frecency;
     match.style = "autofill";
     return match;
   },
 
   _processUrlRow(row) {
     let url = row.getResultByIndex(QUERYINDEX_URL);
     let strippedUrl = stripPrefix(url);
     let prefix = url.substr(0, url.length - strippedUrl.length);
     let frecency = row.getResultByIndex(QUERYINDEX_FRECENCY);
-    let faviconUrl = row.getResultByIndex(QUERYINDEX_ICONURL);
 
     // We must complete the URL up to the next separator (which is /, ? or #).
     let searchString = stripPrefix(this._trimmedOriginalSearchString);
     let separatorIndex = strippedUrl.slice(searchString.length)
                                     .search(/[\/\?\#]/);
     if (separatorIndex != -1) {
       separatorIndex += searchString.length;
       if (strippedUrl[separatorIndex] == "/") {
@@ -1830,37 +1806,33 @@ Search.prototype = {
     let match = {
       value: this._strippedPrefix + strippedUrl,
       // Although this has a frecency, this query is executed before any other
       // queries that would result in frecency matches.
       frecency,
       style: "autofill"
     };
 
-    if (faviconUrl) {
-      match.icon = PlacesUtils.favicons
-                              .getFaviconLinkForIcon(NetUtil.newURI(faviconUrl)).spec;
-    }
-
     // Complete to the found url only if its untrimmed value preserves the
     // user's input.
     if (url.toLowerCase().includes(this._trimmedOriginalSearchString.toLowerCase())) {
       match.finalCompleteValue = prefix + strippedUrl;
     }
 
+    match.icon = "page-icon:" + (match.finalCompleteValue || match.value);
+
     return match;
   },
 
   _processRow(row) {
     let match = {};
     match.placeId = row.getResultByIndex(QUERYINDEX_PLACEID);
     let escapedURL = row.getResultByIndex(QUERYINDEX_URL);
     let openPageCount = row.getResultByIndex(QUERYINDEX_SWITCHTAB) || 0;
     let historyTitle = row.getResultByIndex(QUERYINDEX_TITLE) || "";
-    let iconurl = row.getResultByIndex(QUERYINDEX_ICONURL) || "";
     let bookmarked = row.getResultByIndex(QUERYINDEX_BOOKMARKED);
     let bookmarkTitle = bookmarked ?
       row.getResultByIndex(QUERYINDEX_BOOKMARKTITLE) : null;
     let tags = row.getResultByIndex(QUERYINDEX_TAGS) || "";
     let frecency = row.getResultByIndex(QUERYINDEX_FRECENCY);
 
     // If actions are enabled and the page is open, add only the switch-to-tab
     // result.  Otherwise, add the normal result.
@@ -1906,20 +1878,17 @@ Search.prototype = {
       }
     }
 
     if (action)
       match.style = "action " + action;
 
     match.value = url;
     match.comment = title;
-    if (iconurl) {
-      match.icon = PlacesUtils.favicons
-                              .getFaviconLinkForIcon(NetUtil.newURI(iconurl)).spec;
-    }
+    match.icon = "page-icon:" + escapedURL;
     match.frecency = frecency;
 
     return match;
   },
 
   /**
    * @return a string consisting of the search query to be used based on the
    * previously set urlbar suggestion preferences.
--- a/toolkit/components/places/mozIAsyncFavicons.idl
+++ b/toolkit/components/places/mozIAsyncFavicons.idl
@@ -58,16 +58,17 @@ interface mozIAsyncFavicons : nsISupport
    */
   mozIPlacesPendingOperation setAndFetchFaviconForPage(
     in nsIURI aPageURI,
     in nsIURI aFaviconURI,
     in boolean aForceReload,
     in unsigned long aFaviconLoadType,
     [optional] in nsIFaviconDataCallback aCallback,
     [optional] in nsIPrincipal aLoadingPrincipal);
+
   /**
    * Sets the data for a given favicon URI either by replacing existing data in
    * the database or taking the place of otherwise fetched icon data when
    * calling setAndFetchFaviconForPage later.
    *
    * Favicon data for favicon URIs that are not associated with a page URI via
    * setAndFetchFaviconForPage will be stored in memory, but may be expired at
    * any time, so you should make an effort to associate favicon URIs with page
@@ -138,37 +139,45 @@ interface mozIAsyncFavicons : nsISupport
    * Retrieves the favicon URI associated to the given page, if any.
    *
    * @param aPageURI
    *        URI of the page whose favicon URI we're looking up.
    * @param aCallback
    *        This callback is always invoked to notify the result of the lookup.
    *        The aURI parameter will be the favicon URI, or null when no favicon
    *        is associated with the page or an error occurred while fetching it.
+   * @param aPreferredWidth
+   *        The preferred icon width, 0 for the biggest available.
    *
    * @note When the callback is invoked, aDataLen will be always 0, aData will
    *       be an empty array, and aMimeType will be an empty string, regardless
    *       of whether a favicon is associated with the page.
    *
    * @see nsIFaviconDataCallback in nsIFaviconService.idl.
    */
   void getFaviconURLForPage(in nsIURI aPageURI,
-                            in nsIFaviconDataCallback aCallback);
+                            in nsIFaviconDataCallback aCallback,
+                            [optional] in unsigned short aPreferredWidth);
 
   /**
    * Retrieves the favicon URI and data associated to the given page, if any.
+   * If the page icon is not available, it will try to return the root domain
+   * icon data, when it's known.
    *
    * @param aPageURI
    *        URI of the page whose favicon URI and data we're looking up.
    * @param aCallback
    *        This callback is always invoked to notify the result of the lookup.  The aURI
    *        parameter will be the favicon URI, or null when no favicon is
    *        associated with the page or an error occurred while fetching it.  If
    *        aURI is not null, the other parameters may contain the favicon data.
    *        However, if no favicon data is currently associated with the favicon
    *        URI, aDataLen will be 0, aData will be an empty array, and aMimeType
    *        will be an empty string.
+   * @param aPreferredWidth
+   *        The preferred icon width, 0 for the biggest available.
    *
    * @see nsIFaviconDataCallback in nsIFaviconService.idl.
    */
   void getFaviconDataForPage(in nsIURI aPageURI,
-                             in nsIFaviconDataCallback aCallback);
+                             in nsIFaviconDataCallback aCallback,
+                             [optional] in unsigned short aPreferredWidth);
 };
--- a/toolkit/components/places/nsAnnoProtocolHandler.cpp
+++ b/toolkit/components/places/nsAnnoProtocolHandler.cpp
@@ -28,16 +28,17 @@
 #include "nsInputStreamPump.h"
 #include "nsContentUtils.h"
 #include "nsServiceManagerUtils.h"
 #include "nsStringStream.h"
 #include "SimpleChannel.h"
 #include "mozilla/ScopeExit.h"
 #include "mozilla/storage.h"
 #include "Helpers.h"
+#include "FaviconHelpers.h"
 
 using namespace mozilla;
 using namespace mozilla::places;
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Global Functions
 
 /**
@@ -60,119 +61,115 @@ GetDefaultIcon(nsILoadInfo *aLoadInfo, n
 namespace {
 
 /**
  * An instance of this class is passed to the favicon service as the callback
  * for getting favicon data from the database.  We'll get this data back in
  * HandleResult, and on HandleCompletion, we'll close our output stream which
  * will close the original channel for the favicon request.
  *
- * However, if an error occurs at any point, we do not set mReturnDefaultIcon to
- * false, so we will open up another channel to get the default favicon, and
- * pass that along to our output stream in HandleCompletion.  If anything
- * happens at that point, the world must be against us, so we return nothing.
+ * However, if an error occurs at any point and we don't have mData, we will
+ * just fallback to the default favicon.  If anything happens at that point, the
+ * world must be against us, so we can do nothing.
  */
 class faviconAsyncLoader : public AsyncStatementCallback
 {
 public:
-  faviconAsyncLoader(nsIChannel *aChannel, nsIStreamListener *aListener) :
-      mChannel(aChannel)
+  faviconAsyncLoader(nsIChannel *aChannel, nsIStreamListener *aListener)
+    : mChannel(aChannel)
     , mListener(aListener)
   {
-    NS_ASSERTION(aChannel,
-                 "Not providing a channel will result in crashes!");
-    NS_ASSERTION(aListener,
-                 "Not providing a stream listener will result in crashes!");
+    MOZ_ASSERT(aChannel, "Not providing a channel will result in crashes!");
+    MOZ_ASSERT(aListener, "Not providing a stream listener will result in crashes!");
+    // Set the default content type.
+    Unused << mChannel->SetContentType(NS_LITERAL_CSTRING(PNG_MIME_TYPE));
   }
 
   //////////////////////////////////////////////////////////////////////////////
   //// mozIStorageStatementCallback
 
   NS_IMETHOD HandleResult(mozIStorageResultSet *aResultSet) override
   {
-    // We will only get one row back in total, so we do not need to loop.
     nsCOMPtr<mozIStorageRow> row;
-    nsresult rv = aResultSet->GetNextRow(getter_AddRefs(row));
-    NS_ENSURE_SUCCESS(rv, rv);
+    while (NS_SUCCEEDED(aResultSet->GetNextRow(getter_AddRefs(row))) && row) {
+      // TODO: For now just return the biggest icon, that is the first one.
+      // Later this should allow to return a specific size.
+      if (!mData.IsEmpty()) {
+        return NS_OK;
+      }
 
-    // We do not allow favicons without a MIME type, so we'll return the default
-    // icon.
-    nsAutoCString mimeType;
-    (void)row->GetUTF8String(1, mimeType);
-    NS_ENSURE_FALSE(mimeType.IsEmpty(), NS_OK);
+      int32_t width;
+      nsresult rv = row->GetInt32(1, &width);
+      NS_ENSURE_SUCCESS(rv, rv);
 
-    // Set our mimeType now that we know it.
-    rv = mChannel->SetContentType(mimeType);
-    NS_ENSURE_SUCCESS(rv, rv);
+      // Eventually override the default mimeType for svg.
+      if (width == UINT16_MAX) {
+        rv = mChannel->SetContentType(NS_LITERAL_CSTRING(SVG_MIME_TYPE));
+        NS_ENSURE_SUCCESS(rv, rv);
+      }
 
-    // Obtain the binary blob that contains our favicon data.
-    uint8_t *favicon;
-    uint32_t size = 0;
-    rv = row->GetBlob(0, &size, &favicon);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    nsCOMPtr<nsIInputStream> stream;
-    rv = NS_NewByteInputStream(getter_AddRefs(stream),
-                               reinterpret_cast<char*>(favicon),
-                               size, NS_ASSIGNMENT_ADOPT);
-    if (NS_FAILED(rv)) {
-      free(favicon);
-      return rv;
+      // Obtain the binary blob that contains our favicon data.
+      uint8_t *data;
+      uint32_t dataLen;
+      rv = row->GetBlob(0, &dataLen, &data);
+      NS_ENSURE_SUCCESS(rv, rv);
+      mData.Adopt(TO_CHARBUFFER(data), dataLen);
     }
 
-    RefPtr<nsInputStreamPump> pump;
-    rv = nsInputStreamPump::Create(getter_AddRefs(pump), stream, -1, -1, 0, 0,
-                                   true);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    MOZ_DIAGNOSTIC_ASSERT(mListener);
-    NS_ENSURE_TRUE(mListener, NS_ERROR_UNEXPECTED);
-
-    rv = pump->AsyncRead(mListener, nullptr);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    mListener = nullptr;
     return NS_OK;
   }
 
   NS_IMETHOD HandleCompletion(uint16_t aReason) override
   {
-    // If we've already written our icon data to the channel, there's nothing
-    // more to do. If we didn't, then return the default icon instead.
-    if (!mListener)
-      return NS_OK;
+    MOZ_DIAGNOSTIC_ASSERT(mListener);
+    NS_ENSURE_TRUE(mListener, NS_ERROR_UNEXPECTED);
 
+    nsresult rv;
+    // Ensure we'll break possible cycles with the listener.
     auto cleanup = MakeScopeExit([&] () {
       mListener = nullptr;
     });
 
+    if (!mData.IsEmpty()) {
+      nsCOMPtr<nsIInputStream> stream;
+      rv = NS_NewCStringInputStream(getter_AddRefs(stream), mData);
+      MOZ_ASSERT(NS_SUCCEEDED(rv));
+      if (NS_SUCCEEDED(rv)) {
+        RefPtr<nsInputStreamPump> pump;
+        rv = nsInputStreamPump::Create(getter_AddRefs(pump), stream, -1, -1, 0, 0,
+                                      true);
+        MOZ_ASSERT(NS_SUCCEEDED(rv));
+        if (NS_SUCCEEDED(rv)) {
+          return pump->AsyncRead(mListener, nullptr);
+        }
+      }
+    }
+
+    // Fallback to the default favicon.
     // we should pass the loadInfo of the original channel along
     // to the new channel. Note that mChannel can not be null,
     // constructor checks that.
     nsCOMPtr<nsILoadInfo> loadInfo = mChannel->GetLoadInfo();
     nsCOMPtr<nsIChannel> newChannel;
-    nsresult rv = GetDefaultIcon(loadInfo, getter_AddRefs(newChannel));
-
+    rv = GetDefaultIcon(loadInfo, getter_AddRefs(newChannel));
     if (NS_FAILED(rv)) {
       mListener->OnStartRequest(mChannel, nullptr);
       mListener->OnStopRequest(mChannel, nullptr, rv);
       return rv;
     }
-
-    mChannel->SetContentType(NS_LITERAL_CSTRING("image/png"));
-
     return newChannel->AsyncOpen2(mListener);
   }
 
 protected:
   virtual ~faviconAsyncLoader() {}
 
 private:
   nsCOMPtr<nsIChannel> mChannel;
   nsCOMPtr<nsIStreamListener> mListener;
+  nsCString mData;
 };
 
 } // namespace
 
 ////////////////////////////////////////////////////////////////////////////////
 //// nsAnnoProtocolHandler
 
 NS_IMPL_ISUPPORTS(nsAnnoProtocolHandler, nsIProtocolHandler)
--- a/toolkit/components/places/nsFaviconService.cpp
+++ b/toolkit/components/places/nsFaviconService.cpp
@@ -113,42 +113,41 @@ nsFaviconService::Init()
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsFaviconService::ExpireAllFavicons()
 {
+  NS_ENSURE_STATE(mDB);
+
+  nsCOMPtr<mozIStorageAsyncStatement> removePagesStmt = mDB->GetAsyncStatement(
+    "DELETE FROM moz_pages_w_icons"
+  );
+  NS_ENSURE_STATE(removePagesStmt);
+  nsCOMPtr<mozIStorageAsyncStatement> removeIconsStmt = mDB->GetAsyncStatement(
+    "DELETE FROM moz_icons"
+  );
+  NS_ENSURE_STATE(removeIconsStmt);
   nsCOMPtr<mozIStorageAsyncStatement> unlinkIconsStmt = mDB->GetAsyncStatement(
-    "UPDATE moz_places "
-    "SET favicon_id = NULL "
-    "WHERE favicon_id NOT NULL"
+    "DELETE FROM moz_icons_to_pages"
   );
   NS_ENSURE_STATE(unlinkIconsStmt);
-  nsCOMPtr<mozIStorageAsyncStatement> removeIconsStmt = mDB->GetAsyncStatement(
-    "DELETE FROM moz_favicons WHERE id NOT IN ("
-      "SELECT favicon_id FROM moz_places WHERE favicon_id NOT NULL "
-    ")"
-  );
-  NS_ENSURE_STATE(removeIconsStmt);
 
   mozIStorageBaseStatement* stmts[] = {
-    unlinkIconsStmt.get()
+    removePagesStmt.get()
   , removeIconsStmt.get()
+  , unlinkIconsStmt.get()
   };
   nsCOMPtr<mozIStoragePendingStatement> ps;
   RefPtr<ExpireFaviconsStatementCallbackNotifier> callback =
     new ExpireFaviconsStatementCallbackNotifier();
-  nsresult rv = mDB->MainConn()->ExecuteAsync(
-    stmts, ArrayLength(stmts), callback, getter_AddRefs(ps)
-  );
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_OK;
+  return mDB->MainConn()->ExecuteAsync(stmts, ArrayLength(stmts),
+                                       callback, getter_AddRefs(ps));
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 //// nsITimerCallback
 
 NS_IMETHODIMP
 nsFaviconService::Notify(nsITimer* timer)
 {
@@ -219,20 +218,22 @@ nsFaviconService::SetAndFetchFaviconForP
   NS_ENSURE_ARG(aFaviconURI);
   NS_ENSURE_ARG_POINTER(_canceler);
 
   // If a favicon is in the failed cache, only load it during a forced reload.
   bool previouslyFailed;
   nsresult rv = IsFailedFavicon(aFaviconURI, &previouslyFailed);
   NS_ENSURE_SUCCESS(rv, rv);
   if (previouslyFailed) {
-    if (aForceReload)
+    if (aForceReload) {
       RemoveFailedFavicon(aFaviconURI);
-    else
+    }
+    else {
       return NS_OK;
+    }
   }
 
   nsCOMPtr<nsIPrincipal> loadingPrincipal = aLoadingPrincipal;
   MOZ_ASSERT(loadingPrincipal, "please provide aLoadingPrincipal for this favicon");
   if (!loadingPrincipal) {
     // Let's default to the nullPrincipal if no loadingPrincipal is provided.
     const char16_t* params[] = {
       u"nsFaviconService::setAndFetchFaviconForPage()",
@@ -243,46 +244,47 @@ nsFaviconService::SetAndFetchFaviconForP
                                     nullptr, // aDocument
                                     nsContentUtils::eNECKO_PROPERTIES,
                                     "APIDeprecationWarning",
                                     params, ArrayLength(params));
     loadingPrincipal = NullPrincipal::Create();
   }
   NS_ENSURE_TRUE(loadingPrincipal, NS_ERROR_FAILURE);
 
-  // Check if the icon already exists and fetch it from the network, if needed.
-  // Finally associate the icon to the requested page if not yet associated.
   bool loadPrivate = aFaviconLoadType == nsIFaviconService::FAVICON_LOAD_PRIVATE;
 
+  // Build page data.
   PageData page;
   rv = aPageURI->GetSpec(page.spec);
   NS_ENSURE_SUCCESS(rv, rv);
   // URIs can arguably miss a host.
-  (void)GetReversedHostname(aPageURI, page.revHost);
+  Unused << GetReversedHostname(aPageURI, page.revHost);
   bool canAddToHistory;
   nsNavHistory* navHistory = nsNavHistory::GetHistoryService();
   NS_ENSURE_TRUE(navHistory, NS_ERROR_OUT_OF_MEMORY);
   rv = navHistory->CanAddURI(aPageURI, &canAddToHistory);
   NS_ENSURE_SUCCESS(rv, rv);
   page.canAddToHistory = !!canAddToHistory && !loadPrivate;
 
+  // Build icon data.
   IconData icon;
+  // If we have an in-memory icon payload, it overwrites the actual request.
   UnassociatedIconHashKey* iconKey = mUnassociatedIcons.GetEntry(aFaviconURI);
   if (iconKey) {
     icon = iconKey->iconData;
     mUnassociatedIcons.RemoveEntry(iconKey);
   } else {
     icon.fetchMode = aForceReload ? FETCH_ALWAYS : FETCH_IF_MISSING;
     rv = aFaviconURI->GetSpec(icon.spec);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // If the page url points to an image, the icon's url will be the same.
-  // In future evaluate to store a resample of the image.  For now avoid that
-  // for database size concerns.
+  // TODO (Bug 403651): store a resample of the image.  For now avoid that
+  // for database size and UX concerns.
   // Don't store favicons for error pages too.
   if (icon.spec.Equals(page.spec) ||
       icon.spec.Equals(FAVICON_ERRORPAGE_URL)) {
     return NS_OK;
   }
 
   RefPtr<AsyncFetchAndSetIconForPage> event =
     new AsyncFetchAndSetIconForPage(icon, page, loadPrivate,
@@ -305,18 +307,21 @@ nsFaviconService::ReplaceFaviconData(nsI
                                     const uint8_t* aData,
                                     uint32_t aDataLen,
                                     const nsACString& aMimeType,
                                     PRTime aExpiration)
 {
   MOZ_ASSERT(NS_IsMainThread());
   NS_ENSURE_ARG(aFaviconURI);
   NS_ENSURE_ARG(aData);
-  NS_ENSURE_TRUE(aDataLen > 0, NS_ERROR_INVALID_ARG);
-  NS_ENSURE_TRUE(aMimeType.Length() > 0, NS_ERROR_INVALID_ARG);
+  NS_ENSURE_ARG(aDataLen > 0);
+  NS_ENSURE_ARG(aMimeType.Length() > 0);
+  NS_ENSURE_ARG(imgLoader::SupportImageWithMimeType(PromiseFlatCString(aMimeType).get(),
+                                                     AcceptedMimeTypes::IMAGES));
+
   if (aExpiration == 0) {
     aExpiration = PR_Now() + MAX_FAVICON_EXPIRATION;
   }
 
   UnassociatedIconHashKey* iconKey = mUnassociatedIcons.PutEntry(aFaviconURI);
   if (!iconKey) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
@@ -334,32 +339,32 @@ nsFaviconService::ReplaceFaviconData(nsI
 
   IconData* iconData = &(iconKey->iconData);
   iconData->expiration = aExpiration;
   iconData->status = ICON_STATUS_CACHED;
   iconData->fetchMode = FETCH_NEVER;
   nsresult rv = aFaviconURI->GetSpec(iconData->spec);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  // If the page provided a large image for the favicon (eg, a highres image
-  // or a multiresolution .ico file), we don't want to store more data than
-  // needed.
-  if (aDataLen > MAX_FAVICON_FILESIZE) {
-    rv = OptimizeFaviconImage(aData, aDataLen, aMimeType, iconData->data, iconData->mimeType);
-    NS_ENSURE_SUCCESS(rv, rv);
+  IconPayload payload;
+  payload.mimeType = aMimeType;
+  payload.data.Assign(TO_CHARBUFFER(aData), aDataLen);
+  // There may already be a previous payload, so ensure to only have one.
+  iconData->payloads.Clear();
+  iconData->payloads.AppendElement(payload);
 
-    if (iconData->data.Length() > nsIFaviconService::MAX_FAVICON_BUFFER_SIZE) {
-      // We cannot optimize this favicon size and we are over the maximum size
-      // allowed, so we will not save data to the db to avoid bloating it.
-      mUnassociatedIcons.RemoveEntry(aFaviconURI);
-      return NS_ERROR_FAILURE;
-    }
-  } else {
-    iconData->mimeType.Assign(aMimeType);
-    iconData->data.Assign(TO_CHARBUFFER(aData), aDataLen);
+  rv = OptimizeIconSizes(*iconData);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // If there's not valid payload, don't store the icon into to the database.
+  if ((*iconData).payloads.Length() == 0) {
+    // We cannot optimize this favicon size and we are over the maximum size
+    // allowed, so we will not save data to the db to avoid bloating it.
+    mUnassociatedIcons.RemoveEntry(aFaviconURI);
+    return NS_ERROR_FAILURE;
   }
 
   // If the database contains an icon at the given url, we will update the
   // database immediately so that the associated pages are kept in sync.
   // Otherwise, do nothing and let the icon be picked up from the memory hash.
   RefPtr<AsyncReplaceFaviconData> event = new AsyncReplaceFaviconData(*iconData);
   RefPtr<Database> DB = Database::GetDatabase();
   NS_ENSURE_STATE(DB);
@@ -459,50 +464,52 @@ nsFaviconService::ReplaceFaviconDataFrom
   free(buffer);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsFaviconService::GetFaviconURLForPage(nsIURI *aPageURI,
-                                       nsIFaviconDataCallback* aCallback)
+                                       nsIFaviconDataCallback* aCallback,
+                                       uint16_t aPreferredWidth)
 {
   MOZ_ASSERT(NS_IsMainThread());
   NS_ENSURE_ARG(aPageURI);
   NS_ENSURE_ARG(aCallback);
 
   nsAutoCString pageSpec;
   nsresult rv = aPageURI->GetSpec(pageSpec);
   NS_ENSURE_SUCCESS(rv, rv);
 
   RefPtr<AsyncGetFaviconURLForPage> event =
-    new AsyncGetFaviconURLForPage(pageSpec, aCallback);
+    new AsyncGetFaviconURLForPage(pageSpec, aPreferredWidth, aCallback);
 
   RefPtr<Database> DB = Database::GetDatabase();
   NS_ENSURE_STATE(DB);
   DB->DispatchToAsyncThread(event);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsFaviconService::GetFaviconDataForPage(nsIURI* aPageURI,
-                                        nsIFaviconDataCallback* aCallback)
+                                        nsIFaviconDataCallback* aCallback,
+                                        uint16_t aPreferredWidth)
 {
   MOZ_ASSERT(NS_IsMainThread());
   NS_ENSURE_ARG(aPageURI);
   NS_ENSURE_ARG(aCallback);
 
   nsAutoCString pageSpec;
   nsresult rv = aPageURI->GetSpec(pageSpec);
   NS_ENSURE_SUCCESS(rv, rv);
 
   RefPtr<AsyncGetFaviconDataForPage> event =
-    new AsyncGetFaviconDataForPage(pageSpec, aCallback);
+    new AsyncGetFaviconDataForPage(pageSpec, aPreferredWidth, aCallback);
   RefPtr<Database> DB = Database::GetDatabase();
   NS_ENSURE_STATE(DB);
   DB->DispatchToAsyncThread(event);
 
   return NS_OK;
 }
 
 nsresult
@@ -582,23 +589,17 @@ nsFaviconService::IsFailedFavicon(nsIURI
 //    This computes a favicon URL with string input and using the cached
 //    default one to minimize parsing.
 
 nsresult
 nsFaviconService::GetFaviconLinkForIconString(const nsCString& aSpec,
                                               nsIURI** aOutput)
 {
   if (aSpec.IsEmpty()) {
-    // default icon for empty strings
-    if (! mDefaultIcon) {
-      nsresult rv = NS_NewURI(getter_AddRefs(mDefaultIcon),
-                              NS_LITERAL_CSTRING(FAVICON_DEFAULT_URL));
-      NS_ENSURE_SUCCESS(rv, rv);
-    }
-    return mDefaultIcon->Clone(aOutput);
+    return GetDefaultFavicon(aOutput);
   }
 
   if (StringBeginsWith(aSpec, NS_LITERAL_CSTRING("chrome:"))) {
     // pass through for chrome URLs, since they can be referenced without
     // this service
     return NS_NewURI(aOutput, aSpec);
   }
 
@@ -622,74 +623,123 @@ nsFaviconService::GetFaviconSpecForIconS
   } else if (StringBeginsWith(aSpec, NS_LITERAL_CSTRING("chrome:"))) {
     aOutput = aSpec;
   } else {
     aOutput.AssignLiteral("moz-anno:" FAVICON_ANNOTATION_NAME ":");
     aOutput += aSpec;
   }
 }
 
-
-// nsFaviconService::OptimizeFaviconImage
-//
-// Given a blob of data (a image file already read into a buffer), optimize
-// its size by recompressing it as a 16x16 PNG.
+/**
+ * Checks the icon and evaluates if it needs to be optimized.
+ *
+ * @param aIcon
+ *        The icon to be evaluated.
+ */
 nsresult
-nsFaviconService::OptimizeFaviconImage(const uint8_t* aData, uint32_t aDataLen,
-                                       const nsACString& aMimeType,
-                                       nsACString& aNewData,
-                                       nsACString& aNewMimeType)
+nsFaviconService::OptimizeIconSizes(IconData& aIcon)
 {
-  nsresult rv;
+  // TODO (bug 1346139): move optimization to the async thread.
+  MOZ_ASSERT(NS_IsMainThread());
+  // There should only be a single payload at this point, it may have to be
+  // split though, if it's an ico file.
+  MOZ_ASSERT(aIcon.payloads.Length() == 1);
+
+  // Even if the page provides a large image for the favicon (eg, a highres
+  // image or a multiresolution .ico file), don't try to store more data than
+  // needed.
+  IconPayload payload = aIcon.payloads[0];
+  if (payload.mimeType.EqualsLiteral(SVG_MIME_TYPE)) {
+    // Nothing to optimize, but check the payload size.
+    if (payload.data.Length() >= nsIFaviconService::MAX_FAVICON_BUFFER_SIZE) {
+      aIcon.payloads.Clear();
+    }
+    return NS_OK;
+  }
+
+  // Make space for the optimized payloads.
+  aIcon.payloads.Clear();
 
   nsCOMPtr<nsIInputStream> stream;
-  rv = NS_NewByteInputStream(getter_AddRefs(stream),
-                reinterpret_cast<const char*>(aData), aDataLen,
-                NS_ASSIGNMENT_DEPEND);
+  nsresult rv = NS_NewByteInputStream(getter_AddRefs(stream),
+                                      payload.data.get(),
+                                      payload.data.Length(),
+                                      NS_ASSIGNMENT_DEPEND);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // decode image
   nsCOMPtr<imgIContainer> container;
-  rv = GetImgTools()->DecodeImageData(stream, aMimeType, getter_AddRefs(container));
+  rv = GetImgTools()->DecodeImageData(stream, payload.mimeType,
+                                      getter_AddRefs(container));
   NS_ENSURE_SUCCESS(rv, rv);
 
-  aNewMimeType.AssignLiteral(PNG_MIME_TYPE);
+  IconPayload newPayload;
+  newPayload.mimeType = NS_LITERAL_CSTRING(PNG_MIME_TYPE);
+  // TODO: for ico files we should extract every single payload.
+  int32_t width;
+  rv = container->GetWidth(&width);
+  NS_ENSURE_SUCCESS(rv, rv);
+  int32_t height;
+  rv = container->GetHeight(&height);
+  NS_ENSURE_SUCCESS(rv, rv);
+  // For non-square images, pick the largest side.
+  int32_t originalSize = std::max(width, height);
+  newPayload.width = originalSize;
+  for (uint16_t size : sFaviconSizes) {
+    if (size <= originalSize) {
+      newPayload.width = size;
+      break;
+    }
+  }
 
-  // scale and recompress
-  nsCOMPtr<nsIInputStream> iconStream;
-  rv = GetImgTools()->EncodeScaledImage(container, aNewMimeType,
-                                        DEFAULT_FAVICON_SIZE,
-                                        DEFAULT_FAVICON_SIZE,
-                                        EmptyString(),
-                                        getter_AddRefs(iconStream));
-  NS_ENSURE_SUCCESS(rv, rv);
+  // If the original payload is png and the size is the same, no reason to
+  // rescale the image.
+  if (newPayload.mimeType.Equals(payload.mimeType) &&
+      newPayload.width == originalSize) {
+    newPayload.data = payload.data;
+  } else {
+    // scale and recompress
+    nsCOMPtr<nsIInputStream> iconStream;
+    rv = GetImgTools()->EncodeScaledImage(container,
+                                          newPayload.mimeType,
+                                          newPayload.width,
+                                          newPayload.width,
+                                          EmptyString(),
+                                          getter_AddRefs(iconStream));
+    NS_ENSURE_SUCCESS(rv, rv);
+    // Read the stream into the new buffer.
+    rv = NS_ConsumeStream(iconStream, UINT32_MAX, newPayload.data);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
 
-  // Read the stream into a new buffer.
-  rv = NS_ConsumeStream(iconStream, UINT32_MAX, aNewData);
-  NS_ENSURE_SUCCESS(rv, rv);
+  if (newPayload.data.Length() < nsIFaviconService::MAX_FAVICON_BUFFER_SIZE) {
+    aIcon.payloads.AppendElement(newPayload);
+  }
 
   return NS_OK;
 }
 
 nsresult
 nsFaviconService::GetFaviconDataAsync(nsIURI* aFaviconURI,
                                       mozIStorageStatementCallback *aCallback)
 {
-  NS_ASSERTION(aCallback, "Doesn't make sense to call this without a callback");
+  MOZ_ASSERT(aCallback, "Doesn't make sense to call this without a callback");
+
   nsCOMPtr<mozIStorageAsyncStatement> stmt = mDB->GetAsyncStatement(
-    "SELECT f.data, f.mime_type FROM moz_favicons f WHERE url = :icon_url"
+    "SELECT data, width FROM moz_icons "
+    "WHERE fixed_icon_url_hash = hash(fixup_url(:url)) AND icon_url = :url "
+    "ORDER BY width DESC"
   );
   NS_ENSURE_STATE(stmt);
 
   // Ignore the ref part of the URI before querying the database because
   // we may have added a media fragment for rendering purposes.
-
   nsAutoCString faviconURI;
   aFaviconURI->GetSpecIgnoringRef(faviconURI);
-  nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("icon_url"), faviconURI);
+  nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("url"), faviconURI);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<mozIStoragePendingStatement> pendingStatement;
   return stmt->ExecuteAsync(aCallback, getter_AddRefs(pendingStatement));
 }
 
 void // static
 nsFaviconService::ConvertUnsupportedPayloads(mozIStorageConnection* aDBConn)
--- a/toolkit/components/places/nsFaviconService.h
+++ b/toolkit/components/places/nsFaviconService.h
@@ -23,22 +23,16 @@
 #include "mozilla/Attributes.h"
 
 #include "FaviconHelpers.h"
 
 // The target dimension in pixels for favicons we store, in reverse order.
 static uint16_t sFaviconSizes[8] = {
   256, 192, 144, 96, 64, 48, 32, 16
 };
-// Default size when preferred size is unknown, doubled for hi-dpi.
-#define DEFAULT_FAVICON_SIZE 32
-
-// Favicons bigger than this (in bytes) will not be stored in the database.  We
-// expect that most 32x32 PNG favicons will be no larger due to compression.
-#define MAX_FAVICON_FILESIZE 3072 /* 3 KiB */
 
 // forward class definitions
 class mozIStorageStatementCallback;
 
 class UnassociatedIconHashKey : public nsURIHashKey
 {
 public:
   explicit UnassociatedIconHashKey(const nsIURI* aURI)
@@ -90,19 +84,17 @@ public:
    * Fetch and migrate favicons from an unsupported payload to a supported one.
    */
   static void ConvertUnsupportedPayloads(mozIStorageConnection* aDBConn);
 
   // addition to API for strings to prevent excessive parsing of URIs
   nsresult GetFaviconLinkForIconString(const nsCString& aIcon, nsIURI** aOutput);
   void GetFaviconSpecForIconString(const nsCString& aIcon, nsACString& aOutput);
 
-  nsresult OptimizeFaviconImage(const uint8_t* aData, uint32_t aDataLen,
-                                const nsACString& aMimeType,
-                                nsACString& aNewData, nsACString& aNewMimeType);
+  nsresult OptimizeIconSizes(mozilla::places::IconData& aIcon);
 
   /**
    * Obtains the favicon data asynchronously.
    *
    * @param aFaviconURI
    *        The URI representing the favicon we are looking for.
    * @param aCallback
    *        The callback where results or errors will be dispatch to.  In the
--- a/toolkit/components/places/nsIFaviconService.idl
+++ b/toolkit/components/places/nsIFaviconService.idl
@@ -14,17 +14,17 @@ interface nsIFaviconService : nsISupport
   const unsigned long FAVICON_LOAD_PRIVATE = 1;
   // The favicon is being loaded from a non-private browsing window
   const unsigned long FAVICON_LOAD_NON_PRIVATE = 2;
 
   /**
    * The limit in bytes of the size of favicons in memory and passed via the
    * favicon protocol.
    */
-  const unsigned long MAX_FAVICON_BUFFER_SIZE = 10240;
+  const unsigned long MAX_FAVICON_BUFFER_SIZE = 35840;
 
   /**
    * For a given icon URI, this will return a URI that will result in the image.
    * In most cases, this is an annotation URI.  For chrome URIs, this will do
    * nothing but returning the input URI.
    *
    * No validity checking is done. If you pass an icon URI that we've never
    * seen, you'll get back a URI that references an invalid icon. The moz-anno
@@ -116,25 +116,29 @@ interface nsIFaviconDataCallback : nsISu
    *        favicon URI, or the callback is notifying a failure.
    * @param aDataLen
    *        Size of the icon data in bytes.  Notice that a value of 0 does not
    *        necessarily mean that we don't have an icon.
    * @param aData
    *        Icon data, or an empty array if aDataLen is 0.
    * @param aMimeType
    *        Mime type of the icon, or an empty string if aDataLen is 0.
+   * @param aWidth
+   *        Width of the icon. 0 if the width is unknown or if the icon is
+   *        vectorial.
    *
    * @note If you want to open a network channel to access the favicon, it's
    *       recommended that you call the getFaviconLinkForIcon method to convert
    *       the "favicon URI" into a "favicon link URI".
    */
   void onComplete(in nsIURI aFaviconURI,
                   in unsigned long aDataLen,
                   [const,array,size_is(aDataLen)] in octet aData,
-                  in AUTF8String aMimeType);
+                  in AUTF8String aMimeType,
+                  in unsigned short aWidth);
 };
 
 %{C++
 
 /**
  * Notification sent when all favicons are expired.
  */
 #define NS_PLACES_FAVICONS_EXPIRED_TOPIC_ID "places-favicons-expired"
--- a/toolkit/components/places/nsNavBookmarks.cpp
+++ b/toolkit/components/places/nsNavBookmarks.cpp
@@ -1103,22 +1103,21 @@ nsNavBookmarks::GetDescendantChildren(in
     // Collect children informations.
     // Select all children of a given folder, sorted by position.
     // This is a LEFT JOIN because not all bookmarks types have a place.
     // We construct a result where the first columns exactly match
     // kGetInfoIndex_* order, and additionally contains columns for position,
     // item_child, and folder_child from moz_bookmarks.
     nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
       "SELECT h.id, h.url, IFNULL(b.title, h.title), h.rev_host, h.visit_count, "
-             "h.last_visit_date, f.url, b.id, b.dateAdded, b.lastModified, "
+             "h.last_visit_date, null, b.id, b.dateAdded, b.lastModified, "
              "b.parent, null, h.frecency, h.hidden, h.guid, null, null, null, "
              "b.guid, b.position, b.type, b.fk, b.syncStatus "
       "FROM moz_bookmarks b "
       "LEFT JOIN moz_places h ON b.fk = h.id "
-      "LEFT JOIN moz_favicons f ON h.favicon_id = f.id "
       "WHERE b.parent = :parent "
       "ORDER BY b.position ASC"
     );
     NS_ENSURE_STATE(stmt);
     mozStorageStatementScoper scoper(stmt);
 
     rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aFolderId);
     NS_ENSURE_SUCCESS(rv, rv);
@@ -2142,22 +2141,21 @@ nsNavBookmarks::QueryFolderChildren(
 
   // Select all children of a given folder, sorted by position.
   // This is a LEFT JOIN because not all bookmarks types have a place.
   // We construct a result where the first columns exactly match those returned
   // by mDBGetURLPageInfo, and additionally contains columns for position,
   // item_child, and folder_child from moz_bookmarks.
   nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
     "SELECT h.id, h.url, IFNULL(b.title, h.title), h.rev_host, h.visit_count, "
-           "h.last_visit_date, f.url, b.id, b.dateAdded, b.lastModified, "
+           "h.last_visit_date, null, b.id, b.dateAdded, b.lastModified, "
            "b.parent, null, h.frecency, h.hidden, h.guid, null, null, null, "
            "b.guid, b.position, b.type, b.fk "
     "FROM moz_bookmarks b "
     "LEFT JOIN moz_places h ON b.fk = h.id "
-    "LEFT JOIN moz_favicons f ON h.favicon_id = f.id "
     "WHERE b.parent = :parent "
     "ORDER BY b.position ASC"
   );
   NS_ENSURE_STATE(stmt);
   mozStorageStatementScoper scoper(stmt);
 
   nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aFolderId);
   NS_ENSURE_SUCCESS(rv, rv);
@@ -2280,22 +2278,21 @@ nsNavBookmarks::QueryFolderChildrenAsync
 
   // Select all children of a given folder, sorted by position.
   // This is a LEFT JOIN because not all bookmarks types have a place.
   // We construct a result where the first columns exactly match those returned
   // by mDBGetURLPageInfo, and additionally contains columns for position,
   // item_child, and folder_child from moz_bookmarks.
   nsCOMPtr<mozIStorageAsyncStatement> stmt = mDB->GetAsyncStatement(
     "SELECT h.id, h.url, IFNULL(b.title, h.title), h.rev_host, h.visit_count, "
-           "h.last_visit_date, f.url, b.id, b.dateAdded, b.lastModified, "
+           "h.last_visit_date, null, b.id, b.dateAdded, b.lastModified, "
            "b.parent, null, h.frecency, h.hidden, h.guid, null, null, null, "
            "b.guid, b.position, b.type, b.fk "
     "FROM moz_bookmarks b "
     "LEFT JOIN moz_places h ON b.fk = h.id "
-    "LEFT JOIN moz_favicons f ON h.favicon_id = f.id "
     "WHERE b.parent = :parent "
     "ORDER BY b.position ASC"
   );
   NS_ENSURE_STATE(stmt);
 
   nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("parent"), aFolderId);
   NS_ENSURE_SUCCESS(rv, rv);
 
--- a/toolkit/components/places/nsNavHistory.cpp
+++ b/toolkit/components/places/nsNavHistory.cpp
@@ -1275,17 +1275,17 @@ nsNavHistory::ExecuteQueries(nsINavHisto
       // This is a perf hack to generate an empty query that skips filtering.
       options->SetExcludeItems(true);
     }
   }
 
   if (!rootNode) {
     // Either this is not a folder shortcut, or is a broken one.  In both cases
     // just generate a query node.
-    rootNode = new nsNavHistoryQueryResultNode(EmptyCString(), EmptyCString(),
+    rootNode = new nsNavHistoryQueryResultNode(EmptyCString(),
                                                queries, options);
   }
 
   // Create the result that will hold nodes.  Inject batching status into it.
   RefPtr<nsNavHistoryResult> result;
   rv = nsNavHistoryResult::NewHistoryResult(aQueries, aQueryCount, options,
                                             rootNode, isBatching(),
                                             getter_AddRefs(result));
@@ -1512,21 +1512,20 @@ PlacesSQLQueryBuilder::SelectAsURI()
     case nsINavHistoryQueryOptions::QUERY_TYPE_HISTORY:
       GetTagsSqlFragment(history->GetTagsFolder(),
                          NS_LITERAL_CSTRING("h.id"),
                          mHasSearchTerms,
                          tagsSqlFragment);
 
       mQueryString = NS_LITERAL_CSTRING(
         "SELECT h.id, h.url, h.title AS page_title, h.rev_host, h.visit_count, "
-        "h.last_visit_date, f.url, null, null, null, null, ") +
+        "h.last_visit_date, null, null, null, null, null, ") +
         tagsSqlFragment + NS_LITERAL_CSTRING(", h.frecency, h.hidden, h.guid, "
         "null, null, null "
         "FROM moz_places h "
-        "LEFT JOIN moz_favicons f ON h.favicon_id = f.id "
         // WHERE 1 is a no-op since additonal conditions will start with AND.
         "WHERE 1 "
           "{QUERY_OPTIONS_VISITS} {QUERY_OPTIONS_PLACES} "
           "{ADDITIONAL_CONDITIONS} ");
       break;
 
     case nsINavHistoryQueryOptions::QUERY_TYPE_BOOKMARKS:
       if (mResultType == nsINavHistoryQueryOptions::RESULTS_AS_TAG_CONTENTS) {
@@ -1538,48 +1537,46 @@ PlacesSQLQueryBuilder::SelectAsURI()
 
         GetTagsSqlFragment(history->GetTagsFolder(),
                            NS_LITERAL_CSTRING("b2.fk"),
                            mHasSearchTerms,
                            tagsSqlFragment);
 
         mQueryString = NS_LITERAL_CSTRING(
           "SELECT b2.fk, h.url, COALESCE(b2.title, h.title) AS page_title, "
-            "h.rev_host, h.visit_count, h.last_visit_date, f.url, b2.id, "
+            "h.rev_host, h.visit_count, h.last_visit_date, null, b2.id, "
             "b2.dateAdded, b2.lastModified, b2.parent, ") +
             tagsSqlFragment + NS_LITERAL_CSTRING(", h.frecency, h.hidden, h.guid, "
             "null, null, null, b2.guid, b2.position, b2.type, b2.fk "
           "FROM moz_bookmarks b2 "
           "JOIN (SELECT b.fk "
                 "FROM moz_bookmarks b "
                 // ADDITIONAL_CONDITIONS will filter on parent.
                 "WHERE b.type = 1 {ADDITIONAL_CONDITIONS} "
                 ") AS seed ON b2.fk = seed.fk "
           "JOIN moz_places h ON h.id = b2.fk "
-          "LEFT OUTER JOIN moz_favicons f ON h.favicon_id = f.id "
           "WHERE NOT EXISTS ( "
             "SELECT id FROM moz_bookmarks WHERE id = b2.parent AND parent = ") +
                 nsPrintfCString("%" PRId64, history->GetTagsFolder()) +
           NS_LITERAL_CSTRING(") "
           "ORDER BY b2.fk DESC, b2.lastModified DESC");
       }
       else {
         GetTagsSqlFragment(history->GetTagsFolder(),
                            NS_LITERAL_CSTRING("b.fk"),
                            mHasSearchTerms,
                            tagsSqlFragment);
         mQueryString = NS_LITERAL_CSTRING(
           "SELECT b.fk, h.url, COALESCE(b.title, h.title) AS page_title, "
-            "h.rev_host, h.visit_count, h.last_visit_date, f.url, b.id, "
+            "h.rev_host, h.visit_count, h.last_visit_date, null, b.id, "
             "b.dateAdded, b.lastModified, b.parent, ") +
             tagsSqlFragment + NS_LITERAL_CSTRING(", h.frecency, h.hidden, h.guid,"
             "null, null, null, b.guid, b.position, b.type, b.fk "
           "FROM moz_bookmarks b "
           "JOIN moz_places h ON b.fk = h.id "
-          "LEFT OUTER JOIN moz_favicons f ON h.favicon_id = f.id "
           "WHERE NOT EXISTS "
               "(SELECT id FROM moz_bookmarks "
                 "WHERE id = b.parent AND parent = ") +
                   nsPrintfCString("%" PRId64, history->GetTagsFolder()) +
               NS_LITERAL_CSTRING(") "
             "{ADDITIONAL_CONDITIONS}");
       }
       break;
@@ -1597,22 +1594,21 @@ PlacesSQLQueryBuilder::SelectAsVisit()
   NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY);
   nsAutoCString tagsSqlFragment;
   GetTagsSqlFragment(history->GetTagsFolder(),
                      NS_LITERAL_CSTRING("h.id"),
                      mHasSearchTerms,
                      tagsSqlFragment);
   mQueryString = NS_LITERAL_CSTRING(
     "SELECT h.id, h.url, h.title AS page_title, h.rev_host, h.visit_count, "
-      "v.visit_date, f.url, null, null, null, null, ") +
+      "v.visit_date, null, null, null, null, null, ") +
       tagsSqlFragment + NS_LITERAL_CSTRING(", h.frecency, h.hidden, h.guid, "
       "v.id, v.from_visit, v.visit_type "
     "FROM moz_places h "
     "JOIN moz_historyvisits v ON h.id = v.place_id "
-    "LEFT JOIN moz_favicons f ON h.favicon_id = f.id "
     // WHERE 1 is a no-op since additonal conditions will start with AND.
     "WHERE 1 "
       "{QUERY_OPTIONS_VISITS} {QUERY_OPTIONS_PLACES} "
       "{ADDITIONAL_CONDITIONS} ");
 
   return NS_OK;
 }
 
@@ -2126,21 +2122,20 @@ nsNavHistory::ConstructQueryString(
   if (IsOptimizableHistoryQuery(aQueries, aOptions,
         nsINavHistoryQueryOptions::SORT_BY_DATE_DESCENDING) ||
       IsOptimizableHistoryQuery(aQueries, aOptions,
         nsINavHistoryQueryOptions::SORT_BY_VISITCOUNT_DESCENDING)) {
     // Generate an optimized query for the history menu and most visited
     // smart bookmark.
     queryString = NS_LITERAL_CSTRING(
       "SELECT h.id, h.url, h.title AS page_title, h.rev_host, h.visit_count, h.last_visit_date, "
-          "f.url, null, null, null, null, ") +
+          "null, null, null, null, null, ") +
           tagsSqlFragment + NS_LITERAL_CSTRING(", h.frecency, h.hidden, h.guid, "
           "null, null, null "
         "FROM moz_places h "
-        "LEFT OUTER JOIN moz_favicons f ON h.favicon_id = f.id "
         "WHERE h.hidden = 0 "
           "AND EXISTS (SELECT id FROM moz_historyvisits WHERE place_id = h.id "
                        "AND visit_type NOT IN ") +
                        nsPrintfCString("(0,%d,%d) ",
                                        nsINavHistoryService::TRANSITION_EMBED,
                                        nsINavHistoryService::TRANSITION_FRAMED_LINK) +
                        NS_LITERAL_CSTRING("LIMIT 1) "
           "{QUERY_OPTIONS} "
@@ -3792,21 +3787,16 @@ nsNavHistory::RowToResult(mozIStorageVal
   // title
   nsAutoCString title;
   rv = aRow->GetUTF8String(kGetInfoIndex_Title, title);
   NS_ENSURE_SUCCESS(rv, rv);
 
   uint32_t accessCount = aRow->AsInt32(kGetInfoIndex_VisitCount);
   PRTime time = aRow->AsInt64(kGetInfoIndex_VisitDate);
 
-  // favicon
-  nsAutoCString favicon;
-  rv = aRow->GetUTF8String(kGetInfoIndex_FaviconURL, favicon);
-  NS_ENSURE_SUCCESS(rv, rv);
-
   // itemId
   int64_t itemId = aRow->AsInt64(kGetInfoIndex_ItemId);
   int64_t parentId = -1;
   if (itemId == 0) {
     // This is not a bookmark.  For non-bookmarks we use a -1 itemId value.
     // Notice ids in sqlite tables start from 1, so itemId cannot ever be 0.
     itemId = -1;
   }
@@ -3837,17 +3827,17 @@ nsNavHistory::RowToResult(mozIStorageVal
 
     nsAutoCString guid;
     if (itemId != -1) {
       rv = aRow->GetUTF8String(nsNavBookmarks::kGetChildrenIndex_Guid, guid);
       NS_ENSURE_SUCCESS(rv, rv);
     }
 
     RefPtr<nsNavHistoryResultNode> resultNode;
-    rv = QueryRowToResult(itemId, guid, url, title, accessCount, time, favicon,
+    rv = QueryRowToResult(itemId, guid, url, title, accessCount, time,
                           getter_AddRefs(resultNode));
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (itemId != -1 ||
         aOptions->ResultType() == nsNavHistoryQueryOptions::RESULTS_AS_TAG_QUERY) {
       // RESULTS_AS_TAG_QUERY has date columns
       resultNode->mDateAdded = aRow->AsInt64(kGetInfoIndex_ItemDateAdded);
       resultNode->mLastModified = aRow->AsInt64(kGetInfoIndex_ItemLastModified);
@@ -3859,17 +3849,17 @@ nsNavHistory::RowToResult(mozIStorageVal
       }
     }
 
     resultNode.forget(aResult);
     return rv;
   } else if (aOptions->ResultType() == nsNavHistoryQueryOptions::RESULTS_AS_URI ||
              aOptions->ResultType() == nsNavHistoryQueryOptions::RESULTS_AS_TAG_CONTENTS) {
     RefPtr<nsNavHistoryResultNode> resultNode =
-      new nsNavHistoryResultNode(url, title, accessCount, time, favicon);
+      new nsNavHistoryResultNode(url, title, accessCount, time);
 
     if (itemId != -1) {
       resultNode->mItemId = itemId;
       resultNode->mFolderId = parentId;
       resultNode->mDateAdded = aRow->AsInt64(kGetInfoIndex_ItemDateAdded);
       resultNode->mLastModified = aRow->AsInt64(kGetInfoIndex_ItemLastModified);
 
       rv = aRow->GetUTF8String(nsNavBookmarks::kGetChildrenIndex_Guid,
@@ -3891,17 +3881,17 @@ nsNavHistory::RowToResult(mozIStorageVal
     NS_ENSURE_SUCCESS(rv, rv);
 
     resultNode.forget(aResult);
     return NS_OK;
   }
 
   if (aOptions->ResultType() == nsNavHistoryQueryOptions::RESULTS_AS_VISIT) {
     RefPtr<nsNavHistoryResultNode> resultNode =
-      new nsNavHistoryResultNode(url, title, accessCount, time, favicon);
+      new nsNavHistoryResultNode(url, title, accessCount, time);
 
     nsAutoString tags;
     rv = aRow->GetString(kGetInfoIndex_ItemTags, tags);
     if (!tags.IsVoid())
       resultNode->mTags.Assign(tags);
 
     rv = aRow->GetUTF8String(kGetInfoIndex_Guid, resultNode->mPageGuid);
     NS_ENSURE_SUCCESS(rv, rv);
@@ -3932,18 +3922,18 @@ nsNavHistory::RowToResult(mozIStorageVal
 //    Called by RowToResult when the URI is a place: URI to generate the proper
 //    folder or query node.
 
 nsresult
 nsNavHistory::QueryRowToResult(int64_t itemId,
                                const nsACString& aBookmarkGuid,
                                const nsACString& aURI,
                                const nsACString& aTitle,
-                               uint32_t aAccessCount, PRTime aTime,
-                               const nsACString& aFavicon,
+                               uint32_t aAccessCount,
+                               PRTime aTime,
                                nsNavHistoryResultNode** aNode)
 {
   MOZ_ASSERT((itemId != -1 && !aBookmarkGuid.IsEmpty()) ||
              (itemId == -1 && aBookmarkGuid.IsEmpty()));
 
   nsCOMArray<nsNavHistoryQuery> queries;
   nsCOMPtr<nsNavHistoryQueryOptions> options;
   nsresult rv = QueryStringToQueryArray(aURI, &queries,
@@ -3976,28 +3966,27 @@ nsNavHistory::QueryRowToResult(int64_t i
         // concrete folder title).
         if (!aTitle.IsVoid()) {
           resultNode->mTitle = aTitle;
         }
       }
     }
     else {
       // This is a regular query.
-      resultNode = new nsNavHistoryQueryResultNode(aTitle, EmptyCString(),
-                                                   aTime, queries, options);
+      resultNode = new nsNavHistoryQueryResultNode(aTitle, aTime, queries, options);
       resultNode->mItemId = itemId;
     }
   }
 
   if (NS_FAILED(rv)) {
     NS_WARNING("Generating a generic empty node for a broken query!");
     // This is a broken query, that either did not parse or points to not
     // existing data.  We don't want to return failure since that will kill the
     // whole result.  Instead make a generic empty query node.
-    resultNode = new nsNavHistoryQueryResultNode(aTitle, aFavicon, aURI);
+    resultNode = new nsNavHistoryQueryResultNode(aTitle, aURI);
     resultNode->mItemId = itemId;
     // This is a perf hack to generate an empty query that skips filtering.
     resultNode->GetAsQuery()->Options()->SetExcludeItems(true);
   }
 
   resultNode.forget(aNode);
   return NS_OK;
 }
@@ -4021,37 +4010,35 @@ nsNavHistory::VisitIdToResultNode(int64_
   switch (aOptions->ResultType())
   {
     case nsNavHistoryQueryOptions::RESULTS_AS_VISIT:
     case nsNavHistoryQueryOptions::RESULTS_AS_FULL_VISIT:
       // visit query - want exact visit time
       // Should match kGetInfoIndex_* (see GetQueryResults)
       statement = mDB->GetStatement(NS_LITERAL_CSTRING(
         "SELECT h.id, h.url, h.title, h.rev_host, h.visit_count, "
-               "v.visit_date, f.url, null, null, null, null, "
+               "v.visit_date, null, null, null, null, null, "
                ) + tagsFragment + NS_LITERAL_CSTRING(", h.frecency, h.hidden, h.guid, "
               "v.id, v.from_visit, v.visit_type "
         "FROM moz_places h "
         "JOIN moz_historyvisits v ON h.id = v.place_id "
-        "LEFT JOIN moz_favicons f ON h.favicon_id = f.id "
         "WHERE v.id = :visit_id ")
       );
       break;
 
     case nsNavHistoryQueryOptions::RESULTS_AS_URI:
       // URL results - want last visit time
       // Should match kGetInfoIndex_* (see GetQueryResults)
       statement = mDB->GetStatement(NS_LITERAL_CSTRING(
         "SELECT h.id, h.url, h.title, h.rev_host, h.visit_count, "
-               "h.last_visit_date, f.url, null, null, null, null, "
+               "h.last_visit_date, null, null, null, null, null, "
                ) + tagsFragment + NS_LITERAL_CSTRING(", h.frecency, h.hidden, h.guid, "
               "null, null, null "
         "FROM moz_places h "
         "JOIN moz_historyvisits v ON h.id = v.place_id "
-        "LEFT JOIN moz_favicons f ON h.favicon_id = f.id "
         "WHERE v.id = :visit_id ")
       );
       break;
 
     default:
       // Query base types like RESULTS_AS_*_QUERY handle additions
       // by registering their own observers when they are expanded.
       return NS_OK;
@@ -4082,23 +4069,22 @@ nsNavHistory::BookmarkIdToResultNode(int
                                      nsNavHistoryResultNode** aResult)
 {
   nsAutoCString tagsFragment;
   GetTagsSqlFragment(GetTagsFolder(), NS_LITERAL_CSTRING("h.id"),
                      true, tagsFragment);
   // Should match kGetInfoIndex_*
   nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(NS_LITERAL_CSTRING(
       "SELECT b.fk, h.url, COALESCE(b.title, h.title), "
-             "h.rev_host, h.visit_count, h.last_visit_date, f.url, b.id, "
+             "h.rev_host, h.visit_count, h.last_visit_date, null, b.id, "
              "b.dateAdded, b.lastModified, b.parent, "
              ) + tagsFragment + NS_LITERAL_CSTRING(", h.frecency, h.hidden, h.guid, "
              "null, null, null, b.guid, b.position, b.type, b.fk "
       "FROM moz_bookmarks b "
       "JOIN moz_places h ON b.fk = h.id "
-      "LEFT JOIN moz_favicons f ON h.favicon_id = f.id "
       "WHERE b.id = :item_id ")
   );
   NS_ENSURE_STATE(stmt);
   mozStorageStatementScoper scoper(stmt);
 
   nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("item_id"),
                                       aBookmarkId);
   NS_ENSURE_SUCCESS(rv, rv);
@@ -4123,23 +4109,22 @@ nsNavHistory::URIToResultNode(nsIURI* aU
                               nsNavHistoryResultNode** aResult)
 {
   nsAutoCString tagsFragment;
   GetTagsSqlFragment(GetTagsFolder(), NS_LITERAL_CSTRING("h.id"),
                      true, tagsFragment);
   // Should match kGetInfoIndex_*
   nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(NS_LITERAL_CSTRING(
     "SELECT h.id, :page_url, COALESCE(b.title, h.title), "
-           "h.rev_host, h.visit_count, h.last_visit_date, f.url, "
+           "h.rev_host, h.visit_count, h.last_visit_date, null, "
            "b.id, b.dateAdded, b.lastModified, b.parent, "
            ) + tagsFragment + NS_LITERAL_CSTRING(", h.frecency, h.hidden, h.guid, "
            "null, null, null, b.guid, b.position, b.type, b.fk "
     "FROM moz_places h "
     "LEFT JOIN moz_bookmarks b ON b.fk = h.id "
-    "LEFT JOIN moz_favicons f ON h.favicon_id = f.id "
     "WHERE h.url_hash = hash(:page_url) AND h.url = :page_url ")
   );
   NS_ENSURE_STATE(stmt);
   mozStorageStatementScoper scoper(stmt);
 
   nsresult rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("page_url"), aURI);
   NS_ENSURE_SUCCESS(rv, rv);
 
--- a/toolkit/components/places/nsNavHistory.h
+++ b/toolkit/components/places/nsNavHistory.h
@@ -253,18 +253,18 @@ public:
   // The row must contain the full set of columns.
   nsresult RowToResult(mozIStorageValueArray* aRow,
                        nsNavHistoryQueryOptions* aOptions,
                        nsNavHistoryResultNode** aResult);
   nsresult QueryRowToResult(int64_t aItemId,
                             const nsACString& aBookmarkGuid,
                             const nsACString& aURI,
                             const nsACString& aTitle,
-                            uint32_t aAccessCount, PRTime aTime,
-                            const nsACString& aFavicon,
+                            uint32_t aAccessCount,
+                            PRTime aTime,
                             nsNavHistoryResultNode** aNode);
 
   nsresult VisitIdToResultNode(int64_t visitId,
                                nsNavHistoryQueryOptions* aOptions,
                                nsNavHistoryResultNode** aResult);
 
   nsresult BookmarkIdToResultNode(int64_t aBookmarkId,
                                   nsNavHistoryQueryOptions* aOptions,
--- a/toolkit/components/places/nsNavHistoryResult.cpp
+++ b/toolkit/components/places/nsNavHistoryResult.cpp
@@ -83,24 +83,23 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_INTERFACE_MAP_ENTRY(nsINavHistoryResultNode)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsNavHistoryResultNode)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsNavHistoryResultNode)
 
 nsNavHistoryResultNode::nsNavHistoryResultNode(
     const nsACString& aURI, const nsACString& aTitle, uint32_t aAccessCount,
-    PRTime aTime, const nsACString& aIconURI) :
+    PRTime aTime) :
   mParent(nullptr),
   mURI(aURI),
   mTitle(aTitle),
   mAreTagsSorted(false),
   mAccessCount(aAccessCount),
   mTime(aTime),
-  mFaviconURI(aIconURI),
   mBookmarkIndex(-1),
   mItemId(-1),
   mFolderId(-1),
   mVisitId(-1),
   mFromVisitId(-1),
   mDateAdded(0),
   mLastModified(0),
   mIndentLevel(-1),
@@ -110,24 +109,23 @@ nsNavHistoryResultNode::nsNavHistoryResu
 {
   mTags.SetIsVoid(true);
 }
 
 
 NS_IMETHODIMP
 nsNavHistoryResultNode::GetIcon(nsACString& aIcon)
 {
-  if (mFaviconURI.IsEmpty()) {
-    aIcon.Truncate();
+  if (this->IsContainer() || mURI.IsEmpty()) {
     return NS_OK;
   }
 
-  nsFaviconService* faviconService = nsFaviconService::GetFaviconService();
-  NS_ENSURE_TRUE(faviconService, NS_ERROR_OUT_OF_MEMORY);
-  faviconService->GetFaviconSpecForIconString(mFaviconURI, aIcon);
+  aIcon.AppendLiteral("page-icon:");
+  aIcon.Append(mURI);
+
   return NS_OK;
 }
 
 
 NS_IMETHODIMP
 nsNavHistoryResultNode::GetParent(nsINavHistoryContainerResultNode** aParent)
 {
   NS_IF_ADDREF(*aParent = mParent);
@@ -340,34 +338,32 @@ NS_IMPL_ADDREF_INHERITED(nsNavHistoryCon
 NS_IMPL_RELEASE_INHERITED(nsNavHistoryContainerResultNode, nsNavHistoryResultNode)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsNavHistoryContainerResultNode)
   NS_INTERFACE_MAP_STATIC_AMBIGUOUS(nsNavHistoryContainerResultNode)
   NS_INTERFACE_MAP_ENTRY(nsINavHistoryContainerResultNode)
 NS_INTERFACE_MAP_END_INHERITING(nsNavHistoryResultNode)
 
 nsNavHistoryContainerResultNode::nsNavHistoryContainerResultNode(
-    const nsACString& aURI, const nsACString& aTitle,
-    const nsACString& aIconURI, uint32_t aContainerType,
+    const nsACString& aURI, const nsACString& aTitle, uint32_t aContainerType,
     nsNavHistoryQueryOptions* aOptions) :
-  nsNavHistoryResultNode(aURI, aTitle, 0, 0, aIconURI),
+  nsNavHistoryResultNode(aURI, aTitle, 0, 0),
   mResult(nullptr),
   mContainerType(aContainerType),
   mExpanded(false),
   mOptions(aOptions),
   mAsyncCanceledState(NOT_CANCELED)
 {
 }
 
 nsNavHistoryContainerResultNode::nsNavHistoryContainerResultNode(
     const nsACString& aURI, const nsACString& aTitle,
-    PRTime aTime,
-    const nsACString& aIconURI, uint32_t aContainerType,
+    PRTime aTime, uint32_t aContainerType,
     nsNavHistoryQueryOptions* aOptions) :
-  nsNavHistoryResultNode(aURI, aTitle, 0, aTime, aIconURI),
+  nsNavHistoryResultNode(aURI, aTitle, 0, aTime),
   mResult(nullptr),
   mContainerType(aContainerType),
   mExpanded(false),
   mOptions(aOptions),
   mAsyncCanceledState(NOT_CANCELED)
 {
 }
 
@@ -1758,33 +1754,31 @@ nsNavHistoryContainerResultNode::FindNod
  * a message without doing a requery.  For complex changes or complex queries,
  * we give up and requery.
  */
 NS_IMPL_ISUPPORTS_INHERITED(nsNavHistoryQueryResultNode,
                             nsNavHistoryContainerResultNode,
                             nsINavHistoryQueryResultNode)
 
 nsNavHistoryQueryResultNode::nsNavHistoryQueryResultNode(
-    const nsACString& aTitle, const nsACString& aIconURI,
-    const nsACString& aQueryURI) :
-  nsNavHistoryContainerResultNode(aQueryURI, aTitle, aIconURI,
+    const nsACString& aTitle, const nsACString& aQueryURI) :
+  nsNavHistoryContainerResultNode(aQueryURI, aTitle,
                                   nsNavHistoryResultNode::RESULT_TYPE_QUERY,
                                   nullptr),
   mLiveUpdate(QUERYUPDATE_COMPLEX_WITH_BOOKMARKS),
   mHasSearchTerms(false),
   mContentsValid(false),
   mBatchChanges(0)
 {
 }
 
 nsNavHistoryQueryResultNode::nsNavHistoryQueryResultNode(
-    const nsACString& aTitle, const nsACString& aIconURI,
-    const nsCOMArray<nsNavHistoryQuery>& aQueries,
+    const nsACString& aTitle, const nsCOMArray<nsNavHistoryQuery>& aQueries,
     nsNavHistoryQueryOptions* aOptions) :
-  nsNavHistoryContainerResultNode(EmptyCString(), aTitle, aIconURI,
+  nsNavHistoryContainerResultNode(EmptyCString(), aTitle,
                                   nsNavHistoryResultNode::RESULT_TYPE_QUERY,
                                   aOptions),
   mQueries(aQueries),
   mContentsValid(false),
   mBatchChanges(0),
   mTransitions(mQueries[0]->Transitions())
 {
   NS_ASSERTION(aQueries.Count() > 0, "Must have at least one query");
@@ -1803,21 +1797,21 @@ nsNavHistoryQueryResultNode::nsNavHistor
       uint32_t transition = mTransitions.SafeElementAt(j, 0);
       if (transition && !queryTransitions.Contains(transition))
         mTransitions.RemoveElement(transition);
     }
   }
 }
 
 nsNavHistoryQueryResultNode::nsNavHistoryQueryResultNode(
-    const nsACString& aTitle, const nsACString& aIconURI,
+    const nsACString& aTitle,
     PRTime aTime,
     const nsCOMArray<nsNavHistoryQuery>& aQueries,
     nsNavHistoryQueryOptions* aOptions) :
-  nsNavHistoryContainerResultNode(EmptyCString(), aTitle, aTime, aIconURI,
+  nsNavHistoryContainerResultNode(EmptyCString(), aTitle, aTime,
                                   nsNavHistoryResultNode::RESULT_TYPE_QUERY,
                                   aOptions),
   mQueries(aQueries),
   mContentsValid(false),
   mBatchChanges(0),
   mTransitions(mQueries[0]->Transitions())
 {
   NS_ASSERTION(aQueries.Count() > 0, "Must have at least one query");
@@ -2710,22 +2704,19 @@ nsNavHistoryQueryResultNode::OnClearHist
 {
   nsresult rv = Refresh();
   NS_ENSURE_SUCCESS(rv, rv);
   return NS_OK;
 }
 
 
 static nsresult setFaviconCallback(nsNavHistoryResultNode* aNode,
-                                   const void* aClosure,
+                                   const void * aClosure,
                                    const nsNavHistoryResult* aResult)
 {
-  const nsCString* newFavicon = static_cast<const nsCString*>(aClosure);
-  aNode->mFaviconURI = *newFavicon;
-
   if (aResult && (!aNode->mParent || aNode->mParent->AreChildrenVisible()))
     NOTIFY_RESULT_OBSERVERS(aResult, NodeIconChanged(aNode));
 
   return NS_OK;
 }
 
 
 NS_IMETHODIMP
@@ -2735,23 +2726,22 @@ nsNavHistoryQueryResultNode::OnPageChang
                                            const nsACString& aGUID)
 {
   nsAutoCString spec;
   nsresult rv = aURI->GetSpec(spec);
   NS_ENSURE_SUCCESS(rv, rv);
 
   switch (aChangedAttribute) {
     case nsINavHistoryObserver::ATTRIBUTE_FAVICON: {
-      NS_ConvertUTF16toUTF8 newFavicon(aNewValue);
       bool onlyOneEntry = (mOptions->ResultType() ==
                              nsINavHistoryQueryOptions::RESULTS_AS_URI ||
                              mOptions->ResultType() ==
                              nsINavHistoryQueryOptions::RESULTS_AS_TAG_CONTENTS);
       UpdateURIs(true, onlyOneEntry, false, spec, setFaviconCallback,
-                 &newFavicon);
+                 nullptr);
       break;
     }
     default:
       NS_WARNING("Unknown page changed notification");
   }
   return NS_OK;
 }
 
@@ -3026,17 +3016,17 @@ nsNavHistoryQueryResultNode::OnItemMoved
 NS_IMPL_ISUPPORTS_INHERITED(nsNavHistoryFolderResultNode,
                             nsNavHistoryContainerResultNode,
                             nsINavHistoryQueryResultNode,
                             mozIStorageStatementCallback)
 
 nsNavHistoryFolderResultNode::nsNavHistoryFolderResultNode(
     const nsACString& aTitle, nsNavHistoryQueryOptions* aOptions,
     int64_t aFolderId) :
-  nsNavHistoryContainerResultNode(EmptyCString(), aTitle, EmptyCString(),
+  nsNavHistoryContainerResultNode(EmptyCString(), aTitle,
                                   nsNavHistoryResultNode::RESULT_TYPE_FOLDER,
                                   aOptions),
   mContentsValid(false),
   mTargetFolderItemId(aFolderId),
   mIsRegisteredFolderObserver(false)
 {
   mItemId = aFolderId;
 }
@@ -3761,17 +3751,16 @@ nsNavHistoryResultNode::OnItemChanged(in
   else if (aProperty.EqualsLiteral("uri")) {
     // clear the tags string as well
     mTags.SetIsVoid(true);
     mURI = aNewValue;
     if (shouldNotify)
       NOTIFY_RESULT_OBSERVERS(result, NodeURIChanged(this, mURI));
   }
   else if (aProperty.EqualsLiteral("favicon")) {
-    mFaviconURI = aNewValue;
     if (shouldNotify)
       NOTIFY_RESULT_OBSERVERS(result, NodeIconChanged(this));
   }
   else if (aProperty.EqualsLiteral("cleartime")) {
     mTime = 0;
     if (shouldNotify) {
       NOTIFY_RESULT_OBSERVERS(result,
                               NodeHistoryDetailsChanged(this, 0, mAccessCount));
@@ -4002,17 +3991,17 @@ nsNavHistoryFolderResultNode::OnItemMove
 }
 
 
 /**
  * Separator nodes do not hold any data.
  */
 nsNavHistorySeparatorResultNode::nsNavHistorySeparatorResultNode()
   : nsNavHistoryResultNode(EmptyCString(), EmptyCString(),
-                           0, 0, EmptyCString())
+                           0, 0)
 {
 }
 
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsNavHistoryResult)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsNavHistoryResult)
   tmp->StopObserving();
--- a/toolkit/components/places/nsNavHistoryResult.h
+++ b/toolkit/components/places/nsNavHistoryResult.h
@@ -252,18 +252,17 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsNavHisto
     { return nsNavHistoryResultNode::GetFromVisitId(aFromVisitId); } \
   NS_IMETHOD GetVisitType(uint32_t* aVisitType) override \
     { return nsNavHistoryResultNode::GetVisitType(aVisitType); }
 
 class nsNavHistoryResultNode : public nsINavHistoryResultNode
 {
 public:
   nsNavHistoryResultNode(const nsACString& aURI, const nsACString& aTitle,
-                         uint32_t aAccessCount, PRTime aTime,
-                         const nsACString& aIconURI);
+                         uint32_t aAccessCount, PRTime aTime);
 
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_NAVHISTORYRESULTNODE_IID)
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS(nsNavHistoryResultNode)
 
   NS_IMPLEMENT_SIMPLE_RESULTNODE
   NS_IMETHOD GetIcon(nsACString& aIcon) override;
@@ -362,17 +361,16 @@ public:
 
   RefPtr<nsNavHistoryContainerResultNode> mParent;
   nsCString mURI; // not necessarily valid for containers, call GetUri
   nsCString mTitle;
   nsString mTags;
   bool mAreTagsSorted;
   uint32_t mAccessCount;
   int64_t mTime;
-  nsCString mFaviconURI;
   int32_t mBookmarkIndex;
   int64_t mItemId;
   int64_t mFolderId;
   int64_t mVisitId;
   int64_t mFromVisitId;
   PRTime mDateAdded;
   PRTime mLastModified;
 
@@ -431,23 +429,20 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsNavHisto
   { 0x6e3bf8d3, 0x22aa, 0x4065, { 0x86, 0xbc, 0x37, 0x46, 0xb5, 0xb3, 0x2c, 0xe8 } }
 
 class nsNavHistoryContainerResultNode : public nsNavHistoryResultNode,
                                         public nsINavHistoryContainerResultNode
 {
 public:
   nsNavHistoryContainerResultNode(
     const nsACString& aURI, const nsACString& aTitle,
-    const nsACString& aIconURI, uint32_t aContainerType,
-    nsNavHistoryQueryOptions* aOptions);
+    uint32_t aContainerType, nsNavHistoryQueryOptions* aOptions);
   nsNavHistoryContainerResultNode(
     const nsACString& aURI, const nsACString& aTitle,
-    PRTime aTime,
-    const nsACString& aIconURI, uint32_t aContainerType,
-    nsNavHistoryQueryOptions* aOptions);
+    PRTime aTime, uint32_t aContainerType, nsNavHistoryQueryOptions* aOptions);
 
   virtual nsresult Refresh();
 
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_NAVHISTORYCONTAINERRESULTNODE_IID)
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsNavHistoryContainerResultNode, nsNavHistoryResultNode)
   NS_FORWARD_COMMON_RESULTNODE_TO_BASE
@@ -620,24 +615,21 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsNavHisto
 //    bookmark notifications.
 
 class nsNavHistoryQueryResultNode final : public nsNavHistoryContainerResultNode,
                                           public nsINavHistoryQueryResultNode,
                                           public nsINavBookmarkObserver
 {
 public:
   nsNavHistoryQueryResultNode(const nsACString& aTitle,
-                              const nsACString& aIconURI,
                               const nsACString& aQueryURI);
   nsNavHistoryQueryResultNode(const nsACString& aTitle,
-                              const nsACString& aIconURI,
                               const nsCOMArray<nsNavHistoryQuery>& aQueries,
                               nsNavHistoryQueryOptions* aOptions);
   nsNavHistoryQueryResultNode(const nsACString& aTitle,
-                              const nsACString& aIconURI,
                               PRTime aTime,
                               const nsCOMArray<nsNavHistoryQuery>& aQueries,
                               nsNavHistoryQueryOptions* aOptions);
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_FORWARD_COMMON_RESULTNODE_TO_BASE
   NS_IMETHOD GetType(uint32_t* type) override
     { *type = nsNavHistoryResultNode::RESULT_TYPE_QUERY; return NS_OK; }
--- a/toolkit/components/places/nsPlacesExpiration.js
+++ b/toolkit/components/places/nsPlacesExpiration.js
@@ -264,25 +264,34 @@ const EXPIRATION_QUERIES = {
   // (see nsPlacesTriggers.h).
   QUERY_UPDATE_HOSTS: {
     sql: `DELETE FROM moz_updatehosts_temp`,
     actions: ACTION.CLEAR_HISTORY | ACTION.TIMED | ACTION.TIMED_OVERLIMIT |
              ACTION.SHUTDOWN_DIRTY | ACTION.IDLE_DIRTY | ACTION.IDLE_DAILY |
              ACTION.DEBUG
   },
 
+  // Expire orphan pages from the icons database.
+  QUERY_EXPIRE_FAVICONS_PAGES: {
+    sql: `DELETE FROM moz_pages_w_icons
+          WHERE page_url_hash NOT IN (
+            SELECT url_hash FROM moz_places
+          )`,
+    actions: ACTION.TIMED_OVERLIMIT | ACTION.CLEAR_HISTORY |
+             ACTION.SHUTDOWN_DIRTY | ACTION.IDLE_DIRTY | ACTION.IDLE_DAILY |
+             ACTION.DEBUG
+  },
+
   // Expire orphan icons from the database.
   QUERY_EXPIRE_FAVICONS: {
-    sql: `DELETE FROM moz_favicons WHERE id IN (
-            SELECT f.id FROM moz_favicons f
-            LEFT JOIN moz_places h ON f.id = h.favicon_id
-            WHERE h.favicon_id IS NULL
-            LIMIT :limit_favicons
+    sql: `DELETE FROM moz_icons
+          WHERE id NOT IN (
+            SELECT icon_id FROM moz_icons_to_pages
           )`,
-    actions: ACTION.TIMED | ACTION.TIMED_OVERLIMIT | ACTION.CLEAR_HISTORY |
+    actions: ACTION.TIMED_OVERLIMIT | ACTION.CLEAR_HISTORY |
              ACTION.SHUTDOWN_DIRTY | ACTION.IDLE_DIRTY | ACTION.IDLE_DAILY |
              ACTION.DEBUG
   },
 
   // Expire orphan page annotations from the database.
   QUERY_EXPIRE_ANNOS: {
     sql: `DELETE FROM moz_annos WHERE id in (
             SELECT a.id FROM moz_annos a
@@ -991,19 +1000,16 @@ nsPlacesExpiration.prototype = {
           aLimit == LIMIT.DEBUG && baseLimit == -1 ? 0 : baseLimit;
         break;
       case "QUERY_FIND_URIS_TO_EXPIRE":
         params.limit_uris = baseLimit;
         break;
       case "QUERY_SILENT_EXPIRE_ORPHAN_URIS":
         params.limit_uris = baseLimit;
         break;
-      case "QUERY_EXPIRE_FAVICONS":
-        params.limit_favicons = baseLimit;
-        break;
       case "QUERY_EXPIRE_ANNOS":
         // Each page may have multiple annos.
         params.limit_annos = baseLimit * EXPIRE_AGGRESSIVITY_MULTIPLIER;
         break;
       case "QUERY_EXPIRE_ANNOS_WITH_POLICY":
       case "QUERY_EXPIRE_ITEMS_ANNOS_WITH_POLICY":
         let microNow = Date.now() * 1000;
         ANNOS_EXPIRE_POLICIES.forEach(function(policy) {
--- a/toolkit/components/places/tests/browser/browser_favicon_setAndFetchFaviconForPage_failures.js
+++ b/toolkit/components/places/tests/browser/browser_favicon_setAndFetchFaviconForPage_failures.js
@@ -33,29 +33,29 @@ function test() {
   // This function is called after calling finish() on the test.
   registerCleanupFunction(function() {
     windowsToClose.forEach(function(aWin) {
       aWin.close();
     });
   });
 
   function checkFavIconsDBCount(aCallback) {
-    let stmt = DBConn().createAsyncStatement("SELECT url FROM moz_favicons");
+    let stmt = DBConn().createAsyncStatement("SELECT icon_url FROM moz_icons");
     stmt.executeAsync({
       handleResult: function final_handleResult(aResultSet) {
         while (aResultSet.getNextRow()) {
           favIconsResultCount++;
         }
       },
       handleError: function final_handleError(aError) {
         throw ("Unexpected error (" + aError.result + "): " + aError.message);
       },
       handleCompletion: function final_handleCompletion(aReason) {
         // begin testing
-        info("Previous records in moz_favicons: " + favIconsResultCount);
+        info("Previous records in moz_icons: " + favIconsResultCount);
         if (aCallback) {
           aCallback();
         }
       }
     });
     stmt.finalize();
   }
 
@@ -168,21 +168,21 @@ function test() {
 
   function testFinalVerification(aWindow, aCallback) {
     // Only the last test should raise the onPageChanged notification,
     // executing the waitForFaviconChanged callback.
     waitForFaviconChanged(lastPageURI, favIcon32URI, aWindow,
       function final_callback() {
         // Check that only one record corresponding to the last favicon is present.
         let resultCount = 0;
-        let stmt = DBConn().createAsyncStatement("SELECT url FROM moz_favicons");
+        let stmt = DBConn().createAsyncStatement("SELECT icon_url FROM moz_icons");
         stmt.executeAsync({
           handleResult: function final_handleResult(aResultSet) {
 
-            // If the moz_favicons DB had been previously loaded (before our
+            // If the moz_icons DB had been previously loaded (before our
             // test began), we should focus only in the URI we are testing and
             // skip the URIs not related to our test.
             if (favIconsResultCount > 0) {
               for (let row; (row = aResultSet.getNextRow()); ) {
                 if (favIcon32URI.spec === row.getResultByIndex(0)) {
                   is(favIcon32URI.spec, row.getResultByIndex(0),
                     "Check equal favicons");
                   resultCount++;
--- a/toolkit/components/places/tests/chrome/chrome.ini
+++ b/toolkit/components/places/tests/chrome/chrome.ini
@@ -1,12 +1,13 @@
 [DEFAULT]
+support-files = head.js
 
 [test_303567.xul]
 [test_341972a.xul]
 [test_341972b.xul]
 [test_342484.xul]
 [test_371798.xul]
 [test_381357.xul]
 [test_favicon_annotations.xul]
 [test_reloadLivemarks.xul]
 [test_browser_disableglobalhistory.xul]
-support-files = browser_disableglobalhistory.xul
\ No newline at end of file
+support-files = browser_disableglobalhistory.xul
new file mode 100644
--- /dev/null
+++ b/toolkit/components/places/tests/chrome/head.js
@@ -0,0 +1,10 @@
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "PlacesTestUtils",
+  "resource://testing-common/PlacesTestUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
+  "resource://gre/modules/PlacesUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Task",
+  "resource://gre/modules/Task.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
+  "resource://gre/modules/NetUtil.jsm");
--- a/toolkit/components/places/tests/chrome/test_favicon_annotations.xul
+++ b/toolkit/components/places/tests/chrome/test_favicon_annotations.xul
@@ -12,153 +12,127 @@
         onload="test();">
 
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/WindowSnapshot.js"/>
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+  <script type="application/javascript" src="head.js" />
 
   <script type="application/javascript">
   <![CDATA[
 
 var Cc = Components.classes;
 var Ci = Components.interfaces;
 var Cr = Components.results;
 
-let fs = Cc["@mozilla.org/browser/favicon-service;1"].
-         getService(Ci.nsIFaviconService);
-
-// Test descriptions that will be printed in the case of failure.
-let testDescriptions = [
-  "moz-anno URI with no data in the database loads default icon",
-  "URI added to the database is properly loaded",
-];
+let tests = [
+  {
+    desc: "moz-anno URI with no data in the database loads default icon",
+    url: "http://mozilla.org/2009/made-up-favicon/places-rocks/",
+    expectedIcon: PlacesUtils.favicons.defaultFavicon.spec,
+  },
+  {
+    desc: "URI added to the database is properly loaded",
+    url: "http://mozilla.org/should-be-barney/",
+    expectedIcon: "data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%10%00%00%00%10%08%06%00%00%00%1F%F3%FFa%00%00%00%04gAMA%00%00%AF%C87%05%8A%E9%00%00%00%19tEXtSoftware%00Adobe%20ImageReadyq%C9e%3C%00%00%01%D6IDATx%DAb%FC%FF%FF%3F%03%25%00%20%80%98%909%EF%DF%BFg%EF%EC%EC%FC%AD%AC%AC%FC%DF%95%91%F1%BF%89%89%C9%7F%20%FF%D7%EA%D5%AB%B7%DF%BBwO%16%9B%01%00%01%C4%00r%01%08%9F9s%C6%CD%D8%D8%F8%BF%0B%03%C3%FF3%40%BC%0A%88%EF%02q%1A%10%BB%40%F1%AAU%ABv%C1%D4%C30%40%00%81%89%993g%3E%06%1A%F6%3F%14%AA%11D%97%03%F1%7Fc%08%0D%E2%2B))%FD%17%04%89%A1%19%00%10%40%0C%D00%F8%0F3%00%C8%F8%BF%1B%E4%0Ac%88a%E5%60%17%19%FF%0F%0D%0D%05%1B%02v%D9%DD%BB%0A0%03%00%02%08%AC%B9%A3%A3%E3%17%03%D4v%90%01%EF%18%106%C3%0Cz%07%C5%BB%A1%DE%82y%07%20%80%A0%A6%08B%FCn%0C1%60%26%D4%20d%C3VA%C3%06%26%BE%0A%EA-%80%00%82%B9%E0%F7L4%0D%EF%90%F8%C6%60%2F%0A%82%BD%01%13%07%0700%D0%01%02%88%11%E4%02P%B41%DC%BB%C7%D0%014%0D%E8l%06W%20%06%BA%88%A1%1C%1AS%15%40%7C%16%CA6.%2Fgx%BFg%0F%83%CB%D9%B3%0C%7B%80%7C%80%00%02%BB%00%E8%9F%ED%20%1B%3A%A0%A6%9F%81%DA%DC%01%C5%B0%80%ED%80%FA%BF%BC%BC%FC%3F%83%12%90%9D%96%F6%1F%20%80%18%DE%BD%7B%C7%0E%8E%05AD%20%FEGr%A6%A0%A0%E0%7F%25P%80%02%9D%0F%D28%13%18%23%C6%C0%B0%02E%3D%C8%F5%00%01%04%8F%05P%A8%BA%40my%87%E4%12c%A8%8D%20%8B%D0%D3%00%08%03%04%10%9C%01R%E4%82d%3B%C8%A0%99%C6%90%90%C6%A5%19%84%01%02%08%9E%17%80%C9x%F7%7B%A0%DBVC%F9%A0%C0%5C%7D%16%2C%CE%00%F4%C6O%5C%99%09%20%800L%04y%A5%03%1A%95%A0%80%05%05%14.%DBA%18%20%80%18)%CD%CE%00%01%06%00%0C'%94%C7%C0k%C9%2C%00%00%00%00IEND%AEB%60%82",
+  },
 
-// URIs to load (will be compared with expectedURIs of the same index).
-let testURIs = [
-  "http://mozilla.org/2009/made-up-favicon/places-rocks/",
-  "http://mozilla.org/should-be-barney/",
 ];
 
-// URIs to load for expected results.
-let expectedURIs = [
-  fs.defaultFavicon.spec,
-  "data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%10%00%00%00%10%08%06%00%00%00%1F%F3%FFa%00%00%00%04gAMA%00%00%AF%C87%05%8A%E9%00%00%00%19tEXtSoftware%00Adobe%20ImageReadyq%C9e%3C%00%00%01%D6IDATx%DAb%FC%FF%FF%3F%03%25%00%20%80%98%909%EF%DF%BFg%EF%EC%EC%FC%AD%AC%AC%FC%DF%95%91%F1%BF%89%89%C9%7F%20%FF%D7%EA%D5%AB%B7%DF%BBwO%16%9B%01%00%01%C4%00r%01%08%9F9s%C6%CD%D8%D8%F8%BF%0B%03%C3%FF3%40%BC%0A%88%EF%02q%1A%10%BB%40%F1%AAU%ABv%C1%D4%C30%40%00%81%89%993g%3E%06%1A%F6%3F%14%AA%11D%97%03%F1%7Fc%08%0D%E2%2B))%FD%17%04%89%A1%19%00%10%40%0C%D00%F8%0F3%00%C8%F8%BF%1B%E4%0Ac%88a%E5%60%17%19%FF%0F%0D%0D%05%1B%02v%D9%DD%BB%0A0%03%00%02%08%AC%B9%A3%A3%E3%17%03%D4v%90%01%EF%18%106%C3%0Cz%07%C5%BB%A1%DE%82y%07%20%80%A0%A6%08B%FCn%0C1%60%26%D4%20d%C3VA%C3%06%26%BE%0A%EA-%80%00%82%B9%E0%F7L4%0D%EF%90%F8%C6%60%2F%0A%82%BD%01%13%07%0700%D0%01%02%88%11%E4%02P%B41%DC%BB%C7%D0%014%0D%E8l%06W%20%06%BA%88%A1%1C%1AS%15%40%7C%16%CA6.%2Fgx%BFg%0F%83%CB%D9%B3%0C%7B%80%7C%80%00%02%BB%00%E8%9F%ED%20%1B%3A%A0%A6%9F%81%DA%DC%01%C5%B0%80%ED%80%FA%BF%BC%BC%FC%3F%83%12%90%9D%96%F6%1F%20%80%18%DE%BD%7B%C7%0E%8E%05AD%20%FEGr%A6%A0%A0%E0%7F%25P%80%02%9D%0F%D28%13%18%23%C6%C0%B0%02E%3D%C8%F5%00%01%04%8F%05P%A8%BA%40my%87%E4%12c%A8%8D%20%8B%D0%D3%00%08%03%04%10%9C%01R%E4%82d%3B%C8%A0%99%C6%90%90%C6%A5%19%84%01%02%08%9E%17%80%C9x%F7%7B%A0%DBVC%F9%A0%C0%5C%7D%16%2C%CE%00%F4%C6O%5C%99%09%20%800L%04y%A5%03%1A%95%A0%80%05%05%14.%DBA%18%20%80%18)%CD%CE%00%01%06%00%0C'%94%C7%C0k%C9%2C%00%00%00%00IEND%AEB%60%82",
-];
-
-
 /**
  * The event listener placed on our test windows used to determine when it is
  * safe to compare the two windows.
  */
 let _results = [];
 function loadEventHandler()
 {
   _results.push(snapshotWindow(window));
-
   loadNextTest();
 }
 
 /**
  * This runs the comparison.
  */
 function compareResults(aIndex, aImage1, aImage2)
 {
   let [correct, data1, data2] = compareSnapshots(aImage1, aImage2, true);
   SimpleTest.ok(correct,
-                "Test '" + testDescriptions[aIndex] + "' matches expectations. " +
+                "Test '" + tests[aIndex].desc + "' matches expectations. " +
                 "Data from window 1 is '" + data1 + "'. " +
                 "Data from window 2 is '" + data2 + "'");
 }
 
 /**
  * Loads the next set of URIs to compare against.
  */
 let _counter = -1;
 function loadNextTest()
 {
   _counter++;
   // If we have no more tests, finish.
-  if (_counter / 2 == testDescriptions.length) {
+  if (_counter / 2 == tests.length) {
     for (let i = 0; i < _results.length; i = i + 2)
       compareResults(i / 2, _results[i], _results[i + 1]);
 
     SimpleTest.finish();
     return;
   }
 
   let nextURI = function() {
     let index = Math.floor(_counter / 2);
     if ((_counter % 2) == 0)
-      return "moz-anno:favicon:" + testURIs[index];
-    return expectedURIs[index];
+      return "moz-anno:favicon:" + tests[index].url;
+    return tests[index].expectedIcon;
   }
 
   let img = document.getElementById("favicon");
   img.setAttribute("src", nextURI());
 }
 
 function test()
 {
   SimpleTest.waitForExplicitFinish();
-  let db = Cc["@mozilla.org/browser/nav-history-service;1"].
-           getService(Ci.nsPIPlacesDatabase).
-           DBConnection;
+  Task.spawn(function* () {
+    yield PlacesTestUtils.clearHistory();
 
-  // Empty any old favicons
-  db.executeSimpleSQL("DELETE FROM moz_favicons");
-
-  let ios = Cc["@mozilla.org/network/io-service;1"].
-            getService(Ci.nsIIOService);
-  let uri = function(aSpec) {
-    return ios.newURI(aSpec);
-  };
+    info("Inserting new visit");
+    yield PlacesUtils.history.insert({
+      url: "http://example.com/favicon_annotations",
+      visits: [{
+        transition: PlacesUtils.history.TRANSITIONS.TYPED
+      }]
+    });
 
-  let pageURI = uri("http://example.com/favicon_annotations");
-  let history = Cc["@mozilla.org/browser/history;1"]
-                  .getService(Ci.mozIAsyncHistory);
-  history.updatePlaces(
-    {
-      uri: pageURI,
-      visits: [{ transitionType: Ci.nsINavHistoryService.TRANSITION_TYPED,
-                 visitDate: Date.now() * 1000
-              }],
-    },
-    {
-      handleError: function UP_handleError() {
-        ok(false, "Unexpected error in adding visit.");
-      },
-      handleResult: function () {},
-      handleCompletion: function UP_handleCompletion() {
-        // Set the favicon data.  Note that the "moz-anno:" protocol requires
-        // the favicon to be stored in the database, but the
-        // replaceFaviconDataFromDataURL function will not save the favicon
-        // unless it is associated with a page.  Thus, we must associate the
-        // icon with a page explicitly in order for it to be visible through
-        // the protocol.
-        var systemPrincipal = Cc["@mozilla.org/systemprincipal;1"]
-                                .createInstance(Ci.nsIPrincipal);
+    // Set the favicon data.  Note that the "moz-anno:" protocol requires
+    // the favicon to be stored in the database, but the
+    // replaceFaviconDataFromDataURL function will not save the favicon
+    // unless it is associated with a page.  Thus, we must associate the
+    // icon with a page explicitly in order for it to be visible through
+    // the protocol.
+    info("Replace favicon data");
+    var systemPrincipal = Cc["@mozilla.org/systemprincipal;1"]
+                            .createInstance(Ci.nsIPrincipal);
+    PlacesUtils.favicons.replaceFaviconDataFromDataURL(
+      NetUtil.newURI(tests[1].url),
+      tests[1].expectedIcon,
+      (Date.now() + 86400) * 1000,
+      systemPrincipal);
+    info("Set favicon data");
+    PlacesUtils.favicons.setAndFetchFaviconForPage(
+      NetUtil.newURI("http://example.com/favicon_annotations"),
+      NetUtil.newURI(tests[1].url),
+      true, PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE, null,
+      systemPrincipal);
 
-        fs.replaceFaviconDataFromDataURL(uri(testURIs[1]), expectedURIs[1],
-                                         (Date.now() + 60 * 60 * 24 * 1000) * 1000,
-                                         systemPrincipal);
-
-        fs.setAndFetchFaviconForPage(pageURI, uri(testURIs[1]), true,
-                                     fs.FAVICON_LOAD_NON_PRIVATE,
-                                     null, systemPrincipal);
-
-        // And start our test process.
-        loadNextTest();
-      }
-    }
-  );
-
-
+    // And start our test process.
+    loadNextTest();
+  }.bind(this));
 }
 
   ]]>
   </script>
 
   <body xmlns="http://www.w3.org/1999/xhtml">
     <img id="favicon" onload="loadEventHandler();"/>
     <p id="display"></p>
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..e9e520cb6cf6546bd1ec021ee99741681579889a
GIT binary patch
literal 330
zc$@)B0k!^#P)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0003INkl<ZIE}TE
zv2g=23<NJgh*$-%1=PY8*aA~U?BTNyR0s?BW>mpC;QyPF?2!T+31>LG#o<8dudB62
zN{Nt&2wH3O-gzs$-|s{OA+bp;`&py`lYR%S>kTub?7|{(l_17hBvMMa`_BL~I|1yS
zw-~Ui#HJwUOiGDG;zitA!^~KA0Q&BHfSBNA9tXhP$Luj4&4L4VWOCds`&g%g=k7Bg
zB;1{x^P`gp1e|MICc*Uycx<B9N{KkbmYLCe$K6lSHVNmuBbQn$3Q<ZK4$KU9f0=!g
zxLhuTM9!I7D~o~%v932Bw$OWjvA#{V-~Pl}EAgCs>`8x8y?0^{XYQVJ{tPgKj_}-l
c1ofZw1@I&!G(_A|qyPW_07*qoM6N<$f*u2q)Bpeg
index 9932c18fb87eb07c97aa59a7711f60d98db3cc57..c9db4979a6eb73c5b7313a7655e8ca93e00ce05d
GIT binary patch
literal 79
zc%17D@N?(olHy`uVBq!ia0vp^EFjFm1|(O0oL2{=#5`RbLpWw8CnO~N=x>w!cbFlM
cF@=X=(P`<Y(Sn;-0#z`0y85}Sb4q9e0Iwbt^8f$<
index 9f16bef4338110c5775c8e51693c5e5e3e2432d0..0d6c1b14a0306baa609f5d3545b4118a637e4e8c
GIT binary patch
literal 22020
zc$_?YbyQVPxb{Bep-ZHsBn2g;K{})xkp=;g25FG)4h880NJw|bA*7@`r5mKX&bR%2
z_ujQ&@fWja-uIa&_RJ1fRep<uNsb8s0FIojlsW)FfPcSGH1Lo9=bs+|fCiA064!K3
zKWJ5v{cE?tc1htnE4P~NLt&UFfm8d1ZLi&r2pJa}x@@>&j2?9Ob;h5`WkoL7GaztX
zj#3uiD)7lNjph=61(QUcBaP8B%`jwIi97<UaEBm1Rdh1wGBPZ%I8dnn_6WAATL$M_
z4E%moe>Gius^18^YdhS(U5_=vHYE+DL|njcjjrY95*QKpB<9Io&$fVE8#!OZHv9?V
zdOcS3@%l~_(VcvMW|Y%e6>Ng*l>7ku>iejmd3|QoLJMF!B_k(G0;C^p%iiNp+k~Yl
zKP*;sl+Q7jICqQ)4zh3J1GfU&FOQ3NO_s8d4HlhgY@6fi4EluFHVjC98=4p0QPt2<
zZ&|AHMH5laXQR6;7vEH$mZc#c#-BcTt>q&-{t2o07-&=*(mp0FfZ`{jY%(O{IN)Ek
z-^qVsl7U*mE^&Ru84yakY8t?A($1<&ct9KmBMU&kz59~}y_AOTzw~q6keqW-Tl@?~
zX<9iW+rkGtMQ*exx|Ps>XJY-0TbjQ#ha%WyR9#1Z@QdFM;)`<8vtvhba)cyNK7Pi}
zf~P%Z94tqzp@2_Vp$+jhLPB5X_H*44Uvi<f!!9RY&X5Fl5)}tL9J@kl{z}Z9Wl?%^
zDDRdYE=e69nFgus>OF9UUIx>8DC74!W{#N#t?;_@{RsWJHo`E1kwEW3@}BgE73~+r
zmE~@Sa=Q21dHcat+Y<AxG$jjN5!un;n(c;&<$Z=iDwMC>EEj@(4kjobMP$-BD(Z1{
zFL`}TWD{~v<3z`4W2v5BYl|#8kqj4pEpYiHzA(9@<g{FJ?R1!ebu=#NPW{|73SU%)
zXQh1{GnY!XLYpRn$h{r~I`5BenMNLqg4V^7od`aTZK$MX2DkNo$&h7vH>>3;DH`_S
zdd9ioFCE2g9j>{w|Gi!aKf6ToYRAa&N`c3*#+&OxiOk@BBupaezJ7^r*m}V@+&(0f
zqf0WKB)f!J^&Rm}n&1H)1vn4H@3-gptq^^&)6O`0XK%1T?@4GUz3tt#KAGS`vLe;r
zSJVD&7{wKSzYS?ggLX(pF)Q!18P<RM7zX=*QZMc^IwVYVn~&>~h}(RwAaP<r&{^Uq
z;n~nwsTTEk9rBDqnidt!L`KXp8)~T*08X?jM{Hrz_<+13*2bNLDj*Fw(a^`RdqE@R
z7_idjY@mww#)<{^ZGzG^J|LK48Eymuv;ZC6(o*iVVnn*eFXK)<zVI*@>|!jwJbA_^
zB&yZE)5k}7{M5{(N-VMSH6*~rc+(hvDHNPu1<5-Iv>z+bmt=xxbu_SLU1HH;Ct`H>
zs5bO76rfb}i6-})_V7FA^tcm&iCnK5K=1lOn(`%*iQA){C`H@<%G#v|@-vC)Xc<$_
zLysi)D7dfnL1gz>0+tJ8?~q}Aw7?gq@oYEaXLle{QoBW~n;1k)vBxA)t_FiEO~nk8
z3ctaK+Es{mj;}`39yJ6cXjGF~#FB|s^<PTps2LxSI*jN`#j2swaP;5{GYKHXL{GtO
zA^7O+=7kGc5&GO6P+=#zl*iWQMEu-6EWSlE<5rvHr0WiUDerA`^DD~J1V#J1%CU0F
z>vPo;4OA!>(k~B!ZkV(zT;A<72n=vB3!_=~yD)hdr98N4W64#8>L5i72o0IE2g-4n
zK{7E7A}QN`w*q&!2>(wgu{KJN--<|xH-Usu?u&p5-WKx70a~DjOGDY)al+qorK2?l
zUv#EzkL(Ah!MQA*fa#3ixk3)|#DNLfdb^UtWFAae=+tR9jr(t3n%75ZZ4uaCQ5o(5
zgqhh;-wH4D1rzS8<U7V1rBZJ1Wa86)Nt4&5W~o3(rxaxT1Ho3NoJR1$wsN`pFGWRT
zqafAG#Z_F%jAuw<&7Xs1g>K{V5NsK9GbT%%J*Qr+RXKhzHlZg<ZGRgbao1X)E?WML
z7M;RQXMq_b>jM_20$dd*UmhjZdXP$1sJ}VkXMT{nLUF!y)Lp$#Qh10OxgUNpYEHw1
zNHU?+o#Y-YL*E^F(v7j02Ho>VH}QUtV+QWLciR!D;JRI<@_eq7CzEc0HU1E{-Ht^r
za2GMNJt_1;%pt>K<|0HRRkgK9h0qQpDGnPWS5lKb`<=!c{HMcZ%LPv~qK?_<pO2G8
zb3{`Hip0XCk0;(VRL1fNV0}dm!+JicMZDF9an#8g{lv(hK3f=d|9Oz-iufWaFLo#}
zTBNvqeOiXK&MkI~Kf`@_n)09|mT|8|+!-~xnC4^i<enQ3+*ftk9U?WWMmLG}aKlJ#
z8qcT|1J4TFS4Fs0J=-O!d$eCx&ImrDGP7cqAhRO$t9~;oleR2Cd&2Q7d-EcU#r%z-
zz$<x^jNcC{w08W%kyOuTLwUhP8Br}0Gh&VMa$nE*98q24NhQ!j?U29*UPz1RF+@j8
z=;NDE&xfrytQ~zU_wynWpSwa!66HYgW6uY1D}2179~_oxrSRA0+xI^w!8(xO@bcKB
z2F|^(g<jr-?Ge5MPsXA$toeKy*s{ef%G|lJPDrEu^A@}_D;l{^wEd0Pv_}ode6L86
z#J;8soefRU*q>^><?WiOcX>h*soCkXIy%GKE|XnWd|ZBSFOryn<uqc`<hj-(0G9Rq
zRYn5}9s`?rI!D6{!MUk#R-cq4aBY+SL)c>`*qdOQ>Ry{g??)(ft1W!(LuF|YrHQg%
zqlN1VvyU%FPnP|@(c@qPCvG<?IdzDeN%szir@V0nI&!rsxV#anM;D5o@Yt0$pXygW
z5hm0V!Z4V8^Xy-<eXuA78t@xtR~keAW09by{JNi>&y%uim^(zO3gwa?h5E6Su+U7*
z_)+}uKHlhwekqwB_CihAMp+6*_f}x%xoPvyA5b~tsEdwC??1xsA#G8>JGF0gfi3qj
zH((T@ohwl;1%JQNc+H#bK`u7$cvnYAiqwv-zF3icsPUuRGatrtldggOn}xt*{`2fa
zXd-Njb}*lNsrz}263$ZaOgPs~X$NVb)0&v>xB1hXr;J#t@1F28DNEbacs^$|S)w`M
zq5%gK>MwuKz0@3Q4g2kEXM31}|1m9n+jnIpHf9uASD#tJK-=Un#X&iJ{J{kXXytg1
z{`41W<zM2&wo>h5G}0folIA0Sg*>Wg0q)<#SmSjXx>Kc1M@%CaEehqIU)6U5Ai>5x
zWk_!RWi`Naohkbvvb9EetbQS|m19Rwkf2~53M`VMn}o<pfs1uQuVVFgT)RqqK86qM
zJ^SJ_70|NZ*#wn~E|AY)Y{4_3E?9hc(N>G@Yxd;+3Eu8^6mNx4CtaO{ODi+In8U^e
zO+hByXm!yp$aSZiYBIB!PLc_A{8NZ(5z3&Zo8J-ga63LwgbT0uD7oag+XkD2AEkLQ
zMwGHiS8M&Tr21WibT#-l?KG1!WQDg+wEm-dI*HJ8pXbAo9InxX-M5MS$kQHxN5NG&
ziX_0SU@{a^wavRYg?A*$T#Ok+*0!1IxkwrcLToaqs9J0?vcr({-Wo=pt8w1Qla_%m
zx;mbLWiBo5K(vua>Sf|kCehd2Dopzm@Vh~*miL_dUd{NU0N|*uI~~o@R!Qy&^!{Fl
z5=zbYrEl&RXPK|bsOIz0t`<v)e;urbt&<)$VtMG~@5+`Jk>pW)-%FKGoZ})0U9<Rr
zS9I4pKmv{nzd!P)eTYSvWC1i>3w?OLQ{JL6rVSQs--tIgiVLX)?{}Bl+VbccaBjC&
z))S#>!{Ny0(l^IwM)ejZRe*<1h|`*?69)s1N8zwv!<ZM1Dg^LVc<jH1Wzg(xCTvx|
zu$vu?^_Ft{>n$X7-;G-N>h(Ox&rTx;X`e+SkI3g-=zC>o2cZrle<B!4KnLfuakc1@
zg#;XraNHwUV<sUwu`lfeIFtkDl|?_r_X<|f4A+|c;(Lv05;Bse;C;4*e#%(bFq)EY
zu)t+I{nYoV(*>;`Bg&&U_QUK^VRWZF07P9QOyix1FQ-bX1#gJ4x|QZPu&9clA}<<y
z=lRs`b!-NZHIrYe&iN93L15Tww;*-Y;+gjDyzhO~bFz8Ttr^RqC@uGuBm~q#*IA(%
ze@x)|?GZK3Jy#C1GR;wwH|KUqzh+}E{|SKzBM+YA{gW<18>1WD<d<T%3>PCAaK--j
z2HK+Rvt#T1BgYK*#v4<~{$lK!NCH6AJ?AOx{>N1;njf==Nd>#4?1EjdZ~MCfkTHdh
zcZQ7&E=6T3a7*i=ZmM2Bz8oKygAHDP4I+&{ty=U{*JL-7Cx7jV9pv`Hgc%j`&~WDk
zj(NXp^ca3O&|$O)f9QpC=Hiy##xHBP`q@lwFHik;HNU%C3z)4ds|5|<^7t92dNCal
zc`a^9L{01gIbHNSsnhg!0Y}gX><%q6{doidPGoGe4?X%x*jF;}t@ZF784IL+`5_!J
z<NG)Rf^Q42z==3PP@d#I%joyF)dDK_V>_0&-YKdDF2=Rn!$z0+s_zmnZ4X<Kmh#YT
zB|!_NY33*%<h3{bX^n>>it|@&4dewP2|n_vW<Udc)&cpA3yqvs$Tzk&Wo-c98%DiN
z%8bqH>_zDbUp}EeE$%NUYwnP=?G^sVwR1H?Gd*1+N)8S&8Fh}gWTuneu=2kK4yMn}
zhrU^l2K1H7J9Qo(Yny{u-5-@We_th1uhH6}3=4FF7;mW1`Pa~Q2)k)iL))99)n}%L
z5fw_C5vU%$!EQuT4tGD^s_)v?_kT3W0nD{5t!H4Z96zmbQXS|fy&RM?*bzdj-pS>n
zSG9a5)dDj2xObz!J&+tFgloZI)HdLWln5NV(LqGJBo{_cZ$3U@$|LS$yY?4N8^^W1
zV33>Jvng7zpk9beny2GX^SKVvfeaT;R1vl?j_(GaD-?~qdqyVhD1J`}RFKHjnG2ys
zU>U(#9EqL<kx_NSUOiR#3mWR+8Yy*C9XQiJJwio6GB|%-3h^7`BuqUoe`vvkQh#02
z^OQY$nD$Uz@BL1XajziS3a@iB90D0PDYuT$2jmamP08fqrXDkjkQYDiW`+vUt3jkQ
z_K|qDA%&PJ%kK}*hoG6x{CsG<qwW38C}Q4>4B|;EMyeJ#4m{-SiHScFUndsUMul3N
zS3l0ohc;eUL(5Qa?~n30SjCp77X&XOslsdfMl`9E?V?DnjWUqjNw~M}ieTbB{uOw(
zL2k4LOqWBCD1Ow19=q%qkBKlR6tLU#*)u#^Vg|rU&(9yb>HHp&qs@7x1<HtaYT^6e
zncwG~5{1vp{d_lxOw4oq&+Llo*#|LHlZbZ~Hgbx~it)EaM4xLW5s&}1r`u)vvCL9j
z*m~8332H&3eW$P+{0-81x%a1iE2k;-qIOn9Qh)yb_<(Qb4rf49S)Iz;%88WED8E}N
zNa^^VyDaZVuw=0TW`(w&1R80TNlU7;?rO)iWrunjF|DUPK?B|sGZt6TspNparH-B=
zOtjSamQddj8}U1|Ezh?_Ct2Mm+Th3ET$qhzzp#^>-~pqUJ2meRpKNCT42{O~2-pYm
zn^*m2#<x1?%Z|8vO_)dH#iJ{rWb8q{g#K1R{|aM#e0xs18Ss2qYnJ%Ij}IaSS}n@=
zH-W2vWa66iQKjG4gISv=lsbxFtYA3#rI%`?Xk-O$$(O0>wb%ls3Oo&iw=%Sdq}9x3
zn5aJ`?$u&&rEU8x@9!i7SsXbXwWXuSnH$8lCDmS&d=9y#7TF?NYbnuj_!IRQF%dHN
zj~pt<VQCqgb1fLaB0svh^b42Qrk|B|bw5r-Nas^(VTs7qO*g;q{l=9sX1#v|{oJBn
z_%J3z%GaZ16M3Od+Kl!~Aif2CK#_~o)PrGCmNYN!o!Zn4s$JAA?Os#F^^0NYcWpoL
zCU8<LMH?)n`aW1{R7?mV+atB_#E#vEGRAt$dnMI1mt*!t3Eel#3zQ!Wy+R~e_bTm%
zDKAW}7~LGD+Azu?-Szos<5h5o57mFhD9jAg1f&hLFqlDcc}Q4E=V$1OstHf^mlDYO
zn6%har>q@ZgiP~FzGD2wz0Ps&@P#6u`K81KTg!bT7~Yt@GSVtqv49jz`DrX!L((Wy
z_<EkgMVisP>mH`Z$E!zABdBP%q@K3uBE4zUW|JR|7laNa^+qcvG=MYYm@TM<Ub`i6
z)0}K6Pn(cUE+g*TwzcLqjXbewn=N3$uS~bF0KzBk89MEs=sOIGjjbfbOGcA<ys`T8
zFP%yB*q74V(?`pweg~nhBkHckO}n`VE+bN6@0qk<o)-K>7eB9pKT(iZo{3B_d!Y(U
z-WRFRtQBC6^42a*9;jfb?^&7m!a3CZ?)79GC^Q4aBnHIB6Ht6o3*>3#;@&XDeK&Jh
zfA%nE1wd9Z8_&sb#cqteGpoiNmavsxTxB$1w%`F%p{Sau$pzvwP}-uq+-+^DgW%1x
z{k?h~zK%_trjLT>47FM67=zIhdc(6kp7J|^oH8@bRe=U{+lijUKdr5BjC)jS{6A68
zg;C|`Q-d@Bb|I^T#id+(I-7?Br+sfQGck((7b!-#vqa!}7eF%%0#<<`-R<j=fuP5=
zhGVQ%+F^;N_KcOqV9MM3?>`&M8I)VV1Z$X-T~<gOnnm(lju53DvglMVb&G_*hypHN
z-eNueb-=8_`x~MBDY1U}$o3hLDz!F)R8&*!o*2&eo4#5f4TE%GVVtMradmB1Ru3is
z>4P}$<XqTGpN|!6oiU%0f_Nxy)k>>WK8_j0nz9XI)_h%N3V3D|4!ZYBTd_wa`Fb6W
z9<XXF7T2ka2D}q&qQ|fZ=hU3C`u446ym7&b(|(O7OryAPZK(|nKvkc)Q=}%Io<3)H
zxtrNeAcFu$hgMAm(Kh|7tNEDb)f8KggQ=7(#!*+MheADTf`x9#T7kzCMl7jYmOK3_
zYg_^sOmi6RiR7U0*%`5>p!w)+%EMLvx1`)Ja*?e`N~lc)KvlTdyDb?<3A2#sBN#M{
z0{7e$o^w7zrEjWNQ15&5@Y#&pZHer>3lCT|fP$eyIBd6p-K|Y7T=b^LOs8>}himK0
zEB^?N2}Y((s=-cro@lNl<DS}8?Na+f6cJS5hv-j~LGCf3x)%)}#OKNrFF!<e2$uXf
z;F75Ja9XB6CP({o##ckg&lXm3S(^GMe1|i~M@)w<k%F#jB(w%+;!$k(xL(D+QoFiA
zbx=FB`UQp7v;pYoQMC8a3XG23>@hK|c?YYr%4)v1yd-Kad1lUp8TT|2MtyVPNI!y6
zf*jd4;mm7L_TlUIeG2lRl;3u(4K#$3$E~blm^7F1I5$^*5g|S_TrmFw%p|O^FOrhS
z699*xgqEMTjoU{JhrGvG_K6FkEvo?qM`apayI2CJd9o-@_w5bOT>IUd-&Rjk4!-lV
zzdKjY{Wf~`)LhU*dBwn;n?XlQO&{O7+^nmFUn95>sl+?-X~j6B5ZSpi1&})zuhc<|
zbzg*Y_qycCjeB0AR$+|q8@}O9@5s!YO&>`!a}Rftck^HKU`ztESj>t~+f_~kx+=HG
zwcc&rX^WIdV*P&oFlW+;sNCoYWBHqz*pol4GkVFAWeNPOe=%zZclJqq=3Uh5)6eC2
zzW<QkSpqoC*vyda4fNGKPPq`D+LM7h+C__2V{mUX|NQuFMkLDMN9*t3$Z?<`YZrA$
zV*mZwn$x0$EXKr(8kweS5YB!y9#~a0`5<tiBwVf;a?-)<^&|8ucaca~O2GQnoqgvW
z{$-`ohZk7SZK0PpcD~KkA71v!s=fbYYW{~Ch<eOop&|0<<3nATY~9d)#q7n_S&$oW
zHeyJ+`&+BuguuHN*q)_IdMu49sdTfET$rSJ^;DFd@bzFwze>E1sZ0&#p=r%T$&~`o
zu5!lRrFax1Cvi9Qfx;E#7Y;>_B}1>skNe*PpNB@0P9D;TJD<rsgso4$HF1aA>pdm;
z_2q=chw&oI)fUSK18Cqxf*g%2+zkFQvkJus@Dy`GoXTc?4JoRcbJb<X{Os&B+sm8V
zy>=YE+*9IH*)tOLG<aTB#}@;54H+85E!4klNMyi*f9Mb_Zn?8)-tX@IFx1LL1nYa5
z8gg>MY*eu)M2TQiu!{=hplgTIbIxr9ci2owtylwb`JgZ3$Z%lO+xfQ}7M)iNwe9A*
zRXEd4EgFu`ln6w-H=vURgiC*tQ+{Hz?xWV+l8+LC7Dj+{!Sk`AW?UmqMM=2W<*`b@
zMcAC`ras+BA>^dn{Z)Tat+TJF7S`T5?f%x_Mi-C@C1nyHNb`RMx;cVz_(+mu0?B7-
z+ir=#29vJV7^@_obfSVo<o;;Ku6kdrfYvxWxZ7^WERTGhsgZC^;X4u0A-uwlqg)PF
zO$Xb)(&pNS+G(IYR`b%mmpPi9N_KUFwFU4=Y<c+C#VcyjZjQQW-LEkOIS=!1UD0C{
z@!rZW;~Ys|e-}yDK;LS^u+<2x%9#xF^&tj=1R(QGX#4E}m8LBz^G%o@a*SN#5JG&)
z2l2$(q#|Yu3we7^&Y$K$+$(Og;?5JNeu4{Dd*8o(<$tb3-S%T1W!}mrf9AivGtLcy
zfm-RcmC!s|h2*Gw-J;X`c6p=FBGc=V6CQLOPXA}>qQgc+9xiY9$FXxoKV*YrF~?AB
z0HDz${H!c%70LMl-N&4s`DoJd+5>g46%CEr^anBFbeFr(8b6pzvl_&3TS20E{qU*t
zVO!Fd(Mdt%CQHMS?Bo-^v{X<^O?HOJM29!FW0&2WY=sNxXI&2mw1dfz<-+(HSxTtf
z6Sp!g3yVsaYZXLI&G`P<QMdVo4<<|AGI)o#rG*E+57ivrmRF2!r%A+MhcEbSSX`8U
zfmR3d`Tr~{`B8wf=RfmWd?^X^F{HHb8mpofa^0EX@@%4;8Ta&Z`1<IH5w&F3it&9P
z8XGn_$fc|jQYn0RC&Cn@-{l$+g2d;FPDRJhgu7whSe9w*K@M|J9G(m(?{B{HI@S21
z+-P6>PRtrYqvYL8&lc~I<$XE)+)l89odwS+y{R9{Rf`wEe<dCtAs$;3G0t04e>7QG
zZ2tmd#i=@W(zE5+f&aM46fd`@DIY*$E}d;SgkD^`A@Cw4+PNe9e)y&0Q{%&wU3nob
z$f=!mKExK{cN)!@D-qhQw7pI<Dt1oSf|dPprt<n$d?=xO-WX5fmkMXiP(9xE4&Jzm
z^GLt#tbmL0-?Y(sXJ%H+jb-}4L!;?>je!z47eiLl$Lzw9_P2AXatUHOT4+@S$+k;d
zPuI)+TxU~Jv+DprbVzb}^0PA@)!N8IJL}WI31C%YuIobH8g3@9!*G*WG>?miScs>T
z*WIwx&$Jj{J_tVFt3DNJz*#ZyJRTNS$M^KOJBxvRa{Q@kujdd{7P-lNS|8BLLAuZ*
zb_v<3USmbE9jabYAgZhT=Q7n~BlGw^w~f_2c0}hBV;#C^nx2B0!v#^ghQyN{aw5}$
zG+c;msaCY9qA6^1YJbyZ+I022rNBGd)3iJ4%#IrF>tf1|eVHvh8bH@m7JsFr%Rs%v
z3-rBLoZ~fo!W1WxLs}z|1MBeKL?#fH;&9zh`@%Bty5(&8O~nJbROYzcI-(T!fqzEb
zbmIC&e)N!x{b9UXpTg&EHo4Y5ZtTrG!TVT9|7Wtccku!9Xh3j3QZ7T`21_~zDbr)K
zITrli%PMO#cncQZZ+YY?n5{SS)q}EG-O{u_s<||nKxZ_OQsOOG-xeMO=XplE3~Ek%
z{BS7b{?JpqmY6$8A&eXtx0$GuJ{n>?^AD~5eqLK!5#md>=XO`>i!=qx%w;IRU0)d`
zBG3Ni-Kgi4h#>Zc`tnjRx~U$!nvDiwEJs?>I`x#QEJ<(zeSh_qS080)qHaOHBW`!_
z^9U^IK84qy1$NXp=CL&{v!)ac%aC_83~IFBf`vL=xHMK=Lc~19E?!Y~*zU2USGcu~
zMCjqB4^(`->7C`Y5^P^2MF+JT`M1dzme<0lrZay0>^U6L#Clr~*V*v(;7$4Vr9AYb
zaqGS=4%aAhMrzTau0^iZXA2y4E>B1zj#BH$g?hwdkly;@&q#w=e5A8ETGX}lkQBJ5
zp}aYb&+<Ou4HsW>BzW^HvB@_oK2qsoYZ*m5l?iWsqQkIHk)Z)&C8HT;m_1BheVTv`
z&YYjAbCz@Ov3@ulc~^UrhlY;OgM@0WfOOR$?3nG#*Wb33?*p5q`DJE+%g)nHnv3?k
zggo~JNEU~dU#0a%Ube3aU5AWlT-CPv6+G&ZLRiY>$S1gvQ2Ya_QCOAwW3?|^!)3Pp
zE_Ws+)7jK%9}AAR-bnbH;BcfM9mqI)VNN9~O)DEO8tuFRsiMRv7?aYU4w6cqOD&}v
z%{ABxfpSe642u0M)l@552|fb82JxgVzuBwr1+jk2#SpQ<ks3CmPK+<wy-PU5<n^Nb
zg}8NM9rU6!fkUwevj+E7AKX05>G>}Yg3~H&Ff@Ccb-{~N7_FXwb#{qf493zx{SEm{
zNw_V2YW9Uux5A6kTQ2BI56Ehu=@hJKANOkYC6Uyxw=Av9f$X%Gs6Q$ZzJ6eyUPWM4
zcdDMBVa4mXTT+mJ(?SK4^OLmKX&gIxp4|y5=VHx}+dAC+(EztyT2I9XRP}^0ivpZ6
z)BldpY{3|jZNGmsk$T4*Oy*y-^fm|8V!_`42iy-5{Zc5u%%@}TQ==Iz7yMleyAiP(
zdsc~uz9u=qP5m4FhX*M$v?6f&pl~&nN$7rSABhu;<zN*vOfor4IUa6}jV#JflmcCw
ze(z_e_~tlVpM)>mxF~u5dl$f?rO%Lbjei(N;hq(Ci-cUoqOY7ijO`Jj2g{msLjqnC
zC$BKd9;IS0DQQk%e{-`$z~6v~I6cyRgmch|El-AQB7{2Ldm0}~G;FgCdVhU`*a`|^
z5e2QCc?t{C_Ah^M%?%hkG|O+pU~A)j<z2-yW0ELNBe^}sB+o`&Ee@SCl&9C|zhc{$
zVO~=K*YUgumqVCW8XhyHVr=Hifo>Xeqmfi}Pkf(RQ@!IntEL`C?Dtw*zUUQj%qlBX
zI@`Iiq#7sWIjH))1V1HM3iepedIKkV0<MM|N0&iodJ{AgD+M=SP7_;3Kef(Tx7R}e
zzD&W3Z?@cIOd`Lc)rqsH?|87=cW84imWvo;&m+?{L$SmX<9U`o@0S@72I+jhwtc4&
zOYwtG#U$d_n_w~#`yFE6r~i^)S>Y%zH)%6kx<+V^hi%#LEY=V6AK_2K5-;WR4WBJB
z@C?BB$c}A+IJuBHb3^YbESxRN#GAK_Zw(4sShZ=XHu~i6S9oa*;NC>@At7rYIH`xc
zdnI*}=^gO5r9KlGYMcDu8S(-RNxh9$kQ?o}QtMf074mMnpa<Pl+oz?_^*ew@$+TDO
zKA8<F%rF|OFetX)aw=w#>T)62VwIx1Cnn8X?W_EmFxF{JY<b+t7OpIs?h&IlA#W((
z>(k2{kOqu-%X$ZGmtPR1uj_su%gS~6v^k$<RJnfV3dDWiBy_n-Jc#8&0NYirNTX2T
z{FV1HuIMM{Z6l3sJw5I49wD_XKJWb52~@mKnJHDH{GpvFp`DtLZkY5>YnBpZtPZ8o
zb1m_OQMBop#YL63{$z9xaL&fEVn8gJm}Mtt$yVyi7Ekj>B>W{J?N$yo3?M~nTui#D
z&(vl=&PNV^yZeasL@#9*i3AkTbd_=}YnCX=Cbk9(wt%#iA#ae2@E6g}FOPWJ@A3!T
zYz+Iu6@+&PfWD3BsuYLo<aYi%#t=Oo2-FVwQ<@etEanON&^;a9!epZ4(Zw?gR8g(0
z()Q)oHA*<3A-syB@4r`|e&kW21e3#KEuyZc9ms7e+p=CzOK*`YKVtcpt-pLk{4O9K
zCqYAqlvyJv7g1`>m!P{-txPmy_5{70=^7R*RJWaGO?(xy(bC?L$-ZkX{1uys9lV?w
zi?2YBSZj?T1TYC1u`Cd?df{`>Rjv*>E&syIs)d$CS7^DGr|l$rEAY8<z>j0;H{9MX
z3Yo8CEY~E$6%etJ8ZXen1%OoE^}kTyi8@!f4c5zjvv-kxW(s1&XZ!JL>bb4tCB!So
zY|q|%m6%Nbv-Up&yfA^+Zlp(@11Y8_T#^^E%fe2yY+<=_Mwb?~I5-_He&U#jc|=vD
z3Y-B<dpEXIV&7*|Bb_9I@yo`7u4J|Wng<##D~BJSP=M*SL?dc6h86{OmxbN?u9;Ua
zh9kKNbUtN!op=dcVo?8beLU0xj59)&#{7jaf!`5GM>!`3ZM49*LL5A)7No<%1!6oy
zZL@h2P}CNu8Vb#`Y6yqkCcx=Iat2M+Bo8sjm8QLZjn<taRjcXz4$72ZtHCYycvUIM
zyWL!qnXXebFz`B;A;$!(8tM8)n+HW59w1h&yqJOPN6oN%EEAMT3#ih|A>EB0-goRL
zDMH%k>+zR@^G<dw@MfUV)QQBX(`~5Rzg|RBm46Aks#$s_+DiK^AtF;ImZ0-4Zcu~m
z4M=9`MCe<Y7)~QWZKaY1+Sw&6s!7D4;)5m@{D}~a#NR@JI!QF8Dm`ZsXxv8zR=9Ub
zX4U@T<LX96u_6R;;C~O`3ro?5%<Trwn5R_Af89not<skyy#rg9k6D5D*WNs_ie!Eq
z+YSkZPrNPP_AqUfPvO$K8<pT*^I^Hr9sqfK)#%QJppF3GXBrmU1+KM4CV6Vt(aRPR
zU~B#6*8=|DHa{9>gRDp2&%M8C7Naqk`pEur=0lWef>|W&gA^VCM&V*8jz8QU$hxkE
zf^MgIW*G{c?soV|2}}Pm8Ly^cA833ZUYFf!>orXitOF|5QsO+}rP@QW(K6u2(FKOy
zJ$bJwRKLnG|F4jG+?Qca-m(xo@dzigr1&>X43-rmmq<ObZI4}qpO#6z@qm<(FRh!g
zU<Y;<ZsUN-otu;uRekzG8^m~MRT1};CCSHYeVgjEoFfQLb#4*UkuVC-#9Fk!=kSQ=
zIqOn6yLp=J@`)$!%eT32wc{j}#A68H_=h@s#Tzs#z|a!m8;8Rw>ggU$iQJ(CAbzvo
zQ2J~g^~*#W@P44e29j6$C@=F9mKN{<G&4M!iL0;QH)$a|uK?0@8utEUwmvtpg~`5$
zhH$uo&byTiw~?GVF#zC`5qg|rghl5Ahm8w~lnbsXrX^*STO6z~44(+lCeX<df`yL|
zO(Vs4lzki7eDQz-7g{T5K=k^}k@Q77ZBS{tR!~6vylwEl(S~{Li5Zf|K$Zt9)piy<
z58vdL6;m^!Ij%noBFk*S<g4Y|-CZVI^@e&Ex9wQ^Lc~&UlMKhuN!9&de3p>qvwCz<
zV_}tF`c(~<+T~!Q$|8({$y>tYonH;T`10gRx*#{|P6Fdf8o<Hc$+nEzu+w4S=V@fz
z*w=v%jJu;QI(>g?7&74(qN7i*PG+cJrGD|)+)0n|bx%(aRK<ozB~Riyt|cVDH3@=C
zN4cjXZ*WpSqwlc*eeiZ}@)t-r&j7!3f<$8TK6H1wV6qSA=2(K4*%_>iSX@XO%Nt%h
z>HIfUDy;XI3&i#izk9v1ofI;rN0JNR@l2Fh=F#rg(FRH1$niO9{+R5O$we={w^`J5
zUuCJemD)$lOaf3WszuZth{O!EoxT!Y4SIW3mk&I@yV-vzd-WZKf{g5jWYM?3kq*%f
z-Jb+YJ4TZ13*Z2tM{*LV^bgm3+HzgZe}LQ)FHn45yOBCsYJYvz39=a?Y5y^EuV`Ku
z`0xJU>>ccK(D!2bvADt1Xa#DSVNP`cR=j$i#F#!&it5R0Rh<eFG;am!cM$tre)e~%
z(dRp>=u2pTt;T={S3g#u&m<CXz2JMFb~5t=F4G<SVL~RsXlN|}3X-Vw(*w!k(<LEw
zy+>@tmIPI+AE6(TrC8mR)A^#MJsA#u^so;1#P}bJ*krJ`Q~TbZpaXQbzBzr1e6)j5
z%2gQ1T7%wPS20c_`cLv%AX!b2*Rr5&3<`67PhnByKDu3q?H@UAw0&0|+Grt>UTpN&
zJo-3EC(s=5^Obob+O0Y>6;m;CZAk^1h8^0XBiuI$!(y@M17ttn<b*;GQ?NkxY<Y=S
zj-oXZTWNYO{dIQM^1x@~8PR?`XVna=+uS2L01%jBcens9fA3s&Sl=KU%HhGu!>%ne
zYf#X+tG3>qi&d$EIcWRa`SxqI-)jM~i4%S&XxD{EH8|0~*QxMalx!O&H!_<oe-Jt7
zuHx#0k|b<4Tqt=pbTlbJ{o`+YNe6+j41I{&C5;o2{>*W0^NY0tev&-UgA?ZHYtgdH
zF|$3T9Zn>RxZI`BHUj)O(L4&3%Ppe96z3%LPdkk>(Bo3GAaD!-D9sUMQf9Hdk2czn
zwOnWSi2!?T%@QwOi;l3VAW6*!-BF1*(OxR1qj>I7&9WcORML)&N$d!UDfWqmzBXDJ
zhnnBH!gCAbTp~YH>NOb(04P64tBbXT;-nJ%y?y?dPKZRlq<*Qetk8LcBA|UCez=Qk
zwO_Gq9)4<aK#Ef0jf4ccS#O1-7gZ{)?$$pl!MIaZwDBgH-b5Ae4Rjw?0D<EH%-Qjk
zre9!e24oBD9?HgtX*_ka_JtUD>kr0Jn_q4ukau@8`w3VgoqS|t;ZN51L%eNfgzfLS
zE6uAJEz&vqgxId9MyMb0AYxBfZNaA@GWmL-iPs&rpubtkY~(o`9en1f>I|7DkV~Lx
z!02;2ZZ4^vKQ3`tf40F8?)?0AB^X~=X0-mIDTfi9?%nLL2d-MWn9T>Q;|ah|o2CIb
ztJL~0-_Ms$!0?jh)CP&8a!{o;_BT;6p@3G7=jq%pvc^uHWzWHVH?M{UXj-snU&*;J
zIe>^q{C=B2FKbA8-`wy*rF{e$kar_8*RkArKVHPn!*rLMW-!%5de04Ff|A)J&Q<ze
zR=cHBGxh?xN4||vj+z5e1ua7sG;$$78qbj$`Lg%Z)+j`286k=T0H+E1I%@N7ZqKr(
zRz4$~y!Fa|zG-m89cQ;(nGRSWe#}ER*V--Y_v2@3U*z%hTrCi+I@3B5TIpf^zWMLy
zyb{mkF9n1K>*|ex=BH43rJhUH3A75_(o~r&4p!&96iIe8FOy?7VfD$su2zmkD`^+u
zw=Z&fp<8V_SBe<bNKt<Y5#4YmOb0Fr8Y476dbt$?_kVBc`OIyQ0FX#09S0tp74h1r
zpNclog0ZK^&h*x@-pr&(OgKj*9qDF9<OSIj1h7;q%4+xGBk1?zp+!+g2IvrTeemmg
z3dHoyKH_%fmjLaN(@|%j?_WN}?MeTpeBSM$M{&S7inakK%YL6nKd93T(o;8fR#I+<
z(L>tV9Cs<!<jnm(nMF+J-Korbc(Bu+nC`bdBr!BkpxqF1_d2)Umlar1@x5a5IfbWU
z2a!>&wLrlDf3G)8thKk5I-hCSF1zcqZFGm@w$ca|G=ZB#1hPzQCxF_5h3oz39XodQ
zH=W<xX}dg3dmbpclc3BdUMZQ6hLH_UWbFnTkpMD}+=Lq~h<LTAn6c1D*O<CZae|NT
zPe`N)Q8PY`CX=%oAcu@K8Dj)`+qh{<Is|26?6>a?@cC{bY1QAWG~#Y*udUFAxXz|S
zWle#d3g%C!^RZ6MiVpUoLFTT?-Z!ewEQT5abuOhsQnGK=?;3=;NIG`vjicTz?}xXV
zp*3xilMZV^J?a~A+A{&ak=(lM3j@A}iTLnJXGuwp^)Eqg4?-t$2|*}gTh_bqMHKC`
zC<>;>?@%#sRABsrV-~8!JV6TM4}NynbMp4+XbGL9yW3;rUx@Ph3&GpKBI>(YHb+Cl
zu^K?u2ni4!n(#d4&?WD8Lh>lA_Ch+fPjMBwy*{>>AaN|ioy}(*P7%X3bw+$oeFE$%
z04qKGeeg|vC3n(NjLXg}+fS4{jDO!&sklhR5|l_5+g-t6YoK2w{^Ps<=F@Mq5_^d8
zIr<4&I*~;K#b~E-REJ=Ns{YPmPF$t+c}+v_+fMi?E&G5AYw4N<r<*WZ*kkbl9~`k<
z55z@=U*2?`mX4vm-UtMM;c!WtdBN7F=F%_5{us#t-;8S8q#pm9kN~tAsYSuw9p{r)
zO9E2mbbF>16flb2@pnb+Q$B+Dx~<DB7w8?qC^^eA4~3fo?RlNBwU#iD+JEHZeZrl7
zaN4BSw|X`|f;Z@f?q%}ta-I^>EhPZD;BocR%jKO|XS&PW8iM`hZdJV03B=JGz-+M7
z5w{Z?&qF7~<x_`9Rkm#l-Rsxtw@&hx+cCE4%tfa-&)&~u@rE?o^L{kNg=F2hqwX3^
z{}`V+X|EX$Od5QIQv>Qlyu05~oMwXly&Iu#6+P-Kt_8}^ENWeF4ja=Djr=i|-Y)r7
zm@5Sy$zEH><fEN^(k7w6Xq7QL_w837x!Ngv-Q6UG^zM;Dgb~&NLLRvO?z@(cNV9Zv
z4Y2@e$tTsOK*Z{0N3_#Q0XT|f%Fl-PK`l}PQav${{m}rgLv_g?icS5^$Fg6`hH(Y?
zhQ-sFcG(e4R(GsgFW=1+J&Wr3=}5SUB8mS=Z6mrJH#zE@75JsXIcQMLHc`c)<CmTQ
z15zKF`P3AC!f_xg)Q_vwj}yLU%Lt0QWkj<t>{FWVAqU+<g6h4_7rspfGEk~)B?JF|
zyvb|?4F#Xa?O|i&TEP2tq4qpn@vY1`)stWb?88F6Y7sD9akvew`iMn7mqj%ZqJ8q=
zFzun>>R8?jag(E`)y+DHK_3ZwjaUm>dMsr_fUNNE>xp&P<1P%l=lw5IWK-;e&9D9P
z;o9F$qkm!ng-Q)hr?|*3r?4h@Ya&=t_>@-&gmJx*bqP*1u)(|prK_Ok6J9SMozB*p
zu?eN12yUU@Zt{(SbeP_oug2m*0Fi9yWYJGFzrmYd0uNc;XV&v_uiVt+5<mQ<qc|~z
zwF(R39|U&@U4;OnIW%LjAO2)U@UwR=a`~UPl5}GnM3!Uty5a(c{Y}gfL{g6GSGEV~
zf;XfbUQWK#%fg;v#)m1y#a)a1V}TFFOMDSK?kz|iN}~iM*=<OE+Pb%6d>6VLO4<uk
zF2o%!;kO4M8ZX=O@2*UMYhOeX=>d$O^7QlpxxdOx=QTY$_K6eH5pRGCRr?#PxXCfw
z`dIl+B2XPo3j`)q5c5pp0|$XhZ$l+Z{idbdNhFe2;q?UkuXQVtm-jmiNf$K=%C?Od
z-9Jz-+|+*OM7w)#^WyAC3Air4@})qmBCzSM1Ot4j>Aoo9i?qhaVg!Ve9x9=1vjlmQ
zb@E?kQ@N|U8p!J5Mx^2X!l5{giR3Rt1@=dO;}y4+@|7Nu2&)sTuI4A)V4Y9s<w_C=
zOU3!fm6G_`#{Ih%Dziha{8q5ms^L^ft@tyz%u(EyI!e&EF+VmVhT_~FV`Z0IvJbsR
zahFF9Dh7nuaw$pHLXb`Vl%24!+6jii_JMZ)#P5%XI%m_$bNaQO-ifdrN`FJXr~~I;
zDi=z+3uwUlv&I$utI#FWbJ7lxnw>L>LR2A8MBH{ar&vt=Jm#B9;dqetWyB48&jt)Z
zSi4T`i3MPOMcvU$EGfueBc``D$fJ5W9O<AOnigObPOrS@$yI{`F!IYr5(bC>Ko9An
z6S?+`b?zzGt^&(nFLNQpY1zVbz3<Gwvk>2weT1@E)pX*f7Nom-Z3gnu4~$t21;h2F
zXe5l7alQjkJ5M95mC|6?7`3w!PMN~5T4v=e>UxXA<wQU=`DOd8WptbGN7omFiLXfW
zoY(X<Ka;|6FC}sk$3HxDC%$76)$#*X<Nm6fwAk9-$S`9ode7NvYe@>9FC+gI+zS`b
zEj^;1tbR=`7fm1gx6LvBVHoskN+4bPS!RA}$7@wU9v*bg^Q^McS#K^nA$jv_G*)lq
z=Ew@@GlS`wU$+~Qs=eCoPQ^3S(YiG>uFA+RV_8N93em+z4k0b$E}A-8d-C33?)%lq
z@(`ij-Ud}$w6b3>zXNEbrEE2<=Y)l0)|>MFz1A|&4!01dhk6T6y411-*G|0{#Bdn-
zlWDssgGW%}{k3RR5cZve34is~=3VuS^Rn;qUD7!G4p?~`j@EBD`yJ1M(Ymy>IMHc_
z^ZB#6x2{KR27;uwb&s#w{MTA!6Ls+nXA<cBz0H2i`W*`02A2J}`E*-bbI5A<WqAR<
z4;VW~JyU&Pg`4*sWj4iqXYoG#&xBzdrU;o~_`DwUuKqYUf2w%LGbf?QE<OEH>)kiL
zdZs)h^~YugcI+jCyQe;$=zvcyT{EmZNk_Dr1Z{h=u$Gj9EV!wt^N1vWjxX3{)Q1h=
z<HfX;+iR`&{e7wN?web2?q>iBBP1;lUjPTd1Z3VqQN9u9Nf2zZ{yR=`8zO&70oF!u
z245Ka0Y!v-*wPfI`JK5*5#XZqxABO+1<S;S0{`Q++Z{bIaGc!Ee?nV=^Bl-+26*r~
z%piM3@&@DVZ5Xpy7s!)Sx@J#52B%YxTI1<6J^$)p%>;xw<swZfEWbU+-<<go3oXEg
z(BRzlF`A5^(a^BVLG1#|zXz84d2J;?BonV?O{!G0+h0fZtQy^@&djqCW$ucf|9%i<
zx_kNM-ijN=k9(2p`<t=7M8%Zkot$&aXg|+*dW`VUm+vg`V-n+oJa<Yb!l6f!Ikj~J
z0JumC=VxF?+*0;*eIz){R{*g^XM7PxV=W^qI;Hl{_$xh@oWB;i;QBabA~u~i=7sN)
zX&?mKzsQu)mcn*u11e2xh=H1+B?tG2b<#rtt;CCWUqms1e-|W1k(XzH3X`tpNNXwa
zB~-tv4U0b1S@=0YuTt4neul2MhRTt(xps^*@=)_}+<6+ldE|4S2$RDUTcP_!(ix(v
zL%D=@VYhIy8@WV!yQsjHm)pR~^4Qk08=;>d5^$&#L~lt2yq?QllOWh?n`_zGX4NsG
zSn!NKvt^WiB(@Fk!Q1|2i3dbQNhA{(Q!ivZ5-<PH9sjDuZ&JFK&sos{OA9DK{dN=s
zw=*3umj;nJ(KBn|PqS&2=EZouTU9wEE2rWxOZ1a^*pdRE;qy~#)<vP`s*d*^VCL=*
zdr}0nQ<wqAY%TNoi*Nmn(<L1%p{BjeDKV4rl-8u>V(t_Y3!^^?nI+FUr68w-{oxlG
zY!4(qHi5Xd8&Elxf@cz8ZZfajVknte<?|T+-~*}X0OEIORmrzxPo-~~|DDVQwUvGi
z9CcDZWpqD^9LoKbv`HShbsz{vgsvWAorVH7C0pHW{B*m3kJE<+T8^#xNcAHa@n2k{
zRHAQ-d`AwrC}aWrY1`TFCPT~|4H%!DMmx>25BE0Sis;)#4P*rVb2x=}CX`}5pS<52
zXtTcPqRVUbcT<u_V{TC>KP+<LYJW8d<+y5&3%iiVQMnonuFDorCsIvj;eHT0(~xo;
zQM5xR1O}^tF^$bMZZJ#O5&~gZ&vO~V58n-yrN`iRYu_@6>&+Nb>K>QhyLI@ra)4oZ
z(#ES|O_1XmK~kvi<~E*FbtpVe7p@cfPMh-a`bCKE=2z>Bd^AhBBG!$YLdEZkgTh3?
zg-8)txk%rFy@<xB+WFX#yxD=0IS%D7L9w+!mi^uh?L_^>_-kn_c|{Xtzoz`o|31_S
zqsj@P`=ux=rK8raebQ(t^*+e!5Vs`>?2ed$Htpb-s7qaq(}F{lP4)Mbk<QPb2+Kf^
zBxXZ-C$%oRzAeSxw{<?dF#u<LO-41Y{@Yoxs}U3au;PXTn7I`zqKu1F5iFuunjY^1
zaTml5oVfScFcn;KHXsi#$k0NHUr;8)YQLTQ)4T&CUcRlB*~nw~5#3pv)5xDPU~EIc
z-)oC7YX~DxL^bl3Vi6@}0)-C(ZsDQ{aa7>p=Tl&<H|!F^xEu4Wm3YyqzDDEEb0HSC
z3j>nv-J<NWVvZ{%_wQ6Wp+7a3_{a(Jjf(hHaC%qrQH3yt$vw31fBg%2i`zec_Nd=j
zAC2!vjjLEOQY@l5#MtiW32vB^b)&|&ZSN(Xly8u};?Y5O?hgNVCt`R734mKk{<*?m
z)NT`4=MQn(`**g=@ixsvOfE#wP2Dn($-pF_T}(#{%#9Uzwhh~C_tw;_-q54*z_vU|
zeSFqlk;0<5x@?!fvwO|YJ(O3BJV$wXo{&V;ew(eido_qL%3FJcI&|Lt7LNe@&%+d~
z{q_J5R<{hb2dSV{pk}H{t+d`~E!d%bj&Waz1Wt7cbyt-`M$iu#0G@&-W674GS187`
z;RSHa_2%5R_1qz`u;}UhiMf_Gbp2V74&<~7+i3*s^!T?A8g$4Lf<|@sdW;kC%FE_L
z&r+Uw*-<COv{V&;uQa7;vHj8GD_CX9jxCok+WJTv@b9xTo`0`*8mL~4#3#&JT<~zV
zU^(Skxfl#3`mI}_opF=u|CgIFb_U3PC{;wT^ZePIsXtd)=1aRp>fKrDQ6Z)oJjVZs
z8g}-<R+I)g+SxPPeixbjWx^W`4_iuIO&+DEpSAPre0X>JhT-{#tzgw2Ux9_A9fbai
z2aFAM6ltd#sLZ<y!ZEBA-5s`ot%0P;^9v!_Zu=j}ypTI80F#yBwW>?dm5ERL#`5RC
zfv57+SuVx|3BHm%s+qwUhePq&?*y>RQS#8Toz~hH`Q$8U?ZzhQ_h&5_9vZRk`a&wL
z#TG`hUJfunzLZEN7Mc9|X;a}xgDS{3+Shl9;I0Ix^p&JNKl6?f`w4Jd{4~8-ckXD1
zGl$SCXquivn8TyjRVkRC$!Gcd{TRAQdSF#fyMQD9QZV=DFHr=&oCzE22fuuvJ>%;4
zUt7XY2}OJ&2!u!7*&1+GjBXs1DU-bzCtZk76Ev%Wj@q5tqn!k{e+gDBeXF?|);OIc
zMK|s3Ga_82oAbL61#;)0!0QikCl)7vWyV-9a1KnHcPRs0k;Qq(R42A_z}yGFWM#tX
z`WfwfyJq0-5K|bSsy%gII;`Yy?rp)n@h*BlRng=fv%$lXrA<?T-BKVk6U2j$;G4I-
z1JK?CRWn%aRdHl1qG1a=qL64wBGsF2)3Q2tzCHe~jZF0H`UO>I6yT^Vbz(sP8j@3c
zvfmL%wi<zzreI-LGn*Vn7Sn$|5ta&rtu3KPR<V6K$EW>wUB+FriF$$KYDoR2tw>BK
ziT1zr74+`|V&Zm{6K7QGRsL(%{^$wDqPDy)VJ8CbxbB^2LN`aMLr9vJXwZM8>Nq8#
z`m9%VM)P>$%`bs$XZ!nh<fo$XERmI^l7<Ni3?>EQE0liBisO@#=|muC!Jq81qS1Pn
zodv-t1LSFc)VtXbr-OC@7jQbqYzn(ZH&8p|AUBgggZ>)=H=0S`m+bR=H?{BIECr)q
z4d%hFW<ee7609)Jg?|ONq>dU5`#{{&Nc`=cIKIy#yQweNW0{LF3B2qA@sPPqUy{H#
z8Tn`5GPELtDWqy27Z)I9@);5ld)E7+IBF@mC+5Ub?kBw*{$kvVPrR{*lYc5Q>@(Mp
zw1~Q4-RXn5_PZ_<i(Dpu{$zXs0OIO@cLAu0>Q}ie|J7|U#J3#W8OxJr^)5+kEwhWS
zwd=Ru-!_gwRg=u;^*(hrv3V%VO262Q{r1)GAQFH45`g%*ZqT`hS&L>(_Lg(VNrkH{
zNEA4&@W&7=qE*D&zW#WSU)-VnI-}5OJy9q5CAXqa1iI)@Ane9W`ApLje&S{U=*1hT
z=hr_ktX5e!xLTAxCrz9l6eSyzMDf-`=i^N+Rx&rqMBRXLGXq&v?#G8L%ZDkt_S4WO
z+ufK08zX3@B_E1e)3At2>j%owyABj%zVTE3E<!S$@$=fFX~BUj>fzc2e3{aJeAUJ3
z^y9VieGEU63pVggnB>rfn2gz=JJDwh3176EuECPPy5vi_*H`AO&SyN|Z+9a&21lr`
zZ5kE+@2lp+v<;;>iH`+ttD8A|d#MpCP3Jb&p;XsX5G25|H`rT8XNju^e-RB#G3T36
z(Ftr{Sl5XgpF7BHtnTML?K&7!#PhB5Pvs(BXrFKx(SHt6a;U*md-tb^@)^Vx1&GkY
zEvYW5_Yz#t)}DHnkzrc!io&$<*Mv6|F(3Nx7(bs?;*gl51oM`y(Q#fb?z`D13i>DO
z2A-g6#bbTTL-9_>cVLk(9Q9hhto^s)Hn|@q1Tgs%;;4V;Y7qrmg3kch<aD;V;XZE2
zA19K3S0N>T5Rbv6p<EKt$}1dRbXY`_|F4F#j*9C0+V~x2U}!`nl#rGV2`Lc)QA#=`
zrBPa%fst+jhYkk;6_60=85l|s<O>Kx*MQOzA|Oc2d-<*P-oMYf>#TdudG_-;ckkUM
z4hqzTZrsA0)Kby+$jQLbhC|WOECC%R(AEhOuF#n85XftJfQ}k{p4uIFL;1=tF@!eJ
zkf(OqE*a=lZq&XTbv@67nSXRBuo5O2cXBw$)Z8n7jbCf*|0@`^>VG}XsKEP=YJ+bj
z6ezTCn|SM4BB7)}j1$#nI0Bre?#wX=5;OM^$H{#heLiB0nI`0$3I;}te<E(K{&jqe
zu`&9?xTAwCc@iLja`L4c$eT@;1koQvaW{+~Pe&>QiQVE3ca~9pd6Z7$V>6f?Ij~m7
zp5ktwa9gOd{lSgc(YG+4)QMQ!+w1oRX9mB@En~^S>4CPiJ@;y1sus7{cQyR=vSPCI
zl#(*rs~Qn$<{rIX2@GVXx7B@~0T7j$fThLBe=z5vwvv1Cl1}WFhsu-HiR+Tw?VX=!
z{3em@n%Dq0gUDu1)dGh2G3D+OiA)=wR=1>2WAcWk?Ruahy}v>2$K6Ak-s|^Gi}dq%
z+(GNG8v3zgq%3$=t++)Tnw?cSmwP!$I<laDTT``1%HU29`VAJRWE6h4$uQSs=BCJg
z3!E62v9v$0C=FP2tVZ*w+GG@0e;11eB?W_~MeG;g!y4PJ_WO~n;fpCcj3EPw8I`aH
zdK7qQO$jeL6{6xZ_7XuqAwo*p)Uz_oL5tAd4M9Z+Pf-brPAX*`%|zv+p9c5N-2p*C
zB7pkCmXJqpl|ZSx6ey_-0xp<Pp-#QuWpDh&wmZbfj!IhJtvZDs@egO^pKXmA8l8|Z
zgl>48N6V$!vzx0c<u4U`xbv|i-@^uqsEVOk1a>&!(;XZBiziWd_t|#`WBa>R)s<C7
zuQw(h<tBc2P@ju_-YsUG-#{yB4llzu`Mc)ds86Y|nna5TpE%pk`dY-+Rp2j9SuNW;
zbDSFEe_EqtnM?ZOgAUu;H_u*zMszOFFz2!eFZdOAn2!FOufj4J-UoE&&%a_kQ`Cd@
z3p$kR`~dx|Ozbm-<iKUdlOcqn6qXKdx&KRwejz4}Nt3ssnX?NVMEJA&BUKLKPYMR+
z7c}G?l^msJbyULuy4jppuN^YJKzuII+s=2|%Kgk9)&!BKF1yG81^%e3gGCTRBLZAA
z{XqG_hKdXz&gglxe2{nibpvW3Mq5JUZl?9sqdI9yCdjS;yvc(qWK4kJl|9dr8)7Qn
ztZ8%|u#{-Le?z4+rkkbrVWi}Y_kTAU2P!f`wKRN4wv<>%kO+fk%p41Gjxw3<E!x_n
zdqeWcMM=kkLrxtU5y>DB?Gwd=;x#FPMj+@S>!1Uc&M6sRZD${4K0WH)!tV8A(0#AZ
zhEx+>f1%_=FRZDzJ6K7OHV-*OQXS-O(MHSrLasVTzm#2K)9?Z@VJzhI(SUoEDanFA
zoDIkl6*#p?F)`=g5fqD87t3A!LsMZ-U1HXC*Ut@Z=XmYQn+H>Ue0&7YDabk{kk(k(
zs=@UuW`*jS#mRwN`Up5Q%Km0H(=5t~FAz!wY>5Di_GETu0J7G(TcPIjA0j14YOTCo
z_bp5;-_y$_+lQ&;ggSd>7EQl}0ydiD^{fNLRH%ymbfnL($z+n>c@-Z9_gbvY<s)ew
zSmxQ1CD-#q9=i&gHEG$f9Fz<NLju?V*Eo)CnDI+wgC5gZ2@f9bt%p%(wPWcH5;k12
z@Ynqh(xKewm$|f;-Uuin_Aq1s9zoZMureZ*drw=+CwQl)mbNPPHE^UF%b5$een%$7
z#!mYK6D6r(O8DkW3+&|PSI3XR&|?ZnYf@le>;z`KdwOi9j56UPUa8#f%7{iG+Ni`A
zn}$6q8xQqPn`j(Zs>qMJKyeiiWAfAM)LAb2G)VbTksq*z0J%Cr4GQ`PS4`$g@cx-M
z1k07OBeuJzB=xPXY-xF4wlHWqe21Njl}vW#(Y$)Oj`N)78NNQVKVv%Owm&kOPePE*
z_G<U`p8-G(@6|kZ*I-R<%@w)W7MIby702S@)O(@~6njH)ZBh1Nw!Fg>D3h3<|Gf)x
z-Lh2fdThf|FQyOK6aollnyhF`4r;W8d!sJjAZ1#%6L=VVg416dE*O5e&Ke`p9r&tt
zU4o2zSxy6_a?j*wo&OM0xK@6ODC~~_D_ChIfUeb<0S@^gJGqya-lGWOhn2ry1t=MQ
z-kuY5`)yBchaJ70FQF|R;FDT_l(Oz#7u=}W$2|j7=DZ1u;}x*eNoBmu;UAK`a+=S9
z8#MXI?&HQsMGVbro}QYvR#)g3Mme-IB!ceH_oVCy62Wzg_Qzbf?^%;O^l@?xHuVU&
z1bxLhu_%%y=oVqftD~|Mu;3=yeQ=<XmlcDT*02;=9kx3gBv{2>!K_~_-UVtk!e5Tx
zc)4^w3#5I59eS|uT$k^nraLF7XYiJ`7Wk@kVI+p<uCL3V$m;eJ_&G(B@T!k34E9|l
zL$vIQNUg_#UpK%n=OJ*)adtY8f@`V<avjZ`8|9LfUrpZ5J(SZpyVi}{Un*+3bmyo1
z$sTvtBF*7U1q^W?y9`wx6}@h>j0w)>D1ERW2C~g%)x|E1PO@ID=2;f9l@cTprXF4R
zf^NckH%k=4G3w3omh5?A(w8t1xv(F918bRM_mra%s2h!9nk^H`_jE0CxE)4BPVi3g
zZ&v&Hg6>CSMY^YCL7VCk>?ZgAv2ugZg-FI{lYh_u1xo&$-^l}BFkWIv6`4zXFl(Ci
zv3a2)QJT)7PesX?a&tJSEH*D<rCYhK*lSDQl<~Gx;nLzw$RtKAm0n&ahmliMb3|79
zY0^S{tY{wZW#v=Rq%o{M47R<XZhR5g&tw+}QOTPKPM~*XxBOXd>eBQ6qrje6-0aJ9
zmVUoTf-Dj=Jl19jf4-Bk%6KM2?{$N|lu(4R%_>ad;LZTP<y1a%ZzmbF(S((b#kFlg
zI^=%)DXUEAl*56caw)dmL;L^LwxwmS>`Ljg7YDa}atJGbS#VfchcjCo6;4qZj!eCI
zR1F8VlvK_NI75<s<;(WPD<v$D^(fy;c>RIi=<qEp9jo&J%OR?UBea-C!9z{$w~Hk1
zVbl3uw(jd5dB{xc#KGs40=bsN@pZaU;OjfOPBrSPBx#_P4ls?UB3Pwwqe6GU`2Vb3
zz&=%^8-D+0nxv%<t!Muuyu82N&1s21-F2t=oz`4WHRpw}iv~TO(KRvF^mj?tJ1zOz
zow&fg79ZL(W5{yB|MwUYQF~HzAZR>^pR;Q?{!G49xB$7MvHI>`W080vf<M_hiUnCt
z>D@wxzU*1DRm4cBh63T46Orc_Ih?lB^NM{#-G6*sA?x9`$L~s>;~5uXz|9vTEXL$^
zW555-6Z^<05SK0+_!$0GWR3IhcFEzzufNXXwcHFT(OCm++&juE%<2M_2dB+0g3a?W
zGB~{q{jzIwu-aE{A!&$IukZaQf*k*{E?PE5h0)cO0y^G=-uV3GEYV{BSxRqEj%qbL
zJC}H5kU#kZCyj(3uKPZHa8HA+5A?$yD)DE_?@tPhM~yWL63e|AtjeJm+L7&5-MAyg
znTMJgu!WeoW!3bFmc`@q<Nx)qSnIj(&59yd7IzAdIb3EeXJqz)cZHIeIGL0qI#CGc
z+*>Pg%5Ppg1u@MJZD@+Jf&xJD7<x;fW1bY)lKrLqmITz59=!gie_SI?2TXUvt*@2e
zZT8z#S;T|*xGrkqayFe9Yo=jnV0sNaqi4^Evv--Nv@Jf@K$<UeyW}C8t&||$M{M(?
z{-`k#tAW*BG`y)nP3BAN8C~|J**I?d)74)uSeIa8A6aSeX@a(`I-eNG2*k6>VrR)U
zXQ+jAnA}&1Ut&<^-ye3`uykRr92JvpWaIH+nCYLru3s#lOvbbPv5u~oyz10wFm0P7
zqGBo1H6=OYOv!LpqxRLWe-6F$GvdF+7Hs*G9O?iDBcA*&N63(7e_y2Ji|un<C}1~s
zfA7j`kNp_4zr`Q;O%K#ycOdmNRib`7!^WgtA~I&U67ddD?P;ofC@vhX4b<r<y^@6*
zX7FBSYsOTNaNd$*neD|S8iH8n(ROgUR}M3GhH@zNqy@$Td(NvNfGagHzDjwQy{6P3
zU0r@a2t)rVq<Z_@6?BcDq|He&gKlVdG%9eZQ=vYd0ryPB+NgZvu=l<&@iggP63Fb0
zVn%$!6j(oIFgX0(9!~MYziHmb_0cQLFPcq`=Ls|48M<$gwPf2~bqlm+0v95ySW<ga
z^gb~Yrcxm03)yrB;*B1lH2`qaA-HzZE#9<E$7^Y8Ugk&S8>fAQQeE~hx~x{`4zFmV
z>+>n_gPjTA97<5LtF1tFpdnL^oOU-Gyjp~?RK;HIGBgX{Z}JwEi+o$$g`QBFpF02N
z(w&knb@)V_{qkD-Rd4oXW<a>6JVYRo0c7w=Ss;`WI1s&i$83E%s7wv*;YxVF`I+sr
zd|f?n=iD9KpovTJimrygZj6Mmv?n&jpbS)gj~3dXY@2SyCU6YqK;bFLRuLvG`{$_3
zZB;YCe|tOoF<T$F!e_%2QcCr@qB!D1JqpxaI@N3sfEI#QOINy8`|9~yxjS;mGio}&
zniqRd>RR*msg~cffZzM1sUXP|4~&>wdRR9!&OA--ln>ZUY^Ic|pBop1udho+`n#@B
zt3M@6^d6qSQ^VbPnSnGM@n$)a{PgP(L@_9y%X5l=c@Ft-3j%5}{lPI~2j)NsODm*B
zs1K`u<q`eYK9p8%I;*g~YK?YqTyT(FA|NHp6yn2X_Cd+)Akb7{cF{hWD<t0Tf7dct
z-2Q@N4`%Nxz)K2jn3X5m*B}F#$Bw<o0b4lI#*5^3uiZ{=i=E@ilWf+KxIX1NTuO2F
zw<_K7RI7&(BY3`$=j>azv2+_A7Hb%p0AXEQh3mg!5G-NaJo)_OJbawrnxUFq1gjWp
z#4)%QAtBT`SM^bu3SLA>Zh84zLrr6TQBT}8CY}u>P?a{~D?G!z8C0H_ZG|@#9?w6a
zU)jQy+|wxba6KV1EAva}MR5UPykB(TOGYF|T3L2y)V5OmSJxiooI1mCJF7gi0m6$+
z!n;RXPYQh`2AYp^Um;G0zv%rLnK0>jRh3H)n+zucD(q1G@Vt;a7RP~-xky?F0SeSE
zb>MDU>=F3UnIl4`Y?r?)%iDfez3}4Iyp%JaVpD>84x@sQZIRL~eY5ercXqM~t|mWZ
zurz27cYg>P>oY;s$x?DkG^7~$eEw3TV2gY-RS|}K1wqpMRZuOsB4cV%E~@?630)Lo
zRGO~z+6pCSJNtrxxo4NFO`5!yWf1Xs)#V3&JoA<LCNkhMFRD6M+c{H@{i9lC$(v3W
z&`9QlX1>X7HI<kDB;HkBbg5S4Te6pxYYbK1d7=E#xikKR<>9A&O;dRZv4DhzSL$OZ
z5`k)+HicA62-okpXVl${xz6P>?}UZ!zCEvBG$k|9)gsOAo|4&jPmly-HRfV_Q3mL!
znbAM8Y9&K0YO^N4578x4VrcuI?0A`0X1y(fZX>xUqjPGhyKj*rGQx|FnM-;Oxy7(G
zazQ+*UTjDZNL?f9yTY08^TJ7u*3&qy{p{$B;--;9VIn<R@yq%g^O5NB$o`EHDt6UY
zlnq(GzkrRbs9^}VDjC!^PSw*pNG%rbe5`;C&2H=+H9TCHKJ9R6<@>qtH+1pu`R|kJ
zw4eR0e}jp*aNB$7?|mB!AH`6RBsz{I%{Mdr08l5LGAaT~-s$mU4Zfs@ZXBe%8$z6$
zgJt7a-Hn7Q7~G{Zgw;rN)U9VwOy{x=6UTpwPd<(B*nCsFXO*8>UxRB}TWXHi@4kyt
zEzLTlZ`0cs0{S-qRgH=cgwkj0##RH}2{Rt`Q*G8l4|tXT!t<+rA{V;=JAq1efXQ^x
ze0@7OL24)hvJO~oiN7r^_ub;(9Q&sN$>A~$M7S{`O9Ozi8Tf(XTy0@&R7@N$>%VB}
zoLTRo5Iw`lLv!g`1~yW|DoKar2azS62Aprg(Y$+fVMV31mBv3DutZyO<OoN%q3#ZZ
z(~VtasU_)&=*2@yj}H4NVJ*}9`;I4CmC*O`5bkt&`A3cwuQwAY1_+9;eWCi?Y#qm=
zOc}a#UoxlM<gnwuk18t^in%TLh)s`NDa$)TlD&xXCcP<SP5-b^eOq0PWAQ?${%WOp
z&?Xu~_egLN?vh!xM5WrrWN8qgTd0zVOR@h$9kZc|>Z1@)3%q%i;o{pGu-dHZ{pr`!
zQ`VFosbmWWS6r(%NpHbR(`i+&Ni|}}!*!LnH?$3Tp*v5qD~XAf>nym$B6G+>niuJD
z3p?`Z%(q0hP>ZqHDLF4S&rh_9RkAP?r0m*egTne>X_hu+g@QX8T!FXXv2K6`m%gAG
zdPDl9A>}a?BIz{nZET_TzG47v1LlUYjoDkN)d(89;2-Qivo?QE50hS>IW^^f-S6m`
z-<T(}{dA&U^JAFf>licL(KB+`>7&+pv0!Yoibi!xLH<ct5gP}`)d>d}z0o_Y^rTI7
zdYJOf!cVzyDMy#kIJ-CZB8V;8&P6hso|4X|Ei6eEC6>n&%;c!D@b!-p0QhJ=c&uKn
HY8Ux`W`BUp
index ed158d16111577ea1503d54d08768a4e93c0dbe0..2756cf0cb3ccf1a2f2371bceb8621550b34346b4
GIT binary patch
literal 10698
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
D&U^_D
index 585c9e897e69cd3e49629be7d8c6a45d25c98ee2..5ce32d6340794cc25e02d8b304401a8fb2ba0c49
GIT binary patch
literal 851
zc$}rv?`slq0LSqQ@fKslJ+u+=eK4OC4Sz)JM^H_hdg9DfY`a9u4J#41*_ciSk&l9?
z;e#Ia=%|Oqegy4DAj}7Di0p@nb7HnO@qvh&CUX;`Pk%rJ_2BN_ci-1>ci+2vetSUK
z+T7kuQB<p1rMQFb%<ttajdi~N`yxg0+SQ7iM(>+qLUKuL6ZY@1v^q-y9f6i$i&Q0D
zBkO<P5x%Y01+Vss?`8Mush1t+YJr0@YiyZ1xa_0Kudh))&4s6*MS>e&gH6P}&YSu4
z&%+KrpHFi}qj8upB9RC=H1wD-xm*r8IQWP#i9`a)vQENOt5ur&va|D$FtJz+xw{Kn
zEEbWe6l^AwL8jBNjYfm!u5@+5hQncGPY>+={ywt18#W$~BYS&cE0qe(eOz3GjYgx$
z<z?7Hp@3Xkf=#E>$dwh?dc96_PrP2(NF;)snu5*e^GKf$HknK!{eIZv<71lpQLDjj
zY-}J;Phm@?67u8(Hk-{ND-}CoLZJ}N-4*^+SglqZJ36{g7`NMvESDb;MyJyu4-ajG
znVg)&1w0;{WHy_TV`H!mhXXl24y#luk)xxqe!m|#zOVqxvMh3L4%X>(BIoB}H5v_a
zb{5v-@nAw^GFXGbfRspJ?RGm-DutEH<w&s@c6xdm6CIDkS}Ydi))uVG<w9<6!)mo!
z<mM*q#KZ(%puqrZGMSKiJ?zNH2p*`Ez0T8L?CiYS_HK2xkEag~e-r%oH}!|%7mcQ=
Wyt>VJo>t*!q13kq6d(F*&wc@wNoyzo
index e07dabc79378158a2d707d5e41140206593cef94..348bfcf55e548fb317f12c24c11c8d806d26792d
GIT binary patch
literal 877
zc%17D@N?(olHy`uVBq!ia0vp^6F``Q4M;wBd$farf!V~<#WAE}&f80d+Uk4^?S<1F
zCpoftvPr5)rc5Yl=9$D}<Y9C|;iN*d3YZfEmAwlS@R;t{c3+`3|L6YJ=zq0s&qZzK
zF9L!;r|bUwm^f+Dq$yLUE?u-pDKs>6<;s<wv9YmIQc{<$T$wU$+O#cOw=P}0Sb6T;
zxi8<n^UKfA|MmN~v4zEqhYuh2^!26H)Ywd(JbC84dFS4|$$9qt`Q{xvW;}iRbj`YT
zX|=Vs`|JLmx^^wBtgLL)_U+S~o10~1WuHEIqLQATZfS3S{^`@yb?es`S69#e{rk6#
zo!zsaKP}g<U;p#-bALNKyC4mbPhY=IegFQw2v=)OUEQ<4e{F&GS5{V@ynH!WPEM|<
zynOoX*|TF}W1l{Hq_n^O|GAehGyD4ciz_N-{Qdj)&AWGL)z#KO6*q6(IC1M%R9INp
zrY&2hw6?aMIeRuWEln*rIC$oa87FSsh;VarGcqxm(%9Je<?Gj_%a^MI!{F!7&*`^r
z-@bh7)~sjGo|%}LE!(%xZtK>qE7z^_OHEBZb^3I0RMf0Tj~><k`nI2ei9?}blo%w$
dg8$+R_D?SI6mVz`kOpQW22WQ%mvv4FO#txFDWL!W
--- a/toolkit/components/places/tests/favicons/test_expireAllFavicons.js
+++ b/toolkit/components/places/tests/favicons/test_expireAllFavicons.js
@@ -7,27 +7,27 @@
 const TEST_PAGE_URI = NetUtil.newURI("http://example.com/");
 const BOOKMARKED_PAGE_URI = NetUtil.newURI("http://example.com/bookmarked");
 
 add_task(function* test_expireAllFavicons() {
   // Add a visited page.
   yield PlacesTestUtils.addVisits({ uri: TEST_PAGE_URI, transition: TRANSITION_TYPED });
 
   // Set a favicon for our test page.
-  yield promiseSetIconForPage(TEST_PAGE_URI, SMALLPNG_DATA_URI);
+  yield setFaviconForPage(TEST_PAGE_URI, SMALLPNG_DATA_URI);
 
   // Add a page with a bookmark.
   yield PlacesUtils.bookmarks.insert({
     parentGuid: PlacesUtils.bookmarks.toolbarGuid,
     url: BOOKMARKED_PAGE_URI,
     title: "Test bookmark"
   });
 
   // Set a favicon for our bookmark.
-  yield promiseSetIconForPage(BOOKMARKED_PAGE_URI, SMALLPNG_DATA_URI);
+  yield setFaviconForPage(BOOKMARKED_PAGE_URI, SMALLPNG_DATA_URI);
 
   // Start expiration only after data has been saved in the database.
   let promise = promiseTopicObserved(PlacesUtils.TOPIC_FAVICONS_EXPIRED);
   PlacesUtils.favicons.expireAllFavicons();
   yield promise;
 
   // Check that the favicons for the pages we added were removed.
   yield promiseFaviconMissingForPage(TEST_PAGE_URI);
--- a/toolkit/components/places/tests/favicons/test_favicons_conversions.js
+++ b/toolkit/components/places/tests/favicons/test_favicons_conversions.js
@@ -76,19 +76,19 @@ add_test(function test_storing_a_normal_
   // 32x32 png, 344 bytes.
   // optimized: no
   checkFaviconDataConversion("favicon-normal32.png", "image/png", 344,
                              false, false, run_next_test);
 });
 
 add_test(function test_storing_a_big_16x16_icon() {
   //  in: 16x16 ico, 1406 bytes.
-  // optimized: no
+  // optimized: yes
   checkFaviconDataConversion("favicon-big16.ico", "image/x-icon", 1406,
-                             false, false, run_next_test);
+                             true, false, run_next_test);
 });
 
 add_test(function test_storing_an_oversize_4x4_icon() {
   //  in: 4x4 jpg, 4751 bytes.
   // optimized: yes
   checkFaviconDataConversion("favicon-big4.jpg", "image/jpeg", 4751,
                              true, false, run_next_test);
 });
--- a/toolkit/components/places/tests/favicons/test_replaceFaviconData.js
+++ b/toolkit/components/places/tests/favicons/test_replaceFaviconData.js
@@ -69,16 +69,17 @@ add_task(function* test_replaceFaviconDa
 
   iconsvc.replaceFaviconData(favicon.uri, favicon.data, favicon.data.length,
     favicon.mimetype);
 
   let deferSetAndFetchFavicon = Promise.defer();
   iconsvc.setAndFetchFaviconForPage(pageURI, favicon.uri, true,
     PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE,
     function test_replaceFaviconData_validHistoryURI_check(aURI, aDataLen, aData, aMimeType) {
+dump("GOT " + aMimeType + "\n");
       checkCallbackSucceeded(aMimeType, aData, favicon.mimetype, favicon.data);
       checkFaviconDataForPage(
         pageURI, favicon.mimetype, favicon.data,
         function test_replaceFaviconData_validHistoryURI_callback() {
           favicon.file.remove(false);
           deferSetAndFetchFavicon.resolve();
         });
     }, systemPrincipal);
@@ -185,51 +186,32 @@ add_task(function* test_replaceFaviconDa
     }, systemPrincipal);
   yield deferSetAndFetchFavicon.promise;
 
   yield PlacesTestUtils.clearHistory();
 });
 
 add_task(function* test_replaceFaviconData_badInputs() {
   do_print("test replaceFaviconData to throw on bad inputs");
-
-  let favicon = createFavicon("favicon8.png");
-
-  let ex = null;
-  try {
-    iconsvc.replaceFaviconData(
-      favicon.uri, favicon.data, favicon.data.length, "");
-  } catch (e) {
-    ex = e;
-  } finally {
-    do_check_true(!!ex);
-  }
+  let icon = createFavicon("favicon8.png");
 
-  ex = null;
-  try {
-    iconsvc.replaceFaviconData(
-      null, favicon.data, favicon.data.length, favicon.mimeType);
-  } catch (e) {
-    ex = e;
-  } finally {
-    do_check_true(!!ex);
-  }
+  Assert.throws(
+    () => iconsvc.replaceFaviconData(icon.uri, icon.data, icon.data.length, ""),
+    /NS_ERROR_ILLEGAL_VALUE/);
+  Assert.throws(
+    () => iconsvc.replaceFaviconData(icon.uri, icon.data, icon.data.length, "not-an-image"),
+    /NS_ERROR_ILLEGAL_VALUE/);
+  Assert.throws(
+    () => iconsvc.replaceFaviconData(null, icon.data, icon.data.length, icon.mimetype),
+    /NS_ERROR_ILLEGAL_VALUE/);
+  Assert.throws(
+    () => iconsvc.replaceFaviconData(icon.uri, null, 0, icon.mimetype),
+    /NS_ERROR_ILLEGAL_VALUE/);
 
-  ex = null;
-  try {
-    iconsvc.replaceFaviconData(
-      favicon.uri, null, 0, favicon.mimeType);
-  } catch (e) {
-    ex = e;
-  } finally {
-    do_check_true(!!ex);
-  }
-
-  favicon.file.remove(false);
-
+  icon.file.remove(false);
   yield PlacesTestUtils.clearHistory();
 });
 
 add_task(function* test_replaceFaviconData_twiceReplace() {
   do_print("test replaceFaviconData on multiple replacements");
 
   let pageURI = uri("http://test5.bar/");
   yield PlacesTestUtils.addVisits(pageURI);
rename from toolkit/components/places/tests/unit/test_svg_favicon.js
rename to toolkit/components/places/tests/favicons/test_svg_favicon.js
--- a/toolkit/components/places/tests/unit/test_svg_favicon.js
+++ b/toolkit/components/places/tests/favicons/test_svg_favicon.js
@@ -4,20 +4,21 @@ add_task(function* () {
   // First, add a history entry or else Places can't save a favicon.
   yield PlacesTestUtils.addVisits({
     uri: PAGEURI,
     transition: TRANSITION_LINK,
     visitDate: Date.now() * 1000
   });
 
   yield new Promise(resolve => {
-    function onSetComplete(aURI, aDataLen, aData, aMimeType) {
+    function onSetComplete(aURI, aDataLen, aData, aMimeType, aWidth) {
       equal(aURI.spec, SMALLSVG_DATA_URI.spec, "setFavicon aURI check");
       equal(aDataLen, 263, "setFavicon aDataLen check");
       equal(aMimeType, "image/svg+xml", "setFavicon aMimeType check");
+      dump(aWidth);
       resolve();
     }
 
     PlacesUtils.favicons.setAndFetchFaviconForPage(PAGEURI, SMALLSVG_DATA_URI,
                                                    false,
                                                    PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE,
                                                    onSetComplete,
                                                    Services.scriptSecurityManager.getSystemPrincipal());
--- a/toolkit/components/places/tests/favicons/xpcshell.ini
+++ b/toolkit/components/places/tests/favicons/xpcshell.ini
@@ -1,14 +1,15 @@
 [DEFAULT]
 head = head_favicons.js
 skip-if = toolkit == 'android'
 support-files =
   expected-favicon-big32.jpg.png
   expected-favicon-big4.jpg.png
+  expected-favicon-big16.ico.png
   expected-favicon-big48.ico.png
   expected-favicon-big64.png.png
   expected-favicon-scale160x3.jpg.png
   expected-favicon-scale3x160.jpg.png
   favicon-big16.ico
   favicon-big32.jpg
   favicon-big4.jpg
   favicon-big48.ico
@@ -22,8 +23,9 @@ support-files =
 [test_favicons_conversions.js]
 [test_getFaviconDataForPage.js]
 [test_getFaviconURLForPage.js]
 [test_moz-anno_favicon_mime_type.js]
 [test_page-icon_protocol.js]
 [test_query_result_favicon_changed_on_child.js]
 [test_replaceFaviconData.js]
 [test_replaceFaviconDataFromDataURL.js]
+[test_svg_favicon.js]
--- a/toolkit/components/places/tests/head_common.js
+++ b/toolkit/components/places/tests/head_common.js
@@ -814,33 +814,16 @@ function promiseIsURIVisited(aURI) {
 
   PlacesUtils.asyncHistory.isURIVisited(aURI, function(unused, aIsVisited) {
     deferred.resolve(aIsVisited);
   });
 
   return deferred.promise;
 }
 
-/**
- * Asynchronously set the favicon associated with a page.
- * @param aPageURI
- *        The page's URI
- * @param aIconURI
- *        The URI of the favicon to be set.
- */
-function promiseSetIconForPage(aPageURI, aIconURI) {
-  let deferred = Promise.defer();
-  PlacesUtils.favicons.setAndFetchFaviconForPage(
-    aPageURI, aIconURI, true,
-    PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE,
-    () => { deferred.resolve(); },
-    Services.scriptSecurityManager.getSystemPrincipal());
-  return deferred.promise;
-}
-
 function checkBookmarkObject(info) {
   do_check_valid_places_guid(info.guid);
   do_check_valid_places_guid(info.parentGuid);
   Assert.ok(typeof info.index == "number", "index should be a number");
   Assert.ok(info.dateAdded.constructor.name == "Date", "dateAdded should be a Date");
   Assert.ok(info.lastModified.constructor.name == "Date", "lastModified should be a Date");
   Assert.ok(info.lastModified >= info.dateAdded, "lastModified should never be smaller than dateAdded");
   Assert.ok(typeof info.type == "number", "type should be a number");
@@ -868,8 +851,56 @@ function compareAscending(a, b) {
     return -1;
   }
   return 0;
 }
 
 function sortBy(array, prop) {
   return array.sort((a, b) => compareAscending(a[prop], b[prop]));
 }
+
+/**
+ * Asynchronously set the favicon associated with a page.
+ * @param page
+ *        The page's URL
+ * @param icon
+ *        The URL of the favicon to be set.
+ */
+function setFaviconForPage(page, icon) {
+  let pageURI = page instanceof Ci.nsIURI ? page
+                                          : NetUtil.newURI(new URL(page).href);
+  let iconURI = icon instanceof Ci.nsIURI ? icon
+                                          : NetUtil.newURI(new URL(icon).href);
+  return new Promise(resolve => {
+    PlacesUtils.favicons.setAndFetchFaviconForPage(
+      pageURI, iconURI, true,
+      PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE,
+      resolve,
+      Services.scriptSecurityManager.getSystemPrincipal()
+    );
+  });
+}
+
+/**
+ * Asynchronously compares contents from 2 favicon urls.
+ */
+function* compareFavicons(icon1, icon2, msg) {
+  icon1 = new URL(icon1 instanceof Ci.nsIURI ? icon1.spec : icon1);
+  icon2 = new URL(icon2 instanceof Ci.nsIURI ? icon2.spec : icon2);
+
+  function getIconData(icon) {
+    new Promise((resolve, reject) => {
+      NetUtil.asyncFetch({
+        uri: icon.href, loadUsingSystemPrincipal: true,
+        contentPolicyType: Ci.nsIContentPolicy.TYPE_INTERNAL_IMAGE_FAVICON
+      }, function(inputStream, status) {
+          if (!Components.isSuccessCode(status))
+            reject();
+          let size = inputStream.available();
+          resolve(NetUtil.readInputStreamToString(inputStream, size));
+      });
+    });
+  }
+
+  let data1 = yield getIconData(icon1);
+  let data2 = yield getIconData(icon2);
+  Assert.deepEqual(data1, data2, msg);
+}
--- a/toolkit/components/places/tests/history/test_remove.js
+++ b/toolkit/components/places/tests/history/test_remove.js
@@ -347,13 +347,16 @@ add_task(function* test_orphans() {
   PlacesUtils.favicons.setAndFetchFaviconForPage(
     uri, SMALLPNG_DATA_URI, true, PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE,
     null, Services.scriptSecurityManager.getSystemPrincipal());
   PlacesUtils.annotations.setPageAnnotation(uri, "test", "restval", 0,
                                             PlacesUtils.annotations.EXPIRE_NEVER);
 
   yield PlacesUtils.history.remove(uri);
   Assert.ok(!(yield PlacesTestUtils.isPageInDB(uri)), "Page should have been removed");
+
   let db = yield PlacesUtils.promiseDBConnection();
   let rows = yield db.execute(`SELECT (SELECT count(*) FROM moz_annos) +
-                                      (SELECT count(*) FROM moz_favicons) AS count`);
+                                      (SELECT count(*) FROM moz_icons) +
+                                      (SELECT count(*) FROM moz_pages_w_icons) +
+                                      (SELECT count(*) FROM moz_icons_to_pages) AS count`);
   Assert.equal(rows[0].getResultByName("count"), 0, "Should not find orphans");
 });
--- a/toolkit/components/places/tests/history/test_removeVisitsByFilter.js
+++ b/toolkit/components/places/tests/history/test_removeVisitsByFilter.js
@@ -335,11 +335,13 @@ add_task(function* test_orphans() {
   PlacesUtils.annotations.setPageAnnotation(uri, "test", "restval", 0,
                                             PlacesUtils.annotations.EXPIRE_NEVER);
 
   yield PlacesUtils.history.removeVisitsByFilter({ beginDate: new Date(1999, 9, 9, 9, 9),
                                                    endDate: new Date() });
   Assert.ok(!(yield PlacesTestUtils.isPageInDB(uri)), "Page should have been removed");
   let db = yield PlacesUtils.promiseDBConnection();
   let rows = yield db.execute(`SELECT (SELECT count(*) FROM moz_annos) +
-                                      (SELECT count(*) FROM moz_favicons) AS count`);
+                                      (SELECT count(*) FROM moz_icons) +
+                                      (SELECT count(*) FROM moz_pages_w_icons) +
+                                      (SELECT count(*) FROM moz_icons_to_pages) AS count`);
   Assert.equal(rows[0].getResultByName("count"), 0, "Should not find orphans");
 });
--- a/toolkit/components/places/tests/unifiedcomplete/head_autocomplete.js
+++ b/toolkit/components/places/tests/unifiedcomplete/head_autocomplete.js
@@ -19,20 +19,16 @@ Cu.import("resource://testing-common/htt
   let uri = Services.io.newFileURI(commonFile);
   Services.scriptloader.loadSubScript(uri.spec, this);
 }
 
 // Put any other stuff relative to this test folder below.
 
 const TITLE_SEARCH_ENGINE_SEPARATOR = " \u00B7\u2013\u00B7 ";
 
-function run_test() {
-  run_next_test();
-}
-
 function* cleanup() {
   Services.prefs.clearUserPref("browser.urlbar.autocomplete.enabled");
   Services.prefs.clearUserPref("browser.urlbar.autoFill");
   Services.prefs.clearUserPref("browser.urlbar.autoFill.typed");
   Services.prefs.clearUserPref("browser.urlbar.autoFill.searchEngines");
   let suggestPrefs = [
     "history",
     "bookmark",
@@ -106,17 +102,17 @@ AutoCompleteInput.prototype = {
   onTextEntered: () => false,
   onTextReverted: () => false,
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIAutoCompleteInput])
 }
 
 // A helper for check_autocomplete to check a specific match against data from
 // the controller.
-function _check_autocomplete_matches(match, result) {
+function* _check_autocomplete_matches(match, result) {
   let { uri, title, tags, style } = match;
   if (tags)
     title += " \u2013 " + tags.sort().join(", ");
   if (style)
     style = style.sort();
   else
     style = ["favicon"];
 
@@ -128,18 +124,19 @@ function _check_autocomplete_matches(mat
 
   let actualStyle = result.style.split(/\s+/).sort();
   if (style)
     Assert.equal(actualStyle.toString(), style.toString(), "Match should have expected style");
   if (uri.spec.startsWith("moz-action:")) {
     Assert.ok(actualStyle.includes("action"), "moz-action results should always have 'action' in their style");
   }
 
-  if (match.icon)
-    Assert.equal(result.image, match.icon, "Match should have expected image");
+  if (match.icon) {
+    yield compareFavicons(result.image, match.icon, "Match should have the expected icon");
+  }
 
   return true;
 }
 
 function* check_autocomplete(test) {
   // At this point frecency could still be updating due to latest pages
   // updates.
   // This is not a problem in real life, but autocomplete tests should
@@ -202,17 +199,17 @@ function* check_autocomplete(test) {
         do_print("Checking first match is first autocomplete entry")
         let result = {
           value: controller.getValueAt(0),
           comment: controller.getCommentAt(0),
           style: controller.getStyleAt(0),
           image: controller.getImageAt(0),
         }
         do_print(`First match is "${result.value}", "${result.comment}"`);
-        Assert.ok(_check_autocomplete_matches(matches[0], result), "first item is correct");
+        Assert.ok(yield _check_autocomplete_matches(matches[0], result), "first item is correct");
         do_print("Checking rest of the matches");
       }
 
       for (let i = firstIndexToCheck; i < controller.matchCount; i++) {
         let result = {
           value: controller.getValueAt(i),
           comment: controller.getCommentAt(i),
           style: controller.getStyleAt(i),
@@ -221,17 +218,17 @@ function* check_autocomplete(test) {
         do_print(`Found value: "${result.value}", comment: "${result.comment}", style: "${result.style}" in results...`);
         let lowerBound = test.checkSorting ? i : firstIndexToCheck;
         let upperBound = test.checkSorting ? i + 1 : matches.length;
         let found = false;
         for (let j = lowerBound; j < upperBound; ++j) {
           // Skip processed expected results
           if (matches[j] == undefined)
             continue;
-          if (_check_autocomplete_matches(matches[j], result)) {
+          if (yield _check_autocomplete_matches(matches[j], result)) {
             do_print("Got a match at index " + j + "!");
             // Make it undefined so we don't process it again
             matches[j] = undefined;
             found = true;
             break;
           }
         }
 
@@ -427,29 +424,16 @@ function makeExtensionMatch(extra = {}) 
       content: extra.content,
       keyword: extra.keyword,
     }),
     title: extra.description,
     style,
   };
 }
 
-function setFaviconForHref(href, iconHref) {
-  return new Promise(resolve => {
-    PlacesUtils.favicons.setAndFetchFaviconForPage(
-      NetUtil.newURI(href),
-      NetUtil.newURI(iconHref),
-      true,
-      PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE,
-      resolve,
-      Services.scriptSecurityManager.getSystemPrincipal()
-    );
-  });
-}
-
 function makeTestServer(port = -1) {
   let httpServer = new HttpServer();
   httpServer.start(port);
   do_register_cleanup(() => httpServer.stop(() => {}));
   return httpServer;
 }
 
 function* addTestEngine(basename, httpServer = undefined) {
--- a/toolkit/components/places/tests/unifiedcomplete/test_autofill_default_behavior.js
+++ b/toolkit/components/places/tests/unifiedcomplete/test_autofill_default_behavior.js
@@ -19,19 +19,19 @@ add_task(function* test_default_behavior
     { uri: uri2, title: "visited" },
     { uri: uri4, title: "tpbk", transition: TRANSITION_TYPED },
     { uri: uri6, title: "secure", transition: TRANSITION_TYPED },
   ]);
   yield addBookmark( { uri: uri3, title: "bookmarked" } );
   yield addBookmark( { uri: uri4, title: "tpbk" } );
   yield addBookmark( { uri: uri5, title: "title", tags: ["foo"] } );
 
-  yield setFaviconForHref(uri1.spec, "chrome://global/skin/icons/info.svg");
-  yield setFaviconForHref(uri3.spec, "chrome://global/skin/icons/error-16.png");
-  yield setFaviconForHref(uri6.spec, "chrome://global/skin/icons/question-16.png");
+  yield setFaviconForPage(uri1, "chrome://global/skin/icons/info.svg");
+  yield setFaviconForPage(uri3, "chrome://global/skin/icons/error-16.png");
+  yield setFaviconForPage(uri6, "chrome://global/skin/icons/question-16.png");
 
   // RESTRICT TO HISTORY.
   Services.prefs.setBoolPref("browser.urlbar.suggest.history", true);
   Services.prefs.setBoolPref("browser.urlbar.suggest.history.onlyTyped", false);
   Services.prefs.setBoolPref("browser.urlbar.suggest.bookmark", false);
 
   do_print("Restrict history, common visit, should not autoFill");
   yield check_autocomplete({
@@ -218,18 +218,18 @@ add_task(function* test_default_behavior
   yield PlacesTestUtils.addVisits([
     { uri: uri1, title: "typed", transition: TRANSITION_TYPED },
     { uri: uri2, title: "visited" },
     { uri: uri4, title: "tpbk", transition: TRANSITION_TYPED },
   ]);
   yield addBookmark( { uri: uri3, title: "bookmarked" } );
   yield addBookmark( { uri: uri4, title: "tpbk" } );
 
-  yield setFaviconForHref(uri1.spec, "chrome://global/skin/icons/info.svg");
-  yield setFaviconForHref(uri3.spec, "chrome://global/skin/icons/error-16.png");
+  yield setFaviconForPage(uri1, "chrome://global/skin/icons/info.svg");
+  yield setFaviconForPage(uri3, "chrome://global/skin/icons/error-16.png");
 
   // RESTRICT TO HISTORY.
   Services.prefs.setBoolPref("browser.urlbar.suggest.history", true);
   Services.prefs.setBoolPref("browser.urlbar.suggest.history.onlyTyped", false);
   Services.prefs.setBoolPref("browser.urlbar.suggest.bookmark", false);
   Services.prefs.setBoolPref("browser.urlbar.autoFill.typed", true);
   Services.prefs.setBoolPref("browser.urlbar.autoFill.searchEngines", false);
 
--- a/toolkit/components/places/tests/unifiedcomplete/test_typed.js
+++ b/toolkit/components/places/tests/unifiedcomplete/test_typed.js
@@ -6,31 +6,31 @@
 // ensure autocomplete is able to dinamically switch behavior.
 
 const FAVICON_HREF = NetUtil.newURI(do_get_file("../favicons/favicon-normal16.png")).spec;
 
 add_task(function* test_domain() {
   do_print("Searching for domain should autoFill it");
   Services.prefs.setBoolPref("browser.urlbar.autoFill.typed", false);
   yield PlacesTestUtils.addVisits(NetUtil.newURI("http://mozilla.org/link/"));
-  yield setFaviconForHref("http://mozilla.org/link/", FAVICON_HREF);
+  yield setFaviconForPage("http://mozilla.org/link/", FAVICON_HREF);
   yield check_autocomplete({
     search: "moz",
     autofilled: "mozilla.org/",
     completed: "mozilla.org/",
     icon: "moz-anno:favicon:" + FAVICON_HREF
   });
   yield cleanup();
 });
 
 add_task(function* test_url() {
   do_print("Searching for url should autoFill it");
   Services.prefs.setBoolPref("browser.urlbar.autoFill.typed", false);
   yield PlacesTestUtils.addVisits(NetUtil.newURI("http://mozilla.org/link/"));
-  yield setFaviconForHref("http://mozilla.org/link/", FAVICON_HREF);
+  yield setFaviconForPage("http://mozilla.org/link/", FAVICON_HREF);
   yield check_autocomplete({
     search: "mozilla.org/li",
     autofilled: "mozilla.org/link/",
     completed: "http://mozilla.org/link/",
     icon: "moz-anno:favicon:" + FAVICON_HREF
   });
   yield cleanup();
 });
--- a/toolkit/components/places/tests/unit/test_history_clear.js
+++ b/toolkit/components/places/tests/unit/test_history_clear.js
@@ -133,18 +133,27 @@ add_task(function* test_history_clear() 
     `SELECT h.id FROM moz_places h WHERE
        url_hash NOT BETWEEN hash('place', 'prefix_lo') AND hash('place', 'prefix_hi')
        AND NOT EXISTS (SELECT id FROM moz_bookmarks WHERE fk = h.id) LIMIT 1`);
   do_check_false(stmt.executeStep());
   stmt.finalize();
 
   // Check that we only have favicons for retained places
   stmt = mDBConn.createStatement(
-    `SELECT f.id FROM moz_favicons f WHERE NOT EXISTS
-       (SELECT id FROM moz_places WHERE favicon_id = f.id) LIMIT 1`);
+    `SELECT 1
+     FROM moz_pages_w_icons
+     LEFT JOIN moz_places h ON url_hash = page_url_hash AND url = page_url
+     WHERE h.id ISNULL`);
+  do_check_false(stmt.executeStep());
+  stmt.finalize();
+  stmt = mDBConn.createStatement(
+    `SELECT 1
+     FROM moz_icons WHERE id NOT IN (
+       SELECT icon_id FROM moz_icons_to_pages
+     )`);
   do_check_false(stmt.executeStep());
   stmt.finalize();
 
   // Check that we only have annotations for retained places
   stmt = mDBConn.createStatement(
     `SELECT a.id FROM moz_annos a WHERE NOT EXISTS
        (SELECT id FROM moz_places WHERE id = a.place_id) LIMIT 1`);
   do_check_false(stmt.executeStep());
--- a/toolkit/components/places/tests/unit/test_preventive_maintenance.js
+++ b/toolkit/components/places/tests/unit/test_preventive_maintenance.js
@@ -31,28 +31,44 @@ var defaultBookmarksMaxId = 0;
 function cleanDatabase() {
   mDBConn.executeSimpleSQL("DELETE FROM moz_places");
   mDBConn.executeSimpleSQL("DELETE FROM moz_historyvisits");
   mDBConn.executeSimpleSQL("DELETE FROM moz_anno_attributes");
   mDBConn.executeSimpleSQL("DELETE FROM moz_annos");
   mDBConn.executeSimpleSQL("DELETE FROM moz_items_annos");
   mDBConn.executeSimpleSQL("DELETE FROM moz_inputhistory");
   mDBConn.executeSimpleSQL("DELETE FROM moz_keywords");
-  mDBConn.executeSimpleSQL("DELETE FROM moz_favicons");
+  mDBConn.executeSimpleSQL("DELETE FROM moz_icons");
+  mDBConn.executeSimpleSQL("DELETE FROM moz_pages_w_icons");
   mDBConn.executeSimpleSQL("DELETE FROM moz_bookmarks WHERE id > " + defaultBookmarksMaxId);
 }
 
 function addPlace(aUrl, aFavicon) {
+  let href = new URL(aUrl || "http://www.mozilla.org").href;
   let stmt = mDBConn.createStatement(
-    "INSERT INTO moz_places (url, url_hash, favicon_id) VALUES (:url, hash(:url), :favicon)");
-  stmt.params["url"] = aUrl || "http://www.mozilla.org";
-  stmt.params["favicon"] = aFavicon || null;
+    "INSERT INTO moz_places (url, url_hash) VALUES (:url, hash(:url))");
+  stmt.params["url"] = href;
   stmt.execute();
   stmt.finalize();
-  return mDBConn.lastInsertRowID;
+  let id = mDBConn.lastInsertRowID;
+  if (aFavicon) {
+    stmt = mDBConn.createStatement(
+      "INSERT INTO moz_pages_w_icons (page_url, page_url_hash) VALUES (:url, hash(:url))");
+    stmt.params["url"] = href;
+    stmt.execute();
+    stmt.finalize();
+    stmt = mDBConn.createStatement(
+      "INSERT INTO moz_icons_to_pages (page_id, icon_id) " +
+      "VALUES ((SELECT id FROM moz_pages_w_icons WHERE page_url_hash = hash(:url)), :favicon)");
+    stmt.params["url"] = href;
+    stmt.params["favicon"] = aFavicon;
+    stmt.execute();
+    stmt.finalize();
+  }
+  return id;
 }
 
 function addBookmark(aPlaceId, aType, aParent, aKeywordId, aFolderType, aTitle) {
   let stmt = mDBConn.createStatement(
     `INSERT INTO moz_bookmarks (fk, type, parent, keyword_id, folder_type,
                                 title, guid)
      VALUES (:place_id, :type, :parent, :keyword_id, :folder_type, :title,
              GENERATE_GUID())`);
@@ -784,45 +800,57 @@ tests.push({
     stmt.finalize();
   }
 });
 
 // ------------------------------------------------------------------------------
 
 tests.push({
   name: "E.1",
-  desc: "Remove orphan icons",
+  desc: "Remove orphan icon entries",
 
   _placeId: null,
 
   setup() {
     // Insert favicon entries
-    let stmt = mDBConn.createStatement("INSERT INTO moz_favicons (id, url) VALUES(:favicon_id, :url)");
+    let stmt = mDBConn.createStatement("INSERT INTO moz_icons (id, icon_url, fixed_icon_url_hash) VALUES(:favicon_id, :url, hash(fixup_url(:url)))");
     stmt.params["favicon_id"] = 1;
     stmt.params["url"] = "http://www1.mozilla.org/favicon.ico";
     stmt.execute();
     stmt.reset();
     stmt.params["favicon_id"] = 2;
     stmt.params["url"] = "http://www2.mozilla.org/favicon.ico";
     stmt.execute();
     stmt.finalize();
+    // Insert orphan page.
+    stmt = mDBConn.createStatement("INSERT INTO moz_pages_w_icons (id, page_url, page_url_hash) VALUES(:page_id, :url, hash(:url))");
+    stmt.params["page_id"] = 99;
+    stmt.params["url"] = "http://w99.mozilla.org/";
+    stmt.execute();
+    stmt.finalize();
+
     // Insert a place using the existing favicon entry
     this._placeId = addPlace("http://www.mozilla.org", 1);
   },
 
   check() {
     // Check that used icon is still there
-    let stmt = mDBConn.createStatement("SELECT id FROM moz_favicons WHERE id = :favicon_id");
+    let stmt = mDBConn.createStatement("SELECT id FROM moz_icons WHERE id = :favicon_id");
     stmt.params["favicon_id"] = 1;
     do_check_true(stmt.executeStep());
     stmt.reset();
     // Check that unused icon has been removed
     stmt.params["favicon_id"] = 2;
     do_check_false(stmt.executeStep());
     stmt.finalize();
+    // Check that the orphan page is gone.
+    stmt = mDBConn.createStatement("SELECT id FROM moz_pages_w_icons WHERE id = :page_id");
+    stmt.params["page_id"] = 99;
+    do_check_false(stmt.executeStep());
+    stmt.finalize();
   }
 });
 
 // ------------------------------------------------------------------------------
 
 tests.push({
   name: "F.1",
   desc: "Remove orphan visits",
@@ -1019,60 +1047,16 @@ tests.push({
     let stmt = mDBConn.createStatement("SELECT id FROM moz_keywords WHERE keyword = :keyword");
     // Check that "unused" keyword has gone
     stmt.params["keyword"] = "unused";
     do_check_false(stmt.executeStep());
     stmt.finalize();
   }
 });
 
-
-// ------------------------------------------------------------------------------
-
-tests.push({
-  name: "L.1",
-  desc: "Fix wrong favicon ids",
-
-  _validIconPlaceId: null,
-  _invalidIconPlaceId: null,
-
-  setup() {
-    // Insert a favicon entry
-    let stmt = mDBConn.createStatement("INSERT INTO moz_favicons (id, url) VALUES(1, :url)");
-    stmt.params["url"] = "http://www.mozilla.org/favicon.ico";
-    stmt.execute();
-    stmt.finalize();
-    // Insert a place using the existing favicon entry
-    this._validIconPlaceId = addPlace("http://www1.mozilla.org", 1);
-
-    // Insert a place using a nonexistent favicon entry
-    this._invalidIconPlaceId = addPlace("http://www2.mozilla.org", 1337);
-  },
-
-  check() {
-    // Check that bogus favicon is not there
-    let stmt = mDBConn.createStatement("SELECT id FROM moz_places WHERE favicon_id = :favicon_id");
-    stmt.params["favicon_id"] = 1337;
-    do_check_false(stmt.executeStep());
-    stmt.reset();
-    // Check that valid favicon is still there
-    stmt.params["favicon_id"] = 1;
-    do_check_true(stmt.executeStep());
-    stmt.finalize();
-    // Check that place entries are there
-    stmt = mDBConn.createStatement("SELECT id FROM moz_places WHERE id = :place_id");
-    stmt.params["place_id"] = this._validIconPlaceId;
-    do_check_true(stmt.executeStep());
-    stmt.reset();
-    stmt.params["place_id"] = this._invalidIconPlaceId;
-    do_check_true(stmt.executeStep());
-    stmt.finalize();
-  }
-});
-
 // ------------------------------------------------------------------------------
 
 tests.push({
   name: "L.2",
   desc: "Recalculate visit_count and last_visit_date",
 
   *setup() {
     function setVisitCount(aURL, aValue) {
@@ -1180,18 +1164,16 @@ tests.push({
             let url = row.getResultByIndex(0);
             do_check_true(/redirecting/.test(url));
             this._count++;
           }
         },
         handleError(aError) {
         },
         handleCompletion(aReason) {
-          dump_table("moz_places");
-          dump_table("moz_historyvisits");
           do_check_eq(aReason, Ci.mozIStorageStatementCallback.REASON_FINISHED);
           do_check_eq(this._count, 2);
           resolve();
         }
       });
       stmt.finalize();
     });
   }
--- a/toolkit/components/places/tests/unit/test_promiseBookmarksTree.js
+++ b/toolkit/components/places/tests/unit/test_promiseBookmarksTree.js
@@ -129,18 +129,17 @@ function* compareToNode(aItem, aNode, aI
       compare_prop("uri");
       // node.tags's format is "a, b" whilst promiseBoookmarksTree is "a,b"
       if (aNode.tags === null)
         check_unset("tags");
       else
         compare_prop_to_value("tags", aNode.tags.replace(/, /g, ","), false);
 
       if (aNode.icon) {
-        let nodeIconData = aNode.icon.replace("moz-anno:favicon:", "");
-        compare_prop_to_value("iconuri", nodeIconData);
+        compareFavicons(aNode.icon, aItem.iconuri);
       } else {
         check_unset(aItem.iconuri);
       }
 
       check_unset(...FOLDER_ONLY_PROPS);
 
       let itemURI = uri(aNode.uri);
       compare_prop_to_value("charset",
@@ -218,17 +217,17 @@ add_task(function* () {
   yield new_bookmark({ title: null,
                        parentGuid: folderGuid,
                        keyword: "test_keyword",
                        tags: ["TestTagA", "TestTagB"],
                        annotations: [{ name: "TestAnnoA", value: "TestVal2"}]});
   let urlWithCharsetAndFavicon = uri("http://charset.and.favicon");
   yield new_bookmark({ parentGuid: folderGuid, url: urlWithCharsetAndFavicon });
   yield PlacesUtils.setCharsetForURI(urlWithCharsetAndFavicon, "UTF-8");
-  yield promiseSetIconForPage(urlWithCharsetAndFavicon, SMALLPNG_DATA_URI);
+  yield setFaviconForPage(urlWithCharsetAndFavicon, SMALLPNG_DATA_URI);
   // Test the default places root without specifying it.
   yield test_promiseBookmarksTreeAgainstResult();
 
   // Do specify it
   yield test_promiseBookmarksTreeAgainstResult(PlacesUtils.bookmarks.rootGuid);
 
   // Exclude the bookmarks menu.
   // The calllback should be four times - once for the toolbar, once for
--- a/toolkit/components/places/tests/unit/xpcshell.ini
+++ b/toolkit/components/places/tests/unit/xpcshell.ini
@@ -116,17 +116,16 @@ skip-if = true
 [test_preventive_maintenance.js]
 [test_preventive_maintenance_checkAndFixDatabase.js]
 [test_preventive_maintenance_runTasks.js]
 [test_promiseBookmarksTree.js]
 [test_resolveNullBookmarkTitles.js]
 [test_result_sort.js]
 [test_resultsAsVisit_details.js]
 [test_sql_guid_functions.js]
-[test_svg_favicon.js]
 [test_sync_utils.js]
 [test_tag_autocomplete_search.js]
 [test_tagging.js]
 [test_telemetry.js]
 [test_update_frecency_after_delete.js]
 [test_utils_backups_create.js]
 [test_utils_getURLsForContainerNode.js]
 [test_utils_setAnnotationsFor.js]
--- a/widget/windows/WinUtils.cpp
+++ b/widget/windows/WinUtils.cpp
@@ -1224,17 +1224,18 @@ nsresult AsyncFaviconDataReady::OnFavico
 
   return channel->AsyncOpen2(listener);
 }
 
 NS_IMETHODIMP
 AsyncFaviconDataReady::OnComplete(nsIURI *aFaviconURI,
                                   uint32_t aDataLen,
                                   const uint8_t *aData, 
-                                  const nsACString &aMimeType)
+                                  const nsACString &aMimeType,
+                                  uint16_t aWidth)
 {
   if (!aDataLen || !aData) {
     if (mURLShortcut) {
       OnFaviconDataNotAvailable();
     }
     
     return NS_OK;
   }
@@ -1652,17 +1653,17 @@ nsresult
     do_GetService("@mozilla.org/browser/favicon-service;1"));
   NS_ENSURE_TRUE(favIconSvc, NS_ERROR_FAILURE);
 
   nsCOMPtr<nsIFaviconDataCallback> callback = 
     new mozilla::widget::AsyncFaviconDataReady(aFaviconPageURI, 
                                                aIOThread, 
                                                aURLShortcut);
 
-  favIconSvc->GetFaviconDataForPage(aFaviconPageURI, callback);
+  favIconSvc->GetFaviconDataForPage(aFaviconPageURI, callback, 0);
 #endif
   return NS_OK;
 }
 
 // Obtains the jump list 'ICO cache timeout in seconds' pref
 int32_t FaviconHelper::GetICOCacheSecondsTimeout() {
 
   // Only obtain the setting at most once from the pref service.