Skip to content

Downloading and Caching Favicons and Hero Images

Isabella edited this page Aug 30, 2024 · 4 revisions

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.

Implementation Details

Terms

  • siteURL: The website assigned to a tile, bookmark, or history item
  • faviconURL: The URL of the website's favicon (preferably high quality, such as an apple-touch-icon, since the default /favicon.ico on websites is low quality). This is also sometimes called the resourceURL in the code, referring to a generalized image path for both favicons and hero images associated with a siteURL
  • 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.

Cache Keys

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.

Cache Expiration

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.

Process of obtaining and caching a favicon URL and image

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 this siteURL
    • If no faviconURL exists in the cache, attempt to scrape the siteURL webpage for a high-quality faviconURL
      • Fallback: If no faviconURL is found, return siteURL with /favicon.ico appended (the default path to a low-quality favicon .ico)
    • Save the new faviconURL in the URL Cache
  • 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")
    • Save the new image (or letter favicon) in the Image Cache

Process Visualization

2024-08-29 Favicon Logic Flow

Caching faviconURLs from webpages the user has already loaded

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.

Architectural Breakdown

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).

2024-08-29 Favicon Refactor

Clone this wiki locally