-
Notifications
You must be signed in to change notification settings - Fork 3.1k
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.icoon websites is low quality). This is also sometimes called theresourceURLin the code, referring to a generalized image path for both favicons and hero images associated with asiteURL -
URL Cache: The cache which stores a
faviconURLfor 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
faviconURLexists for thissiteURL- If no
faviconURLexists in the cache, attempt to scrape thesiteURLwebpage for a high-qualityfaviconURL- Fallback: If no
faviconURLis found, returnsiteURLwith/favicon.icoappended (the default path to a low-quality favicon .ico)
- Fallback: If no
- Save the new
faviconURLin 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 faviconURLs 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).
