-
Notifications
You must be signed in to change notification settings - Fork 3k
Downloading and Caching Favicons and Hero Images
Favicons and hero images are downloaded and displayed in many places throughout the app, for example:
- homepage top sites tiles
- homepage bookmarks and thought-provoking stories
- each inactive tab row in the tab tray
- each website row on the bookmarks and history screens
Also refer to How do Top Sites (Shortcuts) Work? for a rundown of the logic governing the top site tiles on the homepage.
-
siteURL
: The website assigned to a tile, bookmark, or history item -
faviconURL
: The URL of the website's favicon (preferably high quality, such as anapple-touch-icon
, since the default/favicon.ico
on websites is low quality). This is also sometimes called theresourceURL
in the code, referring to a generalized image path for both favicons and hero images associated with asiteURL
-
URL Cache: The cache which stores a
faviconURL
for a website. -
Image Cache: The cache which stores downloaded favicon
UIImages
. We are using a library called Kingfisher to cache our images.
The URL Cache and Image Cache both generally use keys prefixed by the short domain of the siteURL
(e.g. "google" for any https://google.com/.../.../...
links). This prevents duplicated requests to scrape the same favicon URL and download the same image when a user visits multiple pages of the same website.
More rarely, favicon URLs and images are keyed by the full faviconURL
path. For example, with DefaultSuggestedSites
, the pinned Google tile, and sponsored top sites. This can only happen when a SiteImageModel
is passed a faviconURL
at initialization time, which then bypasses the requirement to obtain a faviconURL
either from the URL Cache or scraping the associated siteURL
.
In order to keep favicon URLs and images up to date, items in both the URL Cache and Image Cache eventually expire. Note there's a known issue where downloading a favicon image could fail because of a client-side issue (e.g. mobile phone dropped internet connection) and thus a letter favicon placeholder is cached and returned. This won't be "fixed" (i.e. the real favicon image downloaded) until that letter favicon image expires in the Image Cache. This is currently an acceptable middle ground but there's room to develop more sophisticated handling of errors.
Items in the URL Cache expire after 30 days. This is set with daysToExpiration = 30
in DefaultFaviconURLCache.CacheConstants
in FaviconURLCache.swift
.
The Image Cache is backed by Kingfisher. Items in the Image Cache expire using the default Kingfisher expiration time of one week, as no additional options are passed on instantiation in DefaultSiteImageCache
, which simply leverages the ImageCache.default
from Kingfisher.
This is the general process for obtaining a favicon image for the FaviconImageView
once it is assigned a website URL (siteURL
). Note that obtaining a hero image is very similar.
- Check the URL Cache to see if a
faviconURL
exists for thissiteURL
- If no
faviconURL
exists in the cache, attempt to scrape thesiteURL
webpage for a high-qualityfaviconURL
- Fallback: If no
faviconURL
is found, returnsiteURL
with/favicon.ico
appended (the default path to a low-quality favicon .ico)
- Fallback: If no
- Save the new
faviconURL
in the URL Cache
- If no
- Check the Image Cache to see if a favicon image exists for the associated
siteURL
- If no image exists in the cache, attempt to download the image from the network
- Fallback: If no image is obtained, generate a letter favicon based on the
siteURL
's root domain (e.g. "G" for "https://www.google.com")
- Fallback: If no image is obtained, generate a letter favicon based on the
- Save the new image (or letter favicon) in the Image Cache
- If no image exists in the cache, attempt to download the image from the network
Inside MetadataParserHelper.swift
, there is code which runs when a Tab
loads a new webpage. This method fetches metadata about the page. When tab.metadata
is updated with the new metadata, a didSet
call triggers an update of the Tab
's faviconURL
property. This update will subsequently trigger a call to cacheFaviconURL
, which updates the URL CACHE with a faviconURL
for that website.
The intention behind this is to save on scraping webpages for a faviconURL
in the future.
It should be noted the JavaScript library we're using for this is currently deprecated and archived. It also often appears to return favicon.ico
URLs rather than searching for a higher quality URL.
This diagram visualizes the classes, protocols, and methods currently involved in a favicon image request (as of August 2024). The primary access point is via the FaviconImageView
on the top left. The process designated by the pink arrows (top right) illustrates caching faviconURL
s from webpages the user has already loaded.
It is suggested that we refactor this code in the future to simplify some of the complexity. As well, there is a known bug for which we currently have a stopgap solution in the DefaultSiteImageHandler
. We currently use a static queue as a workaround for the fact that many UITableViewCell refreshes on the homepage trigger the initialization of several instances of DefaultSiteImageHandler
, which in turn each trigger new network calls to fetch favicon URLs and images (when not in cache yet). More documentation of the issue exists inside SiteImageHandler.swift
(also see FXIOS-9830 and FXIOS-9427, and some discussion on this PR).