diff --git a/Core/CookieStorage.swift b/Core/CookieStorage.swift index aca9ce7975..978d69197a 100644 --- a/Core/CookieStorage.swift +++ b/Core/CookieStorage.swift @@ -95,7 +95,7 @@ public class CookieStorage { /// Update ALL cookies. The absence of cookie domains here indicateds they have been removed by the website, so be sure to call this with all cookies that might need to be persisted even if those websites have not been visited yet. @discardableResult - func updateCookies(_ cookies: [HTTPCookie], keepingPreservedLogins preservedLogins: PreserveLogins) -> CookieDomainsOnUpdateDiagnostic { + func updateCookies(_ cookies: [HTTPCookie], preservingFireproofedDomains fireproofing: Fireproofing) -> CookieDomainsOnUpdateDiagnostic { guard isConsumed else { return .notConsumed } isConsumed = false @@ -130,7 +130,7 @@ public class CookieStorage { persistedCookiesByDomain.keys.forEach { guard !URL.isDuckDuckGo(domain: $0) else { return } // DDG cookies are for SERP settings only - if !preservedLogins.isAllowed(cookieDomain: $0) { + if !fireproofing.isAllowed(cookieDomain: $0) { persistedCookiesByDomain.removeValue(forKey: $0) } } 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/FaviconRequestModifier.swift b/Core/FaviconRequestModifier.swift index bc9ab68205..899ad75c19 100644 --- a/Core/FaviconRequestModifier.swift +++ b/Core/FaviconRequestModifier.swift @@ -17,6 +17,7 @@ // limitations under the License. // +import Core import Kingfisher class FaviconRequestModifier: ImageDownloadRequestModifier { 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/FaviconsHelper.swift b/Core/FaviconsHelper.swift deleted file mode 100644 index 478da57e54..0000000000 --- a/Core/FaviconsHelper.swift +++ /dev/null @@ -1,34 +0,0 @@ -// -// FaviconsHelper.swift -// DuckDuckGo -// -// Copyright © 2022 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 - -struct FaviconsHelper { - - // this function is now static and outside of Favicons, otherwise there is a circular dependency between - // Favicons and NotFoundCachingDownloader - public static func defaultResource(forDomain domain: String?, sourcesProvider: FaviconSourcesProvider) -> KF.ImageResource? { - guard let domain = domain, - let source = sourcesProvider.mainSource(forDomain: domain) else { return nil } - - let key = Favicons.createHash(ofDomain: domain) - return KF.ImageResource(downloadURL: source, cacheKey: key) - } -} diff --git a/Core/PreserveLogins.swift b/Core/Fireproofing.swift similarity index 70% rename from Core/PreserveLogins.swift rename to Core/Fireproofing.swift index 6ce320e0ef..166c04c562 100644 --- a/Core/PreserveLogins.swift +++ b/Core/Fireproofing.swift @@ -1,5 +1,5 @@ // -// PreserveLogins.swift +// Fireproofing.swift // Core // // Copyright © 2020 DuckDuckGo. All rights reserved. @@ -19,18 +19,32 @@ import Foundation -public class PreserveLogins { +public protocol Fireproofing { + + var loginDetectionEnabled: Bool { get set } + var allowedDomains: [String] { get } + + func isAllowed(cookieDomain: String) -> Bool + func isAllowed(fireproofDomain domain: String) -> Bool + func addToAllowed(domain: String) + func remove(domain: String) + func clearAll() + +} + +// This class is not final because we override allowed domains in WebCacheManagerTests +public class UserDefaultsFireproofing: Fireproofing { + + public static let shared: Fireproofing = UserDefaultsFireproofing() public struct Notifications { public static let loginDetectionStateChanged = Foundation.Notification.Name("com.duckduckgo.ios.PreserveLogins.loginDetectionStateChanged") } - public static let shared = PreserveLogins() - - @UserDefaultsWrapper(key: .preserveLoginsAllowedDomains, defaultValue: []) + @UserDefaultsWrapper(key: .fireproofingAllowedDomains, defaultValue: []) private(set) public var allowedDomains: [String] - @UserDefaultsWrapper(key: .preserveLoginsDetectionEnabled, defaultValue: false) + @UserDefaultsWrapper(key: .fireproofingDetectionEnabled, defaultValue: false) public var loginDetectionEnabled: Bool { didSet { NotificationCenter.default.post(name: Notifications.loginDetectionStateChanged, object: nil) diff --git a/Core/NotFoundCachingDownloader.swift b/Core/NotFoundCachingDownloader.swift index c9ba6daa67..16a957c625 100644 --- a/Core/NotFoundCachingDownloader.swift +++ b/Core/NotFoundCachingDownloader.swift @@ -17,6 +17,7 @@ // limitations under the License. // +import Core import Kingfisher class NotFoundCachingDownloader: ImageDownloader { diff --git a/Core/SyncBookmarksAdapter.swift b/Core/SyncBookmarksAdapter.swift index a50d7315c8..0eb8525d82 100644 --- a/Core/SyncBookmarksAdapter.swift +++ b/Core/SyncBookmarksAdapter.swift @@ -66,6 +66,7 @@ public final class SyncBookmarksAdapter { public let databaseCleaner: BookmarkDatabaseCleaner public let syncDidCompletePublisher: AnyPublisher let syncErrorHandler: SyncErrorHandling + private let faviconStoring: FaviconStoring @UserDefaultsWrapper(key: .syncDidMigrateToImprovedListsHandling, defaultValue: false) private var didMigrateToImprovedListsHandling: Bool @@ -88,10 +89,13 @@ public final class SyncBookmarksAdapter { public init(database: CoreDataDatabase, favoritesDisplayModeStorage: FavoritesDisplayModeStoring, - syncErrorHandler: SyncErrorHandling) { + syncErrorHandler: SyncErrorHandling, + faviconStoring: FaviconStoring) { self.database = database self.favoritesDisplayModeStorage = favoritesDisplayModeStorage self.syncErrorHandler = syncErrorHandler + self.faviconStoring = faviconStoring + syncDidCompletePublisher = syncDidCompleteSubject.eraseToAnyPublisher() databaseCleaner = BookmarkDatabaseCleaner( bookmarkDatabase: database, @@ -172,7 +176,7 @@ public final class SyncBookmarksAdapter { database: database, stateStore: stateStore, fetcher: FaviconFetcher(), - faviconStore: Favicons.shared, + faviconStore: faviconStoring, errorEvents: BookmarksFaviconsFetcherErrorHandler() ) } diff --git a/Core/SyncDataProviders.swift b/Core/SyncDataProviders.swift index fd7c595947..a32f3b8508 100644 --- a/Core/SyncDataProviders.swift +++ b/Core/SyncDataProviders.swift @@ -100,14 +100,16 @@ public class SyncDataProviders: DataProvidersSource { secureVaultErrorReporter: SecureVaultReporting, settingHandlers: [SettingSyncHandler], favoritesDisplayModeStorage: FavoritesDisplayModeStoring, - syncErrorHandler: SyncErrorHandling + syncErrorHandler: SyncErrorHandling, + faviconStoring: FaviconStoring ) { self.bookmarksDatabase = bookmarksDatabase self.secureVaultFactory = secureVaultFactory self.secureVaultErrorReporter = secureVaultErrorReporter bookmarksAdapter = SyncBookmarksAdapter(database: bookmarksDatabase, favoritesDisplayModeStorage: favoritesDisplayModeStorage, - syncErrorHandler: syncErrorHandler) + syncErrorHandler: syncErrorHandler, + faviconStoring: faviconStoring) credentialsAdapter = SyncCredentialsAdapter(secureVaultFactory: secureVaultFactory, secureVaultErrorReporter: secureVaultErrorReporter, syncErrorHandler: syncErrorHandler) diff --git a/Core/UserDefaultsPropertyWrapper.swift b/Core/UserDefaultsPropertyWrapper.swift index ac6a5a6bdf..4858ad964d 100644 --- a/Core/UserDefaultsPropertyWrapper.swift +++ b/Core/UserDefaultsPropertyWrapper.swift @@ -37,9 +37,9 @@ public struct UserDefaultsWrapper { case gridViewEnabled = "com.duckduckgo.ios.tabs.grid" case gridViewSeen = "com.duckduckgo.ios.tabs.seen" - case preserveLoginsAllowedDomains = "com.duckduckgo.ios.PreserveLogins.userDecision.allowedDomains2" - case preserveLoginsDetectionEnabled = "com.duckduckgo.ios.PreserveLogins.detectionEnabled" - case preserveLoginsLegacyAllowedDomains = "com.duckduckgo.ios.PreserveLogins.userDecision.allowedDomains" + case fireproofingAllowedDomains = "com.duckduckgo.ios.PreserveLogins.userDecision.allowedDomains2" + case fireproofingDetectionEnabled = "com.duckduckgo.ios.PreserveLogins.detectionEnabled" + case fireproofingLegacyAllowedDomains = "com.duckduckgo.ios.PreserveLogins.userDecision.allowedDomains" case daxIsDismissed = "com.duckduckgo.ios.daxOnboardingIsDismissed" case daxHomeScreenMessagesSeen = "com.duckduckgo.ios.daxOnboardingHomeScreenMessagesSeen" diff --git a/Core/WebCacheManager.swift b/Core/WebCacheManager.swift index b6655f82b6..13ce952ba8 100644 --- a/Core/WebCacheManager.swift +++ b/Core/WebCacheManager.swift @@ -78,7 +78,7 @@ public class WebCacheManager { } public func clear(cookieStorage: CookieStorage = CookieStorage(), - logins: PreserveLogins = PreserveLogins.shared, + fireproofing: Fireproofing = UserDefaultsFireproofing.shared, dataStoreIdManager: DataStoreIdManaging = DataStoreIdManager.shared) async { var cookiesToUpdate = [HTTPCookie]() @@ -92,7 +92,7 @@ public class WebCacheManager { // Perform legacy clearing to migrate to new container cookiesToUpdate += await legacyDataClearing() ?? [] - cookieStorage.updateCookies(cookiesToUpdate, keepingPreservedLogins: logins) + cookieStorage.updateCookies(cookiesToUpdate, preservingFireproofedDomains: fireproofing) // Attempt to clean up leftover stores again after a delay // This should not be a problem as these containers are not supposed to be used anymore. diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 49a05c4c5e..e1cb0c6e16 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -404,7 +404,7 @@ 85047B8A1F69692C002A95D8 /* contentblocker.js in Resources */ = {isa = PBXBuildFile; fileRef = 85047B891F69692C002A95D8 /* contentblocker.js */; }; 85047C772A0D5D3D00D2FF3F /* SyncSettingsViewController+SyncDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85047C762A0D5D3D00D2FF3F /* SyncSettingsViewController+SyncDelegate.swift */; }; 850559C923C61B5D0055C0D5 /* login-form-detection.js in Resources */ = {isa = PBXBuildFile; fileRef = 850559C823C61B5D0055C0D5 /* login-form-detection.js */; }; - 850559D023CF647C0055C0D5 /* PreserveLogins.swift in Sources */ = {isa = PBXBuildFile; fileRef = 850559CF23CF647C0055C0D5 /* PreserveLogins.swift */; }; + 850559D023CF647C0055C0D5 /* Fireproofing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 850559CF23CF647C0055C0D5 /* Fireproofing.swift */; }; 850559D223CF710C0055C0D5 /* WebCacheManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 850559D123CF710C0055C0D5 /* WebCacheManagerTests.swift */; }; 85058366219AE9EA00ED4EDB /* HomePageConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85058365219AE9EA00ED4EDB /* HomePageConfiguration.swift */; }; 85058369219F424500ED4EDB /* UIColorExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1B745211E549D550072547E /* UIColorExtension.swift */; }; @@ -468,10 +468,10 @@ 853A717620F62FE800FE60BC /* Pixel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 853A717520F62FE800FE60BC /* Pixel.swift */; }; 853A717820F645FB00FE60BC /* PixelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 853A717720F645FB00FE60BC /* PixelTests.swift */; }; 853C5F6121C277C7001F7A05 /* global.swift in Sources */ = {isa = PBXBuildFile; fileRef = 853C5F6021C277C7001F7A05 /* global.swift */; }; - 8540BBA22440857A00017FE4 /* PreserveLoginsWorker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8540BBA12440857A00017FE4 /* PreserveLoginsWorker.swift */; }; - 8540BD5223D8C2220057FDD2 /* PreserveLoginsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8540BD5123D8C2220057FDD2 /* PreserveLoginsTests.swift */; }; - 8540BD5423D8D5080057FDD2 /* PreserveLoginsAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8540BD5323D8D5080057FDD2 /* PreserveLoginsAlert.swift */; }; - 8540BD5623D9E9C20057FDD2 /* PreserveLoginsSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8540BD5523D9E9C20057FDD2 /* PreserveLoginsSettingsViewController.swift */; }; + 8540BBA22440857A00017FE4 /* FireproofingWorking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8540BBA12440857A00017FE4 /* FireproofingWorking.swift */; }; + 8540BD5223D8C2220057FDD2 /* UserDefaultsFireproofingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8540BD5123D8C2220057FDD2 /* UserDefaultsFireproofingTests.swift */; }; + 8540BD5423D8D5080057FDD2 /* FireproofingAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8540BD5323D8D5080057FDD2 /* FireproofingAlert.swift */; }; + 8540BD5623D9E9C20057FDD2 /* FireproofingSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8540BD5523D9E9C20057FDD2 /* FireproofingSettingsViewController.swift */; }; 85449EF523FDA02800512AAF /* KeyboardSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85449EF423FDA02800512AAF /* KeyboardSettingsViewController.swift */; }; 85449EFB23FDA0BC00512AAF /* UserDefaultsPropertyWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85449EFA23FDA0BC00512AAF /* UserDefaultsPropertyWrapper.swift */; }; 85449EFD23FDA71F00512AAF /* KeyboardSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85449EFC23FDA71F00512AAF /* KeyboardSettings.swift */; }; @@ -521,6 +521,14 @@ 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 */; }; + 8598D2E02CEB98B500C45685 /* Favicons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85CA53A324B9F2BD00A6288C /* Favicons.swift */; }; + 8598D2E12CEB98B500C45685 /* NotFoundCachingDownloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85CA53A924BB376800A6288C /* NotFoundCachingDownloader.swift */; }; + 8598D2E22CEB98B500C45685 /* FaviconRequestModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85CA53AB24BBD39300A6288C /* FaviconRequestModifier.swift */; }; + 8598D2E32CEB98B500C45685 /* FaviconUserScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85D2187A24BF9F85004373D2 /* FaviconUserScript.swift */; }; + 8598D2E42CEB98B500C45685 /* FaviconSourcesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85D2187524BF6164004373D2 /* FaviconSourcesProvider.swift */; }; + 8598D2E62CEBAA1F00C45685 /* MockFaviconStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8598D2E52CEBAA1B00C45685 /* MockFaviconStore.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 */; }; @@ -556,15 +564,10 @@ 85C2971A248162CA0063A335 /* DaxOnboardingPadViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85C29719248162CA0063A335 /* DaxOnboardingPadViewController.swift */; }; 85C8E61D2B0E47380029A6BD /* BookmarksDatabaseSetup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85C8E61C2B0E47380029A6BD /* BookmarksDatabaseSetup.swift */; }; 85C91CA224671F4C00A11132 /* AppDeepLinkSchemes.swift in Sources */ = {isa = PBXBuildFile; fileRef = F17D723B1E8BB374003E8B0E /* AppDeepLinkSchemes.swift */; }; - 85CA53A824BB343700A6288C /* Favicons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85CA53A324B9F2BD00A6288C /* Favicons.swift */; }; - 85CA53AA24BB376800A6288C /* NotFoundCachingDownloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85CA53A924BB376800A6288C /* NotFoundCachingDownloader.swift */; }; - 85CA53AC24BBD39300A6288C /* FaviconRequestModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85CA53AB24BBD39300A6288C /* FaviconRequestModifier.swift */; }; 85D2187024BF24DB004373D2 /* FaviconRequestModifierTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85D2186F24BF24DB004373D2 /* FaviconRequestModifierTests.swift */; }; 85D2187224BF24F2004373D2 /* NotFoundCachingDownloaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85D2187124BF24F2004373D2 /* NotFoundCachingDownloaderTests.swift */; }; 85D2187424BF25CD004373D2 /* FaviconsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85D2187324BF25CD004373D2 /* FaviconsTests.swift */; }; - 85D2187624BF6164004373D2 /* FaviconSourcesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85D2187524BF6164004373D2 /* FaviconSourcesProvider.swift */; }; 85D2187924BF6B8B004373D2 /* FaviconSourcesProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85D2187724BF6B88004373D2 /* FaviconSourcesProviderTests.swift */; }; - 85D2187B24BF9F85004373D2 /* FaviconUserScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85D2187A24BF9F85004373D2 /* FaviconUserScript.swift */; }; 85D598872927F84C00FA3B1B /* Crashes in Frameworks */ = {isa = PBXBuildFile; productRef = 85D598862927F84C00FA3B1B /* Crashes */; }; 85DB12EB2A1FE2A4000A4A72 /* LockScreenWidgets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85DB12EA2A1FE2A4000A4A72 /* LockScreenWidgets.swift */; }; 85DB12ED2A1FED0C000A4A72 /* AppDelegate+AppDeepLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85DB12EC2A1FED0C000A4A72 /* AppDelegate+AppDeepLinks.swift */; }; @@ -925,7 +928,6 @@ 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 */; }; 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 */; }; @@ -1408,7 +1410,6 @@ 1E7A711B2934EEBC00B7EA19 /* OmniBarNotification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OmniBarNotification.swift; sourceTree = ""; }; 1E8146A728C8AB3F00D1AF63 /* TrackerAnimationLogicTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackerAnimationLogicTests.swift; sourceTree = ""; }; 1E8146A928C8AB8200D1AF63 /* PrivacyIconLogicTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivacyIconLogicTests.swift; sourceTree = ""; }; - 1E865AEF272042DB001C74F3 /* TextSizeSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextSizeSettingsViewController.swift; sourceTree = ""; }; 1E87615828A1517200C7C5CE /* PrivacyDashboardViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivacyDashboardViewController.swift; sourceTree = ""; }; 1E8AD1C627BE9B2900ABA377 /* DownloadsListDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadsListDataSource.swift; sourceTree = ""; }; 1E8AD1C827BFAD1500ABA377 /* DirectoryMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DirectoryMonitor.swift; sourceTree = ""; }; @@ -1728,7 +1729,7 @@ 85047B891F69692C002A95D8 /* contentblocker.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = contentblocker.js; sourceTree = ""; }; 85047C762A0D5D3D00D2FF3F /* SyncSettingsViewController+SyncDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SyncSettingsViewController+SyncDelegate.swift"; sourceTree = ""; }; 850559C823C61B5D0055C0D5 /* login-form-detection.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = "login-form-detection.js"; sourceTree = ""; }; - 850559CF23CF647C0055C0D5 /* PreserveLogins.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreserveLogins.swift; sourceTree = ""; }; + 850559CF23CF647C0055C0D5 /* Fireproofing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Fireproofing.swift; sourceTree = ""; }; 850559D123CF710C0055C0D5 /* WebCacheManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebCacheManagerTests.swift; sourceTree = ""; }; 85058365219AE9EA00ED4EDB /* HomePageConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomePageConfiguration.swift; sourceTree = ""; }; 850ABD002AC3961100A733DF /* MainViewController+Segues.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainViewController+Segues.swift"; sourceTree = ""; }; @@ -1782,10 +1783,10 @@ 853A717520F62FE800FE60BC /* Pixel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Pixel.swift; sourceTree = ""; }; 853A717720F645FB00FE60BC /* PixelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PixelTests.swift; sourceTree = ""; }; 853C5F6021C277C7001F7A05 /* global.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = global.swift; sourceTree = ""; }; - 8540BBA12440857A00017FE4 /* PreserveLoginsWorker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreserveLoginsWorker.swift; sourceTree = ""; }; - 8540BD5123D8C2220057FDD2 /* PreserveLoginsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreserveLoginsTests.swift; sourceTree = ""; }; - 8540BD5323D8D5080057FDD2 /* PreserveLoginsAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreserveLoginsAlert.swift; sourceTree = ""; }; - 8540BD5523D9E9C20057FDD2 /* PreserveLoginsSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreserveLoginsSettingsViewController.swift; sourceTree = ""; }; + 8540BBA12440857A00017FE4 /* FireproofingWorking.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FireproofingWorking.swift; sourceTree = ""; }; + 8540BD5123D8C2220057FDD2 /* UserDefaultsFireproofingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserDefaultsFireproofingTests.swift; sourceTree = ""; }; + 8540BD5323D8D5080057FDD2 /* FireproofingAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FireproofingAlert.swift; sourceTree = ""; }; + 8540BD5523D9E9C20057FDD2 /* FireproofingSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FireproofingSettingsViewController.swift; sourceTree = ""; }; 85449EF423FDA02800512AAF /* KeyboardSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardSettingsViewController.swift; sourceTree = ""; }; 85449EFA23FDA0BC00512AAF /* UserDefaultsPropertyWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaultsPropertyWrapper.swift; sourceTree = ""; }; 85449EFC23FDA71F00512AAF /* KeyboardSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardSettings.swift; sourceTree = ""; }; @@ -1836,6 +1837,9 @@ 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 = ""; }; + 8598D2E52CEBAA1B00C45685 /* MockFaviconStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockFaviconStore.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 = ""; }; @@ -1871,15 +1875,15 @@ 85C29709247EB7AA0063A335 /* Text.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Text.xcassets; sourceTree = ""; }; 85C29719248162CA0063A335 /* DaxOnboardingPadViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DaxOnboardingPadViewController.swift; sourceTree = ""; }; 85C8E61C2B0E47380029A6BD /* BookmarksDatabaseSetup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarksDatabaseSetup.swift; sourceTree = ""; }; - 85CA53A324B9F2BD00A6288C /* Favicons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Favicons.swift; path = ../DuckDuckGo/Favicons.swift; sourceTree = ""; }; - 85CA53A924BB376800A6288C /* NotFoundCachingDownloader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotFoundCachingDownloader.swift; sourceTree = ""; }; - 85CA53AB24BBD39300A6288C /* FaviconRequestModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FaviconRequestModifier.swift; sourceTree = ""; }; + 85CA53A324B9F2BD00A6288C /* Favicons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Favicons.swift; sourceTree = ""; }; + 85CA53A924BB376800A6288C /* NotFoundCachingDownloader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = NotFoundCachingDownloader.swift; path = ../Core/NotFoundCachingDownloader.swift; sourceTree = ""; }; + 85CA53AB24BBD39300A6288C /* FaviconRequestModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = FaviconRequestModifier.swift; path = ../Core/FaviconRequestModifier.swift; sourceTree = ""; }; 85D2186F24BF24DB004373D2 /* FaviconRequestModifierTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FaviconRequestModifierTests.swift; sourceTree = ""; }; 85D2187124BF24F2004373D2 /* NotFoundCachingDownloaderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotFoundCachingDownloaderTests.swift; sourceTree = ""; }; 85D2187324BF25CD004373D2 /* FaviconsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FaviconsTests.swift; sourceTree = ""; }; - 85D2187524BF6164004373D2 /* FaviconSourcesProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FaviconSourcesProvider.swift; sourceTree = ""; }; + 85D2187524BF6164004373D2 /* FaviconSourcesProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = FaviconSourcesProvider.swift; path = ../Core/FaviconSourcesProvider.swift; sourceTree = ""; }; 85D2187724BF6B88004373D2 /* FaviconSourcesProviderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FaviconSourcesProviderTests.swift; sourceTree = ""; }; - 85D2187A24BF9F85004373D2 /* FaviconUserScript.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FaviconUserScript.swift; sourceTree = ""; }; + 85D2187A24BF9F85004373D2 /* FaviconUserScript.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = FaviconUserScript.swift; path = ../Core/FaviconUserScript.swift; sourceTree = ""; }; 85D33FCB25C97B6E002B91A6 /* IntegrationTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = IntegrationTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 85D33FCF25C97B6E002B91A6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 85DB12EA2A1FE2A4000A4A72 /* LockScreenWidgets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LockScreenWidgets.swift; sourceTree = ""; }; @@ -2735,7 +2739,6 @@ 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 = ""; }; 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 = ""; }; @@ -3543,6 +3546,11 @@ 3157B43627F4C8380042D3D7 /* Favicons */ = { isa = PBXGroup; children = ( + 85CA53A324B9F2BD00A6288C /* Favicons.swift */, + 85CA53A924BB376800A6288C /* NotFoundCachingDownloader.swift */, + 85CA53AB24BBD39300A6288C /* FaviconRequestModifier.swift */, + 85D2187524BF6164004373D2 /* FaviconSourcesProvider.swift */, + 85D2187A24BF9F85004373D2 /* FaviconUserScript.swift */, 3157B43727F4C8490042D3D7 /* FaviconsHelper.swift */, ); name = Favicons; @@ -4386,8 +4394,7 @@ 98F0FC1F21FF18E700CE77AB /* AutoClearSettingsViewController.swift */, 1EE7C298294227EC0026C8CB /* AutoconsentSettingsViewController.swift */, 85449EF423FDA02800512AAF /* KeyboardSettingsViewController.swift */, - 8540BD5523D9E9C20057FDD2 /* PreserveLoginsSettingsViewController.swift */, - 1E865AEF272042DB001C74F3 /* TextSizeSettingsViewController.swift */, + 8540BD5523D9E9C20057FDD2 /* FireproofingSettingsViewController.swift */, 8531A08D1F9950E6000484F0 /* UnprotectedSitesViewController.swift */, 6FBF0F8A2BD7C0A900136CF0 /* AllProtectedCell.swift */, D6E83C672B23B6A3006C8AFB /* FontSettings.swift */, @@ -4575,12 +4582,8 @@ 85CA53A724BB342B00A6288C /* Favicons */ = { isa = PBXGroup; children = ( - C1CCCBA6283E101500CF3791 /* FaviconsHelper.swift */, - 85CA53A324B9F2BD00A6288C /* Favicons.swift */, - 85CA53A924BB376800A6288C /* NotFoundCachingDownloader.swift */, - 85CA53AB24BBD39300A6288C /* FaviconRequestModifier.swift */, - 85D2187524BF6164004373D2 /* FaviconSourcesProvider.swift */, - 85D2187A24BF9F85004373D2 /* FaviconUserScript.swift */, + 8598D2DB2CEB93A600C45685 /* FaviconsCacheType.swift */, + 8598D2DD2CEB97BE00C45685 /* FaviconHasher.swift */, ); name = Favicons; sourceTree = ""; @@ -5809,6 +5812,7 @@ 98559FD0267099F400A83094 /* ContentBlocker */, 31C138A127A334F600FFD4B2 /* Downloads */, D62EC3B72C24695800FC9D04 /* DuckPlayer */, + 85D2186E24BF24BA004373D2 /* Favicons */, 83134D7F20E2E013006CE65D /* Feedback */, 8588026724E4249800C24AB6 /* iPad */, 851DFD88212C5ED600D95F20 /* Main */, @@ -5906,7 +5910,7 @@ 83004E872193E8C700DA013C /* TabViewControllerLongPressMenuExtension.swift */, 8C47244F2217A14B004C9B2D /* TabViewControllerLongPressBookmarkExtension.swift */, 98999D5822FDA41500CBBE1B /* BasicAuthenticationAlert.swift */, - 8540BBA12440857A00017FE4 /* PreserveLoginsWorker.swift */, + 8540BBA12440857A00017FE4 /* FireproofingWorking.swift */, 8548D95D25262B1B005AAE49 /* ViewHighlighter.swift */, 8548D96725262C33005AAE49 /* view_highlight.json */, 31B524562715BB23002225AB /* WebJSAlert.swift */, @@ -6013,7 +6017,7 @@ 85BDC3132434D8F80053DB07 /* DebugUserScript.swift */, 4B60ACA0252EC0B100E8D219 /* FullScreenVideoUserScript.swift */, 85BDC3182436161C0053DB07 /* LoginFormDetectionUserScript.swift */, - 850559CF23CF647C0055C0D5 /* PreserveLogins.swift */, + 850559CF23CF647C0055C0D5 /* Fireproofing.swift */, 4B75EA9126A266CB00018634 /* PrintingUserScript.swift */, 988F3DCE237D5C0F00AEE34C /* SchemeHandler.swift */, 836A941C247F23C600BF8EF5 /* UserAgentManager.swift */, @@ -6068,6 +6072,7 @@ F17669A91E412A17003D3222 /* Mocks */ = { isa = PBXGroup; children = ( + 8598D2E52CEBAA1B00C45685 /* MockFaviconStore.swift */, 9F4CC51A2C48C0C7006A96EB /* MockTabDelegate.swift */, C14882E927F20DD000D59F0C /* MockBookmarksCoreDataStorage.swift */, C158AC7A297AB5DC0008723A /* MockSecureVault.swift */, @@ -6156,7 +6161,7 @@ isa = PBXGroup; children = ( 834DF990248FDDF60075EA48 /* UserAgentTests.swift */, - 8540BD5123D8C2220057FDD2 /* PreserveLoginsTests.swift */, + 8540BD5123D8C2220057FDD2 /* UserDefaultsFireproofingTests.swift */, 850559D123CF710C0055C0D5 /* WebCacheManagerTests.swift */, 981C49AF2C8FA61D00DF11E8 /* DataStoreIdManagerTests.swift */, F198D7971E3A45D90088DA8A /* WKWebViewConfigurationExtensionTests.swift */, @@ -6307,7 +6312,7 @@ 98EF177C21837E35006750C1 /* new_tab_dark.json */, 85371D232121B9D400920548 /* new_tab.json */, 31B2F11E287846320040427A /* NoMicPermissionAlert.swift */, - 8540BD5323D8D5080057FDD2 /* PreserveLoginsAlert.swift */, + 8540BD5323D8D5080057FDD2 /* FireproofingAlert.swift */, 850ABD022AC4D46C00A733DF /* SuggestionTray.storyboard */, 85864FBB24D31EF300E756FF /* SuggestionTrayViewController.swift */, 851DFD86212C39D300D95F20 /* TabSwitcherButton.swift */, @@ -6421,7 +6426,6 @@ 830FA79B1F8E81FB00FCE105 /* ContentBlocker */, F17D722C1E8B3563003E8B0E /* Domain */, EE3B226929DE0EE10082298A /* FeatureFlags */, - 85D2186E24BF24BA004373D2 /* Favicons */, F1134EC91F40E74800B73467 /* Statistics */, F198D78F1E3976300088DA8A /* Utilities */, F198D7961E3A45C00088DA8A /* Web */, @@ -7419,7 +7423,7 @@ 6F64AA592C4818D700CF4489 /* NewTabPageShortcut.swift in Sources */, 3132FA2627A0784600DD7A12 /* FilePreviewHelper.swift in Sources */, 9820FF502244FECC008D4782 /* UIScrollViewExtension.swift in Sources */, - 8540BD5423D8D5080057FDD2 /* PreserveLoginsAlert.swift in Sources */, + 8540BD5423D8D5080057FDD2 /* FireproofingAlert.swift in Sources */, 1E87615928A1517200C7C5CE /* PrivacyDashboardViewController.swift in Sources */, 6F03CAFE2C32DD08004179A8 /* HomePageMessagesConfiguration.swift in Sources */, 851952682CE2522700578553 /* AutocompleteSuggestionsDataSource.swift in Sources */, @@ -7435,7 +7439,7 @@ 31669B9A28020A460071CC18 /* SaveLoginViewModel.swift in Sources */, EE4FB1882A28D11900E5CBA7 /* NetworkProtectionStatusViewModel.swift in Sources */, 6FB1FE9E2C24D41D0075B68B /* NewTabPageSectionsDebugView.swift in Sources */, - 8540BD5623D9E9C20057FDD2 /* PreserveLoginsSettingsViewController.swift in Sources */, + 8540BD5623D9E9C20057FDD2 /* FireproofingSettingsViewController.swift in Sources */, 7B8E0EC62CC81B4900B2B722 /* TipKitController.swift in Sources */, 7B1604EC2CB68BDA00A44EC6 /* TipKitController+ConvenienceInitializers.swift in Sources */, 851672D12BED1FC900592F24 /* AutocompleteView.swift in Sources */, @@ -7755,6 +7759,11 @@ EEC02C142B0519DE0045CE11 /* NetworkProtectionVPNLocationViewModel.swift in Sources */, D63FF8962C1B67E9006DE24D /* YoutubeOverlayUserScript.swift in Sources */, F13B4BC01F180D8A00814661 /* TabsModel.swift in Sources */, + 8598D2E02CEB98B500C45685 /* Favicons.swift in Sources */, + 8598D2E12CEB98B500C45685 /* NotFoundCachingDownloader.swift in Sources */, + 8598D2E22CEB98B500C45685 /* FaviconRequestModifier.swift in Sources */, + 8598D2E32CEB98B500C45685 /* FaviconUserScript.swift in Sources */, + 8598D2E42CEB98B500C45685 /* FaviconSourcesProvider.swift in Sources */, BD862E052B30DB250073E2EE /* VPNFeedbackCategory.swift in Sources */, 85AE6690209724120014CF04 /* NotificationView.swift in Sources */, BDE91CE02C6515420005CB74 /* UnifiedFeedbackFormViewModel.swift in Sources */, @@ -7874,7 +7883,7 @@ D6ACEA322BBD55BF008FADDF /* TabURLInterceptor.swift in Sources */, C13F3F6A2B7F883A0083BE40 /* AuthConfirmationPromptViewController.swift in Sources */, 851624C72B96389D002D5CD7 /* HistoryDebugViewController.swift in Sources */, - 8540BBA22440857A00017FE4 /* PreserveLoginsWorker.swift in Sources */, + 8540BBA22440857A00017FE4 /* FireproofingWorking.swift in Sources */, 0283A2012C6E46E300508FBD /* BrokenSitePromptLimiterStore.swift in Sources */, 85DFEDF924CF3D0E00973FE7 /* TabsBarCell.swift in Sources */, 851672D32BED23FE00592F24 /* AutocompleteViewModel.swift in Sources */, @@ -8029,6 +8038,7 @@ F1134EBC1F40D45700B73467 /* MockStatisticsStore.swift in Sources */, 9FCFCD802C6AF56D006EB7A0 /* LaunchOptionsHandlerTests.swift in Sources */, 983C52E72C2C0ACB007B5747 /* BookmarkStateRepairTests.swift in Sources */, + 8598D2E62CEBAA1F00C45685 /* MockFaviconStore.swift in Sources */, 31C138AC27A403CB00FFD4B2 /* DownloadManagerTests.swift in Sources */, BDE219EA2C457B46005D5884 /* PrivacyProDataReporterTests.swift in Sources */, EEFE9C732A603CE9005B0A26 /* NetworkProtectionStatusViewModelTests.swift in Sources */, @@ -8205,7 +8215,7 @@ 9FEA22352C327226006B03BF /* MockTimer.swift in Sources */, EE7917912A83DE93008DFF28 /* CombineTestUtilities.swift in Sources */, 6FD0C41F2C5BF097000561C9 /* NewTabPageIntroMessageSetupTests.swift in Sources */, - 8540BD5223D8C2220057FDD2 /* PreserveLoginsTests.swift in Sources */, + 8540BD5223D8C2220057FDD2 /* UserDefaultsFireproofingTests.swift in Sources */, 85F200072217032E006BB258 /* AddressDisplayHelperTests.swift in Sources */, B6AD9E3728D4510A0019CDE9 /* ContentBlockingUpdatingTests.swift in Sources */, C14882E427F20D9A00D59F0C /* BookmarksImporterTests.swift in Sources */, @@ -8308,7 +8318,6 @@ CB258D1E29A52AF900DEBA24 /* FileStore.swift in Sources */, F1075C921E9EF827006BE8A8 /* UserDefaultsExtension.swift in Sources */, 851624C22B95F8BD002D5CD7 /* HistoryCapture.swift in Sources */, - 85CA53AC24BBD39300A6288C /* FaviconRequestModifier.swift in Sources */, CB258D1D29A52AF900DEBA24 /* EtagStorage.swift in Sources */, 31B2F10F2C92FECC00CD30E3 /* MarketplaceAdPostbackManager.swift in Sources */, C1B7B52D2894469D0098FD6A /* DefaultVariantManager.swift in Sources */, @@ -8335,10 +8344,9 @@ 85372447220DD103009D09CD /* UIKeyCommandExtension.swift in Sources */, 85A1B3B220C6CD9900C18F15 /* CookieStorage.swift in Sources */, 9856A1992933D2EB00ACB44F /* BookmarksModelsErrorHandling.swift in Sources */, - 850559D023CF647C0055C0D5 /* PreserveLogins.swift in Sources */, + 850559D023CF647C0055C0D5 /* Fireproofing.swift in Sources */, 4B27FBAE2C924EC6007E21A7 /* PersistentPixelStoring.swift in Sources */, 6F7FB8E32C660BF300867DA7 /* DailyPixelFiring.swift in Sources */, - C1CCCBA7283E101500CF3791 /* FaviconsHelper.swift in Sources */, 9813F79822BA71AA00A80EDB /* StorageCache.swift in Sources */, B603974929C19F6F00902A34 /* Assertions.swift in Sources */, F1134EB51F40AEEA00B73467 /* StatisticsLoader.swift in Sources */, @@ -8356,13 +8364,13 @@ 85449EFB23FDA0BC00512AAF /* UserDefaultsPropertyWrapper.swift in Sources */, 56D8556A2BEA9169009F9698 /* CurrentDateProviding.swift in Sources */, 830381C01F850AAF00863075 /* WKWebViewConfigurationExtension.swift in Sources */, - 85CA53AA24BB376800A6288C /* NotFoundCachingDownloader.swift in Sources */, 4B60ACA1252EC0B100E8D219 /* FullScreenVideoUserScript.swift in Sources */, F1A886781F29394E0096251E /* WebCacheManager.swift in Sources */, 6F03CB072C32F173004179A8 /* PixelFiring.swift in Sources */, 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 */, @@ -8380,7 +8388,6 @@ 85011867290028C400BDEE27 /* BookmarksDatabase.swift in Sources */, 1D8F727F2BA86D8000E31493 /* PixelExperiment.swift in Sources */, 56D8556C2BEA91C4009F9698 /* SyncAlertsPresenting.swift in Sources */, - 85D2187B24BF9F85004373D2 /* FaviconUserScript.swift in Sources */, 37FD780F2A29E28B00B36DB1 /* SyncErrorHandler.swift in Sources */, 85F21DC621145DD5002631A6 /* global.swift in Sources */, 372A0FF02B2389590033BF7F /* SyncMetricsEventsHandler.swift in Sources */, @@ -8388,12 +8395,10 @@ F4F6DFBA26EFF28A00ED7E12 /* BookmarkObjects.swift in Sources */, EE7A92872AC6DE4700832A36 /* NetworkProtectionNotificationIdentifier.swift in Sources */, 836A941D247F23C600BF8EF5 /* UserAgentManager.swift in Sources */, - 85CA53A824BB343700A6288C /* Favicons.swift in Sources */, F143C3181E4A99D200CFDE3A /* Link.swift in Sources */, 6F03CB092C32F331004179A8 /* PixelFiringAsync.swift in Sources */, 37CEFCAC2A673B90001EF741 /* CredentialsCleanupErrorHandling.swift in Sources */, CB2A7EF128410DF700885F67 /* PixelEvent.swift in Sources */, - 85D2187624BF6164004373D2 /* FaviconSourcesProvider.swift in Sources */, 98B000532915C46E0034BCA0 /* LegacyBookmarksStoreMigration.swift in Sources */, 6F04224D2CD2A3AD00729FA6 /* StorageInconsistencyMonitor.swift in Sources */, 85200FA11FBC5BB5001AF290 /* DDGPersistenceContainer.swift in Sources */, @@ -8419,6 +8424,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/AppDelegate.swift b/DuckDuckGo/AppDelegate.swift index 185cf88fa1..8020b7ad4b 100644 --- a/DuckDuckGo/AppDelegate.swift +++ b/DuckDuckGo/AppDelegate.swift @@ -272,7 +272,8 @@ import os.log secureVaultErrorReporter: SecureVaultReporter(), settingHandlers: [FavoritesDisplayModeSyncHandler()], favoritesDisplayModeStorage: FavoritesDisplayModeStorage(), - syncErrorHandler: syncErrorHandler + syncErrorHandler: syncErrorHandler, + faviconStoring: Favicons.shared ) let syncService = DDGSync( diff --git a/DuckDuckGo/Base.lproj/Settings.storyboard b/DuckDuckGo/Base.lproj/Settings.storyboard index 39927df63a..22cf4b4816 100644 --- a/DuckDuckGo/Base.lproj/Settings.storyboard +++ b/DuckDuckGo/Base.lproj/Settings.storyboard @@ -1,9 +1,9 @@ - + - + @@ -467,12 +467,12 @@ - + - + @@ -514,7 +514,7 @@ - + @@ -553,7 +553,7 @@ - + @@ -572,7 +572,7 @@ - + diff --git a/DuckDuckGo/ContentBlockingUpdating.swift b/DuckDuckGo/ContentBlockingUpdating.swift index 95002bca9e..dc3b8a720d 100644 --- a/DuckDuckGo/ContentBlockingUpdating.swift +++ b/DuckDuckGo/ContentBlockingUpdating.swift @@ -84,7 +84,7 @@ public final class ContentBlockingUpdating { // prefs changes notifications with initially published value for combineLatest to work. // Not all of these will trigger Tab reload, // refer TabViewController.swift:2116 for the list of notifications triggering reload - .combineLatest(onNotificationWithInitial(PreserveLogins.Notifications.loginDetectionStateChanged), combine) + .combineLatest(onNotificationWithInitial(UserDefaultsFireproofing.Notifications.loginDetectionStateChanged), combine) .combineLatest(onNotificationWithInitial(AppUserDefaults.Notifications.doNotSellStatusChange), combine) .combineLatest(onNotificationWithInitial(AppUserDefaults.Notifications.autofillEnabledChange), combine) .combineLatest(onNotificationWithInitial(AppUserDefaults.Notifications.didVerifyInternalUser), combine) diff --git a/DuckDuckGo/CookieDebugViewController.swift b/DuckDuckGo/CookieDebugViewController.swift index ed2b84fb16..4efc382742 100644 --- a/DuckDuckGo/CookieDebugViewController.swift +++ b/DuckDuckGo/CookieDebugViewController.swift @@ -39,7 +39,17 @@ class CookieDebugViewController: UITableViewController { } var loaded = false + let fireproofing: Fireproofing + init(fireproofing: Fireproofing = UserDefaultsFireproofing.shared) { + self.fireproofing = fireproofing + super.init() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + override func viewDidLoad() { super.viewDidLoad() fetchCookies() @@ -66,7 +76,7 @@ class CookieDebugViewController: UITableViewController { .reversed()) let domainName = domain + - (PreserveLogins.shared.isAllowed(cookieDomain: domain) ? " 👩‍🚒" : "") + (fireproofing.isAllowed(cookieDomain: domain) ? " 👩‍🚒" : "") tmp.append(DomainCookies(domain: domainName, cookies: domainCookies)) } 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..719b1fa2cd 100644 --- a/DuckDuckGo/Favicons.swift +++ b/DuckDuckGo/Favicons.swift @@ -24,104 +24,26 @@ import UIKit import LinkPresentation import WidgetKit import os.log +import Core 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) @@ -129,13 +51,16 @@ public class Favicons { let sourcesProvider: FaviconSourcesProvider let downloader: NotFoundCachingDownloader + let fireproofing: Fireproofing let userAgentManager: UserAgentManager = DefaultUserAgentManager.shared init(sourcesProvider: FaviconSourcesProvider = DefaultFaviconSourcesProvider(), - downloader: NotFoundCachingDownloader = NotFoundCachingDownloader()) { + downloader: NotFoundCachingDownloader = NotFoundCachingDownloader(), + fireproofing: Fireproofing = UserDefaultsFireproofing.shared) { self.sourcesProvider = sourcesProvider self.downloader = downloader + self.fireproofing = fireproofing // Prevents the caches being cleaned up NotificationCenter.default.removeObserver(Constants.fireproofCache) @@ -219,7 +144,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,17 +152,17 @@ 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) } public func removeBookmarkFavicon(forDomain domain: String) { - guard !PreserveLogins.shared.isAllowed(fireproofDomain: domain) else { return } + guard !fireproofing.isAllowed(fireproofDomain: domain) else { return } removeFavicon(forDomain: domain, fromCache: .fireproof) } @@ -253,7 +178,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 +202,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) { @@ -441,7 +366,7 @@ public class Favicons { return FaviconsHelper.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 +398,6 @@ public class Favicons { ] } - public static func createHash(ofDomain domain: String) -> String { - return "\(Constants.salt)\(domain)".sha256() - } - } extension Favicons: Bookmarks.FaviconStoring { @@ -509,3 +430,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..b319e5146c 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) { @@ -142,5 +142,14 @@ struct FaviconsHelper { return icon.withRenderingMode(.alwaysOriginal) } - + // this function is now static and outside of Favicons, otherwise there is a circular dependency between + // Favicons and NotFoundCachingDownloader + public static func defaultResource(forDomain domain: String?, sourcesProvider: FaviconSourcesProvider) -> KF.ImageResource? { + guard let domain = domain, + let source = sourcesProvider.mainSource(forDomain: domain) else { return nil } + + let key = FaviconHasher.createHash(ofDomain: domain) + return KF.ImageResource(downloadURL: source, cacheKey: key) + } + } 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/PreserveLoginsAlert.swift b/DuckDuckGo/FireproofingAlert.swift similarity index 68% rename from DuckDuckGo/PreserveLoginsAlert.swift rename to DuckDuckGo/FireproofingAlert.swift index 9bdc020ebe..593f376eb6 100644 --- a/DuckDuckGo/PreserveLoginsAlert.swift +++ b/DuckDuckGo/FireproofingAlert.swift @@ -1,5 +1,5 @@ // -// PreserveLoginsAlert.swift +// FireproofingAlert.swift // DuckDuckGo // // Copyright © 2020 DuckDuckGo. All rights reserved. @@ -20,22 +20,22 @@ import Foundation import Core -class PreserveLoginsAlert { +class FireproofingAlert { static func showFireproofDisabledMessage(usingController controller: UIViewController, - worker: PreserveLoginsWorker, + worker: FireproofingWorking, forDomain domain: String) { - let message = UserText.preserveLoginsRemovalConfirmMessage.format(arguments: domain.droppingWwwPrefix()) + let message = UserText.fireproofingRemovalConfirmMessage.format(arguments: domain.droppingWwwPrefix()) ActionMessageView.present(message: message, actionTitle: UserText.actionGenericUndo, onAction: { worker.handleUserEnablingFireproofing(forDomain: domain) }) } static func showFireproofEnabledMessage(usingController controller: UIViewController, - worker: PreserveLoginsWorker, + worker: FireproofingWorking, forDomain domain: String) { - let message = UserText.preserveLoginsFireproofConfirmMessage.format(arguments: domain.droppingWwwPrefix()) + let message = UserText.fireproofingConfirmMessage.format(arguments: domain.droppingWwwPrefix()) ActionMessageView.present(message: message, actionTitle: UserText.actionGenericUndo, onAction: { worker.handleUserDisablingFireproofing(forDomain: domain) }) @@ -44,10 +44,10 @@ class PreserveLoginsAlert { static func showConfirmFireproofWebsite(usingController controller: UIViewController, forDomain domain: String, onConfirmHandler: @escaping () -> Void) { - let prompt = UIAlertController(title: UserText.preserveLoginsFireproofAskTitle.format(arguments: domain.droppingWwwPrefix()), - message: UserText.preserveLoginsFireproofAskMessage, + let prompt = UIAlertController(title: UserText.fireproofingAskTitle.format(arguments: domain.droppingWwwPrefix()), + message: UserText.fireproofingAskMessage, preferredStyle: controller.isPad ? .alert : .actionSheet) - prompt.addAction(title: UserText.preserveLoginsFireproofConfirmAction, style: .default) { + prompt.addAction(title: UserText.FireproofingConfirmAction, style: .default) { onConfirmHandler() } prompt.addAction(title: UserText.actionCancel, style: .cancel) @@ -57,21 +57,21 @@ class PreserveLoginsAlert { static func showFireproofWebsitePrompt(usingController controller: UIViewController, forDomain domain: String, onConfirmHandler: @escaping () -> Void) { - let prompt = UIAlertController(title: UserText.preserveLoginsFireproofAskTitle.format(arguments: domain.droppingWwwPrefix()), - message: UserText.preserveLoginsFireproofAskMessage, + let prompt = UIAlertController(title: UserText.fireproofingAskTitle.format(arguments: domain.droppingWwwPrefix()), + message: UserText.fireproofingAskMessage, preferredStyle: controller.isPad ? .alert : .actionSheet) - prompt.addAction(title: UserText.preserveLoginsFireproofConfirmAction) { + prompt.addAction(title: UserText.FireproofingConfirmAction) { onConfirmHandler() } - prompt.addAction(title: UserText.preserveLoginsFireproofDefer, style: .cancel) + prompt.addAction(title: UserText.fireproofingDeferAction, style: .cancel) controller.present(prompt, animated: true) } static func showClearAllAlert(usingController controller: UIViewController, cancelled: @escaping () -> Void, confirmed: @escaping () -> Void) { if controller.isPad { - let alert = UIAlertController(title: UserText.preserveLoginsRemoveAll, message: nil, preferredStyle: .alert) - alert.addAction(title: UserText.preserveLoginsRemoveAllOk, style: .destructive) { + let alert = UIAlertController(title: UserText.fireproofingRemoveAllTitle, message: nil, preferredStyle: .alert) + alert.addAction(title: UserText.fireproofingRemoveAllOk, style: .destructive) { confirmed() } alert.addAction(title: UserText.actionCancel, style: .cancel) { @@ -80,7 +80,7 @@ class PreserveLoginsAlert { controller.present(alert, animated: true) } else { let alert = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) - alert.addAction(title: UserText.preserveLoginsRemoveAll, style: .destructive) { + alert.addAction(title: UserText.fireproofingRemoveAllTitle, style: .destructive) { confirmed() } alert.addAction(title: UserText.actionCancel, style: .cancel) { diff --git a/DuckDuckGo/PreserveLoginsSettingsViewController.swift b/DuckDuckGo/FireproofingSettingsViewController.swift similarity index 86% rename from DuckDuckGo/PreserveLoginsSettingsViewController.swift rename to DuckDuckGo/FireproofingSettingsViewController.swift index 3de524b611..08bc88b0c7 100644 --- a/DuckDuckGo/PreserveLoginsSettingsViewController.swift +++ b/DuckDuckGo/FireproofingSettingsViewController.swift @@ -1,5 +1,5 @@ // -// PreserveLoginsSettingsViewController.swift +// FireproofingSettingsViewController.swift // DuckDuckGo // // Copyright © 2020 DuckDuckGo. All rights reserved. @@ -21,7 +21,7 @@ import UIKit import Core import WebKit -class PreserveLoginsSettingsViewController: UITableViewController { +class FireproofingSettingsViewController: UITableViewController { enum Section: Int, CaseIterable { case info @@ -35,6 +35,17 @@ class PreserveLoginsSettingsViewController: UITableViewController { var model = [String]() private var shouldShowRemoveAll = false + + private let fireproofing: Fireproofing + + init?(coder: NSCoder, fireproofing: Fireproofing) { + self.fireproofing = fireproofing + super.init(coder: coder) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } override func viewDidLoad() { super.viewDidLoad() @@ -114,7 +125,7 @@ class PreserveLoginsSettingsViewController: UITableViewController { override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { switch Section(rawValue: section) { case .some(.domainList): - return UserText.preserveLoginsListTitle + return UserText.fireproofingListTitle default: return nil @@ -124,7 +135,7 @@ class PreserveLoginsSettingsViewController: UITableViewController { override func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? { switch Section(rawValue: section) { case .some(.info): - return UserText.preserveLoginsListFooter + return UserText.fireproofingListFooter default: return nil @@ -144,7 +155,7 @@ class PreserveLoginsSettingsViewController: UITableViewController { guard editingStyle == .delete else { return } let domain = model.remove(at: indexPath.row) - PreserveLogins.shared.remove(domain: domain) + fireproofing.remove(domain: domain) Favicons.shared.removeFireproofFavicon(forDomain: domain) if self.model.isEmpty { @@ -171,19 +182,20 @@ class PreserveLoginsSettingsViewController: UITableViewController { } func createSwitchCell(forTableView tableView: UITableView, withTheme theme: Theme) -> UITableViewCell { - guard let cell = tableView.dequeueReusableCell(withIdentifier: "SettingCell") as? PreserveLoginsSwitchCell else { + guard let cell = tableView.dequeueReusableCell(withIdentifier: "SettingCell") as? FireproofingSwitchCell else { fatalError("Cell should be dequeued") } cell.label.textColor = theme.tableCellTextColor cell.toggle.onTintColor = theme.buttonTintColor - cell.toggle.isOn = PreserveLogins.shared.loginDetectionEnabled + cell.toggle.isOn = fireproofing.loginDetectionEnabled + cell.fireproofing = fireproofing cell.controller = self cell.decorate(with: theme) return cell } func createDomainCell(forTableView tableView: UITableView, withTheme theme: Theme, forIndex index: Int) -> UITableViewCell { - guard let cell = tableView.dequeueReusableCell(withIdentifier: "DomainCell") as? PreserveLoginDomainCell else { + guard let cell = tableView.dequeueReusableCell(withIdentifier: "DomainCell") as? FireproofingDomainCell else { fatalError("Cell should be dequeued") } cell.label.textColor = theme.tableCellTextColor @@ -209,12 +221,12 @@ class PreserveLoginsSettingsViewController: UITableViewController { func clearAll() { guard !model.isEmpty else { return } - PreserveLoginsAlert.showClearAllAlert(usingController: self, cancelled: { [weak self] in + FireproofingAlert.showClearAllAlert(usingController: self, cancelled: { [weak self] in self?.refreshModel() }, confirmed: { [weak self] in Task { @MainActor in await WebCacheManager.shared.removeCookies(forDomains: self?.model ?? [], dataStore: WKWebsiteDataStore.current()) - PreserveLogins.shared.clearAll() + self?.fireproofing.clearAll() self?.refreshModel() self?.endEditing() } @@ -222,14 +234,14 @@ class PreserveLoginsSettingsViewController: UITableViewController { } func refreshModel() { - model = PreserveLogins.shared.allowedDomains.sorted(by: { (lhs, rhs) -> Bool in + model = fireproofing.allowedDomains.sorted(by: { (lhs, rhs) -> Bool in return lhs.droppingWwwPrefix() < rhs.droppingWwwPrefix() }) tableView.reloadData() } } -extension PreserveLoginsSettingsViewController { +extension FireproofingSettingsViewController { private func decorate() { let theme = ThemeManager.shared.currentTheme @@ -243,20 +255,21 @@ extension PreserveLoginsSettingsViewController { } -class PreserveLoginsSwitchCell: UITableViewCell { +class FireproofingSwitchCell: UITableViewCell { @IBOutlet weak var toggle: UISwitch! @IBOutlet weak var label: UILabel! - weak var controller: PreserveLoginsSettingsViewController! + weak var controller: FireproofingSettingsViewController! + var fireproofing: Fireproofing? @IBAction func onToggle() { - PreserveLogins.shared.loginDetectionEnabled = toggle.isOn + fireproofing?.loginDetectionEnabled = toggle.isOn } } -class PreserveLoginDomainCell: UITableViewCell { +class FireproofingDomainCell: UITableViewCell { @IBOutlet weak var faviconImage: UIImageView! @IBOutlet weak var label: UILabel! @@ -265,7 +278,7 @@ class PreserveLoginDomainCell: UITableViewCell { private extension IndexPath { - func isInSection(section: PreserveLoginsSettingsViewController.Section) -> Bool { + func isInSection(section: FireproofingSettingsViewController.Section) -> Bool { return self.section == section.rawValue } diff --git a/DuckDuckGo/PreserveLoginsWorker.swift b/DuckDuckGo/FireproofingWorking.swift similarity index 84% rename from DuckDuckGo/PreserveLoginsWorker.swift rename to DuckDuckGo/FireproofingWorking.swift index 54e98f8f58..53a976c619 100644 --- a/DuckDuckGo/PreserveLoginsWorker.swift +++ b/DuckDuckGo/FireproofingWorking.swift @@ -1,5 +1,5 @@ // -// PreserveLoginsWorker.swift +// FireproofingWorking.swift // DuckDuckGo // // Copyright © 2017 DuckDuckGo. All rights reserved. @@ -20,18 +20,19 @@ import UIKit import Core -struct PreserveLoginsWorker { +struct FireproofingWorking { private struct Constants { static let timeForAutofillToBlockFireproofPrompt = 10.0 } weak var controller: UIViewController? + let fireproofing: Fireproofing func handleLoginDetection(detectedURL: URL?, currentURL: URL?, isAutofillEnabled: Bool, saveLoginPromptLastDismissed: Date?, saveLoginPromptIsPresenting: Bool) -> Bool { guard let detectedURL = detectedURL, let currentURL = currentURL else { return false } guard let domain = detectedURL.host, domainOrPathDidChange(detectedURL, currentURL) else { return false } - guard !PreserveLogins.shared.isAllowed(fireproofDomain: domain) else { return false } + guard !fireproofing.isAllowed(fireproofDomain: domain) else { return false } if isAutofillEnabled && autofillShouldBlockPrompt(saveLoginPromptLastDismissed, saveLoginPromptIsPresenting: saveLoginPromptIsPresenting) { return false } @@ -72,23 +73,23 @@ struct PreserveLoginsWorker { private func promptToFireproof(_ domain: String) { guard let controller = controller else { return } - PreserveLoginsAlert.showFireproofWebsitePrompt(usingController: controller, forDomain: domain) { + FireproofingAlert.showFireproofWebsitePrompt(usingController: controller, forDomain: domain) { self.addDomain(domain) } } private func addDomain(_ domain: String) { guard let controller = controller else { return } - PreserveLogins.shared.addToAllowed(domain: domain) + fireproofing.addToAllowed(domain: domain) Favicons.shared.loadFavicon(forDomain: domain, intoCache: .fireproof, fromCache: .tabs) - PreserveLoginsAlert.showFireproofEnabledMessage(usingController: controller, worker: self, forDomain: domain) + FireproofingAlert.showFireproofEnabledMessage(usingController: controller, worker: self, forDomain: domain) } private func removeDomain(_ domain: String) { guard let controller = controller else { return } - PreserveLogins.shared.remove(domain: domain) + fireproofing.remove(domain: domain) Favicons.shared.removeFireproofFavicon(forDomain: domain) - PreserveLoginsAlert.showFireproofDisabledMessage(usingController: controller, worker: self, forDomain: domain) + FireproofingAlert.showFireproofDisabledMessage(usingController: controller, worker: self, forDomain: domain) } } diff --git a/DuckDuckGo/ImageCacheDebugViewController.swift b/DuckDuckGo/ImageCacheDebugViewController.swift index 1c6cd52b1c..37c6a838fe 100644 --- a/DuckDuckGo/ImageCacheDebugViewController.swift +++ b/DuckDuckGo/ImageCacheDebugViewController.swift @@ -49,6 +49,7 @@ class ImageCacheDebugViewController: UITableViewController { private let tabsModel = TabsModel.get() ?? TabsModel(desktop: false) private let bookmarksContext: NSManagedObjectContext + private let fireproofing: Fireproofing private var fireproofFavicons = [String: UIImage]() private var tabFavicons = [String: UIImage]() @@ -59,9 +60,11 @@ class ImageCacheDebugViewController: UITableViewController { private var tabs = [String: String]() init?(coder: NSCoder, - bookmarksDatabase: CoreDataDatabase) { + bookmarksDatabase: CoreDataDatabase, + fireproofing: Fireproofing = UserDefaultsFireproofing.shared) { bookmarksContext = bookmarksDatabase.makeContext(concurrencyType: .mainQueueConcurrencyType) + self.fireproofing = fireproofing super.init(coder: coder) } @@ -89,13 +92,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) } @@ -141,8 +144,8 @@ class ImageCacheDebugViewController: UITableViewController { } private func loadAllFireproofSites() { - let preservedLoginSites = PreserveLogins.shared.allowedDomains - for site in preservedLoginSites { + let allowedDomains = fireproofing.allowedDomains + for site in allowedDomains { if let imageResource = Favicons.shared.defaultResource(forDomain: site) { fireproofSites[imageResource.cacheKey] = site } diff --git a/DuckDuckGo/MainViewController.swift b/DuckDuckGo/MainViewController.swift index 29364843b7..c6b5f59729 100644 --- a/DuckDuckGo/MainViewController.swift +++ b/DuckDuckGo/MainViewController.swift @@ -177,7 +177,7 @@ class MainViewController: UIViewController { fatalError("Use init?(code:") } - let preserveLogins: PreserveLogins + let fireproofing: Fireproofing let textZoomCoordinator: TextZoomCoordinating var historyManager: HistoryManaging @@ -204,7 +204,7 @@ class MainViewController: UIViewController { subscriptionFeatureAvailability: SubscriptionFeatureAvailability, voiceSearchHelper: VoiceSearchHelperProtocol, featureFlagger: FeatureFlagger, - preserveLogins: PreserveLogins = .shared, + fireproofing: Fireproofing = UserDefaultsFireproofing.shared, subscriptionCookieManager: SubscriptionCookieManaging, textZoomCoordinator: TextZoomCoordinating ) { @@ -243,7 +243,7 @@ class MainViewController: UIViewController { self.statisticsStore = statisticsStore self.subscriptionFeatureAvailability = subscriptionFeatureAvailability self.voiceSearchHelper = voiceSearchHelper - self.preserveLogins = preserveLogins + self.fireproofing = fireproofing self.subscriptionCookieManager = subscriptionCookieManager self.textZoomCoordinator = textZoomCoordinator @@ -2749,7 +2749,7 @@ extension MainViewController: AutoClearWorker { } private func forgetTextZoom() { - let allowedDomains = preserveLogins.allowedDomains + let allowedDomains = fireproofing.allowedDomains textZoomCoordinator.resetTextZoomLevels(excludingDomains: allowedDomains) } diff --git a/DuckDuckGo/RootDebugViewController.swift b/DuckDuckGo/RootDebugViewController.swift index 0283c332dd..58258a5a07 100644 --- a/DuckDuckGo/RootDebugViewController.swift +++ b/DuckDuckGo/RootDebugViewController.swift @@ -250,13 +250,15 @@ protocol DiagnosticReportDataSourceDelegate: AnyObject { class DiagnosticReportDataSource: UIActivityItemProvider { weak var delegate: DiagnosticReportDataSourceDelegate? + var fireproofing: Fireproofing? @UserDefaultsWrapper(key: .lastConfigurationRefreshDate, defaultValue: .distantPast) private var lastRefreshDate: Date - convenience init(delegate: DiagnosticReportDataSourceDelegate) { + convenience init(delegate: DiagnosticReportDataSourceDelegate, fireproofing: Fireproofing = UserDefaultsFireproofing.shared) { self.init(placeholderItem: "") self.delegate = delegate + self.fireproofing = fireproofing } override var item: Any { @@ -288,7 +290,7 @@ class DiagnosticReportDataSource: UIActivityItemProvider { } func fireproofingReport() -> String { - let allowedDomains = PreserveLogins.shared.allowedDomains.map { "* \($0)" } + let allowedDomains = fireproofing?.allowedDomains.map { "* \($0)" } ?? [] let allowedDomainsEntry = ["### Allowed Domains"] + (allowedDomains.isEmpty ? [""] : allowedDomains) diff --git a/DuckDuckGo/ScriptSourceProviding.swift b/DuckDuckGo/ScriptSourceProviding.swift index 99ed069329..17d612401f 100644 --- a/DuckDuckGo/ScriptSourceProviding.swift +++ b/DuckDuckGo/ScriptSourceProviding.swift @@ -37,7 +37,7 @@ protocol ScriptSourceProviding { struct DefaultScriptSourceProvider: ScriptSourceProviding { - var loginDetectionEnabled: Bool { PreserveLogins.shared.loginDetectionEnabled } + var loginDetectionEnabled: Bool { fireproofing.loginDetectionEnabled } let sendDoNotSell: Bool let contentBlockerRulesConfig: ContentBlockerUserScriptConfig @@ -48,16 +48,19 @@ struct DefaultScriptSourceProvider: ScriptSourceProviding { let privacyConfigurationManager: PrivacyConfigurationManaging let contentBlockingManager: ContentBlockerRulesManagerProtocol + let fireproofing: Fireproofing init(appSettings: AppSettings = AppDependencyProvider.shared.appSettings, privacyConfigurationManager: PrivacyConfigurationManaging = ContentBlocking.shared.privacyConfigurationManager, - contentBlockingManager: ContentBlockerRulesManagerProtocol = ContentBlocking.shared.contentBlockingManager) { - + contentBlockingManager: ContentBlockerRulesManagerProtocol = ContentBlocking.shared.contentBlockingManager, + fireproofing: Fireproofing = UserDefaultsFireproofing.shared) { + sendDoNotSell = appSettings.sendDoNotSell self.privacyConfigurationManager = privacyConfigurationManager self.contentBlockingManager = contentBlockingManager - + self.fireproofing = fireproofing + contentBlockerRulesConfig = Self.buildContentBlockerRulesConfig(contentBlockingManager: contentBlockingManager, privacyConfigurationManager: privacyConfigurationManager) surrogatesConfig = Self.buildSurrogatesConfig(contentBlockingManager: contentBlockingManager, diff --git a/DuckDuckGo/SettingsLegacyViewProvider.swift b/DuckDuckGo/SettingsLegacyViewProvider.swift index dee451e380..3b74346f78 100644 --- a/DuckDuckGo/SettingsLegacyViewProvider.swift +++ b/DuckDuckGo/SettingsLegacyViewProvider.swift @@ -28,25 +28,34 @@ import Common class SettingsLegacyViewProvider: ObservableObject { + enum StoryboardName { + static let settings = "Settings" + static let homeRow = "HomeRow" + static let feedback = "Feedback" + } + let syncService: DDGSyncing let syncDataProviders: SyncDataProviders let appSettings: AppSettings let bookmarksDatabase: CoreDataDatabase let tabManager: TabManager let syncPausedStateManager: any SyncPausedStateManaging + let fireproofing: Fireproofing init(syncService: any DDGSyncing, syncDataProviders: SyncDataProviders, appSettings: any AppSettings, bookmarksDatabase: CoreDataDatabase, tabManager: TabManager, - syncPausedStateManager: any SyncPausedStateManaging) { + syncPausedStateManager: any SyncPausedStateManaging, + fireproofing: Fireproofing = UserDefaultsFireproofing.shared) { self.syncService = syncService self.syncDataProviders = syncDataProviders self.appSettings = appSettings self.bookmarksDatabase = bookmarksDatabase self.tabManager = tabManager self.syncPausedStateManager = syncPausedStateManager + self.fireproofing = fireproofing } enum LegacyView { @@ -63,28 +72,37 @@ class SettingsLegacyViewProvider: ObservableObject { feedback, debug } - + private func instantiate(_ identifier: String, fromStoryboard name: String) -> UIViewController { let storyboard = UIStoryboard(name: name, bundle: nil) return storyboard.instantiateViewController(withIdentifier: identifier) } - - // Legacy UIKit Views (Pushed unmodified) - var addToDock: UIViewController { instantiate( "instructions", fromStoryboard: "HomeRow") } - var appIcon: UIViewController { instantiate("AppIcon", fromStoryboard: "Settings") } - var gpc: UIViewController { instantiate("DoNotSell", fromStoryboard: "Settings") } - var autoConsent: UIViewController { instantiate("AutoconsentSettingsViewController", fromStoryboard: "Settings") } - var unprotectedSites: UIViewController { instantiate("UnprotectedSites", fromStoryboard: "Settings") } - var fireproofSites: UIViewController { instantiate("FireProofSites", fromStoryboard: "Settings") } - var keyboard: UIViewController { instantiate("Keyboard", fromStoryboard: "Settings") } - var feedback: UIViewController { instantiate("Feedback", fromStoryboard: "Feedback") } - var autoclearData: UIViewController { - let storyboard = UIStoryboard(name: "Settings", bundle: nil) + + private func instantiateFireproofingController() -> UIViewController { + let storyboard = UIStoryboard(name: StoryboardName.settings, bundle: nil) + return storyboard.instantiateViewController(identifier: "FireProofSites") { coder in + return FireproofingSettingsViewController(coder: coder, fireproofing: self.fireproofing) + } + } + + private func instantiateAutoClearController() -> UIViewController { + let storyboard = UIStoryboard(name: StoryboardName.settings, bundle: nil) return storyboard.instantiateViewController(identifier: "AutoClearSettingsViewController", creator: { coder in return AutoClearSettingsViewController(appSettings: self.appSettings, coder: coder) }) } + // Legacy UIKit Views (Pushed unmodified) + var addToDock: UIViewController { instantiate( "instructions", fromStoryboard: StoryboardName.homeRow) } + var appIcon: UIViewController { instantiate("AppIcon", fromStoryboard: StoryboardName.settings) } + var gpc: UIViewController { instantiate("DoNotSell", fromStoryboard: StoryboardName.settings) } + var autoConsent: UIViewController { instantiate("AutoconsentSettingsViewController", fromStoryboard: StoryboardName.settings) } + var unprotectedSites: UIViewController { instantiate("UnprotectedSites", fromStoryboard: StoryboardName.settings) } + var fireproofSites: UIViewController { instantiateFireproofingController() } + var keyboard: UIViewController { instantiate("Keyboard", fromStoryboard: StoryboardName.settings) } + var feedback: UIViewController { instantiate("Feedback", fromStoryboard: StoryboardName.feedback) } + var autoclearData: UIViewController { instantiateAutoClearController() } + @MainActor func syncSettings(source: String? = nil) -> SyncSettingsViewController { return SyncSettingsViewController(syncService: self.syncService, diff --git a/DuckDuckGo/Subscription/PrivacyProDataReporting.swift b/DuckDuckGo/Subscription/PrivacyProDataReporting.swift index a873629777..43c2150504 100644 --- a/DuckDuckGo/Subscription/PrivacyProDataReporting.swift +++ b/DuckDuckGo/Subscription/PrivacyProDataReporting.swift @@ -110,6 +110,7 @@ final class PrivacyProDataReporter: PrivacyProDataReporting { private let secureVaultMaker: () -> (any AutofillSecureVault)? private var syncService: DDGSyncing? private var tabsModel: TabsModel? + private let fireproofing: Fireproofing private let dateGenerator: () -> Date private var secureVault: (any AutofillSecureVault)? @@ -126,6 +127,7 @@ final class PrivacyProDataReporter: PrivacyProDataReporting { secureVaultMaker: @escaping () -> (any AutofillSecureVault)? = { try? AutofillSecureVaultFactory.makeVault(reporter: SecureVaultReporter()) }, syncService: DDGSyncing? = nil, tabsModel: TabsModel? = nil, + fireproofing: Fireproofing = UserDefaultsFireproofing.shared, dateGenerator: @escaping () -> Date = Date.init) { self.configurationManager = configurationManager self.variantManager = variantManager @@ -139,6 +141,7 @@ final class PrivacyProDataReporter: PrivacyProDataReporting { self.secureVaultMaker = secureVaultMaker self.syncService = syncService self.tabsModel = tabsModel + self.fireproofing = fireproofing self.dateGenerator = dateGenerator } @@ -293,7 +296,7 @@ final class PrivacyProDataReporter: PrivacyProDataReporting { } var _fireproofedDomainsCount: Int { - PreserveLogins.shared.allowedDomains.count + fireproofing.allowedDomains.count } var _lastSessionEnded: Date? { 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/TabViewController.swift b/DuckDuckGo/TabViewController.swift index 7e4bb51b8c..fbf7dd63db 100644 --- a/DuckDuckGo/TabViewController.swift +++ b/DuckDuckGo/TabViewController.swift @@ -128,7 +128,7 @@ class TabViewController: UIViewController { private var performanceMetrics: PerformanceMetricsSubfeature? private var detectedLoginURL: URL? - private var preserveLoginsWorker: PreserveLoginsWorker? + private var fireproofingWorker: FireproofingWorking? private var trackersInfoWorkItem: DispatchWorkItem? @@ -370,6 +370,7 @@ class TabViewController: UIViewController { let contextualOnboardingLogic: ContextualOnboardingLogic let onboardingPixelReporter: OnboardingCustomInteractionPixelReporting let textZoomCoordinator: TextZoomCoordinating + let fireproofing: Fireproofing required init?(coder aDecoder: NSCoder, tabModel: Tab, @@ -386,7 +387,8 @@ class TabViewController: UIViewController { urlCredentialCreator: URLCredentialCreating = URLCredentialCreator(), featureFlagger: FeatureFlagger, subscriptionCookieManager: SubscriptionCookieManaging, - textZoomCoordinator: TextZoomCoordinating) { + textZoomCoordinator: TextZoomCoordinating, + fireproofing: Fireproofing = UserDefaultsFireproofing.shared) { self.tabModel = tabModel self.appSettings = appSettings self.bookmarksDatabase = bookmarksDatabase @@ -407,6 +409,7 @@ class TabViewController: UIViewController { self.featureFlagger = featureFlagger self.subscriptionCookieManager = subscriptionCookieManager self.textZoomCoordinator = textZoomCoordinator + self.fireproofing = fireproofing super.init(coder: aDecoder) @@ -421,7 +424,7 @@ class TabViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - preserveLoginsWorker = PreserveLoginsWorker(controller: self) + fireproofingWorker = FireproofingWorking(controller: self, fireproofing: fireproofing) initAttributionLogic() decorate() addTextZoomObserver() @@ -794,14 +797,14 @@ class TabViewController: UIViewController { } func enableFireproofingForDomain(_ domain: String) { - PreserveLoginsAlert.showConfirmFireproofWebsite(usingController: self, forDomain: domain) { [weak self] in + FireproofingAlert.showConfirmFireproofWebsite(usingController: self, forDomain: domain) { [weak self] in Pixel.fire(pixel: .browsingMenuFireproof) - self?.preserveLoginsWorker?.handleUserEnablingFireproofing(forDomain: domain) + self?.fireproofingWorker?.handleUserEnablingFireproofing(forDomain: domain) } } func disableFireproofingForDomain(_ domain: String) { - preserveLoginsWorker?.handleUserDisablingFireproofing(forDomain: domain) + fireproofingWorker?.handleUserDisablingFireproofing(forDomain: domain) } func dismissContextualDaxFireDialog() { @@ -1621,18 +1624,18 @@ extension TabViewController: WKNavigationDelegate { } private func checkLoginDetectionAfterNavigation() { - if preserveLoginsWorker?.handleLoginDetection(detectedURL: detectedLoginURL, - currentURL: url, - isAutofillEnabled: AutofillSettingStatus.isAutofillEnabledInSettings, - saveLoginPromptLastDismissed: saveLoginPromptLastDismissed, - saveLoginPromptIsPresenting: saveLoginPromptIsPresenting) - ?? false { + if fireproofingWorker?.handleLoginDetection(detectedURL: detectedLoginURL, + currentURL: url, + isAutofillEnabled: AutofillSettingStatus.isAutofillEnabledInSettings, + saveLoginPromptLastDismissed: saveLoginPromptLastDismissed, + saveLoginPromptIsPresenting: saveLoginPromptIsPresenting) ?? false { + detectedLoginURL = nil saveLoginPromptLastDismissed = nil saveLoginPromptIsPresenting = false } } - + func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) { Logger.general.debug("didFailNavigation; error: \(error)") adClickAttributionDetection.onDidFailNavigation() @@ -2569,7 +2572,7 @@ extension TabViewController: UserContentControllerDelegate { let tdsKey = DefaultContentBlockerRulesListsSource.Constants.trackerDataSetRulesListName let notificationsTriggeringReload = [ - PreserveLogins.Notifications.loginDetectionStateChanged, + UserDefaultsFireproofing.Notifications.loginDetectionStateChanged, AppUserDefaults.Notifications.doNotSellStatusChange ] if updateEvent.changes[tdsKey]?.contains(.unprotectedSites) == true diff --git a/DuckDuckGo/TabViewControllerBrowsingMenuExtension.swift b/DuckDuckGo/TabViewControllerBrowsingMenuExtension.swift index fe193e42a0..d76c6c18b2 100644 --- a/DuckDuckGo/TabViewControllerBrowsingMenuExtension.swift +++ b/DuckDuckGo/TabViewControllerBrowsingMenuExtension.swift @@ -173,7 +173,7 @@ extension TabViewController { private func buildKeepSignInEntry(forLink link: Link) -> BrowsingMenuEntry? { guard let domain = link.url.host, !link.url.isDuckDuckGo else { return nil } - let isFireproofed = PreserveLogins.shared.isAllowed(cookieDomain: domain) + let isFireproofed = fireproofing.isAllowed(cookieDomain: domain) if isFireproofed { return BrowsingMenuEntry.regular(name: UserText.disablePreservingLogins, 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/DuckDuckGo/UserText.swift b/DuckDuckGo/UserText.swift index 43e48a8762..b601eb9f28 100644 --- a/DuckDuckGo/UserText.swift +++ b/DuckDuckGo/UserText.swift @@ -229,19 +229,19 @@ public struct UserText { public static let onboardingDefaultBrowserTitle = NSLocalizedString("onboardingDefaultBrowserTitle", value: "Make DuckDuckGo your default browser.", comment: "") public static let onboardingDefaultBrowserMaybeLater = NSLocalizedString("onboardingDefaultBrowserMaybeLater", value: "Maybe Later", comment: "") - public static let preserveLoginsListTitle = NSLocalizedString("preserveLogins.domain.list.title", value: "Fireproof Sites", comment: "Section header above Fireproofed websites list") - public static let preserveLoginsListFooter = NSLocalizedString("preserveLogins.domain.list.footer", value: "Websites rely on cookies to keep you signed in. When you Fireproof a site, cookies won’t be erased and you’ll stay signed in, even after using the Fire Button. We still block third-party trackers found on Fireproof websites.", comment: "") - public static let preserveLoginsRemoveAll = NSLocalizedString("preserveLogins.remove.all", value: "Remove All", comment: "Alert title") - public static let preserveLoginsRemoveAllOk = NSLocalizedString("preserveLogins.remove.all.ok", value: "OK", comment: "Confirmation button in alert") + public static let fireproofingListTitle = NSLocalizedString("preserveLogins.domain.list.title", value: "Fireproof Sites", comment: "Section header above Fireproofed websites list") + public static let fireproofingListFooter = NSLocalizedString("preserveLogins.domain.list.footer", value: "Websites rely on cookies to keep you signed in. When you Fireproof a site, cookies won’t be erased and you’ll stay signed in, even after using the Fire Button. We still block third-party trackers found on Fireproof websites.", comment: "") + public static let fireproofingRemoveAllTitle = NSLocalizedString("preserveLogins.remove.all", value: "Remove All", comment: "Alert title") + public static let fireproofingRemoveAllOk = NSLocalizedString("preserveLogins.remove.all.ok", value: "OK", comment: "Confirmation button in alert") - public static let preserveLoginsFireproofAskTitle = NSLocalizedString("preserveLogins.fireproof.title", value: "Fireproof %@ to stay signed in?", comment: "Parameter is a string - domain name. Alert title prompting user to fireproof a site so they can stay signed in") - public static let preserveLoginsFireproofAskMessage = NSLocalizedString("preserveLogins.fireproof.message", value: "Fireproofing this site will keep you signed in after using the Fire Button.", comment: "Alert message explaining to users that the benefit of fireproofing a site is that they will be kept signed in") + public static let fireproofingAskTitle = NSLocalizedString("preserveLogins.fireproof.title", value: "Fireproof %@ to stay signed in?", comment: "Parameter is a string - domain name. Alert title prompting user to fireproof a site so they can stay signed in") + public static let fireproofingAskMessage = NSLocalizedString("preserveLogins.fireproof.message", value: "Fireproofing this site will keep you signed in after using the Fire Button.", comment: "Alert message explaining to users that the benefit of fireproofing a site is that they will be kept signed in") public static let enablePreservingLogins = NSLocalizedString("preserveLogins.menu.enable", value: "Fireproof This Site", comment: "Enable fireproofing for site") public static let disablePreservingLogins = NSLocalizedString("preserveLogins.menu.disable", value: "Remove Fireproofing", comment: "Disable fireproofing for site") - public static let preserveLoginsFireproofConfirmAction = NSLocalizedString("preserveLogins.menu.confirm", value: "Fireproof", comment: "Confirm fireproofing action") - public static let preserveLoginsFireproofDefer = NSLocalizedString("preserveLogins.menu.defer", value: "Not Now", comment: "Deny fireproofing action") - public static let preserveLoginsFireproofConfirmMessage = NSLocalizedString("preserveLogins.menu.confirm.message", value: "%@ is now Fireproof", comment: "Parameter is a website URL. Messege confirms that given website has been fireproofed.") - public static let preserveLoginsRemovalConfirmMessage = NSLocalizedString("preserveLogins.menu.removal.message", value: "Fireproofing removed", comment: " Messege confirms that website is no longer fireproofed.") + public static let FireproofingConfirmAction = NSLocalizedString("preserveLogins.menu.confirm", value: "Fireproof", comment: "Confirm fireproofing action") + public static let fireproofingDeferAction = NSLocalizedString("preserveLogins.menu.defer", value: "Not Now", comment: "Deny fireproofing action") + public static let fireproofingConfirmMessage = NSLocalizedString("preserveLogins.menu.confirm.message", value: "%@ is now Fireproof", comment: "Parameter is a website URL. Messege confirms that given website has been fireproofed.") + public static let fireproofingRemovalConfirmMessage = NSLocalizedString("preserveLogins.menu.removal.message", value: "Fireproofing removed", comment: " Messege confirms that website is no longer fireproofed.") public static let homeTabSearchAndFavorites = NSLocalizedString("homeTab.searchAndFavorites", value: "Search or enter address", comment: "This describes empty tab") public static let homeTabTitle = NSLocalizedString("homeTab.title", value: "Home", comment: "Home tab title") diff --git a/DuckDuckGoTests/ContentBlockingUpdatingTests.swift b/DuckDuckGoTests/ContentBlockingUpdatingTests.swift index dc68adcd9e..522187cfe0 100644 --- a/DuckDuckGoTests/ContentBlockingUpdatingTests.swift +++ b/DuckDuckGoTests/ContentBlockingUpdatingTests.swift @@ -120,7 +120,7 @@ final class ContentBlockingUpdatingTests: XCTestCase { } } - func testWhenPreserveLoginsNotificationSentThenUserScriptsAreRebuild() { + func testWhenFireproffingNotificationSentThenUserScriptsAreRebuild() { let e1 = expectation(description: "should post initial update") var e2: XCTestExpectation! @@ -141,7 +141,7 @@ final class ContentBlockingUpdatingTests: XCTestCase { withExtendedLifetime(c) { waitForExpectations(timeout: 1, handler: nil) e2 = expectation(description: "should rebuild user scripts") - NotificationCenter.default.post(name: PreserveLogins.Notifications.loginDetectionStateChanged, object: nil) + NotificationCenter.default.post(name: UserDefaultsFireproofing.Notifications.loginDetectionStateChanged, object: nil) waitForExpectations(timeout: 1, handler: nil) } } diff --git a/DuckDuckGoTests/CookieStorageTests.swift b/DuckDuckGoTests/CookieStorageTests.swift index 07927f155c..f200fc251e 100644 --- a/DuckDuckGoTests/CookieStorageTests.swift +++ b/DuckDuckGoTests/CookieStorageTests.swift @@ -26,8 +26,8 @@ public class CookieStorageTests: XCTestCase { var storage: CookieStorage! // This is updated by the `make` function which preserves any cookies added as part of this test - let logins = PreserveLogins.shared - + let fireproofing = UserDefaultsFireproofing.shared + static let userDefaultsSuiteName = "test" public override func setUp() { @@ -36,20 +36,20 @@ public class CookieStorageTests: XCTestCase { defaults.removePersistentDomain(forName: Self.userDefaultsSuiteName) storage = CookieStorage(userDefaults: defaults) storage.isConsumed = true - logins.clearAll() + fireproofing.clearAll() } func testWhenDomainRemovesAllCookesThenTheyAreClearedFromPersisted() { - logins.addToAllowed(domain: "example.com") + fireproofing.addToAllowed(domain: "example.com") XCTAssertEqual(storage.updateCookies([ make("example.com", name: "x", value: "1"), - ], keepingPreservedLogins: logins), .empty) + ], preservingFireproofedDomains: fireproofing), .empty) XCTAssertEqual(1, storage.cookies.count) storage.isConsumed = true - storage.updateCookies([], keepingPreservedLogins: logins) + storage.updateCookies([], preservingFireproofedDomains: fireproofing) XCTAssertEqual(0, storage.cookies.count) @@ -58,7 +58,7 @@ public class CookieStorageTests: XCTestCase { func testWhenUpdatedThenDuckDuckGoCookiesAreNotRemoved() { storage.updateCookies([ make("duckduckgo.com", name: "x", value: "1"), - ], keepingPreservedLogins: logins) + ], preservingFireproofedDomains: fireproofing) XCTAssertEqual(1, storage.cookies.count) @@ -66,7 +66,7 @@ public class CookieStorageTests: XCTestCase { storage.updateCookies([ make("duckduckgo.com", name: "x", value: "1"), make("test.com", name: "x", value: "1"), - ], keepingPreservedLogins: logins) + ], preservingFireproofedDomains: fireproofing) XCTAssertEqual(2, storage.cookies.count) @@ -75,7 +75,7 @@ public class CookieStorageTests: XCTestCase { make("usedev1.duckduckgo.com", name: "x", value: "1"), make("duckduckgo.com", name: "x", value: "1"), make("test.com", name: "x", value: "1"), - ], keepingPreservedLogins: logins) + ], preservingFireproofedDomains: fireproofing) XCTAssertEqual(3, storage.cookies.count) @@ -85,7 +85,7 @@ public class CookieStorageTests: XCTestCase { storage.updateCookies([ make("test.com", name: "x", value: "1", expires: .distantFuture), make("example.com", name: "x", value: "1"), - ], keepingPreservedLogins: logins) + ], preservingFireproofedDomains: fireproofing) XCTAssertEqual(2, storage.cookies.count) XCTAssertTrue(storage.cookies.contains(where: { $0.domain == "test.com" })) @@ -102,7 +102,7 @@ public class CookieStorageTests: XCTestCase { storage.isConsumed = true storage.updateCookies([ make("example.com", name: "x", value: "1"), - ], keepingPreservedLogins: logins) + ], preservingFireproofedDomains: fireproofing) XCTAssertEqual(1, storage.cookies.count) XCTAssertFalse(storage.cookies.contains(where: { $0.domain == "test.com" })) @@ -114,24 +114,24 @@ public class CookieStorageTests: XCTestCase { storage.updateCookies([ make("example.com", name: "x", value: "1", expires: Date(timeIntervalSinceNow: -100)), - ], keepingPreservedLogins: logins) + ], preservingFireproofedDomains: fireproofing) XCTAssertEqual(0, storage.cookies.count) } - func testWhenUpdatedThenNoLongerPreservedDomainsAreCleared() { + func testWhenUpdatedThenNoLongerFireproofedDomainsAreCleared() { storage.updateCookies([ make("test.com", name: "x", value: "1"), make("example.com", name: "x", value: "1"), - ], keepingPreservedLogins: logins) + ], preservingFireproofedDomains: fireproofing) - logins.remove(domain: "test.com") + fireproofing.remove(domain: "test.com") storage.isConsumed = true storage.updateCookies([ make("example.com", name: "x", value: "1"), - ], keepingPreservedLogins: logins) + ], preservingFireproofedDomains: fireproofing) XCTAssertEqual(1, storage.cookies.count) XCTAssertFalse(storage.cookies.contains(where: { $0.domain == "test.com" })) @@ -148,14 +148,14 @@ public class CookieStorageTests: XCTestCase { XCTAssertTrue(storage.isConsumed) storage.updateCookies([ make("test.com", name: "x", value: "1") - ], keepingPreservedLogins: logins) + ], preservingFireproofedDomains: fireproofing) XCTAssertFalse(storage.isConsumed) } func testWhenStorageIsReinstanciatedThenUsesStoredData() { storage.updateCookies([ make("test.com", name: "x", value: "1") - ], keepingPreservedLogins: logins) + ], preservingFireproofedDomains: fireproofing) storage.isConsumed = true let otherStorage = CookieStorage(userDefaults: UserDefaults(suiteName: Self.userDefaultsSuiteName)!) @@ -166,20 +166,20 @@ public class CookieStorageTests: XCTestCase { func testWhenStorageIsUpdatedThenUpdatingAddsNewCookies() { storage.updateCookies([ make("test.com", name: "x", value: "1") - ], keepingPreservedLogins: logins) + ], preservingFireproofedDomains: fireproofing) XCTAssertEqual(1, storage.cookies.count) } func testWhenStorageHasMatchingDOmainThenUpdatingReplacesCookies() { storage.updateCookies([ make("test.com", name: "x", value: "1") - ], keepingPreservedLogins: logins) + ], preservingFireproofedDomains: fireproofing) storage.isConsumed = true storage.updateCookies([ make("test.com", name: "x", value: "2"), make("test.com", name: "y", value: "3"), - ], keepingPreservedLogins: logins) + ], preservingFireproofedDomains: fireproofing) XCTAssertEqual(2, storage.cookies.count) XCTAssertFalse(storage.cookies.contains(where: { $0.domain == "test.com" && $0.name == "x" && $0.value == "1" })) @@ -190,18 +190,18 @@ public class CookieStorageTests: XCTestCase { func testWhenStorageUpdatedAndNotConsumedThenNothingHappens() { storage.updateCookies([ make("test.com", name: "x", value: "1") - ], keepingPreservedLogins: logins) + ], preservingFireproofedDomains: fireproofing) storage.updateCookies([ make("example.com", name: "y", value: "3"), - ], keepingPreservedLogins: logins) + ], preservingFireproofedDomains: fireproofing) XCTAssertEqual(1, storage.cookies.count) XCTAssertTrue(storage.cookies.contains(where: { $0.domain == "test.com" && $0.name == "x" && $0.value == "1" })) } func make(_ domain: String, name: String, value: String, expires: Date? = nil) -> HTTPCookie { - logins.addToAllowed(domain: domain) + fireproofing.addToAllowed(domain: domain) return HTTPCookie(properties: [ .domain: domain, .name: name, diff --git a/DuckDuckGoTests/FaviconRequestModifierTests.swift b/DuckDuckGoTests/FaviconRequestModifierTests.swift index a13b3ec0bd..53d31c1472 100644 --- a/DuckDuckGoTests/FaviconRequestModifierTests.swift +++ b/DuckDuckGoTests/FaviconRequestModifierTests.swift @@ -21,6 +21,7 @@ import BrowserServicesKit import XCTest @testable import Core +@testable import DuckDuckGo class MockEmbeddedDataProvider: EmbeddedDataProvider { var embeddedDataEtag: String diff --git a/DuckDuckGoTests/FaviconSourcesProviderTests.swift b/DuckDuckGoTests/FaviconSourcesProviderTests.swift index e7f6aa5932..7e15e0f72a 100644 --- a/DuckDuckGoTests/FaviconSourcesProviderTests.swift +++ b/DuckDuckGoTests/FaviconSourcesProviderTests.swift @@ -18,7 +18,9 @@ // import XCTest + @testable import Core +@testable import DuckDuckGo class FaviconSourcesProviderTests: XCTestCase { diff --git a/DuckDuckGoTests/FaviconsTests.swift b/DuckDuckGoTests/FaviconsTests.swift index ddbb966caf..68a1303c33 100644 --- a/DuckDuckGoTests/FaviconsTests.swift +++ b/DuckDuckGoTests/FaviconsTests.swift @@ -23,6 +23,7 @@ import Kingfisher import XCTest @testable import Core +@testable import DuckDuckGo class FaviconsTests: XCTestCase { @@ -110,7 +111,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/FireButtonReferenceTests.swift b/DuckDuckGoTests/FireButtonReferenceTests.swift index 2a92662ec5..5ab2d225c3 100644 --- a/DuckDuckGoTests/FireButtonReferenceTests.swift +++ b/DuckDuckGoTests/FireButtonReferenceTests.swift @@ -48,13 +48,13 @@ final class FireButtonReferenceTests: XCTestCase { @MainActor func testClearDataUsingLegacyContainer() async throws { // Using WKWebsiteDataStore(forIdentifier:) doesn't persist cookies in a testable way, so use the legacy container here. - let preservedLogins = PreserveLogins.shared - preservedLogins.clearAll() + let fireproofing = UserDefaultsFireproofing.shared + fireproofing.clearAll() for site in testData.fireButtonFireproofing.fireproofedSites { let sanitizedSite = sanitizedSite(site) print("Adding %s to fireproofed sites", sanitizedSite) - preservedLogins.addToAllowed(domain: sanitizedSite) + fireproofing.addToAllowed(domain: sanitizedSite) } let referenceTests = testData.fireButtonFireproofing.tests.filter { @@ -75,7 +75,7 @@ final class FireButtonReferenceTests: XCTestCase { // Pretend the webview was loaded and the cookies were previously consumed cookieStorage.isConsumed = true - await WebCacheManager.shared.clear(cookieStorage: cookieStorage, logins: preservedLogins, dataStoreIdManager: DataStoreIdManager(store: MockKeyValueStore())) + await WebCacheManager.shared.clear(cookieStorage: cookieStorage, fireproofing: fireproofing, dataStoreIdManager: DataStoreIdManager(store: MockKeyValueStore())) let testCookie = cookieStorage.cookies.filter { $0.name == test.cookieName }.first @@ -92,13 +92,13 @@ final class FireButtonReferenceTests: XCTestCase { } func testCookieStorage() throws { - let preservedLogins = PreserveLogins.shared - preservedLogins.clearAll() + let fireproofing = UserDefaultsFireproofing.shared + fireproofing.clearAll() for site in testData.fireButtonFireproofing.fireproofedSites { let sanitizedSite = sanitizedSite(site) print("Adding %s to fireproofed sites", sanitizedSite) - preservedLogins.addToAllowed(domain: sanitizedSite) + fireproofing.addToAllowed(domain: sanitizedSite) } let referenceTests = testData.fireButtonFireproofing.tests.filter { @@ -116,7 +116,7 @@ final class FireButtonReferenceTests: XCTestCase { // This simulates loading the cookies from the current web view data stores and updating the storage cookieStorage.updateCookies([ cookie - ], keepingPreservedLogins: preservedLogins) + ], preservingFireproofedDomains: fireproofing) let testCookie = cookieStorage.cookies.filter { $0.name == test.cookieName }.first 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/MockFaviconStore.swift b/DuckDuckGoTests/MockFaviconStore.swift new file mode 100644 index 0000000000..087fbd4fb3 --- /dev/null +++ b/DuckDuckGoTests/MockFaviconStore.swift @@ -0,0 +1,30 @@ +// +// MockFaviconStore.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 +@testable import Bookmarks + +class MockFaviconStore: FaviconStoring { + func hasFavicon(for domain: String) -> Bool { + return false + } + + func storeFavicon(_ imageData: Data, with url: URL?, for documentURL: URL) async throws { + } +} diff --git a/DuckDuckGoTests/NotFoundCachingDownloaderTests.swift b/DuckDuckGoTests/NotFoundCachingDownloaderTests.swift index 661e14f46e..85112a9274 100644 --- a/DuckDuckGoTests/NotFoundCachingDownloaderTests.swift +++ b/DuckDuckGoTests/NotFoundCachingDownloaderTests.swift @@ -20,6 +20,7 @@ import XCTest @testable import Core +@testable import DuckDuckGo class NotFoundCachingDownloaderTests: XCTestCase { @@ -38,6 +39,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 +56,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/DuckDuckGoTests/OnboardingDaxFavouritesTests.swift b/DuckDuckGoTests/OnboardingDaxFavouritesTests.swift index 99ad0e1b60..3aa6866fb5 100644 --- a/DuckDuckGoTests/OnboardingDaxFavouritesTests.swift +++ b/DuckDuckGoTests/OnboardingDaxFavouritesTests.swift @@ -44,7 +44,8 @@ final class OnboardingDaxFavouritesTests: XCTestCase { secureVaultErrorReporter: SecureVaultReporter(), settingHandlers: [], favoritesDisplayModeStorage: MockFavoritesDisplayModeStoring(), - syncErrorHandler: SyncErrorHandler() + syncErrorHandler: SyncErrorHandler(), + faviconStoring: MockFaviconStore() ) let remoteMessagingClient = RemoteMessagingClient( diff --git a/DuckDuckGoTests/OnboardingNavigationDelegateTests.swift b/DuckDuckGoTests/OnboardingNavigationDelegateTests.swift index 3f1762975b..52e2da5b9a 100644 --- a/DuckDuckGoTests/OnboardingNavigationDelegateTests.swift +++ b/DuckDuckGoTests/OnboardingNavigationDelegateTests.swift @@ -44,7 +44,8 @@ final class OnboardingNavigationDelegateTests: XCTestCase { secureVaultErrorReporter: SecureVaultReporter(), settingHandlers: [], favoritesDisplayModeStorage: MockFavoritesDisplayModeStoring(), - syncErrorHandler: SyncErrorHandler() + syncErrorHandler: SyncErrorHandler(), + faviconStoring: MockFaviconStore() ) let remoteMessagingClient = RemoteMessagingClient( diff --git a/DuckDuckGoTests/SyncBookmarksAdapterTests.swift b/DuckDuckGoTests/SyncBookmarksAdapterTests.swift index acf9a6da12..89e2050513 100644 --- a/DuckDuckGoTests/SyncBookmarksAdapterTests.swift +++ b/DuckDuckGoTests/SyncBookmarksAdapterTests.swift @@ -48,7 +48,8 @@ final class SyncBookmarksAdapterTests: XCTestCase { options: [:]) adapter = SyncBookmarksAdapter(database: database, favoritesDisplayModeStorage: MockFavoriteDisplayModeStorage(), - syncErrorHandler: errorHandler) + syncErrorHandler: errorHandler, + faviconStoring: MockFaviconStore()) cancellables = [] } diff --git a/DuckDuckGoTests/SyncSettingsViewControllerErrorTests.swift b/DuckDuckGoTests/SyncSettingsViewControllerErrorTests.swift index 42b94cc253..9d3e2fb551 100644 --- a/DuckDuckGoTests/SyncSettingsViewControllerErrorTests.swift +++ b/DuckDuckGoTests/SyncSettingsViewControllerErrorTests.swift @@ -49,7 +49,8 @@ final class SyncSettingsViewControllerErrorTests: XCTestCase { let bookmarksAdapter = SyncBookmarksAdapter( database: database, favoritesDisplayModeStorage: MockFavoritesDisplayModeStoring(), - syncErrorHandler: CapturingAdapterErrorHandler()) + syncErrorHandler: CapturingAdapterErrorHandler(), + faviconStoring: MockFaviconStore()) let credentialsAdapter = SyncCredentialsAdapter( secureVaultErrorReporter: MockSecureVaultReporting(), syncErrorHandler: CapturingAdapterErrorHandler()) diff --git a/DuckDuckGoTests/PreserveLoginsTests.swift b/DuckDuckGoTests/UserDefaultsFireproofingTests.swift similarity index 67% rename from DuckDuckGoTests/PreserveLoginsTests.swift rename to DuckDuckGoTests/UserDefaultsFireproofingTests.swift index 921fad110f..58154219d1 100644 --- a/DuckDuckGoTests/PreserveLoginsTests.swift +++ b/DuckDuckGoTests/UserDefaultsFireproofingTests.swift @@ -1,5 +1,5 @@ // -// PreserveLoginsTests.swift +// UserDefaultsFireproofingTests.swift // UnitTests // // Copyright © 2020 DuckDuckGo. All rights reserved. @@ -20,7 +20,7 @@ import XCTest @testable import Core -class PreserveLoginsTests: XCTestCase { +class UserDefaultsFireproofingTests: XCTestCase { override func setUp() { super.setUp() @@ -29,15 +29,15 @@ class PreserveLoginsTests: XCTestCase { } func testWhenAllowedDomainsContainsFireproofedDomainThenReturnsTrue() { - let logins = PreserveLogins() - XCTAssertFalse(logins.isAllowed(fireproofDomain: "example.com")) - logins.addToAllowed(domain: "example.com") - XCTAssertTrue(logins.isAllowed(fireproofDomain: "example.com")) + let fireproofing = UserDefaultsFireproofing() + XCTAssertFalse(fireproofing.isAllowed(fireproofDomain: "example.com")) + fireproofing.addToAllowed(domain: "example.com") + XCTAssertTrue(fireproofing.isAllowed(fireproofDomain: "example.com")) } func testWhenNewThenAllowedDomainsIsEmpty() { - let logins = PreserveLogins() - XCTAssertTrue(logins.allowedDomains.isEmpty) + let fireproofing = UserDefaultsFireproofing() + XCTAssertTrue(fireproofing.allowedDomains.isEmpty) } } diff --git a/DuckDuckGoTests/WebCacheManagerTests.swift b/DuckDuckGoTests/WebCacheManagerTests.swift index 94c1f7dd20..196762db89 100644 --- a/DuckDuckGoTests/WebCacheManagerTests.swift +++ b/DuckDuckGoTests/WebCacheManagerTests.swift @@ -41,19 +41,20 @@ class WebCacheManagerTests: XCTestCase { @available(iOS 17, *) @MainActor func testEnsureIdAllocatedAfterClearing() async throws { - let logins = MockPreservedLogins(domains: []) + let fireproofing = MockFireproofing(domains: []) + let storage = CookieStorage() let inMemoryDataStoreIdManager = DataStoreIdManager(store: MockKeyValueStore()) XCTAssertNil(inMemoryDataStoreIdManager.currentId) - await WebCacheManager.shared.clear(cookieStorage: storage, logins: logins, dataStoreIdManager: inMemoryDataStoreIdManager) + await WebCacheManager.shared.clear(cookieStorage: storage, fireproofing: fireproofing, dataStoreIdManager: inMemoryDataStoreIdManager) XCTAssertNotNil(inMemoryDataStoreIdManager.currentId) let oldId = inMemoryDataStoreIdManager.currentId?.uuidString XCTAssertNotNil(oldId) - await WebCacheManager.shared.clear(cookieStorage: storage, logins: logins, dataStoreIdManager: inMemoryDataStoreIdManager) + await WebCacheManager.shared.clear(cookieStorage: storage, fireproofing: fireproofing, dataStoreIdManager: inMemoryDataStoreIdManager) XCTAssertNotNil(inMemoryDataStoreIdManager.currentId) XCTAssertNotEqual(inMemoryDataStoreIdManager.currentId?.uuidString, oldId) @@ -62,9 +63,7 @@ class WebCacheManagerTests: XCTestCase { @available(iOS 17, *) @MainActor func testWhenCookiesHaveSubDomainsOnSubDomainsAndWidlcardsThenOnlyMatchingCookiesRetained() async throws { - let logins = MockPreservedLogins(domains: [ - "mobile.twitter.com" - ]) + let fireproofing = MockFireproofing(domains: ["mobile.twitter.com"]) let defaultStore = WKWebsiteDataStore.default() await defaultStore.removeData(ofTypes: WKWebsiteDataStore.allWebsiteDataTypes(), modifiedSince: .distantPast) @@ -82,7 +81,7 @@ class WebCacheManagerTests: XCTestCase { XCTAssertEqual(5, loadedCount) let cookieStore = CookieStorage() - await WebCacheManager.shared.clear(cookieStorage: cookieStore, logins: logins, dataStoreIdManager: DataStoreIdManager(store: MockKeyValueStore())) + await WebCacheManager.shared.clear(cookieStorage: cookieStore, fireproofing: fireproofing, dataStoreIdManager: DataStoreIdManager(store: MockKeyValueStore())) let cookies = await defaultStore.httpCookieStore.allCookies() XCTAssertEqual(cookies.count, 0) @@ -113,9 +112,7 @@ class WebCacheManagerTests: XCTestCase { @MainActor func testWhenClearedThenCookiesWithParentDomainsAreRetained() async { - let logins = MockPreservedLogins(domains: [ - "www.example.com" - ]) + let fireproofing = MockFireproofing(domains: ["www.example.com"]) let defaultStore = WKWebsiteDataStore.default() await defaultStore.removeData(ofTypes: WKWebsiteDataStore.allWebsiteDataTypes(), modifiedSince: .distantPast) @@ -130,7 +127,7 @@ class WebCacheManagerTests: XCTestCase { let cookieStorage = CookieStorage() await WebCacheManager.shared.clear(cookieStorage: cookieStorage, - logins: logins, + fireproofing: fireproofing, dataStoreIdManager: DataStoreIdManager(store: MockKeyValueStore())) let cookies = await defaultStore.httpCookieStore.allCookies() @@ -150,9 +147,7 @@ class WebCacheManagerTests: XCTestCase { @MainActor func testWhenClearedWithLegacyContainerThenDDGCookiesAreRetained() async { - let logins = MockPreservedLogins(domains: [ - "www.example.com" - ]) + let fireproofing = MockFireproofing(domains: ["www.example.com"]) let cookieStore = WKWebsiteDataStore.default().httpCookieStore await cookieStore.setCookie(.make(name: "name", value: "value", domain: "duckduckgo.com")) @@ -161,7 +156,7 @@ class WebCacheManagerTests: XCTestCase { let storage = CookieStorage() storage.isConsumed = true - await WebCacheManager.shared.clear(cookieStorage: storage, logins: logins, dataStoreIdManager: DataStoreIdManager(store: MockKeyValueStore())) + await WebCacheManager.shared.clear(cookieStorage: storage, fireproofing: fireproofing, dataStoreIdManager: DataStoreIdManager(store: MockKeyValueStore())) XCTAssertEqual(storage.cookies.count, 2) XCTAssertTrue(storage.cookies.contains(where: { $0.domain == "duckduckgo.com" })) @@ -170,9 +165,7 @@ class WebCacheManagerTests: XCTestCase { @MainActor func testWhenClearedThenCookiesForLoginsAreRetained() async { - let logins = MockPreservedLogins(domains: [ - "www.example.com" - ]) + let fireproofing = MockFireproofing(domains: ["www.example.com"]) let defaultStore = WKWebsiteDataStore.default() await defaultStore.removeData(ofTypes: WKWebsiteDataStore.allWebsiteDataTypes(), modifiedSince: .distantPast) @@ -188,7 +181,7 @@ class WebCacheManagerTests: XCTestCase { let cookieStore = CookieStorage() - await WebCacheManager.shared.clear(cookieStorage: cookieStore, logins: logins, dataStoreIdManager: DataStoreIdManager(store: MockKeyValueStore())) + await WebCacheManager.shared.clear(cookieStorage: cookieStore, fireproofing: fireproofing, dataStoreIdManager: DataStoreIdManager(store: MockKeyValueStore())) let cookies = await defaultStore.httpCookieStore.allCookies() XCTAssertEqual(cookies.count, 0) @@ -202,21 +195,18 @@ class WebCacheManagerTests: XCTestCase { let pool = WebCacheManager.shared.getValidDatabasePool() XCTAssertNotNil(pool, "DatabasePool should not be nil") } - + // MARK: Mocks - class MockPreservedLogins: PreserveLogins { - - let domains: [String] - + class MockFireproofing: UserDefaultsFireproofing { override var allowedDomains: [String] { return domains } - + + let domains: [String] init(domains: [String]) { self.domains = domains } - } } 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.