From 70fbdec1e0a90d2ab1e3a0ab89057c2fa1252509 Mon Sep 17 00:00:00 2001 From: amddg44 Date: Thu, 16 Nov 2023 17:37:14 +0100 Subject: [PATCH] Autofill "Never Save for this Site" support for iOS (#1827) Task/Issue URL: https://app.asana.com/0/0/1205783642285427/f Tech Design URL: CC: Description: BSK update to support "Never Save for this Site" on iOS including support for getRuntimeConfiguration --- DuckDuckGo.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/swiftpm/Package.resolved | 8 +-- .../ContentOverlayViewController.swift | 15 ++++++ .../ScriptSourceProviding.swift | 10 ++-- .../TabExtensions/AutofillTabExtension.swift | 24 +++++++++ LocalPackages/Account/Package.swift | 2 +- .../DataBrokerProtection/Package.swift | 2 +- .../NetworkProtectionMac/Package.swift | 2 +- UnitTests/DataExport/MockSecureVault.swift | 53 +++++++++++++++++++ 9 files changed, 106 insertions(+), 12 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index bbf787f5b4..39e48b0cbe 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -14431,7 +14431,7 @@ repositoryURL = "https://github.com/duckduckgo/BrowserServicesKit"; requirement = { kind = exactVersion; - version = 84.0.1; + version = 84.1.0; }; }; AA06B6B52672AF8100F541C5 /* XCRemoteSwiftPackageReference "Sparkle" */ = { diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index af89299036..936c0a391a 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -14,8 +14,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/duckduckgo/BrowserServicesKit", "state" : { - "revision" : "075773533bfca9196115674d2ab1b085570854dd", - "version" : "84.0.1" + "revision" : "641018cef1a3a13e9fdeb490b18639d1cb25569f", + "version" : "84.1.0" } }, { @@ -32,8 +32,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/duckduckgo/duckduckgo-autofill.git", "state" : { - "revision" : "c8e895c8fd50dc76e8d8dc827a636ad77b7f46ff", - "version" : "9.0.0" + "revision" : "93677cc02cfe650ce7f417246afd0e8e972cd83e", + "version" : "10.0.0" } }, { diff --git a/DuckDuckGo/Autofill/ContentOverlayViewController.swift b/DuckDuckGo/Autofill/ContentOverlayViewController.swift index 839020e14b..26eb6e4733 100644 --- a/DuckDuckGo/Autofill/ContentOverlayViewController.swift +++ b/DuckDuckGo/Autofill/ContentOverlayViewController.swift @@ -53,6 +53,8 @@ public final class ContentOverlayViewController: NSViewController, EmailManagerR lazy var passwordManagerCoordinator: PasswordManagerCoordinating = PasswordManagerCoordinator.shared + lazy var privacyConfigurationManager: PrivacyConfigurationManaging = AppPrivacyFeatures.shared.contentBlocking.privacyConfigurationManager + public override func viewDidLoad() { initWebView() addTrackingArea() @@ -339,4 +341,17 @@ extension ContentOverlayViewController: SecureVaultManagerDelegate { autofillPreferencesModel.showAutofillPopover(.logins) } } + + public func secureVaultManager(_: SecureVaultManager, didRequestRuntimeConfigurationForDomain domain: String, completionHandler: @escaping (String?) -> Void) { + let properties = ContentScopeProperties(gpcEnabled: PrivacySecurityPreferences.shared.gpcEnabled, + sessionKey: topAutofillUserScript?.sessionKey ?? "", + featureToggles: ContentScopeFeatureToggles.supportedFeaturesOnMacOS(privacyConfigurationManager.privacyConfig)) + + let runtimeConfiguration = DefaultAutofillSourceProvider.Builder(privacyConfigurationManager: privacyConfigurationManager, + properties: properties) + .build() + .buildRuntimeConfigResponse() + + completionHandler(runtimeConfiguration) + } } diff --git a/DuckDuckGo/ContentBlocker/ScriptSourceProviding.swift b/DuckDuckGo/ContentBlocker/ScriptSourceProviding.swift index 9711c7de08..eeb484e27c 100644 --- a/DuckDuckGo/ContentBlocker/ScriptSourceProviding.swift +++ b/DuckDuckGo/ContentBlocker/ScriptSourceProviding.swift @@ -82,10 +82,12 @@ struct ScriptSourceProvider: ScriptSourceProviding { public func buildAutofillSource() -> AutofillUserScriptSourceProvider { let privacyConfig = self.privacyConfigurationManager.privacyConfig - return DefaultAutofillSourceProvider(privacyConfigurationManager: self.privacyConfigurationManager, - properties: ContentScopeProperties(gpcEnabled: privacySettings.gpcEnabled, - sessionKey: self.sessionKey ?? "", - featureToggles: ContentScopeFeatureToggles.supportedFeaturesOnMacOS(privacyConfig))) + return DefaultAutofillSourceProvider.Builder(privacyConfigurationManager: privacyConfigurationManager, + properties: ContentScopeProperties(gpcEnabled: privacySettings.gpcEnabled, + sessionKey: self.sessionKey ?? "", + featureToggles: ContentScopeFeatureToggles.supportedFeaturesOnMacOS(privacyConfig))) + .withJSLoading() + .build() } private func buildContentBlockerRulesConfig() -> ContentBlockerUserScriptConfig { diff --git a/DuckDuckGo/Tab/TabExtensions/AutofillTabExtension.swift b/DuckDuckGo/Tab/TabExtensions/AutofillTabExtension.swift index 716138097f..ce81bde4d4 100644 --- a/DuckDuckGo/Tab/TabExtensions/AutofillTabExtension.swift +++ b/DuckDuckGo/Tab/TabExtensions/AutofillTabExtension.swift @@ -51,9 +51,12 @@ final class AutofillTabExtension: TabExtension { autofillScript?.currentOverlayTab = self.delegate } } + private lazy var cachedRuntimeConfigurationForDomain: [String: String?] = [:] + private var emailManager: AutofillEmailDelegate? private var vaultManager: AutofillSecureVaultDelegate? private var passwordManagerCoordinator: PasswordManagerCoordinating = PasswordManagerCoordinator.shared + private let privacyConfigurationManager: PrivacyConfigurationManaging = AppPrivacyFeatures.shared.contentBlocking.privacyConfigurationManager private let isBurner: Bool @Published var autofillDataToSave: AutofillData? @@ -178,6 +181,27 @@ extension AutofillTabExtension: SecureVaultManagerDelegate { func secureVaultManager(_: SecureVaultManager, didRequestPasswordManagerForDomain domain: String) { // no-op } + + func secureVaultManager(_: SecureVaultManager, didRequestRuntimeConfigurationForDomain domain: String, completionHandler: @escaping (String?) -> Void) { + if let runtimeConfigurationForDomain = cachedRuntimeConfigurationForDomain[domain] as? String { + completionHandler(runtimeConfigurationForDomain) + return + } + let runtimeConfiguration = DefaultAutofillSourceProvider.Builder(privacyConfigurationManager: privacyConfigurationManager, + properties: buildContentScopePropertiesForDomain(domain)) + .build() + .buildRuntimeConfigResponse() + + cachedRuntimeConfigurationForDomain = [domain: runtimeConfiguration] + completionHandler(runtimeConfiguration) + } + + private func buildContentScopePropertiesForDomain(_ domain: String) -> ContentScopeProperties { + return ContentScopeProperties(gpcEnabled: PrivacySecurityPreferences.shared.gpcEnabled, + sessionKey: autofillScript?.sessionKey ?? "", + featureToggles: ContentScopeFeatureToggles.supportedFeaturesOnMacOS(privacyConfigurationManager.privacyConfig) + ) + } } extension AutofillType { diff --git a/LocalPackages/Account/Package.swift b/LocalPackages/Account/Package.swift index a6a84fa06e..bcb787f495 100644 --- a/LocalPackages/Account/Package.swift +++ b/LocalPackages/Account/Package.swift @@ -12,7 +12,7 @@ let package = Package( targets: ["Account"]), ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "84.0.1"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "84.1.0"), .package(path: "../Purchase") ], targets: [ diff --git a/LocalPackages/DataBrokerProtection/Package.swift b/LocalPackages/DataBrokerProtection/Package.swift index a9e3feaff7..214c3179ec 100644 --- a/LocalPackages/DataBrokerProtection/Package.swift +++ b/LocalPackages/DataBrokerProtection/Package.swift @@ -29,7 +29,7 @@ let package = Package( targets: ["DataBrokerProtection"]) ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "84.0.1"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "84.1.0"), .package(path: "../PixelKit"), .package(path: "../SwiftUIExtensions"), .package(path: "../XPCHelper") diff --git a/LocalPackages/NetworkProtectionMac/Package.swift b/LocalPackages/NetworkProtectionMac/Package.swift index 10aedecf01..43c2ce5338 100644 --- a/LocalPackages/NetworkProtectionMac/Package.swift +++ b/LocalPackages/NetworkProtectionMac/Package.swift @@ -30,7 +30,7 @@ let package = Package( .library(name: "NetworkProtectionUI", targets: ["NetworkProtectionUI"]) ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "84.0.1"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "84.1.0"), .package(path: "../XPCHelper"), .package(path: "../SwiftUIExtensions") ], diff --git a/UnitTests/DataExport/MockSecureVault.swift b/UnitTests/DataExport/MockSecureVault.swift index d178d01ca6..bfcffdd70b 100644 --- a/UnitTests/DataExport/MockSecureVault.swift +++ b/UnitTests/DataExport/MockSecureVault.swift @@ -41,6 +41,7 @@ final class MockSecureVault: AutofillSecureVault { var storedAccounts: [SecureVaultModels.WebsiteAccount] = [] var storedCredentials: [Int64: SecureVaultModels.WebsiteCredentials] = [:] + var storedNeverPromptWebsites = [SecureVaultModels.NeverPromptWebsites]() var storedNotes: [SecureVaultModels.Note] = [] var storedIdentities: [SecureVaultModels.Identity] = [] var storedCards: [SecureVaultModels.CreditCard] = [] @@ -80,6 +81,29 @@ final class MockSecureVault: AutofillSecureVault { storedCredentials[accountId] = nil } + func neverPromptWebsites() throws -> [SecureVaultModels.NeverPromptWebsites] { + return storedNeverPromptWebsites + } + + func hasNeverPromptWebsitesFor(domain: String) throws -> Bool { + return !storedNeverPromptWebsites.filter { $0.domain == domain }.isEmpty + } + + func storeNeverPromptWebsites(_ neverPromptWebsite: SecureVaultModels.NeverPromptWebsites) throws -> Int64 { + if let neverPromptWebsiteId = neverPromptWebsite.id { + storedNeverPromptWebsites.append(neverPromptWebsite) + return neverPromptWebsiteId + } else { + storedNeverPromptWebsites.append(neverPromptWebsite) + return -1 + } + + } + + func deleteAllNeverPromptWebsites() throws { + storedNeverPromptWebsites = [] + } + func notes() throws -> [SecureVaultModels.Note] { return storedNotes } @@ -210,6 +234,8 @@ class MockDatabaseProvider: AutofillDatabaseProvider { var _forDomain = [String]() var _credentialsDict = [Int64: SecureVaultModels.WebsiteCredentials]() var _note: SecureVaultModels.Note? + var _neverPromptWebsites = [SecureVaultModels.NeverPromptWebsites]() + var db: GRDB.DatabaseWriter // swiftlint:enable identifier_name @@ -253,6 +279,33 @@ class MockDatabaseProvider: AutofillDatabaseProvider { return _accounts } + func neverPromptWebsites() throws -> [SecureVaultModels.NeverPromptWebsites] { + return _neverPromptWebsites + } + + func hasNeverPromptWebsitesFor(domain: String) throws -> Bool { + return false + } + + func storeNeverPromptWebsite(_ neverPromptWebsite: SecureVaultModels.NeverPromptWebsites) throws -> Int64 { + if let neverPromptWebsiteId = neverPromptWebsite.id { + _neverPromptWebsites.append(neverPromptWebsite) + return neverPromptWebsiteId + } else { + return -1 + } + } + + func deleteAllNeverPromptWebsites() throws { + _neverPromptWebsites.removeAll() + } + + func updateNeverPromptWebsite(_ neverPromptWebsite: SecureVaultModels.NeverPromptWebsites) throws { + } + + func insertNeverPromptWebsite(_ neverPromptWebsite: SecureVaultModels.NeverPromptWebsites) throws { + } + func notes() throws -> [SecureVaultModels.Note] { return _notes }