diff --git a/Core/FaviconsHelper.swift b/Core/CoreFaviconsHelper.swift similarity index 92% rename from Core/FaviconsHelper.swift rename to Core/CoreFaviconsHelper.swift index 478da57e54..5c1396d033 100644 --- a/Core/FaviconsHelper.swift +++ b/Core/CoreFaviconsHelper.swift @@ -20,7 +20,7 @@ import Foundation import Kingfisher -struct FaviconsHelper { +struct CoreFaviconsHelper { // this function is now static and outside of Favicons, otherwise there is a circular dependency between // Favicons and NotFoundCachingDownloader @@ -28,7 +28,10 @@ struct FaviconsHelper { guard let domain = domain, let source = sourcesProvider.mainSource(forDomain: domain) else { return nil } - let key = Favicons.createHash(ofDomain: domain) + let key = FaviconHasher.createHash(ofDomain: domain) return KF.ImageResource(downloadURL: source, cacheKey: key) } + } + + diff --git a/Core/FaviconHasher.swift b/Core/FaviconHasher.swift new file mode 100644 index 0000000000..1a71f594d5 --- /dev/null +++ b/Core/FaviconHasher.swift @@ -0,0 +1,31 @@ +// +// FaviconHasher.swift +// DuckDuckGo +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + + +import Foundation +import Kingfisher + +public struct FaviconHasher { + + static let salt = "DDGSalt:" + public static func createHash(ofDomain domain: String) -> String { + return "\(Self.salt)\(domain)".sha256() + } + +} diff --git a/Core/FaviconsCacheType.swift b/Core/FaviconsCacheType.swift new file mode 100644 index 0000000000..38dfbee948 --- /dev/null +++ b/Core/FaviconsCacheType.swift @@ -0,0 +1,43 @@ +// +// FaviconsCacheType.swift +// DuckDuckGo +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +public enum FaviconsCacheType: String { + + static let faviconsFolderName = "Favicons" + + case tabs + case fireproof + + public func cacheLocation() -> URL? { + return baseCacheURL()?.appendingPathComponent(Self.faviconsFolderName) + } + + private func baseCacheURL() -> URL? { + switch self { + case .fireproof: + let groupName = BookmarksDatabase.Constants.bookmarksGroupID + return FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: groupName) + + case .tabs: + return FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first + } + } +} diff --git a/Core/NotFoundCachingDownloader.swift b/Core/NotFoundCachingDownloader.swift index c9ba6daa67..b46d0f2109 100644 --- a/Core/NotFoundCachingDownloader.swift +++ b/Core/NotFoundCachingDownloader.swift @@ -46,7 +46,7 @@ class NotFoundCachingDownloader: ImageDownloader { } func noFaviconsFound(forDomain domain: String) { - guard let hashedKey = FaviconsHelper.defaultResource(forDomain: domain, sourcesProvider: sourcesProvider)?.cacheKey else { return } + guard let hashedKey = CoreFaviconsHelper.defaultResource(forDomain: domain, sourcesProvider: sourcesProvider)?.cacheKey else { return } notFoundCache[hashedKey] = Date().timeIntervalSince1970 } @@ -56,7 +56,7 @@ class NotFoundCachingDownloader: ImageDownloader { } func shouldDownload(forDomain domain: String, referenceDate: Date = Date()) -> Bool { - guard let hashedKey = FaviconsHelper.defaultResource(forDomain: domain, sourcesProvider: sourcesProvider)?.cacheKey else { return false } + guard let hashedKey = CoreFaviconsHelper.defaultResource(forDomain: domain, sourcesProvider: sourcesProvider)?.cacheKey else { return false } if let cacheAddTime = notFoundCache[hashedKey], referenceDate.timeIntervalSince1970 - cacheAddTime < Self.expiry { return false diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 49a05c4c5e..c9923564f7 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -521,6 +521,8 @@ 8590CB67268A2E520089F6BF /* RootDebugViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8590CB66268A2E520089F6BF /* RootDebugViewController.swift */; }; 8590CB69268A4E190089F6BF /* DebugEtagStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8590CB68268A4E190089F6BF /* DebugEtagStorage.swift */; }; 8596C30D2B7EB1800058EF90 /* DataStoreWarmup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8596C30C2B7EB1800058EF90 /* DataStoreWarmup.swift */; }; + 8598D2DC2CEB93AD00C45685 /* FaviconsCacheType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8598D2DB2CEB93A600C45685 /* FaviconsCacheType.swift */; }; + 8598D2DE2CEB97BE00C45685 /* FaviconHasher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8598D2DD2CEB97BE00C45685 /* FaviconHasher.swift */; }; 8598F67B2405EB8D00FBC70C /* KeyboardSettingsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8598F6792405EB8600FBC70C /* KeyboardSettingsTests.swift */; }; 8599690F29D2F1C100DBF9FA /* DDGSync in Frameworks */ = {isa = PBXBuildFile; productRef = 8599690E29D2F1C100DBF9FA /* DDGSync */; }; 859DB8132CE6263C001F7210 /* TextZoomStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 859DB80D2CE6263B001F7210 /* TextZoomStorage.swift */; }; @@ -925,7 +927,7 @@ C1BF0BA529B63D7200482B73 /* AutofillLoginPromptHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1BF0BA429B63D7200482B73 /* AutofillLoginPromptHelper.swift */; }; C1BF0BA929B63E2200482B73 /* AutofillLoginPromptViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1BF0BA729B63E1A00482B73 /* AutofillLoginPromptViewModelTests.swift */; }; C1BF26152C74D10F00F6405E /* SyncPromoManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1BF26142C74D10F00F6405E /* SyncPromoManager.swift */; }; - C1CCCBA7283E101500CF3791 /* FaviconsHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1CCCBA6283E101500CF3791 /* FaviconsHelper.swift */; }; + C1CCCBA7283E101500CF3791 /* CoreFaviconsHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1CCCBA6283E101500CF3791 /* CoreFaviconsHelper.swift */; }; C1CDA3162AFB9C7F006D1476 /* AutofillNeverPromptWebsitesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1CDA3152AFB9C7F006D1476 /* AutofillNeverPromptWebsitesManager.swift */; }; C1CDA31E2AFBF811006D1476 /* AutofillNeverPromptWebsitesManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1CDA31D2AFBF811006D1476 /* AutofillNeverPromptWebsitesManagerTests.swift */; }; C1D21E2D293A5965006E5A05 /* AutofillLoginSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1D21E2C293A5965006E5A05 /* AutofillLoginSession.swift */; }; @@ -1836,6 +1838,8 @@ 8590CB66268A2E520089F6BF /* RootDebugViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootDebugViewController.swift; sourceTree = ""; }; 8590CB68268A4E190089F6BF /* DebugEtagStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugEtagStorage.swift; sourceTree = ""; }; 8596C30C2B7EB1800058EF90 /* DataStoreWarmup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataStoreWarmup.swift; sourceTree = ""; }; + 8598D2DB2CEB93A600C45685 /* FaviconsCacheType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FaviconsCacheType.swift; sourceTree = ""; }; + 8598D2DD2CEB97BE00C45685 /* FaviconHasher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FaviconHasher.swift; sourceTree = ""; }; 8598F6792405EB8600FBC70C /* KeyboardSettingsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardSettingsTests.swift; sourceTree = ""; }; 859DB80D2CE6263B001F7210 /* TextZoomStorage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextZoomStorage.swift; sourceTree = ""; }; 859DB80E2CE6263B001F7210 /* TextZoomController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextZoomController.swift; sourceTree = ""; }; @@ -2735,7 +2739,7 @@ C1BF0BA429B63D7200482B73 /* AutofillLoginPromptHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AutofillLoginPromptHelper.swift; sourceTree = ""; }; C1BF0BA729B63E1A00482B73 /* AutofillLoginPromptViewModelTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AutofillLoginPromptViewModelTests.swift; sourceTree = ""; }; C1BF26142C74D10F00F6405E /* SyncPromoManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncPromoManager.swift; sourceTree = ""; }; - C1CCCBA6283E101500CF3791 /* FaviconsHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FaviconsHelper.swift; sourceTree = ""; }; + C1CCCBA6283E101500CF3791 /* CoreFaviconsHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreFaviconsHelper.swift; sourceTree = ""; }; C1CDA3152AFB9C7F006D1476 /* AutofillNeverPromptWebsitesManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AutofillNeverPromptWebsitesManager.swift; sourceTree = ""; }; C1CDA31D2AFBF811006D1476 /* AutofillNeverPromptWebsitesManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutofillNeverPromptWebsitesManagerTests.swift; sourceTree = ""; }; C1D21E2C293A5965006E5A05 /* AutofillLoginSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutofillLoginSession.swift; sourceTree = ""; }; @@ -4575,7 +4579,9 @@ 85CA53A724BB342B00A6288C /* Favicons */ = { isa = PBXGroup; children = ( - C1CCCBA6283E101500CF3791 /* FaviconsHelper.swift */, + 8598D2DB2CEB93A600C45685 /* FaviconsCacheType.swift */, + C1CCCBA6283E101500CF3791 /* CoreFaviconsHelper.swift */, + 8598D2DD2CEB97BE00C45685 /* FaviconHasher.swift */, 85CA53A324B9F2BD00A6288C /* Favicons.swift */, 85CA53A924BB376800A6288C /* NotFoundCachingDownloader.swift */, 85CA53AB24BBD39300A6288C /* FaviconRequestModifier.swift */, @@ -8338,7 +8344,7 @@ 850559D023CF647C0055C0D5 /* PreserveLogins.swift in Sources */, 4B27FBAE2C924EC6007E21A7 /* PersistentPixelStoring.swift in Sources */, 6F7FB8E32C660BF300867DA7 /* DailyPixelFiring.swift in Sources */, - C1CCCBA7283E101500CF3791 /* FaviconsHelper.swift in Sources */, + C1CCCBA7283E101500CF3791 /* CoreFaviconsHelper.swift in Sources */, 9813F79822BA71AA00A80EDB /* StorageCache.swift in Sources */, B603974929C19F6F00902A34 /* Assertions.swift in Sources */, F1134EB51F40AEEA00B73467 /* StatisticsLoader.swift in Sources */, @@ -8363,6 +8369,7 @@ C14882DA27F2011C00D59F0C /* BookmarksExporter.swift in Sources */, 854858E32937BC550063610B /* CollectionExtension.swift in Sources */, 85528AA72C7CA95D0017BCCA /* UsageSegmentationCalculator.swift in Sources */, + 8598D2DC2CEB93AD00C45685 /* FaviconsCacheType.swift in Sources */, 1E6A4D692984208800A371D3 /* LocaleExtension.swift in Sources */, 98F6EA472863124100720957 /* ContentBlockerRulesLists.swift in Sources */, 566B73732BECE4F200FF1959 /* SyncErrorHandling.swift in Sources */, @@ -8419,6 +8426,7 @@ 9887DC252354D2AA005C85F5 /* Database.swift in Sources */, F143C3171E4A99D200CFDE3A /* AppURLs.swift in Sources */, C1963863283794A000298D4D /* BookmarksCachingSearch.swift in Sources */, + 8598D2DE2CEB97BE00C45685 /* FaviconHasher.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/DuckDuckGo/FaviconViewModel.swift b/DuckDuckGo/FaviconViewModel.swift index 2b9ee92dd7..a7516dbf50 100644 --- a/DuckDuckGo/FaviconViewModel.swift +++ b/DuckDuckGo/FaviconViewModel.swift @@ -25,12 +25,12 @@ final class FaviconViewModel { private let domain: String private let useFakeFavicon: Bool - private let cacheType: Favicons.CacheType + private let cacheType: FaviconsCacheType private let preferredFaviconLetters: String? internal init(domain: String, useFakeFavicon: Bool = true, - cacheType: Favicons.CacheType = .tabs, + cacheType: FaviconsCacheType = .tabs, preferredFakeFaviconLetters: String? = nil) { self.domain = domain diff --git a/DuckDuckGo/Favicons.swift b/DuckDuckGo/Favicons.swift index ba17101c97..052d48dcf6 100644 --- a/DuckDuckGo/Favicons.swift +++ b/DuckDuckGo/Favicons.swift @@ -29,99 +29,20 @@ public class Favicons { public struct Constants { - static let salt = "DDGSalt:" - static let faviconsFolderName = "Favicons" static let requestModifier = FaviconRequestModifier() - static let fireproofCache = CacheType.fireproof.create() - static let tabsCache = CacheType.tabs.create() + static let fireproofCache = FaviconsCacheType.fireproof.create() + static let tabsCache = FaviconsCacheType.tabs.create() static let targetImageSizePoints: CGFloat = 64 public static let tabsCachePath = "com.onevcat.Kingfisher.ImageCache.tabs" public static let maxFaviconSize: CGSize = CGSize(width: 192, height: 192) public static let caches = [ - CacheType.fireproof: fireproofCache, - CacheType.tabs: tabsCache + FaviconsCacheType.fireproof: fireproofCache, + FaviconsCacheType.tabs: tabsCache ] } - public enum CacheType: String { - - case tabs - case fireproof - - func create() -> ImageCache { - - // If unable to create cache in desired location default to Kingfisher's default location which is Library/Cache. Images may disappear - // but at least the app won't crash. This should not happen. - let cache = createCacheInDesiredLocation() ?? ImageCache(name: rawValue) - - // We hash the resource key when loading the resource so don't use Kingfisher's hashing which is md5 based - cache.diskStorage.config.usesHashedFileName = false - - if self == .fireproof { - migrateBookmarksCacheContents(to: cache.diskStorage.directoryURL) - } - - return cache - } - - public func cacheLocation() -> URL? { - return baseCacheURL()?.appendingPathComponent(Constants.faviconsFolderName) - } - - private func createCacheInDesiredLocation() -> ImageCache? { - - guard var url = cacheLocation() else { return nil } - - if !FileManager.default.fileExists(atPath: url.path) { - try? FileManager.default.createDirectory(at: url, - withIntermediateDirectories: true, - attributes: nil) - - // Exclude from backup - var resourceValues = URLResourceValues() - resourceValues.isExcludedFromBackup = true - try? url.setResourceValues(resourceValues) - } - - Logger.general.debug("favicons \(rawValue) location \(url.absoluteString)") - return try? ImageCache(name: self.rawValue, cacheDirectoryURL: url) - } - - private func baseCacheURL() -> URL? { - switch self { - case .fireproof: - let groupName = BookmarksDatabase.Constants.bookmarksGroupID - return FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: groupName) - - case .tabs: - return FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first - } - } - - private func migrateBookmarksCacheContents(to url: URL) { - guard let cacheUrl = CacheType.fireproof.cacheLocation() else { return } - - // Using hardcoded path as this is a one time migration - let bookmarksCache = cacheUrl.appendingPathComponent("com.onevcat.Kingfisher.ImageCache.bookmarks") - guard FileManager.default.fileExists(atPath: bookmarksCache.path) else { return } - - if let contents = try? FileManager.default.contentsOfDirectory(at: bookmarksCache, includingPropertiesForKeys: nil, options: []) { - contents.forEach { - let destination = url.appendingPathComponent($0.lastPathComponent) - try? FileManager.default.moveItem(at: $0, to: destination) - } - } - - do { - try FileManager.default.removeItem(at: bookmarksCache) - } catch { - Logger.general.error("Failed to remove favicon bookmarks cache: \(error.localizedDescription, privacy: .public)") - } - } - } - public static let shared = Favicons() @UserDefaultsWrapper(key: .faviconSizeNeedsMigration, defaultValue: true) @@ -219,7 +140,7 @@ public class Favicons { } } - public func clearCache(_ cacheType: CacheType, clearMemoryCache: Bool = false) { + public func clearCache(_ cacheType: FaviconsCacheType, clearMemoryCache: Bool = false) { Constants.caches[cacheType]?.clearDiskCache() if clearMemoryCache { @@ -227,12 +148,12 @@ public class Favicons { } } - private func removeFavicon(forDomain domain: String, fromCache cacheType: CacheType) { + private func removeFavicon(forDomain domain: String, fromCache cacheType: FaviconsCacheType) { let key = defaultResource(forDomain: domain)?.cacheKey ?? domain Constants.caches[cacheType]?.removeImage(forKey: key, fromDisk: true) } - private func removeFavicon(forCacheKey key: String, fromCache cacheType: CacheType) { + private func removeFavicon(forCacheKey key: String, fromCache cacheType: FaviconsCacheType) { Constants.caches[cacheType]?.removeImage(forKey: key, fromDisk: true) } @@ -253,7 +174,7 @@ public class Favicons { removeFavicon(forCacheKey: key, fromCache: .tabs) } - private func copyFavicon(forDomain domain: String, fromCache: CacheType, toCache: CacheType, completion: ((UIImage?) -> Void)? = nil) { + private func copyFavicon(forDomain domain: String, fromCache: FaviconsCacheType, toCache: FaviconsCacheType, completion: ((UIImage?) -> Void)? = nil) { guard let resource = defaultResource(forDomain: domain), let options = kfOptions(forDomain: domain, usingCache: toCache) else { return } @@ -277,8 +198,8 @@ public class Favicons { // e.g. if launching a bookmark, or clicking on a tab. public func loadFavicon(forDomain domain: String?, fromURL url: URL? = nil, - intoCache targetCacheType: CacheType, - fromCache: CacheType? = nil, + intoCache targetCacheType: FaviconsCacheType, + fromCache: FaviconsCacheType? = nil, queue: DispatchQueue? = OperationQueue.current?.underlyingQueue, completion: ((UIImage?) -> Void)? = nil) { @@ -438,10 +359,10 @@ public class Favicons { } public func defaultResource(forDomain domain: String?) -> KF.ImageResource? { - return FaviconsHelper.defaultResource(forDomain: domain, sourcesProvider: sourcesProvider) + return CoreFaviconsHelper.defaultResource(forDomain: domain, sourcesProvider: sourcesProvider) } - public func kfOptions(forDomain domain: String?, withURL url: URL? = nil, usingCache cacheType: CacheType) -> KingfisherOptionsInfo? { + public func kfOptions(forDomain domain: String?, withURL url: URL? = nil, usingCache cacheType: FaviconsCacheType) -> KingfisherOptionsInfo? { guard let domain = domain else { return nil } @@ -473,10 +394,6 @@ public class Favicons { ] } - public static func createHash(ofDomain domain: String) -> String { - return "\(Constants.salt)\(domain)".sha256() - } - } extension Favicons: Bookmarks.FaviconStoring { @@ -509,3 +426,63 @@ extension Favicons: Bookmarks.FaviconStoring { } } } + +extension FaviconsCacheType { + + func create() -> ImageCache { + + // If unable to create cache in desired location default to Kingfisher's default location which is Library/Cache. Images may disappear + // but at least the app won't crash. This should not happen. + let cache = createCacheInDesiredLocation() ?? ImageCache(name: rawValue) + + // We hash the resource key when loading the resource so don't use Kingfisher's hashing which is md5 based + cache.diskStorage.config.usesHashedFileName = false + + if self == .fireproof { + migrateBookmarksCacheContents(to: cache.diskStorage.directoryURL) + } + + return cache + } + + private func createCacheInDesiredLocation() -> ImageCache? { + + guard var url = cacheLocation() else { return nil } + + if !FileManager.default.fileExists(atPath: url.path) { + try? FileManager.default.createDirectory(at: url, + withIntermediateDirectories: true, + attributes: nil) + + // Exclude from backup + var resourceValues = URLResourceValues() + resourceValues.isExcludedFromBackup = true + try? url.setResourceValues(resourceValues) + } + + Logger.general.debug("favicons \(rawValue) location \(url.absoluteString)") + return try? ImageCache(name: self.rawValue, cacheDirectoryURL: url) + } + + private func migrateBookmarksCacheContents(to url: URL) { + guard let cacheUrl = FaviconsCacheType.fireproof.cacheLocation() else { return } + + // Using hardcoded path as this is a one time migration + let bookmarksCache = cacheUrl.appendingPathComponent("com.onevcat.Kingfisher.ImageCache.bookmarks") + guard FileManager.default.fileExists(atPath: bookmarksCache.path) else { return } + + if let contents = try? FileManager.default.contentsOfDirectory(at: bookmarksCache, includingPropertiesForKeys: nil, options: []) { + contents.forEach { + let destination = url.appendingPathComponent($0.lastPathComponent) + try? FileManager.default.moveItem(at: $0, to: destination) + } + } + + do { + try FileManager.default.removeItem(at: bookmarksCache) + } catch { + Logger.general.error("Failed to remove favicon bookmarks cache: \(error.localizedDescription, privacy: .public)") + } + } + +} diff --git a/DuckDuckGo/FaviconsHelper.swift b/DuckDuckGo/FaviconsHelper.swift index 051acccb6a..4a64b0d09b 100644 --- a/DuckDuckGo/FaviconsHelper.swift +++ b/DuckDuckGo/FaviconsHelper.swift @@ -27,7 +27,7 @@ struct FaviconsHelper { private static let tld: TLD = AppDependencyProvider.shared.storageCache.tld static func loadFaviconSync(forDomain domain: String?, - usingCache cacheType: Favicons.CacheType, + usingCache cacheType: FaviconsCacheType, useFakeFavicon: Bool, preferredFakeFaviconLetters: String? = nil) -> (image: UIImage?, isFake: Bool) { @@ -90,7 +90,7 @@ struct FaviconsHelper { } static func loadFaviconSync(forDomain domain: String?, - usingCache cacheType: Favicons.CacheType, + usingCache cacheType: FaviconsCacheType, useFakeFavicon: Bool, preferredFakeFaviconLetters: String? = nil, completion: ((UIImage?, Bool) -> Void)? = nil) { diff --git a/DuckDuckGo/FireproofFaviconUpdater.swift b/DuckDuckGo/FireproofFaviconUpdater.swift index 09cc60f967..6d7de2439a 100644 --- a/DuckDuckGo/FireproofFaviconUpdater.swift +++ b/DuckDuckGo/FireproofFaviconUpdater.swift @@ -31,14 +31,14 @@ extension Tab: TabNotifying {} protocol FaviconProviding { - func loadFavicon(forDomain domain: String, fromURL url: URL?, intoCache cacheType: Favicons.CacheType, completion: ((UIImage?) -> Void)?) + func loadFavicon(forDomain domain: String, fromURL url: URL?, intoCache cacheType: FaviconsCacheType, completion: ((UIImage?) -> Void)?) func replaceFireproofFavicon(forDomain domain: String?, withImage: UIImage) } extension Favicons: FaviconProviding { - func loadFavicon(forDomain domain: String, fromURL url: URL?, intoCache cacheType: CacheType, completion: ((UIImage?) -> Void)?) { + func loadFavicon(forDomain domain: String, fromURL url: URL?, intoCache cacheType: FaviconsCacheType, completion: ((UIImage?) -> Void)?) { self.loadFavicon(forDomain: domain, fromURL: url, intoCache: cacheType, fromCache: nil, completion: completion) } diff --git a/DuckDuckGo/ImageCacheDebugViewController.swift b/DuckDuckGo/ImageCacheDebugViewController.swift index 1c6cd52b1c..8c98e4f62d 100644 --- a/DuckDuckGo/ImageCacheDebugViewController.swift +++ b/DuckDuckGo/ImageCacheDebugViewController.swift @@ -89,13 +89,13 @@ class ImageCacheDebugViewController: UITableViewController { } private func loadAllFireproofFavicons() { - guard let cacheUrl = Favicons.CacheType.fireproof.cacheLocation() else { return } + guard let cacheUrl = FaviconsCacheType.fireproof.cacheLocation() else { return } let fireproofCacheUrl = cacheUrl.appendingPathComponent(Constants.fireproofCachePath) fireproofFavicons = loadFaviconImages(from: fireproofCacheUrl) } private func loadAllTabFavicons() { - guard let cacheUrl = Favicons.CacheType.tabs.cacheLocation() else { return } + guard let cacheUrl = FaviconsCacheType.tabs.cacheLocation() else { return } let tabCacheUrl = cacheUrl.appendingPathComponent(Constants.tabsCachePath) tabFavicons = loadFaviconImages(from: tabCacheUrl) } diff --git a/DuckDuckGo/TabManager.swift b/DuckDuckGo/TabManager.swift index 9f9786fecb..2690c7b518 100644 --- a/DuckDuckGo/TabManager.swift +++ b/DuckDuckGo/TabManager.swift @@ -311,7 +311,7 @@ class TabManager { DispatchQueue.global(qos: .background).async { [weak self] in guard let self = self, - let tabsCacheUrl = Favicons.CacheType.tabs.cacheLocation()?.appendingPathComponent(Favicons.Constants.tabsCachePath), + let tabsCacheUrl = FaviconsCacheType.tabs.cacheLocation()?.appendingPathComponent(Favicons.Constants.tabsCachePath), let contents = try? FileManager.default.contentsOfDirectory(at: tabsCacheUrl, includingPropertiesForKeys: nil, options: []), !contents.isEmpty else { return } @@ -327,7 +327,7 @@ class TabManager { }) // hash the unique tab hosts - let tabLinksHashed = tabLink.map { Favicons.createHash(ofDomain: $0) } + let tabLinksHashed = tabLink.map { FaviconHasher.createHash(ofDomain: $0) } // filter images that don't have a corresponding tab let toDelete = imageDomainURLs.filter { !tabLinksHashed.contains($0) } diff --git a/DuckDuckGo/UIImageViewExtension.swift b/DuckDuckGo/UIImageViewExtension.swift index dc2f7bf4e0..0678c54e78 100644 --- a/DuckDuckGo/UIImageViewExtension.swift +++ b/DuckDuckGo/UIImageViewExtension.swift @@ -25,7 +25,7 @@ extension UIImageView { /// Load a favicon from the cache in to this uiview. This will not load the favicon from the network. func loadFavicon(forDomain domain: String?, - usingCache cacheType: Favicons.CacheType, + usingCache cacheType: FaviconsCacheType, useFakeFavicon: Bool = true, preferredFakeFaviconLetters: String? = nil, completion: ((UIImage?, Bool) -> Void)? = nil) { diff --git a/DuckDuckGoTests/FaviconsTests.swift b/DuckDuckGoTests/FaviconsTests.swift index ddbb966caf..5641df7876 100644 --- a/DuckDuckGoTests/FaviconsTests.swift +++ b/DuckDuckGoTests/FaviconsTests.swift @@ -110,7 +110,7 @@ class FaviconsTests: XCTestCase { func testWhenGeneratingKingfisherResourceThenCorrectKeyAndURLAreGenerated() { let resource = favicons.defaultResource(forDomain: Constants.exampleDomain) - XCTAssertEqual(resource?.cacheKey, "\(Favicons.Constants.salt)\(Constants.exampleDomain)".sha256()) + XCTAssertEqual(resource?.cacheKey, "\(FaviconHasher.salt)\(Constants.exampleDomain)".sha256()) XCTAssertEqual(resource?.downloadURL, URL(string: "https://example.com/apple-touch-icon.png")) } diff --git a/DuckDuckGoTests/FireproofFaviconUpdaterTests.swift b/DuckDuckGoTests/FireproofFaviconUpdaterTests.swift index 3a1925f62d..eceebc598c 100644 --- a/DuckDuckGoTests/FireproofFaviconUpdaterTests.swift +++ b/DuckDuckGoTests/FireproofFaviconUpdaterTests.swift @@ -34,7 +34,7 @@ class FireproofFaviconUpdaterTests: XCTestCase, TabNotifying, FaviconProviding { var loadFaviconDomain: String? var loadFaviconURL: URL? - var loadFaviconCache: Favicons.CacheType? + var loadFaviconCache: FaviconsCacheType? var image: UIImage? @@ -120,7 +120,7 @@ class FireproofFaviconUpdaterTests: XCTestCase, TabNotifying, FaviconProviding { didUpdateFaviconCalled = true } - func loadFavicon(forDomain domain: String, fromURL url: URL?, intoCache cacheType: Favicons.CacheType, completion: ((UIImage?) -> Void)?) { + func loadFavicon(forDomain domain: String, fromURL url: URL?, intoCache cacheType: FaviconsCacheType, completion: ((UIImage?) -> Void)?) { loadFaviconDomain = domain loadFaviconURL = url loadFaviconCache = cacheType diff --git a/DuckDuckGoTests/NotFoundCachingDownloaderTests.swift b/DuckDuckGoTests/NotFoundCachingDownloaderTests.swift index 661e14f46e..c911b9d8e0 100644 --- a/DuckDuckGoTests/NotFoundCachingDownloaderTests.swift +++ b/DuckDuckGoTests/NotFoundCachingDownloaderTests.swift @@ -38,6 +38,12 @@ class NotFoundCachingDownloaderTests: XCTestCase { super.tearDown() } + // If this test fails... ask yourself why have you changed the salt? + // If it was intentional, then please update this test. + func testSaltValueHasNotChanged() { + XCTAssertEqual("DDGSalt:", FaviconHasher.salt) + } + func testWhenURLSavedNotStoredInPlainText() { downloader.noFaviconsFound(forDomain: "example.com") @@ -49,7 +55,7 @@ class NotFoundCachingDownloaderTests: XCTestCase { XCTAssertEqual(1, domains.count) domains.forEach { - XCTAssertEqual($0.key, "\(Favicons.Constants.salt)example.com".sha256()) + XCTAssertEqual($0.key, "\(FaviconHasher.salt)example.com".sha256()) } } diff --git a/Widgets/Widgets.swift b/Widgets/Widgets.swift index 4972055a03..8e545bb352 100644 --- a/Widgets/Widgets.swift +++ b/Widgets/Widgets.swift @@ -142,8 +142,8 @@ class Provider: TimelineProvider { private func loadImageFromCache(forDomain domain: String?) -> UIImage? { guard let domain = domain else { return nil } - let key = Favicons.createHash(ofDomain: domain) - guard let cacheUrl = Favicons.CacheType.fireproof.cacheLocation() else { return nil } + let key = FaviconHasher.createHash(ofDomain: domain) + guard let cacheUrl = FaviconsCacheType.fireproof.cacheLocation() else { return nil } // Slight leap here to avoid loading Kingisher as a library for the widgets. // Once dependency management is fixed, link it and use Favicons directly.