diff --git a/.github/workflows/sync-end-to-end.yml b/.github/workflows/sync-end-to-end.yml index b2ea9a8661..c70dc3ef37 100644 --- a/.github/workflows/sync-end-to-end.yml +++ b/.github/workflows/sync-end-to-end.yml @@ -72,7 +72,7 @@ jobs: timeout-minutes: 90 strategy: matrix: - os-version: [15, 16, 17] + os-version: [17] #[15, 16, 17] max-parallel: 1 # Uncomment this line to run tests sequentially. fail-fast: false diff --git a/Core/BoolFileMarkerTests.swift b/Core/BoolFileMarkerTests.swift index 23893a5668..5ad4aa0910 100644 --- a/Core/BoolFileMarkerTests.swift +++ b/Core/BoolFileMarkerTests.swift @@ -41,7 +41,11 @@ final class BoolFileMarkerTests: XCTestCase { let fileURL = try XCTUnwrap(testFileURL) let attributes = try FileManager.default.attributesOfItem(atPath: fileURL.path) +#if targetEnvironment(simulator) XCTAssertNil(attributes[.protectionKey]) +#else + XCTAssertEqual(attributes[.protectionKey] as? FileProtectionType, FileProtectionType.none) +#endif XCTAssertTrue(FileManager.default.fileExists(atPath: fileURL.path)) XCTAssertEqual(marker.isPresent, true) } 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/FeatureFlag.swift b/Core/FeatureFlag.swift index 81e9b34dd4..da04bcfaf6 100644 --- a/Core/FeatureFlag.swift +++ b/Core/FeatureFlag.swift @@ -53,6 +53,9 @@ public enum FeatureFlag: String { /// https://app.asana.com/0/72649045549333/1208617860225199/f case networkProtectionEnforceRoutes + + /// https://app.asana.com/0/1208592102886666/1208613627589762/f + case crashReportOptInStatusResetting } extension FeatureFlag: FeatureFlagDescribing { @@ -118,6 +121,8 @@ extension FeatureFlag: FeatureFlagDescribing { return .remoteDevelopment(.subfeature(NetworkProtectionSubfeature.enforceRoutes)) case .adAttributionReporting: return .remoteReleasable(.feature(.adAttributionReporting)) + case .crashReportOptInStatusResetting: + return .internalOnly } } } 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 32053533ef..b614834fed 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -315,6 +315,7 @@ 6F64AA5F2C49463C00CF4489 /* ShortcutsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F64AA5E2C49463C00CF4489 /* ShortcutsModel.swift */; }; 6F655BE22BAB289E00AC3597 /* DefaultTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F655BE12BAB289E00AC3597 /* DefaultTheme.swift */; }; 6F691CCA2C4979EC002E9553 /* FavoritesTooltip.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F691CC92C4979EC002E9553 /* FavoritesTooltip.swift */; }; + 6F7BACD42CEE084B00F561D8 /* OmniBarEqualityCheckTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F7BACD32CEE084100F561D8 /* OmniBarEqualityCheckTests.swift */; }; 6F7FB8E12C660B3E00867DA7 /* NewTabPageFavoritesModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F7FB8DF2C660B1A00867DA7 /* NewTabPageFavoritesModelTests.swift */; }; 6F7FB8E32C660BF300867DA7 /* DailyPixelFiring.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F7FB8E22C660BF300867DA7 /* DailyPixelFiring.swift */; }; 6F7FB8E52C66158D00867DA7 /* NewTabPageShortcutsSettingsModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F7FB8E42C66158D00867DA7 /* NewTabPageShortcutsSettingsModelTests.swift */; }; @@ -403,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 */; }; 85058366219AE9EA00ED4EDB /* HomePageConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85058365219AE9EA00ED4EDB /* HomePageConfiguration.swift */; }; 85058369219F424500ED4EDB /* UIColorExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1B745211E549D550072547E /* UIColorExtension.swift */; }; 8505836A219F424500ED4EDB /* UIAlertControllerExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83004E832193E14C00DA013C /* UIAlertControllerExtension.swift */; }; @@ -466,9 +467,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 */; }; - 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 */; }; @@ -518,6 +520,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 */; }; @@ -552,15 +562,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 */; }; @@ -937,7 +942,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 */; }; @@ -1426,7 +1430,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 = ""; }; @@ -1641,6 +1644,7 @@ 6F64AA5E2C49463C00CF4489 /* ShortcutsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShortcutsModel.swift; sourceTree = ""; }; 6F655BE12BAB289E00AC3597 /* DefaultTheme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultTheme.swift; sourceTree = ""; }; 6F691CC92C4979EC002E9553 /* FavoritesTooltip.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FavoritesTooltip.swift; sourceTree = ""; }; + 6F7BACD32CEE084100F561D8 /* OmniBarEqualityCheckTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OmniBarEqualityCheckTests.swift; sourceTree = ""; }; 6F7FB8DF2C660B1A00867DA7 /* NewTabPageFavoritesModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewTabPageFavoritesModelTests.swift; sourceTree = ""; }; 6F7FB8E22C660BF300867DA7 /* DailyPixelFiring.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DailyPixelFiring.swift; sourceTree = ""; }; 6F7FB8E42C66158D00867DA7 /* NewTabPageShortcutsSettingsModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewTabPageShortcutsSettingsModelTests.swift; sourceTree = ""; }; @@ -1746,7 +1750,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 = ""; }; @@ -1800,10 +1804,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 = ""; }; @@ -1854,6 +1858,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 = ""; }; @@ -1889,15 +1896,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 = ""; }; @@ -2757,7 +2764,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 = ""; }; @@ -3582,6 +3588,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; @@ -4429,8 +4440,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 */, @@ -4618,12 +4628,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 = ""; @@ -5866,6 +5872,7 @@ 98559FD0267099F400A83094 /* ContentBlocker */, 31C138A127A334F600FFD4B2 /* Downloads */, D62EC3B72C24695800FC9D04 /* DuckPlayer */, + 85D2186E24BF24BA004373D2 /* Favicons */, 83134D7F20E2E013006CE65D /* Feedback */, 8588026724E4249800C24AB6 /* iPad */, 851DFD88212C5ED600D95F20 /* Main */, @@ -5963,7 +5970,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 */, @@ -6070,7 +6077,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 */, @@ -6125,6 +6132,7 @@ F17669A91E412A17003D3222 /* Mocks */ = { isa = PBXGroup; children = ( + 8598D2E52CEBAA1B00C45685 /* MockFaviconStore.swift */, 9F4CC51A2C48C0C7006A96EB /* MockTabDelegate.swift */, C14882E927F20DD000D59F0C /* MockBookmarksCoreDataStorage.swift */, C158AC7A297AB5DC0008723A /* MockSecureVault.swift */, @@ -6351,7 +6359,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 */, @@ -6392,6 +6400,7 @@ isa = PBXGroup; children = ( 6F3529FE2CDCEDF700A59170 /* OmniBarLoadingStateBearerTests.swift */, + 6F7BACD32CEE084100F561D8 /* OmniBarEqualityCheckTests.swift */, BBFF18B02C76448100C48D7D /* QuerySubmittedTests.swift */, 8588026424E4209900C24AB6 /* LargeOmniBarStateTests.swift */, 85F20005221702F7006BB258 /* AddressDisplayHelperTests.swift */, @@ -6465,7 +6474,6 @@ 830FA79B1F8E81FB00FCE105 /* ContentBlocker */, F17D722C1E8B3563003E8B0E /* Domain */, EE3B226929DE0EE10082298A /* FeatureFlags */, - 85D2186E24BF24BA004373D2 /* Favicons */, F1134EC91F40E74800B73467 /* Statistics */, F198D78F1E3976300088DA8A /* Utilities */, ); @@ -7500,7 +7508,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 */, @@ -7516,7 +7524,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 */, @@ -7836,6 +7844,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 */, @@ -7955,7 +7968,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 */, @@ -8110,6 +8123,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 */, @@ -8171,6 +8185,7 @@ 85D2187224BF24F2004373D2 /* NotFoundCachingDownloaderTests.swift in Sources */, C1935A242C89CC6D001AD72D /* AutofillHeaderViewFactoryTests.swift in Sources */, C111B26927F579EF006558B1 /* BookmarkOrFolderTests.swift in Sources */, + 6F7BACD42CEE084B00F561D8 /* OmniBarEqualityCheckTests.swift in Sources */, 6F7FB8E72C66197E00867DA7 /* NewTabPageSectionsSettingsModelTests.swift in Sources */, 851CD674244D7E6000331B98 /* UserDefaultsExtension.swift in Sources */, 569437362BE5160600C0881B /* SyncSettingsViewControllerErrorTests.swift in Sources */, @@ -8397,7 +8412,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 */, @@ -8424,10 +8438,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 */, @@ -8445,13 +8458,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 */, @@ -8469,7 +8482,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 */, @@ -8477,12 +8489,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 */, @@ -8508,6 +8518,7 @@ 9887DC252354D2AA005C85F5 /* Database.swift in Sources */, F143C3171E4A99D200CFDE3A /* AppURLs.swift in Sources */, C1963863283794A000298D4D /* BookmarksCachingSearch.swift in Sources */, + 8598D2DE2CEB97BE00C45685 /* FaviconHasher.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -9347,7 +9358,7 @@ CODE_SIGN_ENTITLEMENTS = PacketTunnelProvider/PacketTunnelProvider.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; @@ -9384,7 +9395,7 @@ CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -9474,7 +9485,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = ShareExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -9501,7 +9512,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -9647,7 +9658,7 @@ CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGo.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_ASSET_PATHS = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -9671,7 +9682,7 @@ CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGo.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; INFOPLIST_FILE = DuckDuckGo/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -9738,7 +9749,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEAD_CODE_STRIPPING = NO; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = Widgets/Info.plist; @@ -9772,7 +9783,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEAD_CODE_STRIPPING = NO; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -9805,7 +9816,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = OpenAction/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -9835,7 +9846,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -10224,7 +10235,7 @@ CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGoAlpha.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_ASSET_PATHS = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -10255,7 +10266,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = ShareExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -10283,7 +10294,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = OpenAction/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -10316,7 +10327,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEAD_CODE_STRIPPING = NO; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = Widgets/Info.plist; @@ -10346,7 +10357,7 @@ CODE_SIGN_ENTITLEMENTS = PacketTunnelProvider/PacketTunnelProviderAlpha.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; @@ -10379,11 +10390,11 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 0; + DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Core/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -10613,7 +10624,7 @@ CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGoAlpha.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_ASSET_PATHS = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -10641,7 +10652,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -10673,7 +10684,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -10710,7 +10721,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEAD_CODE_STRIPPING = NO; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -10745,7 +10756,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -10780,11 +10791,11 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 0; + DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Core/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -10956,11 +10967,11 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 0; + DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Core/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -10989,10 +11000,10 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 0; + DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Core/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -11229,7 +11240,7 @@ repositoryURL = "https://github.com/DuckDuckGo/BrowserServicesKit"; requirement = { kind = exactVersion; - version = 211.0.0; + version = 211.1.0; }; }; 9F8FE9472BAE50E50071E372 /* XCRemoteSwiftPackageReference "lottie-spm" */ = { diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index cdb32f0f44..bb0f553a9f 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -32,8 +32,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/DuckDuckGo/BrowserServicesKit", "state" : { - "revision" : "7033b0d6f166ac8152cff602f1a1301641f4da60", - "version" : "211.0.0" + "revision" : "ce0223a5cc5e404867be5e691f5df1de9ea24387", + "version" : "211.1.0" } }, { @@ -41,8 +41,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/duckduckgo/content-scope-scripts", "state" : { - "revision" : "f2caf4ff814f4714d07d6fc2cf02498cb54a1389", - "version" : "6.36.0" + "revision" : "6f8b28d98bee6e15c4020d46577143d49619c248", + "version" : "6.38.0" } }, { diff --git a/DuckDuckGo/AppDelegate.swift b/DuckDuckGo/AppDelegate.swift index 19582ea55d..80f6a48a49 100644 --- a/DuckDuckGo/AppDelegate.swift +++ b/DuckDuckGo/AppDelegate.swift @@ -284,7 +284,8 @@ import os.log secureVaultErrorReporter: SecureVaultReporter(), settingHandlers: [FavoritesDisplayModeSyncHandler()], favoritesDisplayModeStorage: FavoritesDisplayModeStorage(), - syncErrorHandler: syncErrorHandler + syncErrorHandler: syncErrorHandler, + faviconStoring: Favicons.shared ) let syncService = DDGSync( diff --git a/DuckDuckGo/AppSettings.swift b/DuckDuckGo/AppSettings.swift index 4c7c9d8991..f7242d9a88 100644 --- a/DuckDuckGo/AppSettings.swift +++ b/DuckDuckGo/AppSettings.swift @@ -79,6 +79,7 @@ protocol AppSettings: AnyObject, AppDebugSettings { var autoconsentEnabled: Bool { get set } var crashCollectionOptInStatus: CrashCollectionOptInStatus { get set } + var crashCollectionShouldRevertOptedInStatusTrigger: Int { get set } var duckPlayerMode: DuckPlayerMode { get set } var duckPlayerAskModeOverlayHidden: Bool { get set } diff --git a/DuckDuckGo/AppUserDefaults.swift b/DuckDuckGo/AppUserDefaults.swift index 559a521900..2c17e2ac1e 100644 --- a/DuckDuckGo/AppUserDefaults.swift +++ b/DuckDuckGo/AppUserDefaults.swift @@ -75,6 +75,7 @@ public class AppUserDefaults: AppSettings { static let favoritesDisplayMode = "com.duckduckgo.ios.favoritesDisplayMode" static let crashCollectionOptInStatus = "com.duckduckgo.ios.crashCollectionOptInStatus" + static let crashCollectionShouldRevertOptedInStatusTrigger = "com.duckduckgo.ios.crashCollectionShouldRevertOptedInStatusTrigger" static let duckPlayerMode = "com.duckduckgo.ios.duckPlayerMode" static let duckPlayerAskModeOverlayHidden = "com.duckduckgo.ios.duckPlayerAskModeOverlayHidden" @@ -399,6 +400,19 @@ public class AppUserDefaults: AppSettings { } } + var crashCollectionShouldRevertOptedInStatusTrigger: Int { + get { + if let resetTrigger = userDefaults?.integer(forKey: Keys.crashCollectionShouldRevertOptedInStatusTrigger) { + return resetTrigger + } else { + return 0 + } + } + set { + userDefaults?.setValue(newValue, forKey: Keys.crashCollectionShouldRevertOptedInStatusTrigger) + } + } + var duckPlayerMode: DuckPlayerMode { get { if let value = userDefaults?.string(forKey: Keys.duckPlayerMode), diff --git a/DuckDuckGo/AutofillLoginSettingsListViewController.swift b/DuckDuckGo/AutofillLoginSettingsListViewController.swift index 11e2934c0a..49884b187b 100644 --- a/DuckDuckGo/AutofillLoginSettingsListViewController.swift +++ b/DuckDuckGo/AutofillLoginSettingsListViewController.swift @@ -228,6 +228,7 @@ final class AutofillLoginSettingsListViewController: UIViewController { super.viewDidLoad() title = UserText.autofillLoginListTitle extendedLayoutIncludesOpaqueBars = true + navigationController?.presentationController?.delegate = self setupCancellables() installSubviews() installConstraints() @@ -449,6 +450,11 @@ final class AutofillLoginSettingsListViewController: UIViewController { } } + private func dismissSearchIfRequired() { + guard searchController.isActive else { return } + searchController.dismiss(animated: false) + } + private func presentDeleteConfirmation(for title: String, domain: String) { let message = title.isEmpty ? UserText.autofillLoginListLoginDeletedToastMessageNoTitle : UserText.autofillLoginListLoginDeletedToastMessage(for: title) @@ -805,11 +811,11 @@ final class AutofillLoginSettingsListViewController: UIViewController { let cell = tableView.dequeueCell(ofType: AutofillBreakageReportTableViewCell.self, for: indexPath) let contentView = AutofillBreakageReportCellContentView(onReport: { [weak self] in - guard let alert = self?.viewModel.createBreakageReporterAlert() else { + guard let self = self, let alert = self.viewModel.createBreakageReporterAlert() else { return } - self?.present(controller: alert, fromView: tableView) + self.present(controller: alert, fromView: self.tableView) Pixel.fire(pixel: .autofillLoginsReportConfirmationPromptDisplayed) }) @@ -1125,6 +1131,16 @@ extension AutofillLoginSettingsListViewController: UISearchBarDelegate { } } +// MARK: UIAdaptivePresentationControllerDelegate + +extension AutofillLoginSettingsListViewController: UIAdaptivePresentationControllerDelegate { + + func presentationControllerDidDismiss(_ presentationController: UIPresentationController) { + dismissSearchIfRequired() + } + +} + // MARK: Keyboard extension AutofillLoginSettingsListViewController { 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/CrashCollectionOnboarding.swift b/DuckDuckGo/CrashCollectionOnboarding.swift index 98a364220b..dd9e54af7c 100644 --- a/DuckDuckGo/CrashCollectionOnboarding.swift +++ b/DuckDuckGo/CrashCollectionOnboarding.swift @@ -20,6 +20,7 @@ import Combine import Foundation import SwiftUI +import BrowserServicesKit enum CrashCollectionOptInStatus: String { case undetermined, optedIn, optedOut @@ -35,16 +36,33 @@ final class CrashCollectionOnboardingViewController: UIHostingController Void) { let isCurrentlyPresenting = viewController.presentedViewController != nil + + if featureFlagger.isFeatureOn(.crashReportOptInStatusResetting) { + if appSettings.crashCollectionOptInStatus == .optedIn && + appSettings.crashCollectionShouldRevertOptedInStatusTrigger < crashCollectionShouldRevertOptedInStatusTriggerTargetValue { + appSettings.crashCollectionOptInStatus = .undetermined + appSettings.crashCollectionShouldRevertOptedInStatusTrigger = crashCollectionShouldRevertOptedInStatusTriggerTargetValue + } + } + guard shouldPresentOnboarding, !isCurrentlyPresenting else { if appSettings.crashCollectionOptInStatus == .optedIn { sendReport() 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/OmniBar.swift b/DuckDuckGo/OmniBar.swift index 91a1214bd4..fda9757981 100644 --- a/DuckDuckGo/OmniBar.swift +++ b/DuckDuckGo/OmniBar.swift @@ -360,15 +360,19 @@ class OmniBar: UIView { } fileprivate func refreshState(_ newState: any OmniBarState) { - if !newState.isEquivalent(to: state) { + if state.requiresUpdate(transitioningInto: newState) { Logger.general.debug("OmniBar entering \(newState.description) from \(self.state.description)") - if newState.clearTextOnStart { - clear() + + if state.isDifferentState(than: newState) { + if newState.clearTextOnStart { + clear() + } + cancelAllAnimations() } + state = newState - cancelAllAnimations() } - + searchFieldContainer.adjustTextFieldOffset(for: state) setVisibility(privacyInfoContainer, hidden: !state.showPrivacyIcon) diff --git a/DuckDuckGo/OmniBarState.swift b/DuckDuckGo/OmniBarState.swift index 3e486652c5..6dbba113b3 100644 --- a/DuckDuckGo/OmniBarState.swift +++ b/DuckDuckGo/OmniBarState.swift @@ -29,7 +29,7 @@ protocol OmniBarState: CustomStringConvertible { var showForwardButton: Bool { get } var showBookmarksButton: Bool { get } var showShareButton: Bool { get } - + var clearTextOnStart: Bool { get } var allowsTrackersAnimation: Bool { get } var showSearchLoupe: Bool { get } @@ -61,12 +61,20 @@ protocol OmniBarState: CustomStringConvertible { func withLoading() -> Self func withoutLoading() -> Self - func isEquivalent(to other: OmniBarState) -> Bool + func requiresUpdate(transitioningInto other: OmniBarState) -> Bool + func isDifferentState(than other: OmniBarState) -> Bool } extension OmniBarState { - func isEquivalent(to other: OmniBarState) -> Bool { - name == other.name && isLoading == other.isLoading + /// Returns if new state requires UI update + func requiresUpdate(transitioningInto other: OmniBarState) -> Bool { + name != other.name || isLoading != other.isLoading + } + + /// Checks whether the state type is different. + /// If `true` it may require transitioning to a different appearance and/or cancelling pending animations. + func isDifferentState(than other: OmniBarState) -> Bool { + name != other.name } var description: String { 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 9d1d79a2c4..61a47e794f 100644 --- a/DuckDuckGo/ScriptSourceProviding.swift +++ b/DuckDuckGo/ScriptSourceProviding.swift @@ -38,7 +38,7 @@ protocol ScriptSourceProviding { struct DefaultScriptSourceProvider: ScriptSourceProviding { - var loginDetectionEnabled: Bool { PreserveLogins.shared.loginDetectionEnabled } + var loginDetectionEnabled: Bool { fireproofing.loginDetectionEnabled } let sendDoNotSell: Bool let contentBlockerRulesConfig: ContentBlockerUserScriptConfig @@ -50,16 +50,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 2f8e8f90b8..a5bbd41846 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/AppSettingsMock.swift b/DuckDuckGoTests/AppSettingsMock.swift index 4d9b09321e..13ced3eb65 100644 --- a/DuckDuckGoTests/AppSettingsMock.swift +++ b/DuckDuckGoTests/AppSettingsMock.swift @@ -82,6 +82,8 @@ class AppSettingsMock: AppSettings { var autoconsentEnabled = true var crashCollectionOptInStatus: CrashCollectionOptInStatus = .undetermined + + var crashCollectionShouldRevertOptedInStatusTrigger: Int = 0 var newTabPageSectionsEnabled: Bool = false 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/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 673b4188ec..fe6b6b8215 100644 --- a/DuckDuckGoTests/FireButtonReferenceTests.swift +++ b/DuckDuckGoTests/FireButtonReferenceTests.swift @@ -49,13 +49,13 @@ final class FireButtonReferenceTests: XCTestCase { 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 { @@ -80,7 +80,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 @@ -97,13 +97,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 { @@ -121,7 +121,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/OmniBarEqualityCheckTests.swift b/DuckDuckGoTests/OmniBarEqualityCheckTests.swift new file mode 100644 index 0000000000..c10e468259 --- /dev/null +++ b/DuckDuckGoTests/OmniBarEqualityCheckTests.swift @@ -0,0 +1,113 @@ +// +// OmniBarEqualityCheckTests.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 XCTest +@testable import DuckDuckGo + +final class OmniBarEqualityCheckTests: XCTestCase { + func testRequiresUpdateChecksForIsLoading() { + let loadingOmniBarState = DummyOmniBarState(isLoading: true) + let notLoadingOmniBarState = DummyOmniBarState(isLoading: false) + + XCTAssertTrue(loadingOmniBarState.requiresUpdate(transitioningInto: notLoadingOmniBarState)) + } + + func testRequiresUpdateChecksForName() { + let fooOmniBarState = DummyOmniBarState(name: "foo") + let barOmniBarState = DummyOmniBarState(name: "bar") + + XCTAssertTrue(fooOmniBarState.requiresUpdate(transitioningInto: barOmniBarState)) + } + + func testIsDifferentStateChecksForName() { + let fooOmniBarState = DummyOmniBarState(name: "foo") + let barOmniBarState = DummyOmniBarState(name: "bar") + + XCTAssertTrue(fooOmniBarState.isDifferentState(than: barOmniBarState)) + } + + func testIsDifferentStateIgnoresOtherProperties() { + let fooOmniBarState = DummyOmniBarState() + var barOmniBarState = DummyOmniBarState() + + barOmniBarState.hasLargeWidth = !fooOmniBarState.hasLargeWidth + barOmniBarState.showBackButton = !fooOmniBarState.showBackButton + barOmniBarState.showForwardButton = !fooOmniBarState.showForwardButton + barOmniBarState.showBookmarksButton = !fooOmniBarState.showBookmarksButton + barOmniBarState.showShareButton = !fooOmniBarState.showShareButton + barOmniBarState.clearTextOnStart = !fooOmniBarState.clearTextOnStart + barOmniBarState.allowsTrackersAnimation = !fooOmniBarState.allowsTrackersAnimation + barOmniBarState.showSearchLoupe = !fooOmniBarState.showSearchLoupe + barOmniBarState.showCancel = !fooOmniBarState.showCancel + barOmniBarState.showPrivacyIcon = !fooOmniBarState.showPrivacyIcon + barOmniBarState.showBackground = !fooOmniBarState.showBackground + barOmniBarState.showClear = !fooOmniBarState.showClear + barOmniBarState.showRefresh = !fooOmniBarState.showRefresh + barOmniBarState.showMenu = !fooOmniBarState.showMenu + barOmniBarState.showSettings = !fooOmniBarState.showSettings + barOmniBarState.showVoiceSearch = !fooOmniBarState.showVoiceSearch + barOmniBarState.showAbort = !fooOmniBarState.showAbort + + XCTAssertFalse(fooOmniBarState.isDifferentState(than: barOmniBarState)) + } +} + +private struct DummyOmniBarState: OmniBarState, OmniBarLoadingBearerStateCreating { + var name: String + var isLoading: Bool + var voiceSearchHelper: VoiceSearchHelperProtocol + + var hasLargeWidth = false + var showBackButton = false + var showForwardButton = false + var showBookmarksButton = false + var showShareButton = false + var clearTextOnStart = false + var allowsTrackersAnimation = false + var showSearchLoupe = false + var showCancel = false + var showPrivacyIcon = false + var showBackground = false + var showClear = false + var showRefresh = false + var showMenu = false + var showSettings = false + var showVoiceSearch = false + var showAbort = false + + var onEditingStoppedState: OmniBarState { DummyOmniBarState() } + var onEditingStartedState: OmniBarState { DummyOmniBarState() } + var onTextClearedState: OmniBarState { DummyOmniBarState() } + var onTextEnteredState: OmniBarState { DummyOmniBarState() } + var onBrowsingStartedState: OmniBarState { DummyOmniBarState() } + var onBrowsingStoppedState: OmniBarState { DummyOmniBarState() } + var onEnterPhoneState: OmniBarState { DummyOmniBarState() } + var onEnterPadState: OmniBarState { DummyOmniBarState() } + var onReloadState: OmniBarState { DummyOmniBarState() } + + init(voiceSearchHelper: VoiceSearchHelperProtocol, isLoading: Bool) { + self.init(isLoading: isLoading, voiceSearchHelper: voiceSearchHelper) + } + + init(name: String = "DummyOmniBarState", isLoading: Bool = false, voiceSearchHelper: VoiceSearchHelperProtocol = MockVoiceSearchHelper()) { + self.name = name + self.isLoading = isLoading + self.voiceSearchHelper = voiceSearchHelper + } +} 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 0163b4c750..4a055578a5 100644 --- a/DuckDuckGoTests/OnboardingNavigationDelegateTests.swift +++ b/DuckDuckGoTests/OnboardingNavigationDelegateTests.swift @@ -46,7 +46,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 0b5057250e..7322c55cc0 100644 --- a/DuckDuckGoTests/SyncSettingsViewControllerErrorTests.swift +++ b/DuckDuckGoTests/SyncSettingsViewControllerErrorTests.swift @@ -50,7 +50,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/UserDefaultsFireproofingTests.swift b/DuckDuckGoTests/UserDefaultsFireproofingTests.swift new file mode 100644 index 0000000000..fea966e7bc --- /dev/null +++ b/DuckDuckGoTests/UserDefaultsFireproofingTests.swift @@ -0,0 +1,86 @@ +// +// UserDefaultsFireproofingTests.swift +// UnitTests +// +// Copyright © 2020 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 XCTest +@testable import Core + +<<<<<<<< HEAD:WebViewUnitTests/PreserveLoginsTests.swift +// TODO: More broad shared TestUtils +extension UserDefaultsWrapper { + + public static func clearAll() { + Key.allCases.forEach { key in + UserDefaults.app.removeObject(forKey: key.rawValue) + } + } +} + +extension XCTestCase { + + func temporaryUserDefaultSuite(with filePath: String) -> String { + guard let lastPathComponent = NSURL(fileURLWithPath: filePath).lastPathComponent else { + fatalError("Path should have a last path component") + } + + do { + let temporaryDirectory = try FileManager.default.url( + for: .itemReplacementDirectory, + in: .userDomainMask, + appropriateFor: FileManager.default.temporaryDirectory, + create: true + ) + + return "\(temporaryDirectory)\(lastPathComponent)" + } catch { + fatalError("temporary directory should always be created") + } + } + + func setupUserDefault(with path: String) { + let tmpPath = temporaryUserDefaultSuite(with: path) + UserDefaults.app.removePersistentDomain(forName: tmpPath) + UserDefaults.app = UserDefaults(suiteName: tmpPath)! + } + +} + +class PreserveLoginsTests: XCTestCase { +======== +class UserDefaultsFireproofingTests: XCTestCase { +>>>>>>>> main:DuckDuckGoTests/UserDefaultsFireproofingTests.swift + + override func setUp() { + super.setUp() + setupUserDefault(with: #file) + UserDefaultsWrapper.clearAll() + } + + func testWhenAllowedDomainsContainsFireproofedDomainThenReturnsTrue() { + let fireproofing = UserDefaultsFireproofing() + XCTAssertFalse(fireproofing.isAllowed(fireproofDomain: "example.com")) + fireproofing.addToAllowed(domain: "example.com") + XCTAssertTrue(fireproofing.isAllowed(fireproofDomain: "example.com")) + } + + func testWhenNewThenAllowedDomainsIsEmpty() { + let fireproofing = UserDefaultsFireproofing() + XCTAssertTrue(fireproofing.allowedDomains.isEmpty) + } + +} diff --git a/WebViewUnitTests/CookieStorageTests.swift b/WebViewUnitTests/CookieStorageTests.swift index 07927f155c..f200fc251e 100644 --- a/WebViewUnitTests/CookieStorageTests.swift +++ b/WebViewUnitTests/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/WebViewUnitTests/PreserveLoginsTests.swift b/WebViewUnitTests/UserDefaultsFireproofingTests.swift similarity index 82% rename from WebViewUnitTests/PreserveLoginsTests.swift rename to WebViewUnitTests/UserDefaultsFireproofingTests.swift index bd0ed471ed..7c138f16e8 100644 --- a/WebViewUnitTests/PreserveLoginsTests.swift +++ b/WebViewUnitTests/UserDefaultsFireproofingTests.swift @@ -1,5 +1,5 @@ // -// PreserveLoginsTests.swift +// UserDefaultsFireproofingTests.swift // UnitTests // // Copyright © 2020 DuckDuckGo. All rights reserved. @@ -56,10 +56,9 @@ extension XCTestCase { UserDefaults.app.removePersistentDomain(forName: tmpPath) UserDefaults.app = UserDefaults(suiteName: tmpPath)! } - } -class PreserveLoginsTests: XCTestCase { +class UserDefaultsFireproofingTests: XCTestCase { override func setUp() { super.setUp() @@ -68,15 +67,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/WebViewUnitTests/WebCacheManagerTests.swift b/WebViewUnitTests/WebCacheManagerTests.swift index 4f62ec8d46..785f513ffb 100644 --- a/WebViewUnitTests/WebCacheManagerTests.swift +++ b/WebViewUnitTests/WebCacheManagerTests.swift @@ -64,19 +64,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) @@ -85,9 +86,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) @@ -105,7 +104,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) @@ -136,9 +135,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) @@ -153,7 +150,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() @@ -173,9 +170,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")) @@ -184,7 +179,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" })) @@ -193,9 +188,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) @@ -211,7 +204,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) @@ -225,21 +218,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.