diff --git a/Shared/Favicons/FaviconDownloader.swift b/Shared/Favicons/FaviconDownloader.swift index e18be583e..83412ea20 100644 --- a/Shared/Favicons/FaviconDownloader.swift +++ b/Shared/Favicons/FaviconDownloader.swift @@ -23,7 +23,8 @@ final class FaviconDownloader { private let folder: String private let diskCache: BinaryDiskCache private var singleFaviconDownloaderCache = [String: SingleFaviconDownloader]() // faviconURL: SingleFaviconDownloader - + private var remainingFaviconURLs = [String: ArraySlice]() // homePageURL: array of faviconURLs that haven't been checked yet + private var homePageToFaviconURLCache = [String: String]() //homePageURL: faviconURL private var homePageToFaviconURLCachePath: String private var homePageToFaviconURLCacheDirty = false { @@ -71,11 +72,11 @@ final class FaviconDownloader { assert(Thread.isMainThread) + var homePageURL = webFeed.homePageURL if let faviconURL = webFeed.faviconURL { - return favicon(with: faviconURL) + return favicon(with: faviconURL, homePageURL: homePageURL) } - var homePageURL = webFeed.homePageURL if homePageURL == nil { // Base homePageURL off feedURL if needed. Won’t always be accurate, but is good enough. if let feedURL = URL(string: webFeed.url), let scheme = feedURL.scheme, let host = feedURL.host { @@ -106,9 +107,8 @@ final class FaviconDownloader { return nil } - func favicon(with faviconURL: String) -> IconImage? { - - let downloader = faviconDownloader(withURL: faviconURL) + func favicon(with faviconURL: String, homePageURL: String?) -> IconImage? { + let downloader = faviconDownloader(withURL: faviconURL, homePageURL: homePageURL) return downloader.iconImage } @@ -118,20 +118,24 @@ final class FaviconDownloader { if homePageURLsWithNoFaviconURLCache.contains(url) { return nil } - + if let faviconURL = homePageToFaviconURLCache[url] { - return favicon(with: faviconURL) + return favicon(with: faviconURL, homePageURL: url) } - findFaviconURL(with: url) { (faviconURL) in - if let faviconURL = faviconURL { - self.homePageToFaviconURLCache[url] = faviconURL - self.homePageToFaviconURLCacheDirty = true - let _ = self.favicon(with: faviconURL) + findFaviconURLs(with: url) { (faviconURLs) in + var hasIcons = false + + if let faviconURLs = faviconURLs { + if let firstIconURL = faviconURLs.first { + hasIcons = true + let _ = self.favicon(with: firstIconURL, homePageURL: url) + self.remainingFaviconURLs[url] = faviconURLs.dropFirst() + } } - else { + + if (!hasIcons) { self.homePageURLsWithNoFaviconURLCache.insert(url) - self.homePageURLsWithNoFaviconURLCacheDirty = true } } @@ -145,9 +149,28 @@ final class FaviconDownloader { guard let singleFaviconDownloader = note.object as? SingleFaviconDownloader else { return } - guard let _ = singleFaviconDownloader.iconImage else { + guard let homePageURL = singleFaviconDownloader.homePageURL else { return } + guard let _ = singleFaviconDownloader.iconImage else { + if let faviconURLs = remainingFaviconURLs[homePageURL] { + if let nextIconURL = faviconURLs.first { + let _ = favicon(with: nextIconURL, homePageURL: singleFaviconDownloader.homePageURL) + remainingFaviconURLs[homePageURL] = faviconURLs.dropFirst(); + } else { + remainingFaviconURLs[homePageURL] = nil + } + } + return + } + + remainingFaviconURLs[homePageURL] = nil + + if let url = singleFaviconDownloader.homePageURL { + if self.homePageToFaviconURLCache[url] == nil { + self.homePageToFaviconURLCache[url] = singleFaviconDownloader.faviconURL + } + } postFaviconDidBecomeAvailableNotification(singleFaviconDownloader.faviconURL) } @@ -169,38 +192,40 @@ private extension FaviconDownloader { static let localeForLowercasing = Locale(identifier: "en_US") - func findFaviconURL(with homePageURL: String, _ completion: @escaping (String?) -> Void) { + func findFaviconURLs(with homePageURL: String, _ completion: @escaping ([String]?) -> Void) { guard let url = URL(string: homePageURL) else { completion(nil) return } - FaviconURLFinder.findFaviconURL(homePageURL) { (faviconURL) in + FaviconURLFinder.findFaviconURLs(homePageURL) { (faviconURLs) in + var defaultFaviconURL: String? = nil - if let faviconURL = faviconURL { - completion(faviconURL) + if let scheme = url.scheme, let host = url.host { + defaultFaviconURL = "\(scheme)://\(host)/favicon.ico".lowercased(with: FaviconDownloader.localeForLowercasing) + } + + if var faviconURLs = faviconURLs { + if let defaultFaviconURL = defaultFaviconURL { + faviconURLs.append(defaultFaviconURL) + } + completion(faviconURLs) return } - guard let scheme = url.scheme, let host = url.host else { - completion(nil) - return - } - - let defaultFaviconURL = "\(scheme)://\(host)/favicon.ico".lowercased(with: FaviconDownloader.localeForLowercasing) - completion(defaultFaviconURL) + completion(defaultFaviconURL != nil ? [defaultFaviconURL!] : nil) } } - func faviconDownloader(withURL faviconURL: String) -> SingleFaviconDownloader { + func faviconDownloader(withURL faviconURL: String, homePageURL: String?) -> SingleFaviconDownloader { if let downloader = singleFaviconDownloaderCache[faviconURL] { downloader.downloadFaviconIfNeeded() return downloader } - let downloader = SingleFaviconDownloader(faviconURL: faviconURL, diskCache: diskCache, queue: queue) + let downloader = SingleFaviconDownloader(faviconURL: faviconURL, homePageURL: homePageURL, diskCache: diskCache, queue: queue) singleFaviconDownloaderCache[faviconURL] = downloader return downloader } diff --git a/Shared/Favicons/FaviconURLFinder.swift b/Shared/Favicons/FaviconURLFinder.swift index e91787ee9..766ad0451 100644 --- a/Shared/Favicons/FaviconURLFinder.swift +++ b/Shared/Favicons/FaviconURLFinder.swift @@ -9,11 +9,11 @@ import Foundation import RSParser -// The favicon URL may be specified in the head section of the home page. +// The favicon URLs may be specified in the head section of the home page. struct FaviconURLFinder { - static func findFaviconURL(_ homePageURL: String, _ callback: @escaping (String?) -> Void) { + static func findFaviconURLs(_ homePageURL: String, _ callback: @escaping ([String]?) -> Void) { guard let _ = URL(string: homePageURL) else { callback(nil) @@ -21,7 +21,7 @@ struct FaviconURLFinder { } HTMLMetadataDownloader.downloadMetadata(for: homePageURL) { (htmlMetadata) in - callback(htmlMetadata?.faviconLink) + callback(htmlMetadata?.faviconLinks) } } } diff --git a/Shared/Favicons/SingleFaviconDownloader.swift b/Shared/Favicons/SingleFaviconDownloader.swift index 55d9f96ff..1772680aa 100644 --- a/Shared/Favicons/SingleFaviconDownloader.swift +++ b/Shared/Favicons/SingleFaviconDownloader.swift @@ -26,6 +26,7 @@ final class SingleFaviconDownloader { let faviconURL: String var iconImage: IconImage? + let homePageURL: String? private var lastDownloadAttemptDate: Date private var diskStatus = DiskStatus.unknown @@ -36,9 +37,10 @@ final class SingleFaviconDownloader { return (faviconURL as NSString).rs_md5Hash() } - init(faviconURL: String, diskCache: BinaryDiskCache, queue: DispatchQueue) { + init(faviconURL: String, homePageURL: String?, diskCache: BinaryDiskCache, queue: DispatchQueue) { self.faviconURL = faviconURL + self.homePageURL = homePageURL self.diskCache = diskCache self.queue = queue self.lastDownloadAttemptDate = Date() @@ -85,6 +87,7 @@ private extension SingleFaviconDownloader { self.iconImage = IconImage(image) self.postDidLoadFaviconNotification() } + } } } diff --git a/submodules/RSParser b/submodules/RSParser index 3b5c88aa1..81c400a76 160000 --- a/submodules/RSParser +++ b/submodules/RSParser @@ -1 +1 @@ -Subproject commit 3b5c88aa1c3d50e2a7500f2a260f5ffce81c71d2 +Subproject commit 81c400a7665309a08414bf43ca5161d90d072501