From c226cf6d1a04a2b1614771d8d91144ac2cc68092 Mon Sep 17 00:00:00 2001 From: Sabrina Tardio <44158575+SabrinaTardio@users.noreply.github.com> Date: Thu, 23 May 2024 09:13:03 +0200 Subject: [PATCH 01/11] move permanent survey card to first position (#2804) Task/Issue URL: https://app.asana.com/0/1201048563534612/1207354538419159/f **Description**: Make the permanent survey first card --- .../HomePage/Model/HomePageContinueSetUpModel.swift | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/DuckDuckGo/HomePage/Model/HomePageContinueSetUpModel.swift b/DuckDuckGo/HomePage/Model/HomePageContinueSetUpModel.swift index 22ddf43e7e..df7ea99ccb 100644 --- a/DuckDuckGo/HomePage/Model/HomePageContinueSetUpModel.swift +++ b/DuckDuckGo/HomePage/Model/HomePageContinueSetUpModel.swift @@ -286,12 +286,10 @@ extension HomePage.Models { } var randomisedFeatures: [FeatureType] { - var features = FeatureType.allCases - features.shuffle() - for (index, feature) in features.enumerated() where feature == .defaultBrowser { - features.remove(at: index) - features.insert(feature, at: 0) - } + var features: [FeatureType] = [.permanentSurvey, .defaultBrowser] + var shuffledFeatures = FeatureType.allCases.filter { $0 != .defaultBrowser && $0 != .permanentSurvey } + shuffledFeatures.shuffle() + features.append(contentsOf: shuffledFeatures) return features } From 2104521e66c7b9e872e19268e73a634043d581d9 Mon Sep 17 00:00:00 2001 From: Fernando Bunn Date: Thu, 23 May 2024 11:26:49 +0100 Subject: [PATCH 02/11] Check for entitlement in DBP agent (#2802) Task/Issue URL: https://app.asana.com/0/1201011656765697/1206366654222841/f **Description**: Deactivates the DBP agent if entitlements are invalid --- DuckDuckGo/Application/AppDelegate.swift | 13 +- .../DataBrokerProtectionPixelsHandler.swift | 6 +- ...erProtectionSubscriptionEventHandler.swift | 43 +++- ...rokerProtectionEntitlementMonitoring.swift | 56 +++++ .../Pixels/DataBrokerProtectionPixels.swift | 19 +- .../DataBrokerProtectionAgentManager.swift | 43 ++-- .../DataBrokerProtectionAgentStopper.swift | 110 ++++++++++ ...ataBrokerProtectionAgentManagerTests.swift | 98 +++++++-- ...ataBrokerProtectionAgentStopperTests.swift | 198 ++++++++++++++++++ .../DataBrokerProtectionTests/Mocks.swift | 36 +++- 10 files changed, 570 insertions(+), 52 deletions(-) create mode 100644 LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Authentication/DataBrokerProtectionEntitlementMonitoring.swift create mode 100644 LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Utils/DataBrokerProtectionAgentStopper.swift create mode 100644 LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerProtectionAgentStopperTests.swift diff --git a/DuckDuckGo/Application/AppDelegate.swift b/DuckDuckGo/Application/AppDelegate.swift index f0811eca7f..2b734be8f9 100644 --- a/DuckDuckGo/Application/AppDelegate.swift +++ b/DuckDuckGo/Application/AppDelegate.swift @@ -94,7 +94,13 @@ final class AppDelegate: NSObject, NSApplicationDelegate { private var networkProtectionSubscriptionEventHandler: NetworkProtectionSubscriptionEventHandler? #if DBP - private var dataBrokerProtectionSubscriptionEventHandler: DataBrokerProtectionSubscriptionEventHandler? + private lazy var dataBrokerProtectionSubscriptionEventHandler: DataBrokerProtectionSubscriptionEventHandler = { + let authManager = DataBrokerAuthenticationManagerBuilder.buildAuthenticationManager(subscriptionManager: subscriptionManager) + return DataBrokerProtectionSubscriptionEventHandler(featureDisabler: DataBrokerProtectionFeatureDisabler(), + authenticationManager: authManager, + pixelHandler: DataBrokerProtectionPixelsHandler()) + }() + #endif private var didFinishLaunching = false @@ -216,9 +222,6 @@ final class AppDelegate: NSObject, NSApplicationDelegate { networkProtectionSubscriptionEventHandler = NetworkProtectionSubscriptionEventHandler(subscriptionManager: subscriptionManager, tunnelController: tunnelController, vpnUninstaller: vpnUninstaller) -#if DBP - dataBrokerProtectionSubscriptionEventHandler = DataBrokerProtectionSubscriptionEventHandler(subscriptionManager: subscriptionManager) -#endif } // swiftlint:disable:next function_body_length @@ -310,7 +313,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate { UNUserNotificationCenter.current().delegate = self #if DBP - dataBrokerProtectionSubscriptionEventHandler?.registerForSubscriptionAccountManagerEvents() + dataBrokerProtectionSubscriptionEventHandler.registerForSubscriptionAccountManagerEvents() #endif #if DBP diff --git a/DuckDuckGo/DBP/DataBrokerProtectionPixelsHandler.swift b/DuckDuckGo/DBP/DataBrokerProtectionPixelsHandler.swift index 494d141be5..3632dc549b 100644 --- a/DuckDuckGo/DBP/DataBrokerProtectionPixelsHandler.swift +++ b/DuckDuckGo/DBP/DataBrokerProtectionPixelsHandler.swift @@ -98,7 +98,11 @@ public class DataBrokerProtectionPixelsHandler: EventMapping - init(subscriptionManager: SubscriptionManaging, - authRepository: AuthenticationRepository = KeychainAuthenticationData(), - featureDisabler: DataBrokerProtectionFeatureDisabling = DataBrokerProtectionFeatureDisabler()) { - self.subscriptionManager = subscriptionManager - self.authRepository = authRepository + init(featureDisabler: DataBrokerProtectionFeatureDisabling, + authenticationManager: DataBrokerProtectionAuthenticationManaging, + pixelHandler: EventMapping) { self.featureDisabler = featureDisabler + self.authenticationManager = authenticationManager + self.pixelHandler = pixelHandler } func registerForSubscriptionAccountManagerEvents() { - NotificationCenter.default.addObserver(self, selector: #selector(handleAccountDidSignOut), name: .accountDidSignOut, object: nil) + NotificationCenter.default.addObserver(self, + selector: #selector(handleAccountDidSignOut), + name: .accountDidSignOut, + object: nil) + + NotificationCenter.default.addObserver(self, + selector: #selector(entitlementsDidChange), + name: .entitlementsDidChange, + object: nil) } @objc private func handleAccountDidSignOut() { featureDisabler.disableAndDelete() } + + @objc private func entitlementsDidChange() { + Task { @MainActor in + do { + if try await authenticationManager.hasValidEntitlement() { + pixelHandler.fire(.entitlementCheckValid) + } else { + pixelHandler.fire(.entitlementCheckInvalid) + featureDisabler.disableAndDelete() + } + } catch { + /// We don't want to disable the agent in case of an error while checking for entitlements. + /// Since this is a destructive action, the only situation that should cause the data to be deleted and the agent to be removed is .success(false) + pixelHandler.fire(.entitlementCheckError) + assertionFailure("Error validating entitlement \(error)") + } + } + } } #endif diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Authentication/DataBrokerProtectionEntitlementMonitoring.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Authentication/DataBrokerProtectionEntitlementMonitoring.swift new file mode 100644 index 0000000000..77b6d883b2 --- /dev/null +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Authentication/DataBrokerProtectionEntitlementMonitoring.swift @@ -0,0 +1,56 @@ +// +// DataBrokerProtectionEntitlementMonitoring.swift +// +// 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 + +protocol DataBrokerProtectionEntitlementMonitoring { + func start(checkEntitlementFunction: @escaping () async throws -> Bool, interval: TimeInterval, callback: @escaping (DataBrokerProtectionEntitlementMonitorResult) -> Void) + func stop() +} + +public enum DataBrokerProtectionEntitlementMonitorResult { + case enabled + case disabled + case error +} + +final class DataBrokerProtectionEntitlementMonitor: DataBrokerProtectionEntitlementMonitoring { + private var timer: Timer? + + func start(checkEntitlementFunction: @escaping () async throws -> Bool, interval: TimeInterval, callback: @escaping (DataBrokerProtectionEntitlementMonitorResult) -> Void) { + timer = Timer.scheduledTimer(withTimeInterval: interval, repeats: true) { _ in + Task { + do { + switch try await checkEntitlementFunction() { + case true: + callback(.enabled) + case false: + callback(.disabled) + } + } catch { + callback(.error) + } + } + } + } + + func stop() { + timer?.invalidate() + timer = nil + } +} diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Pixels/DataBrokerProtectionPixels.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Pixels/DataBrokerProtectionPixels.swift index f83d3c6aeb..45f954a4d4 100644 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Pixels/DataBrokerProtectionPixels.swift +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Pixels/DataBrokerProtectionPixels.swift @@ -164,6 +164,11 @@ public enum DataBrokerProtectionPixels { case initialScanSiteLoadDuration(duration: Double, hasError: Bool, brokerURL: String, sleepDuration: Double) case initialScanPostLoadingDuration(duration: Double, hasError: Bool, brokerURL: String, sleepDuration: Double) case initialScanPreStartDuration(duration: Double) + + // Entitlements + case entitlementCheckValid + case entitlementCheckInvalid + case entitlementCheckError } extension DataBrokerProtectionPixels: PixelKitEvent { @@ -267,6 +272,11 @@ extension DataBrokerProtectionPixels: PixelKitEvent { case .initialScanSiteLoadDuration: return "m_mac_dbp_scan_broker_site_loaded" case .initialScanPostLoadingDuration: return "m_mac_dbp_initial_scan_broker_post_loading" case .initialScanPreStartDuration: return "m_mac_dbp_initial_scan_pre_start_duration" + + // Entitlements + case .entitlementCheckValid: return "m_mac_dbp_macos_entitlement_valid" + case .entitlementCheckInvalid: return "m_mac_dbp_macos_entitlement_invalid" + case .entitlementCheckError: return "m_mac_dbp_macos_entitlement_error" } } @@ -361,7 +371,9 @@ extension DataBrokerProtectionPixels: PixelKitEvent { .homeViewShowBadPathError, .homeViewCTAMoveApplicationClicked, .homeViewCTAGrantPermissionClicked, - + .entitlementCheckValid, + .entitlementCheckInvalid, + .entitlementCheckError, .secureVaultInitError, .secureVaultError: return [:] @@ -486,7 +498,10 @@ public class DataBrokerProtectionPixelsHandler: EventMapping + private let agentStopper: DataBrokerProtectionAgentStopper // Used for debug functions only, so not injected private lazy var browserWindowManager = BrowserWindowManager() @@ -111,7 +119,9 @@ public final class DataBrokerProtectionAgentManager { queueManager: DataBrokerProtectionQueueManager, dataManager: DataBrokerProtectionDataManaging, operationDependencies: DataBrokerOperationDependencies, - pixelHandler: EventMapping) { + pixelHandler: EventMapping, + agentStopper: DataBrokerProtectionAgentStopper + ) { self.userNotificationService = userNotificationService self.activityScheduler = activityScheduler self.ipcServer = ipcServer @@ -119,6 +129,7 @@ public final class DataBrokerProtectionAgentManager { self.dataManager = dataManager self.operationDependencies = operationDependencies self.pixelHandler = pixelHandler + self.agentStopper = agentStopper self.activityScheduler.delegate = self self.ipcServer.serverDelegate = self @@ -127,20 +138,20 @@ public final class DataBrokerProtectionAgentManager { public func agentFinishedLaunching() { - do { - // If there's no saved profile we don't need to start the scheduler - // Theoretically this should never happen, if there's no data, the agent shouldn't be running - guard (try dataManager.fetchProfile()) != nil else { - return - } - } catch { - os_log("Error during AgentManager.agentFinishedLaunching when trying to fetchProfile, error: %{public}@", log: .dataBrokerProtection, error.localizedDescription) - return + Task { @MainActor in + // The browser shouldn't start the agent if these prerequisites aren't met. + // However, since the agent can auto-start after a reboot without the browser, we need to validate it again. + // If the agent needs to be stopped, this function will stop it, so the subsequent calls after it will not be made. + await agentStopper.validateRunPrerequisitesAndStopAgentIfNecessary() + + activityScheduler.startScheduler() + didStartActivityScheduler = true + queueManager.startScheduledOperationsIfPermitted(showWebView: false, operationDependencies: operationDependencies, completion: nil) + + /// Monitors entitlement changes every 60 minutes to optimize system performance and resource utilization by avoiding unnecessary operations when entitlement is invalid. + /// While keeping the agent active with invalid entitlement has no significant risk, setting the monitoring interval at 60 minutes is a good balance to minimize backend checks. + agentStopper.monitorEntitlementAndStopAgentIfEntitlementIsInvalid(interval: .minutes(60)) } - - activityScheduler.startScheduler() - didStartActivityScheduler = true - queueManager.startScheduledOperationsIfPermitted(showWebView: false, operationDependencies: operationDependencies, completion: nil) } } diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Utils/DataBrokerProtectionAgentStopper.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Utils/DataBrokerProtectionAgentStopper.swift new file mode 100644 index 0000000000..fe6c7734be --- /dev/null +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Utils/DataBrokerProtectionAgentStopper.swift @@ -0,0 +1,110 @@ +// +// DataBrokerProtectionAgentStopper.swift +// +// Copyright © 2023 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 Common + +protocol DataBrokerProtectionAgentStopper { + /// Validates if the user has profile data, is authenticated, and has valid entitlement. If any of these conditions are not met, the agent will be stopped. + func validateRunPrerequisitesAndStopAgentIfNecessary() async + + /// Monitors the entitlement package. If the entitlement check returns false, the agent will be stopped. + /// This function ensures that the agent is stopped if the user's subscription has expired, even if the browser is not active. Regularly checking for entitlement is required since notifications are not posted to agents. + func monitorEntitlementAndStopAgentIfEntitlementIsInvalid(interval: TimeInterval) +} + +struct DefaultDataBrokerProtectionAgentStopper: DataBrokerProtectionAgentStopper { + private let dataManager: DataBrokerProtectionDataManaging + private let entitlementMonitor: DataBrokerProtectionEntitlementMonitoring + private let authenticationManager: DataBrokerProtectionAuthenticationManaging + private let pixelHandler: EventMapping + private let stopAction: DataProtectionStopAction + + init(dataManager: DataBrokerProtectionDataManaging, + entitlementMonitor: DataBrokerProtectionEntitlementMonitoring, + authenticationManager: DataBrokerProtectionAuthenticationManaging, + pixelHandler: EventMapping, + stopAction: DataProtectionStopAction = DefaultDataProtectionStopAction()) { + self.dataManager = dataManager + self.entitlementMonitor = entitlementMonitor + self.authenticationManager = authenticationManager + self.pixelHandler = pixelHandler + self.stopAction = stopAction + } + + public func validateRunPrerequisitesAndStopAgentIfNecessary() async { + do { + guard try dataManager.fetchProfile() != nil, + authenticationManager.isUserAuthenticated else { + os_log("Prerequisites are invalid", log: .dataBrokerProtection) + stopAgent() + return + } + os_log("Prerequisites are valid", log: .dataBrokerProtection) + } catch { + os_log("Error validating prerequisites, error: %{public}@", log: .dataBrokerProtection, error.localizedDescription) + stopAgent() + } + + do { + let result = try await authenticationManager.hasValidEntitlement() + stopAgentBasedOnEntitlementCheckResult(result ? .enabled : .disabled) + } catch { + stopAgentBasedOnEntitlementCheckResult(.error) + } + } + + public func monitorEntitlementAndStopAgentIfEntitlementIsInvalid(interval: TimeInterval) { + entitlementMonitor.start(checkEntitlementFunction: authenticationManager.hasValidEntitlement, + interval: interval) { result in + stopAgentBasedOnEntitlementCheckResult(result) + } + } + + private func stopAgent() { + stopAction.stopAgent() + } + + private func stopAgentBasedOnEntitlementCheckResult(_ result: DataBrokerProtectionEntitlementMonitorResult) { + switch result { + case .enabled: + os_log("Valid entitlement", log: .dataBrokerProtection) + pixelHandler.fire(.entitlementCheckValid) + case .disabled: + os_log("Invalid entitlement", log: .dataBrokerProtection) + pixelHandler.fire(.entitlementCheckInvalid) + stopAgent() + case .error: + os_log("Error when checking entitlement", log: .dataBrokerProtection) + /// We don't want to disable the agent in case of an error while checking for entitlements. + /// Since this is a destructive action, the only situation that should cause the data to be deleted and the agent to be removed is .success(false) + pixelHandler.fire(.entitlementCheckError) + } + } +} + +protocol DataProtectionStopAction { + func stopAgent() +} + +struct DefaultDataProtectionStopAction: DataProtectionStopAction { + func stopAgent() { + os_log("Stopping DataBrokerProtection Agent", log: .dataBrokerProtection) + exit(EXIT_SUCCESS) + } +} diff --git a/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerProtectionAgentManagerTests.swift b/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerProtectionAgentManagerTests.swift index f4d00a5d5c..185e21f2f8 100644 --- a/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerProtectionAgentManagerTests.swift +++ b/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerProtectionAgentManagerTests.swift @@ -31,12 +31,14 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { private var mockPixelHandler: MockPixelHandler! private var mockDependencies: DefaultDataBrokerOperationDependencies! private var mockProfile: DataBrokerProtectionProfile! + private var mockAgentStopper: MockAgentStopper! override func setUpWithError() throws { mockPixelHandler = MockPixelHandler() mockActivityScheduler = MockDataBrokerProtectionBackgroundActivityScheduler() mockNotificationService = MockUserNotificationService() + mockAgentStopper = MockAgentStopper() let mockDatabase = MockDatabase() let mockMismatchCalculator = MockMismatchCalculator(database: mockDatabase, pixelHandler: mockPixelHandler) @@ -75,30 +77,42 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { queueManager: mockQueueManager, dataManager: mockDataManager, operationDependencies: mockDependencies, - pixelHandler: mockPixelHandler) + pixelHandler: mockPixelHandler, + agentStopper: mockAgentStopper) mockDataManager.profileToReturn = mockProfile + let schedulerStartedExpectation = XCTestExpectation(description: "Scheduler started") var schedulerStarted = false mockActivityScheduler.startSchedulerCompletion = { schedulerStarted = true + schedulerStartedExpectation.fulfill() } + let scanCalledExpectation = XCTestExpectation(description: "Scan called") var startScheduledScansCalled = false mockQueueManager.startScheduledOperationsIfPermittedCalledCompletion = { _ in startScheduledScansCalled = true + scanCalledExpectation.fulfill() } // When sut.agentFinishedLaunching() // Then + await fulfillment(of: [scanCalledExpectation, schedulerStartedExpectation], timeout: 1.0) XCTAssertTrue(schedulerStarted) XCTAssertTrue(startScheduledScansCalled) } - func testWhenAgentStart_andProfileDoesNotExist_thenActivityIsNotScheduled_andSheduledOpereationsNotRun() async throws { + func testWhenAgentStart_andProfileDoesNotExist_thenActivityIsNotScheduled_andStopAgentIsCalled() async throws { // Given + let mockStopAction = MockDataProtectionStopAction() + let agentStopper = DefaultDataBrokerProtectionAgentStopper(dataManager: mockDataManager, + entitlementMonitor: DataBrokerProtectionEntitlementMonitor(), + authenticationManager: MockAuthenticationManager(), + pixelHandler: mockPixelHandler, + stopAction: mockStopAction) sut = DataBrokerProtectionAgentManager( userNotificationService: mockNotificationService, activityScheduler: mockActivityScheduler, @@ -106,26 +120,64 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { queueManager: mockQueueManager, dataManager: mockDataManager, operationDependencies: mockDependencies, - pixelHandler: mockPixelHandler) + pixelHandler: mockPixelHandler, + agentStopper: agentStopper) mockDataManager.profileToReturn = nil - var schedulerStarted = false - mockActivityScheduler.startSchedulerCompletion = { - schedulerStarted = true + let stopAgentExpectation = XCTestExpectation(description: "Stop agent expectation") + + var stopAgentWasCalled = false + mockStopAction.stopAgentCompletion = { + stopAgentWasCalled = true + stopAgentExpectation.fulfill() } - var startScheduledScansCalled = false - mockQueueManager.startScheduledOperationsIfPermittedCalledCompletion = { _ in - startScheduledScansCalled = true + // When + sut.agentFinishedLaunching() + await fulfillment(of: [stopAgentExpectation], timeout: 1.0) + + // Then + XCTAssertTrue(stopAgentWasCalled) + } + + func testWhenAgentStart_thenPrerequisitesAreValidated_andEntitlementsAreMonitored() async { + // Given + let mockAgentStopper = MockAgentStopper() + + sut = DataBrokerProtectionAgentManager( + userNotificationService: mockNotificationService, + activityScheduler: mockActivityScheduler, + ipcServer: mockIPCServer, + queueManager: mockQueueManager, + dataManager: mockDataManager, + operationDependencies: mockDependencies, + pixelHandler: mockPixelHandler, + agentStopper: mockAgentStopper) + + mockDataManager.profileToReturn = nil + + let preRequisitesExpectation = XCTestExpectation(description: "preRequisitesExpectation expectation") + var runPrerequisitesWasCalled = false + mockAgentStopper.validateRunPrerequisitesCompletion = { + runPrerequisitesWasCalled = true + preRequisitesExpectation.fulfill() + } + + let monitorEntitlementExpectation = XCTestExpectation(description: "monitorEntitlement expectation") + var monitorEntitlementWasCalled = false + mockAgentStopper.monitorEntitlementCompletion = { + monitorEntitlementWasCalled = true + monitorEntitlementExpectation.fulfill() } // When sut.agentFinishedLaunching() + await fulfillment(of: [preRequisitesExpectation, monitorEntitlementExpectation], timeout: 1.0) // Then - XCTAssertFalse(schedulerStarted) - XCTAssertFalse(startScheduledScansCalled) + XCTAssertTrue(runPrerequisitesWasCalled) + XCTAssertTrue(monitorEntitlementWasCalled) } func testWhenActivitySchedulerTriggers_thenSheduledOpereationsRun() async throws { @@ -137,7 +189,8 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { queueManager: mockQueueManager, dataManager: mockDataManager, operationDependencies: mockDependencies, - pixelHandler: mockPixelHandler) + pixelHandler: mockPixelHandler, + agentStopper: mockAgentStopper) mockDataManager.profileToReturn = mockProfile @@ -162,7 +215,8 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { queueManager: mockQueueManager, dataManager: mockDataManager, operationDependencies: mockDependencies, - pixelHandler: mockPixelHandler) + pixelHandler: mockPixelHandler, + agentStopper: mockAgentStopper) mockDataManager.profileToReturn = mockProfile @@ -187,7 +241,8 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { queueManager: mockQueueManager, dataManager: mockDataManager, operationDependencies: mockDependencies, - pixelHandler: mockPixelHandler) + pixelHandler: mockPixelHandler, + agentStopper: mockAgentStopper) mockNotificationService.reset() @@ -207,7 +262,8 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { queueManager: mockQueueManager, dataManager: mockDataManager, operationDependencies: mockDependencies, - pixelHandler: mockPixelHandler) + pixelHandler: mockPixelHandler, + agentStopper: mockAgentStopper) mockNotificationService.reset() @@ -227,7 +283,8 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { queueManager: mockQueueManager, dataManager: mockDataManager, operationDependencies: mockDependencies, - pixelHandler: mockPixelHandler) + pixelHandler: mockPixelHandler, + agentStopper: mockAgentStopper) mockNotificationService.reset() mockQueueManager.startImmediateOperationsIfPermittedCompletionError = DataBrokerProtectionAgentErrorCollection(oneTimeError: NSError(domain: "test", code: 10)) @@ -248,7 +305,8 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { queueManager: mockQueueManager, dataManager: mockDataManager, operationDependencies: mockDependencies, - pixelHandler: mockPixelHandler) + pixelHandler: mockPixelHandler, + agentStopper: mockAgentStopper) mockNotificationService.reset() mockDataManager.shouldReturnHasMatches = true @@ -269,7 +327,8 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { queueManager: mockQueueManager, dataManager: mockDataManager, operationDependencies: mockDependencies, - pixelHandler: mockPixelHandler) + pixelHandler: mockPixelHandler, + agentStopper: mockAgentStopper) mockNotificationService.reset() mockDataManager.shouldReturnHasMatches = false @@ -290,7 +349,8 @@ final class DataBrokerProtectionAgentManagerTests: XCTestCase { queueManager: mockQueueManager, dataManager: mockDataManager, operationDependencies: mockDependencies, - pixelHandler: mockPixelHandler) + pixelHandler: mockPixelHandler, + agentStopper: mockAgentStopper) var startScheduledScansCalled = false mockQueueManager.startScheduledOperationsIfPermittedCalledCompletion = { _ in diff --git a/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerProtectionAgentStopperTests.swift b/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerProtectionAgentStopperTests.swift new file mode 100644 index 0000000000..5d8c25ebee --- /dev/null +++ b/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerProtectionAgentStopperTests.swift @@ -0,0 +1,198 @@ +// +// DataBrokerProtectionAgentStopperTests.swift +// +// 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 XCTest +import Common + +@testable import DataBrokerProtection + +final class DataBrokerProtectionAgentStopperTests: XCTestCase { + private var mockPixelHandler: EventMapping! + private var mockAuthenticationManager: MockAuthenticationManager! + private var mockEntitlementMonitor: DataBrokerProtectionEntitlementMonitor! + private var mockDataManager: MockDataBrokerProtectionDataManager! + private var mockStopAction: MockDataProtectionStopAction! + + private var fakeProfile: DataBrokerProtectionProfile { + let name = DataBrokerProtectionProfile.Name(firstName: "John", lastName: "Doe") + let address = DataBrokerProtectionProfile.Address(city: "City", state: "State") + + return DataBrokerProtectionProfile(names: [name], addresses: [address], phones: [String](), birthYear: 1900) + } + + override func setUp() { + mockPixelHandler = MockDataBrokerProtectionPixelsHandler() + mockAuthenticationManager = MockAuthenticationManager() + mockPixelHandler = MockPixelHandler() + mockEntitlementMonitor = DataBrokerProtectionEntitlementMonitor() + mockDataManager = MockDataBrokerProtectionDataManager(pixelHandler: mockPixelHandler, + fakeBrokerFlag: DataBrokerDebugFlagFakeBroker()) + mockStopAction = MockDataProtectionStopAction() + } + + override func tearDown() { + mockPixelHandler = nil + mockAuthenticationManager = nil + mockPixelHandler = nil + mockEntitlementMonitor = nil + mockDataManager = nil + mockStopAction = nil + } + + func testNoProfile_thenStopAgentIsCalled() async { + mockAuthenticationManager.isUserAuthenticatedValue = true + mockAuthenticationManager.hasValidEntitlementValue = true + mockDataManager.profileToReturn = nil + + let stopper = DefaultDataBrokerProtectionAgentStopper(dataManager: mockDataManager, + entitlementMonitor: mockEntitlementMonitor, + authenticationManager: mockAuthenticationManager, + pixelHandler: mockPixelHandler, + stopAction: mockStopAction) + await stopper.validateRunPrerequisitesAndStopAgentIfNecessary() + + XCTAssertTrue(mockStopAction.wasStopCalled) + } + + func testInvalidEntitlement_thenStopAgentIsCalled() async { + mockAuthenticationManager.isUserAuthenticatedValue = true + mockAuthenticationManager.hasValidEntitlementValue = false + mockDataManager.profileToReturn = fakeProfile + + let stopper = DefaultDataBrokerProtectionAgentStopper(dataManager: mockDataManager, + entitlementMonitor: mockEntitlementMonitor, + authenticationManager: mockAuthenticationManager, + pixelHandler: mockPixelHandler, + stopAction: mockStopAction) + await stopper.validateRunPrerequisitesAndStopAgentIfNecessary() + + XCTAssertTrue(mockStopAction.wasStopCalled) + } + + func testUserNotAuthenticated_thenStopAgentIsCalled() async { + mockAuthenticationManager.isUserAuthenticatedValue = false + mockAuthenticationManager.hasValidEntitlementValue = true + mockDataManager.profileToReturn = fakeProfile + + let stopper = DefaultDataBrokerProtectionAgentStopper(dataManager: mockDataManager, + entitlementMonitor: mockEntitlementMonitor, + authenticationManager: mockAuthenticationManager, + pixelHandler: mockPixelHandler, + stopAction: mockStopAction) + await stopper.validateRunPrerequisitesAndStopAgentIfNecessary() + + XCTAssertTrue(mockStopAction.wasStopCalled) + } + + func testErrorEntitlement_thenStopAgentIsNotCalled() async { + mockAuthenticationManager.isUserAuthenticatedValue = true + mockAuthenticationManager.shouldThrowEntitlementError = true + mockDataManager.profileToReturn = fakeProfile + + let stopper = DefaultDataBrokerProtectionAgentStopper(dataManager: mockDataManager, + entitlementMonitor: mockEntitlementMonitor, + authenticationManager: mockAuthenticationManager, + pixelHandler: mockPixelHandler, + stopAction: mockStopAction) + await stopper.validateRunPrerequisitesAndStopAgentIfNecessary() + + XCTAssertFalse(mockStopAction.wasStopCalled) + } + + func testValidEntitlement_thenStopAgentIsNotCalled() async { + mockAuthenticationManager.isUserAuthenticatedValue = true + mockAuthenticationManager.hasValidEntitlementValue = true + mockDataManager.profileToReturn = fakeProfile + + let stopper = DefaultDataBrokerProtectionAgentStopper(dataManager: mockDataManager, + entitlementMonitor: mockEntitlementMonitor, + authenticationManager: mockAuthenticationManager, + pixelHandler: mockPixelHandler, + stopAction: mockStopAction) + await stopper.validateRunPrerequisitesAndStopAgentIfNecessary() + + XCTAssertFalse(mockStopAction.wasStopCalled) + } + + func testEntitlementMonitorWithValidResult_thenStopAgentIsNotCalled() { + mockAuthenticationManager.isUserAuthenticatedValue = true + mockAuthenticationManager.hasValidEntitlementValue = true + mockDataManager.profileToReturn = fakeProfile + + let stopper = DefaultDataBrokerProtectionAgentStopper(dataManager: mockDataManager, + entitlementMonitor: mockEntitlementMonitor, + authenticationManager: mockAuthenticationManager, + pixelHandler: mockPixelHandler, + stopAction: mockStopAction) + + let expectation = XCTestExpectation(description: "Wait for monitor") + stopper.monitorEntitlementAndStopAgentIfEntitlementIsInvalid(interval: 0.1) + + DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { [self] in + XCTAssertFalse(mockStopAction.wasStopCalled) + expectation.fulfill() + } + + wait(for: [expectation], timeout: 1) + } + + func testEntitlementMonitorWithInValidResult_thenStopAgentIsCalled() { + mockAuthenticationManager.isUserAuthenticatedValue = true + mockAuthenticationManager.hasValidEntitlementValue = false + mockDataManager.profileToReturn = fakeProfile + + let stopper = DefaultDataBrokerProtectionAgentStopper(dataManager: mockDataManager, + entitlementMonitor: mockEntitlementMonitor, + authenticationManager: mockAuthenticationManager, + pixelHandler: mockPixelHandler, + stopAction: mockStopAction) + + let expectation = XCTestExpectation(description: "Wait for monitor") + stopper.monitorEntitlementAndStopAgentIfEntitlementIsInvalid(interval: 0.1) + + DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { [self] in + XCTAssertTrue(mockStopAction.wasStopCalled) + expectation.fulfill() + } + + wait(for: [expectation], timeout: 1) + } + + func testEntitlementMonitorWithErrorResult_thenStopAgentIsNotCalled() { + mockAuthenticationManager.isUserAuthenticatedValue = true + mockAuthenticationManager.shouldThrowEntitlementError = true + mockDataManager.profileToReturn = fakeProfile + + let stopper = DefaultDataBrokerProtectionAgentStopper(dataManager: mockDataManager, + entitlementMonitor: mockEntitlementMonitor, + authenticationManager: mockAuthenticationManager, + pixelHandler: mockPixelHandler, + stopAction: mockStopAction) + + let expectation = XCTestExpectation(description: "Wait for monitor") + stopper.monitorEntitlementAndStopAgentIfEntitlementIsInvalid(interval: 0.1) + + DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { [self] in + XCTAssertFalse(mockStopAction.wasStopCalled) + expectation.fulfill() + } + + wait(for: [expectation], timeout: 1) + } +} diff --git a/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/Mocks.swift b/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/Mocks.swift index c4b2804e3f..d6bb74149d 100644 --- a/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/Mocks.swift +++ b/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/Mocks.swift @@ -1374,13 +1374,18 @@ final class MockAuthenticationManager: DataBrokerProtectionAuthenticationManagin var shouldAskForInviteCodeValue = false var redeemCodeCalled = false var authHeaderValue: String? = "fake auth header" + var hasValidEntitlementValue = false + var shouldThrowEntitlementError = false var isUserAuthenticated: Bool { isUserAuthenticatedValue } var accessToken: String? { accessTokenValue } func hasValidEntitlement() async throws -> Bool { - return true + if shouldThrowEntitlementError { + throw NSError(domain: "duck.com", code: 0, userInfo: [NSLocalizedDescriptionKey: "Error"]) + } + return hasValidEntitlementValue } func shouldAskForInviteCode() -> Bool { shouldAskForInviteCodeValue } @@ -1397,5 +1402,34 @@ final class MockAuthenticationManager: DataBrokerProtectionAuthenticationManagin shouldAskForInviteCodeValue = false redeemCodeCalled = false authHeaderValue = "fake auth header" + hasValidEntitlementValue = false + shouldThrowEntitlementError = false + } +} + +final class MockAgentStopper: DataBrokerProtectionAgentStopper { + var validateRunPrerequisitesCompletion: (() -> Void)? + var monitorEntitlementCompletion: (() -> Void)? + + func validateRunPrerequisitesAndStopAgentIfNecessary() async { + validateRunPrerequisitesCompletion?() + } + + func monitorEntitlementAndStopAgentIfEntitlementIsInvalid(interval: TimeInterval) { + monitorEntitlementCompletion?() + } +} + +final class MockDataProtectionStopAction: DataProtectionStopAction { + var wasStopCalled = false + var stopAgentCompletion: (() -> Void)? + + func stopAgent() { + wasStopCalled = true + stopAgentCompletion?() + } + + func reset() { + wasStopCalled = false } } From 8ab6aaddfb1c225994164b484f135e7263a61c97 Mon Sep 17 00:00:00 2001 From: Federico Cappelli Date: Thu, 23 May 2024 12:32:42 +0200 Subject: [PATCH 03/11] Subscription refactoring, BSK update (#2809) Task/Issue URL: https://app.asana.com/0/72649045549333/1206805455884775/f Tech Design URL: https://app.asana.com/0/481882893211075/1207147511614062/f BSK update --- DuckDuckGo.xcodeproj/project.pbxproj | 78 +++++++++---------- .../xcshareddata/swiftpm/Package.resolved | 8 +- .../DataBrokerProtection/Package.swift | 2 +- .../NetworkProtectionMac/Package.swift | 2 +- LocalPackages/SubscriptionUI/Package.swift | 2 +- 5 files changed, 42 insertions(+), 50 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index f4c1f200d8..08e3fe56ea 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -169,7 +169,6 @@ 3158B1492B0BF73000AF130C /* DBPHomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3192EC872A4DCF21001E97A5 /* DBPHomeViewController.swift */; }; 3158B14A2B0BF74300AF130C /* DataBrokerProtectionDebugMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 316850712AF3AD58009A2828 /* DataBrokerProtectionDebugMenu.swift */; }; 3158B14D2B0BF74D00AF130C /* DataBrokerProtectionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3139A1512AA4B3C000969C7D /* DataBrokerProtectionManager.swift */; }; - 3158B1502B0BF75200AF130C /* DataBrokerProtectionLoginItemInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B6D98662ADEB4B000CD35FE /* DataBrokerProtectionLoginItemInterface.swift */; }; 3158B1532B0BF75700AF130C /* LoginItem+DataBrokerProtection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D8FA00B2AC5BDCE005DD0D0 /* LoginItem+DataBrokerProtection.swift */; }; 3158B1562B0BF75D00AF130C /* DataBrokerProtectionFeatureVisibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31C5FFB82AF64D120008A79F /* DataBrokerProtectionFeatureVisibility.swift */; }; 3158B1592B0BF76400AF130C /* DataBrokerProtectionFeatureDisabler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3199C6F82AF94F5B002A7BA1 /* DataBrokerProtectionFeatureDisabler.swift */; }; @@ -221,7 +220,6 @@ 31ECDA142BED339600AE679F /* DataBrokerAuthenticationManagerBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31ECDA102BED339600AE679F /* DataBrokerAuthenticationManagerBuilder.swift */; }; 31EF1E802B63FFA800E6DB17 /* DBPHomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3192EC872A4DCF21001E97A5 /* DBPHomeViewController.swift */; }; 31EF1E812B63FFB800E6DB17 /* DataBrokerProtectionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3139A1512AA4B3C000969C7D /* DataBrokerProtectionManager.swift */; }; - 31EF1E822B63FFC200E6DB17 /* DataBrokerProtectionLoginItemInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B6D98662ADEB4B000CD35FE /* DataBrokerProtectionLoginItemInterface.swift */; }; 31EF1E832B63FFCA00E6DB17 /* LoginItem+DataBrokerProtection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D8FA00B2AC5BDCE005DD0D0 /* LoginItem+DataBrokerProtection.swift */; }; 31EF1E842B63FFD100E6DB17 /* DataBrokerProtectionDebugMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 316850712AF3AD58009A2828 /* DataBrokerProtectionDebugMenu.swift */; }; 31F28C4F28C8EEC500119F70 /* YoutubePlayerUserScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31F28C4C28C8EEC500119F70 /* YoutubePlayerUserScript.swift */; }; @@ -2611,10 +2609,18 @@ F1B33DF32BAD929D001128B3 /* SubscriptionAppStoreRestorer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1B33DF12BAD929D001128B3 /* SubscriptionAppStoreRestorer.swift */; }; F1B33DF62BAD970E001128B3 /* SubscriptionErrorReporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1B33DF52BAD970E001128B3 /* SubscriptionErrorReporter.swift */; }; F1B33DF72BAD970E001128B3 /* SubscriptionErrorReporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1B33DF52BAD970E001128B3 /* SubscriptionErrorReporter.swift */; }; + F1C70D792BFF50A400599292 /* DataBrokerProtectionLoginItemInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1C70D782BFF50A400599292 /* DataBrokerProtectionLoginItemInterface.swift */; }; + F1C70D7A2BFF50A400599292 /* DataBrokerProtectionLoginItemInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1C70D782BFF50A400599292 /* DataBrokerProtectionLoginItemInterface.swift */; }; + F1C70D7C2BFF510000599292 /* SubscriptionEnvironment+Default.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1C70D7B2BFF510000599292 /* SubscriptionEnvironment+Default.swift */; }; + F1C70D7D2BFF510000599292 /* SubscriptionEnvironment+Default.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1C70D7B2BFF510000599292 /* SubscriptionEnvironment+Default.swift */; }; + F1C70D7E2BFF510000599292 /* SubscriptionEnvironment+Default.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1C70D7B2BFF510000599292 /* SubscriptionEnvironment+Default.swift */; }; + F1C70D7F2BFF510000599292 /* SubscriptionEnvironment+Default.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1C70D7B2BFF510000599292 /* SubscriptionEnvironment+Default.swift */; }; + F1C70D802BFF510000599292 /* SubscriptionEnvironment+Default.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1C70D7B2BFF510000599292 /* SubscriptionEnvironment+Default.swift */; }; + F1C70D812BFF510000599292 /* SubscriptionEnvironment+Default.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1C70D7B2BFF510000599292 /* SubscriptionEnvironment+Default.swift */; }; + F1C70D822BFF510000599292 /* SubscriptionEnvironment+Default.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1C70D7B2BFF510000599292 /* SubscriptionEnvironment+Default.swift */; }; + F1C70D832BFF510000599292 /* SubscriptionEnvironment+Default.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1C70D7B2BFF510000599292 /* SubscriptionEnvironment+Default.swift */; }; F1D0428E2BFB9F9C00A31506 /* Subscription in Frameworks */ = {isa = PBXBuildFile; productRef = F1D0428D2BFB9F9C00A31506 /* Subscription */; }; F1D042902BFB9FA300A31506 /* Subscription in Frameworks */ = {isa = PBXBuildFile; productRef = F1D0428F2BFB9FA300A31506 /* Subscription */; }; - F1D042912BFB9FD700A31506 /* SubscriptionEnvironment+Default.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1FDC9282BF4DF48006B1435 /* SubscriptionEnvironment+Default.swift */; }; - F1D042922BFB9FD800A31506 /* SubscriptionEnvironment+Default.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1FDC9282BF4DF48006B1435 /* SubscriptionEnvironment+Default.swift */; }; F1D042942BFBA12300A31506 /* DataBrokerProtectionSettings+Environment.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1D042932BFBA12300A31506 /* DataBrokerProtectionSettings+Environment.swift */; }; F1D042952BFBA12300A31506 /* DataBrokerProtectionSettings+Environment.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1D042932BFBA12300A31506 /* DataBrokerProtectionSettings+Environment.swift */; }; F1D042992BFBABA100A31506 /* SubscriptionManager+StandardConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1D042982BFBABA100A31506 /* SubscriptionManager+StandardConfiguration.swift */; }; @@ -2655,12 +2661,6 @@ F1DF95E42BD1807C0045E591 /* Crashes in Frameworks */ = {isa = PBXBuildFile; productRef = 537FC71EA5115A983FAF3170 /* Crashes */; }; F1DF95E52BD1807C0045E591 /* Subscription in Frameworks */ = {isa = PBXBuildFile; productRef = DC3F73D49B2D44464AFEFCD8 /* Subscription */; }; F1DF95E72BD188B60045E591 /* LoginItems in Frameworks */ = {isa = PBXBuildFile; productRef = F1DF95E62BD188B60045E591 /* LoginItems */; }; - F1FDC9292BF4DF48006B1435 /* SubscriptionEnvironment+Default.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1FDC9282BF4DF48006B1435 /* SubscriptionEnvironment+Default.swift */; }; - F1FDC92A2BF4DF48006B1435 /* SubscriptionEnvironment+Default.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1FDC9282BF4DF48006B1435 /* SubscriptionEnvironment+Default.swift */; }; - F1FDC92B2BF4DFEC006B1435 /* SubscriptionEnvironment+Default.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1FDC9282BF4DF48006B1435 /* SubscriptionEnvironment+Default.swift */; }; - F1FDC92C2BF4DFED006B1435 /* SubscriptionEnvironment+Default.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1FDC9282BF4DF48006B1435 /* SubscriptionEnvironment+Default.swift */; }; - F1FDC92D2BF4E001006B1435 /* SubscriptionEnvironment+Default.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1FDC9282BF4DF48006B1435 /* SubscriptionEnvironment+Default.swift */; }; - F1FDC92E2BF4E001006B1435 /* SubscriptionEnvironment+Default.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1FDC9282BF4DF48006B1435 /* SubscriptionEnvironment+Default.swift */; }; F1FDC9382BF51F41006B1435 /* VPNSettings+Environment.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1FDC9372BF51F41006B1435 /* VPNSettings+Environment.swift */; }; F1FDC9392BF51F41006B1435 /* VPNSettings+Environment.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1FDC9372BF51F41006B1435 /* VPNSettings+Environment.swift */; }; F1FDC93A2BF51F41006B1435 /* VPNSettings+Environment.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1FDC9372BF51F41006B1435 /* VPNSettings+Environment.swift */; }; @@ -3392,7 +3392,6 @@ 7B4D8A202BDA857300852966 /* VPNOperationErrorRecorder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNOperationErrorRecorder.swift; sourceTree = ""; }; 7B5291882A1697680022E406 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 7B5291892A169BC90022E406 /* DeveloperID.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = DeveloperID.xcconfig; sourceTree = ""; }; - 7B6D98662ADEB4B000CD35FE /* DataBrokerProtectionLoginItemInterface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataBrokerProtectionLoginItemInterface.swift; sourceTree = ""; }; 7B6EC5E42AE2D8AF004FE6DF /* DuckDuckGoDBPAgentAppStore.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = DuckDuckGoDBPAgentAppStore.xcconfig; sourceTree = ""; }; 7B6EC5E52AE2D8AF004FE6DF /* DuckDuckGoDBPAgent.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = DuckDuckGoDBPAgent.xcconfig; sourceTree = ""; }; 7B76E6852AD5D77600186A84 /* XPCHelper */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = XPCHelper; sourceTree = ""; }; @@ -4138,12 +4137,13 @@ F18826832BBEE31700D9AC4F /* PixelKit+Assertion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "PixelKit+Assertion.swift"; sourceTree = ""; }; F1B33DF12BAD929D001128B3 /* SubscriptionAppStoreRestorer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionAppStoreRestorer.swift; sourceTree = ""; }; F1B33DF52BAD970E001128B3 /* SubscriptionErrorReporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionErrorReporter.swift; sourceTree = ""; }; + F1C70D782BFF50A400599292 /* DataBrokerProtectionLoginItemInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataBrokerProtectionLoginItemInterface.swift; sourceTree = ""; }; + F1C70D7B2BFF510000599292 /* SubscriptionEnvironment+Default.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SubscriptionEnvironment+Default.swift"; sourceTree = ""; }; F1D042932BFBA12300A31506 /* DataBrokerProtectionSettings+Environment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DataBrokerProtectionSettings+Environment.swift"; sourceTree = ""; }; F1D042982BFBABA100A31506 /* SubscriptionManager+StandardConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SubscriptionManager+StandardConfiguration.swift"; sourceTree = ""; }; F1D43AED2B98D8DF00BAB743 /* MainMenuActions+VanillaBrowser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "MainMenuActions+VanillaBrowser.swift"; sourceTree = ""; }; F1DA51842BF607D200CF29FA /* SubscriptionAttributionPixelHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubscriptionAttributionPixelHandler.swift; sourceTree = ""; }; F1DA51852BF607D200CF29FA /* SubscriptionRedirectManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubscriptionRedirectManager.swift; sourceTree = ""; }; - F1FDC9282BF4DF48006B1435 /* SubscriptionEnvironment+Default.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SubscriptionEnvironment+Default.swift"; sourceTree = ""; }; F1FDC9372BF51F41006B1435 /* VPNSettings+Environment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "VPNSettings+Environment.swift"; sourceTree = ""; }; F41D174025CB131900472416 /* NSColorExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSColorExtension.swift; sourceTree = ""; }; F44C130125C2DA0400426E3E /* NSAppearanceExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSAppearanceExtension.swift; sourceTree = ""; }; @@ -4625,21 +4625,21 @@ 3192EC862A4DCF0E001E97A5 /* DBP */ = { isa = PBXGroup; children = ( - 3169132B2BD2C7960051B46D /* ErrorView */, - 316913222BD2B6250051B46D /* DataBrokerProtectionPixelsHandler.swift */, + 316913252BD2B76F0051B46D /* DataBrokerPrerequisitesStatusVerifier.swift */, + 3199C6FC2AF97367002A7BA1 /* DataBrokerProtectionAppEvents.swift */, 316850712AF3AD58009A2828 /* DataBrokerProtectionDebugMenu.swift */, - 3192EC872A4DCF21001E97A5 /* DBPHomeViewController.swift */, - 3139A1512AA4B3C000969C7D /* DataBrokerProtectionManager.swift */, - 7B6D98662ADEB4B000CD35FE /* DataBrokerProtectionLoginItemInterface.swift */, - 9D8FA00B2AC5BDCE005DD0D0 /* LoginItem+DataBrokerProtection.swift */, - 31C5FFB82AF64D120008A79F /* DataBrokerProtectionFeatureVisibility.swift */, + BBDFDC592B2B8A0900F62D90 /* DataBrokerProtectionExternalWaitlistPixels.swift */, 3199C6F82AF94F5B002A7BA1 /* DataBrokerProtectionFeatureDisabler.swift */, - 3199C6FC2AF97367002A7BA1 /* DataBrokerProtectionAppEvents.swift */, + 31C5FFB82AF64D120008A79F /* DataBrokerProtectionFeatureVisibility.swift */, + F1C70D782BFF50A400599292 /* DataBrokerProtectionLoginItemInterface.swift */, 31AA6B962B960B870025014E /* DataBrokerProtectionLoginItemPixels.swift */, - BBDFDC592B2B8A0900F62D90 /* DataBrokerProtectionExternalWaitlistPixels.swift */, + 3139A1512AA4B3C000969C7D /* DataBrokerProtectionManager.swift */, + 316913222BD2B6250051B46D /* DataBrokerProtectionPixelsHandler.swift */, BB5789712B2CA70F0009DFE2 /* DataBrokerProtectionSubscriptionEventHandler.swift */, + 3192EC872A4DCF21001E97A5 /* DBPHomeViewController.swift */, + 3169132B2BD2C7960051B46D /* ErrorView */, + 9D8FA00B2AC5BDCE005DD0D0 /* LoginItem+DataBrokerProtection.swift */, 4B37EE652B4CFC9500A89A61 /* RemoteMessaging */, - 316913252BD2B76F0051B46D /* DataBrokerPrerequisitesStatusVerifier.swift */, ); path = DBP; sourceTree = ""; @@ -8292,10 +8292,10 @@ F118EA7B2BEA2B8700F77634 /* Subscription */ = { isa = PBXGroup; children = ( + F1C70D7B2BFF510000599292 /* SubscriptionEnvironment+Default.swift */, F1D042932BFBA12300A31506 /* DataBrokerProtectionSettings+Environment.swift */, F118EA7C2BEA2B8700F77634 /* DefaultSubscriptionFeatureAvailability+DefaultInitializer.swift */, F1DA51842BF607D200CF29FA /* SubscriptionAttributionPixelHandler.swift */, - F1FDC9282BF4DF48006B1435 /* SubscriptionEnvironment+Default.swift */, F1D042982BFBABA100A31506 /* SubscriptionManager+StandardConfiguration.swift */, F1DA51852BF607D200CF29FA /* SubscriptionRedirectManager.swift */, F1FDC9372BF51F41006B1435 /* VPNSettings+Environment.swift */, @@ -9836,7 +9836,6 @@ 3706FB43293F65D500E42796 /* DuckPlayer.swift in Sources */, 3706FB44293F65D500E42796 /* Favicon.swift in Sources */, 3706FB45293F65D500E42796 /* SuggestionContainerViewModel.swift in Sources */, - F1FDC92A2BF4DF48006B1435 /* SubscriptionEnvironment+Default.swift in Sources */, 3706FB46293F65D500E42796 /* FirePopoverWrapperViewController.swift in Sources */, 3706FB47293F65D500E42796 /* NSPasteboardItemExtension.swift in Sources */, 3706FB48293F65D500E42796 /* AutofillPreferencesModel.swift in Sources */, @@ -9893,6 +9892,7 @@ 7B7FCD102BA33B2700C04FBE /* UserDefaults+vpnLegacyUser.swift in Sources */, 3706FB6C293F65D500E42796 /* FaviconStore.swift in Sources */, 3706FB6D293F65D500E42796 /* SuggestionListCharacteristics.swift in Sources */, + F1C70D7A2BFF50A400599292 /* DataBrokerProtectionLoginItemInterface.swift in Sources */, 377D801F2AB48191002AF251 /* FavoritesDisplayModeSyncHandler.swift in Sources */, 3706FB6F293F65D500E42796 /* BookmarkListViewController.swift in Sources */, 3706FB70293F65D500E42796 /* SecureVaultLoginImporter.swift in Sources */, @@ -10185,6 +10185,7 @@ 4B9DB03F2A983B24000927DB /* JoinWaitlistView.swift in Sources */, 987799F22999993C005D8EB6 /* LegacyBookmarkStore.swift in Sources */, 37A6A8F72AFCCA59008580A3 /* FaviconsFetcherOnboardingViewController.swift in Sources */, + F1C70D7D2BFF510000599292 /* SubscriptionEnvironment+Default.swift in Sources */, 3706FC2C293F65D500E42796 /* BookmarkHTMLReader.swift in Sources */, 3706FC2D293F65D500E42796 /* Tab+NSSecureCoding.swift in Sources */, 3706FC2E293F65D500E42796 /* NSNotificationName+EmailManager.swift in Sources */, @@ -10201,7 +10202,6 @@ 3706FC34293F65D500E42796 /* PermissionAuthorizationViewController.swift in Sources */, 3706FC35293F65D500E42796 /* BookmarkNode.swift in Sources */, B6ABD0CB2BC03F610000EB69 /* SecurityScopedFileURLController.swift in Sources */, - 31EF1E822B63FFC200E6DB17 /* DataBrokerProtectionLoginItemInterface.swift in Sources */, B6B140892ABDBCC1004F8E85 /* HoverTrackingArea.swift in Sources */, 3706FC36293F65D500E42796 /* LongPressButton.swift in Sources */, 3706FC37293F65D500E42796 /* CoreDataStore.swift in Sources */, @@ -10735,7 +10735,6 @@ EE66418D2B9B1981005BCD17 /* NetworkProtectionTokenStore+SubscriptionTokenKeychainStorage.swift in Sources */, EEBCA0C72BD7CE2C004DF19C /* VPNFailureRecoveryPixel.swift in Sources */, F1DA51932BF6081D00CF29FA /* AttributionPixelHandler.swift in Sources */, - F1FDC92C2BF4DFED006B1435 /* SubscriptionEnvironment+Default.swift in Sources */, 7B2E52252A5FEC09000C6D39 /* NetworkProtectionAgentNotificationsPresenter.swift in Sources */, F1DA51892BF607D200CF29FA /* SubscriptionAttributionPixelHandler.swift in Sources */, B602E8232A1E260E006D261F /* Bundle+NetworkProtectionExtensions.swift in Sources */, @@ -10749,6 +10748,7 @@ 7B0099822B65C6B300FE7C31 /* MacTransparentProxyProvider.swift in Sources */, B65DA5F32A77D3C700CBEE8D /* UserDefaultsWrapper.swift in Sources */, 4B2537722A11BF8B00610219 /* main.swift in Sources */, + F1C70D7F2BFF510000599292 /* SubscriptionEnvironment+Default.swift in Sources */, EEF12E6F2A2111880023E6BF /* MacPacketTunnelProvider.swift in Sources */, 4B2D06292A11C0C900DE1F49 /* Bundle+VPN.swift in Sources */, F1D0429C2BFBABA100A31506 /* SubscriptionManager+StandardConfiguration.swift in Sources */, @@ -10782,7 +10782,7 @@ 7BD1688E2AD4A4C400D24876 /* NetworkExtensionController.swift in Sources */, 7BA7CC3E2AD11E380042E5CE /* TunnelControllerIPCService.swift in Sources */, F1DA51982BF6083B00CF29FA /* PrivacyProPixel.swift in Sources */, - F1FDC92D2BF4E001006B1435 /* SubscriptionEnvironment+Default.swift in Sources */, + F1C70D802BFF510000599292 /* SubscriptionEnvironment+Default.swift in Sources */, 7BA7CC402AD11E3D0042E5CE /* AppLauncher+DefaultInitializer.swift in Sources */, 7B0694982B6E980F00FA4DBA /* VPNProxyLauncher.swift in Sources */, BDA764842BC49E3F00D0400C /* NetworkProtectionVPNCountryLabelsModel.swift in Sources */, @@ -10825,7 +10825,7 @@ 4BF0E5152AD25A2600FFEC9E /* DuckDuckGoUserAgent.swift in Sources */, 7BFE95592A9DF2AF0081ABE9 /* UserDefaults+NetworkProtectionWaitlist.swift in Sources */, F1DA51992BF6083B00CF29FA /* PrivacyProPixel.swift in Sources */, - F1FDC92E2BF4E001006B1435 /* SubscriptionEnvironment+Default.swift in Sources */, + F1C70D812BFF510000599292 /* SubscriptionEnvironment+Default.swift in Sources */, 7BA7CC5C2AD120C30042E5CE /* EventMapping+NetworkProtectionError.swift in Sources */, B65DA5F02A77CC3C00CBEE8D /* Bundle+NetworkProtectionExtensions.swift in Sources */, BDA764852BC49E4000D0400C /* NetworkProtectionVPNCountryLabelsModel.swift in Sources */, @@ -10878,6 +10878,7 @@ B602E8182A1E2570006D261F /* URL+NetworkProtection.swift in Sources */, B65DA5F52A77D3FA00CBEE8D /* BundleExtension.swift in Sources */, 4B4D60892A0B2A1C00BCD287 /* NetworkProtectionUNNotificationsPresenter.swift in Sources */, + F1C70D7E2BFF510000599292 /* SubscriptionEnvironment+Default.swift in Sources */, 4B4D60A02A0B2D5B00BCD287 /* Bundle+VPN.swift in Sources */, 4B4D60AD2A0C807300BCD287 /* NSApplicationExtension.swift in Sources */, F1DA51922BF6081C00CF29FA /* AttributionPixelHandler.swift in Sources */, @@ -10885,7 +10886,6 @@ 4BF0E50C2AD2552300FFEC9E /* NetworkProtectionPixelEvent.swift in Sources */, 4B4D60AC2A0C804B00BCD287 /* OptionalExtension.swift in Sources */, B65DA5F22A77D3C600CBEE8D /* UserDefaultsWrapper.swift in Sources */, - F1FDC92B2BF4DFEC006B1435 /* SubscriptionEnvironment+Default.swift in Sources */, F1DA51882BF607D200CF29FA /* SubscriptionAttributionPixelHandler.swift in Sources */, F1FDC93A2BF51F41006B1435 /* VPNSettings+Environment.swift in Sources */, ); @@ -10939,10 +10939,7 @@ files = ( 31A83FB72BE28D8A00F74E67 /* UserText+DBP.swift in Sources */, F1D042942BFBA12300A31506 /* DataBrokerProtectionSettings+Environment.swift in Sources */, - 9D9AE92C2AAB84FF0026E7DC /* DBPMocks.swift in Sources */, - F1D042912BFB9FD700A31506 /* SubscriptionEnvironment+Default.swift in Sources */, - 7B0099792B65013800FE7C31 /* BrowserWindowManager.swift in Sources */, - 9D9AE9292AAA43EB0026E7DC /* DataBrokerProtectionBackgroundManager.swift in Sources */, + F1C70D822BFF510000599292 /* SubscriptionEnvironment+Default.swift in Sources */, 9D9AE91D2AAA3B450026E7DC /* DuckDuckGoDBPBackgroundAgentAppDelegate.swift in Sources */, 9D9AE9212AAA3B450026E7DC /* UserText.swift in Sources */, 31ECDA132BED339600AE679F /* DataBrokerAuthenticationManagerBuilder.swift in Sources */, @@ -10957,10 +10954,7 @@ files = ( 31A83FB82BE28D8A00F74E67 /* UserText+DBP.swift in Sources */, F1D042952BFBA12300A31506 /* DataBrokerProtectionSettings+Environment.swift in Sources */, - 7B1459542B7D437200047F2C /* BrowserWindowManager.swift in Sources */, - F1D042922BFB9FD800A31506 /* SubscriptionEnvironment+Default.swift in Sources */, - 9D9AE92D2AAB84FF0026E7DC /* DBPMocks.swift in Sources */, - 9D9AE92A2AAA43EB0026E7DC /* DataBrokerProtectionBackgroundManager.swift in Sources */, + F1C70D832BFF510000599292 /* SubscriptionEnvironment+Default.swift in Sources */, 9D9AE91E2AAA3B450026E7DC /* DuckDuckGoDBPBackgroundAgentAppDelegate.swift in Sources */, 9D9AE9222AAA3B450026E7DC /* UserText.swift in Sources */, 31ECDA142BED339600AE679F /* DataBrokerAuthenticationManagerBuilder.swift in Sources */, @@ -11107,6 +11101,7 @@ 1E7E2E9029029A2A00C01B54 /* ContentBlockingRulesUpdateObserver.swift in Sources */, 4B8AC93926B48A5100879451 /* FirefoxLoginReader.swift in Sources */, F18826902BC0105800D9AC4F /* PixelDataRecord.swift in Sources */, + F1C70D792BFF50A400599292 /* DataBrokerProtectionLoginItemInterface.swift in Sources */, F18826912BC0105800D9AC4F /* PixelDataStore.swift in Sources */, B69B503E2726A12500758A2B /* AtbParser.swift in Sources */, 37F19A6528E1B3FB00740DC6 /* PreferencesDuckPlayerView.swift in Sources */, @@ -11160,10 +11155,7 @@ 85707F26276A335700DC0649 /* Onboarding.swift in Sources */, B68C92C1274E3EF4002AC6B0 /* PopUpWindow.swift in Sources */, AA5FA6A0275F948900DCE9C9 /* Favicons.xcdatamodeld in Sources */, - 3158B1502B0BF75200AF130C /* DataBrokerProtectionLoginItemInterface.swift in Sources */, 9F6434612BEC82B700D2D8A0 /* AttributionPixelHandler.swift in Sources */, - 3158B1502B0BF75200AF130C /* DataBrokerProtectionLoginItemScheduler.swift in Sources */, - 7BBA7CE62BAB03C1007579A3 /* DefaultSubscriptionFeatureAvailability+DefaultInitializer.swift in Sources */, B684592225C93BE000DC17B6 /* Publisher.asVoid.swift in Sources */, 4B9DB01D2A983B24000927DB /* Waitlist.swift in Sources */, BBDFDC5A2B2B8A0900F62D90 /* DataBrokerProtectionExternalWaitlistPixels.swift in Sources */, @@ -11537,6 +11529,7 @@ 4BCF15D72ABB8A110083F6DF /* NetworkProtectionRemoteMessaging.swift in Sources */, C168B9AC2B31DC7E001AFAD9 /* AutofillNeverPromptWebsitesManager.swift in Sources */, 9FA173E72B7B122E00EE4E6E /* BookmarkDialogStackedContentView.swift in Sources */, + F1C70D7C2BFF510000599292 /* SubscriptionEnvironment+Default.swift in Sources */, D64A5FF82AEA5C2B00B6D6E7 /* HomeButtonMenuFactory.swift in Sources */, 37A6A8F62AFCCA59008580A3 /* FaviconsFetcherOnboardingViewController.swift in Sources */, AA3F895324C18AD500628DDE /* SuggestionViewModel.swift in Sources */, @@ -11645,7 +11638,6 @@ AAC82C60258B6CB5009B6B42 /* TabPreviewWindowController.swift in Sources */, AAC5E4E425D6BA9C007F5990 /* NSSizeExtension.swift in Sources */, AA6820EB25503D6A005ED0D5 /* Fire.swift in Sources */, - F1FDC9292BF4DF48006B1435 /* SubscriptionEnvironment+Default.swift in Sources */, 3158B1492B0BF73000AF130C /* DBPHomeViewController.swift in Sources */, 9F56CFA92B82DC4300BB7F11 /* AddEditBookmarkFolderView.swift in Sources */, 37445F9C2A1569F00029F789 /* SyncBookmarksAdapter.swift in Sources */, @@ -13034,8 +13026,8 @@ isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/duckduckgo/BrowserServicesKit"; requirement = { - branch = fcappelli/subscription_refactoring_2; - kind = branch; + kind = exactVersion; + version = 146.0.0; }; }; 9FF521422BAA8FF300B9819B /* XCRemoteSwiftPackageReference "lottie-spm" */ = { diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 981f226c05..7c7e84edb5 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" : { - "branch" : "fcappelli/subscription_refactoring_2", - "revision" : "874ae4269db821797742655e134e72199c2813c8" + "revision" : "b01a7ba359b650f0c5c3ab00a756e298b1ae650c", + "version" : "146.0.0" } }, { @@ -131,8 +131,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-argument-parser.git", "state" : { - "revision" : "46989693916f56d1186bd59ac15124caef896560", - "version" : "1.3.1" + "revision" : "0fbc8848e389af3bb55c182bc19ca9d5dc2f255b", + "version" : "1.4.0" } }, { diff --git a/LocalPackages/DataBrokerProtection/Package.swift b/LocalPackages/DataBrokerProtection/Package.swift index b199cd1256..203c5dda21 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: "145.3.3"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "146.0.0"), .package(path: "../SwiftUIExtensions"), .package(path: "../XPCHelper"), ], diff --git a/LocalPackages/NetworkProtectionMac/Package.swift b/LocalPackages/NetworkProtectionMac/Package.swift index 6bdacb0bff..9b10b4534f 100644 --- a/LocalPackages/NetworkProtectionMac/Package.swift +++ b/LocalPackages/NetworkProtectionMac/Package.swift @@ -31,7 +31,7 @@ let package = Package( .library(name: "NetworkProtectionUI", targets: ["NetworkProtectionUI"]), ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "145.3.3"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "146.0.0"), .package(url: "https://github.com/airbnb/lottie-spm", exact: "4.4.1"), .package(path: "../XPCHelper"), .package(path: "../SwiftUIExtensions"), diff --git a/LocalPackages/SubscriptionUI/Package.swift b/LocalPackages/SubscriptionUI/Package.swift index cd969a3ce6..210d28971a 100644 --- a/LocalPackages/SubscriptionUI/Package.swift +++ b/LocalPackages/SubscriptionUI/Package.swift @@ -12,7 +12,7 @@ let package = Package( targets: ["SubscriptionUI"]), ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "145.3.3"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "146.0.0"), .package(path: "../SwiftUIExtensions") ], targets: [ From 00c70b4aadcc14f352d8abc4b2bd3911a215dc10 Mon Sep 17 00:00:00 2001 From: Fernando Bunn Date: Thu, 23 May 2024 12:28:52 +0100 Subject: [PATCH 04/11] Increase test timeout (#2810) Task/Issue URL: https://app.asana.com/0/1205237866452338/1207390952710955/f Tech Design URL: CC: **Description**: Increase test timeout --- .../DataBrokerProtectionAgentStopperTests.swift | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerProtectionAgentStopperTests.swift b/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerProtectionAgentStopperTests.swift index 5d8c25ebee..3271a31f69 100644 --- a/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerProtectionAgentStopperTests.swift +++ b/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerProtectionAgentStopperTests.swift @@ -144,12 +144,12 @@ final class DataBrokerProtectionAgentStopperTests: XCTestCase { let expectation = XCTestExpectation(description: "Wait for monitor") stopper.monitorEntitlementAndStopAgentIfEntitlementIsInvalid(interval: 0.1) - DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { [self] in + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [self] in XCTAssertFalse(mockStopAction.wasStopCalled) expectation.fulfill() } - wait(for: [expectation], timeout: 1) + wait(for: [expectation], timeout: 3) } func testEntitlementMonitorWithInValidResult_thenStopAgentIsCalled() { @@ -166,12 +166,12 @@ final class DataBrokerProtectionAgentStopperTests: XCTestCase { let expectation = XCTestExpectation(description: "Wait for monitor") stopper.monitorEntitlementAndStopAgentIfEntitlementIsInvalid(interval: 0.1) - DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { [self] in + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [self] in XCTAssertTrue(mockStopAction.wasStopCalled) expectation.fulfill() } - wait(for: [expectation], timeout: 1) + wait(for: [expectation], timeout: 3) } func testEntitlementMonitorWithErrorResult_thenStopAgentIsNotCalled() { @@ -188,11 +188,11 @@ final class DataBrokerProtectionAgentStopperTests: XCTestCase { let expectation = XCTestExpectation(description: "Wait for monitor") stopper.monitorEntitlementAndStopAgentIfEntitlementIsInvalid(interval: 0.1) - DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { [self] in + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [self] in XCTAssertFalse(mockStopAction.wasStopCalled) expectation.fulfill() } - wait(for: [expectation], timeout: 1) + wait(for: [expectation], timeout: 3) } } From 03bc5dc325b13f5f2a355fc45481ec42d0b117a9 Mon Sep 17 00:00:00 2001 From: Brian Hall Date: Thu, 23 May 2024 07:20:11 -0500 Subject: [PATCH 05/11] Add mylife data broker (#2786) Task/Issue URL: https://app.asana.com/0/1206873150423133/1207316296802490/f Tech Design URL: CC: **Description**: **Steps to test this PR**: 1. --- ###### Internal references: [Pull Request Review Checklist](https://app.asana.com/0/1202500774821704/1203764234894239/f) [Software Engineering Expectations](https://app.asana.com/0/59792373528535/199064865822552) [Technical Design Template](https://app.asana.com/0/59792373528535/184709971311943) [Pull Request Documentation](https://app.asana.com/0/1202500774821704/1204012835277482/f) --- .../Resources/JSON/mylife.com.json | 140 ++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Resources/JSON/mylife.com.json diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Resources/JSON/mylife.com.json b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Resources/JSON/mylife.com.json new file mode 100644 index 0000000000..5d26c83a07 --- /dev/null +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Resources/JSON/mylife.com.json @@ -0,0 +1,140 @@ +{ + "name": "mylife", + "url": "mylife.com", + "version": "0.0.1", + "addedDatetime": 1715797497496, + "steps": [ + { + "stepType": "scan", + "scanType": "templatedUrl", + "actions": [ + { + "actionType": "navigate", + "id": "31285970-27bd-4ec6-a4c1-afc5fb501624", + "url": "https://www.mylife.com/pub-multisearch.pubview?searchFirstName=${firstName}&searchLastName=${lastName}&searchLocation=${city}%2C+${state|upcase}&whyReg=peoplesearch&whySub=Member+Profile+Sub&pageType=ps" + }, + { + "actionType": "extract", + "id": "9a08be56-d596-48e5-8745-0574b541e9df", + "selector": ".ais-InfiniteHits-item", + "profile": { + "name": { + "selector": ".hit-name", + "beforeText": "," + }, + "alternativeNamesList": { + "selector": ".hit-akas .hit-values", + "findElements": true + }, + "age": { + "selector": ".hit-name", + "afterText": "," + }, + "addressCityState": { + "selector": ".hit-location" + }, + "addressCityStateList": { + "selector": ".hit-pastAddresses .hit-values", + "findElements": true + }, + "profileUrl": { + "selector": ".hit-btn-lg", + "identifierType": "path", + "identifier": "https://www.mylife.com/${firstName}-${lastName}/${id}" + } + } + } + ] + }, + { + "stepType": "optOut", + "optOutType": "formOptOut", + "actions": [ + { + "actionType": "navigate", + "id": "8c7e8f10-5bd2-4dba-b99a-d198b3c0bbc9", + "url": "https://www.mylife.com/ccpa/index.pubview" + }, + { + "actionType": "fillForm", + "id": "1cd0b2b7-7203-4ae1-9d22-d76aa6de8a26", + "selector": "//form", + "dataSource": "userProfile", + "elements": [ + { + "type": "firstName", + "selector": ".//input[@name='firstname']" + }, + { + "type": "lastName", + "selector": ".//input[@name='lastname']" + }, + { + "type": "city", + "selector": ".//input[@name='city']" + }, + { + "type": "state", + "selector": ".//input[@name='state']" + } + ] + }, + { + "actionType": "fillForm", + "id": "ed570894-ebbe-4f9c-a9f7-9d58e81bdc28", + "selector": "//form", + "elements": [ + { + "type": "email", + "selector": ".//input[@name='emailAddress']" + }, + { + "type": "$generated_zip_code$", + "selector": ".//input[@name='zipcode']" + }, + { + "type": "profileUrl", + "selector": ".//input[@name='profileUrl']" + } + ] + }, + { + "actionType": "getCaptchaInfo", + "id": "aeda8b17-92cf-43ce-8974-12a13fb9bcfd", + "selector": ".g-recaptcha" + }, + { + "actionType": "solveCaptcha", + "id": "6b8a962e-19ed-4f33-8c56-4f4a1f17cad3", + "selector": ".g-recaptcha" + }, + { + "actionType": "click", + "id": "6cb0e6f4-e881-4937-872e-29627223bdb8", + "elements": [ + { + "type": "button", + "selector": ".//input[@type='submit']" + } + ] + }, + { + "actionType": "expectation", + "id": "fe7201a7-4e92-4ad3-90fb-d836019d71e0", + "expectations": [ + { + "type": "text", + "selector": "#successRequest", + "expect": "Your request has been received." + } + ] + } + ] + } + ], + "schedulingConfig": { + "retryError": 48, + "confirmOptOutScan": 72, + "maintenanceScan": 240 + } +} From d826ec2899d752267e4b5fde918be052121d041d Mon Sep 17 00:00:00 2001 From: amddg44 Date: Thu, 23 May 2024 16:45:42 +0200 Subject: [PATCH 06/11] New autofill save & update password prompt pixels for alignment with iOS (#2801) Task/Issue URL: https://app.asana.com/0/72649045549333/1207361449520263/f Tech Design URL: CC: Description: New pixels to bring macOS password management pixel reporting up to date with iOS --- .../View/SaveCredentialsViewController.swift | 104 ++++++++++++++++++ DuckDuckGo/Statistics/GeneralPixel.swift | 44 ++++++++ .../Tab/ViewModel/TabViewModelTests.swift | 2 +- 3 files changed, 149 insertions(+), 1 deletion(-) diff --git a/DuckDuckGo/SecureVault/View/SaveCredentialsViewController.swift b/DuckDuckGo/SecureVault/View/SaveCredentialsViewController.swift index 02a5595240..e9f565bb9b 100644 --- a/DuckDuckGo/SecureVault/View/SaveCredentialsViewController.swift +++ b/DuckDuckGo/SecureVault/View/SaveCredentialsViewController.swift @@ -63,6 +63,12 @@ final class SaveCredentialsViewController: NSViewController { @IBOutlet var fireproofCheck: NSButton! @IBOutlet weak var fireproofCheckDescription: NSTextFieldCell! + private enum Action { + case displayed + case confirmed + case dismissed + } + weak var delegate: SaveCredentialsDelegate? private var credentials: SecureVaultModels.WebsiteCredentials? @@ -139,6 +145,9 @@ final class SaveCredentialsViewController: NSViewController { // Only use the non-editable state if a credential was automatically saved and it didn't already exist. let condition = credentials.account.id != nil && !(credentials.account.username?.isEmpty ?? true) && automaticallySaved updateViewState(editable: !condition) + + let existingCredentials = getExistingCredentialsFrom(credentials) + evaluateCredentialsAndFirePixels(for: .displayed, credentials: existingCredentials) } private func updateViewState(editable: Bool) { @@ -208,6 +217,7 @@ final class SaveCredentialsViewController: NSViewController { domain: domainLabel.stringValue) account.id = credentials?.account.id let credentials = SecureVaultModels.WebsiteCredentials(account: account, password: passwordData) + let existingCredentials = getExistingCredentialsFrom(credentials) do { if passwordManagerCoordinator.isEnabled { @@ -231,6 +241,8 @@ final class SaveCredentialsViewController: NSViewController { PixelKit.fire(DebugEvent(GeneralPixel.secureVaultError(error: error))) } + evaluateCredentialsAndFirePixels(for: .confirmed, credentials: existingCredentials) + PixelKit.fire(GeneralPixel.autofillItemSaved(kind: .password)) if passwordManagerCoordinator.isEnabled { @@ -250,6 +262,9 @@ final class SaveCredentialsViewController: NSViewController { @IBAction func onDontUpdateClicked(_ sender: Any) { delegate?.shouldCloseSaveCredentialsViewController(self) + + let existingCredentials = getExistingCredentialsFrom(credentials) + evaluateCredentialsAndFirePixels(for: .dismissed, credentials: existingCredentials) } @IBAction func onNotNowSegmentedControlClicked(_ sender: Any) { @@ -280,6 +295,9 @@ final class SaveCredentialsViewController: NSViewController { delegate?.shouldCloseSaveCredentialsViewController(self) } + let existingCredentials = getExistingCredentialsFrom(credentials) + evaluateCredentialsAndFirePixels(for: .dismissed, credentials: existingCredentials) + guard DataClearingPreferences.shared.isLoginDetectionEnabled else { notifyDelegate() return @@ -365,4 +383,90 @@ final class SaveCredentialsViewController: NSViewController { } } + private func getExistingCredentialsFrom(_ credentials: SecureVaultModels.WebsiteCredentials?) -> SecureVaultModels.WebsiteCredentials? { + guard let credentials = credentials, let id = credentials.account.id else { + return nil + } + + var existingCredentials: SecureVaultModels.WebsiteCredentials? + + if passwordManagerCoordinator.isEnabled { + guard !passwordManagerCoordinator.isLocked else { + os_log("Failed to access credentials: Password manager is locked") + return existingCredentials + } + + passwordManagerCoordinator.websiteCredentialsFor(accountId: id) { credentials, _ in + existingCredentials = credentials + } + } else { + if let idInt = Int64(id) { + existingCredentials = try? AutofillSecureVaultFactory.makeVault(reporter: SecureVaultReporter.shared).websiteCredentialsFor(accountId: idInt) + } + } + + return existingCredentials + } + + private func isUsernameUpdated(credentials: SecureVaultModels.WebsiteCredentials) -> Bool { + if credentials.account.username != self.usernameField.stringValue.trimmingWhitespace() { + return true + } + return false + } + + private func isPasswordUpdated(credentials: SecureVaultModels.WebsiteCredentials) -> Bool { + if credentials.password != self.passwordData { + return true + } + return false + } + + private func evaluateCredentialsAndFirePixels(for action: Action, credentials: SecureVaultModels.WebsiteCredentials?) { + switch action { + case .displayed: + if let credentials = credentials { + if isPasswordUpdated(credentials: credentials) { + PixelKit.fire(GeneralPixel.autofillLoginsUpdatePasswordInlineDisplayed) + } else { + PixelKit.fire(GeneralPixel.autofillLoginsUpdateUsernameInlineDisplayed) + } + } else { + if usernameField.stringValue.trimmingWhitespace().isEmpty { + PixelKit.fire(GeneralPixel.autofillLoginsSavePasswordInlineDisplayed) + } else { + PixelKit.fire(GeneralPixel.autofillLoginsSaveLoginInlineDisplayed) + } + } + case .confirmed, .dismissed: + if let credentials = credentials { + if isUsernameUpdated(credentials: credentials) { + firePixel(for: action, + confirmedPixel: GeneralPixel.autofillLoginsUpdateUsernameInlineConfirmed, + dismissedPixel: GeneralPixel.autofillLoginsUpdateUsernameInlineDismissed) + } + if isPasswordUpdated(credentials: credentials) { + firePixel(for: action, + confirmedPixel: GeneralPixel.autofillLoginsUpdatePasswordInlineConfirmed, + dismissedPixel: GeneralPixel.autofillLoginsUpdatePasswordInlineDismissed) + } + } else { + if usernameField.stringValue.trimmingWhitespace().isEmpty { + firePixel(for: action, + confirmedPixel: GeneralPixel.autofillLoginsSavePasswordInlineConfirmed, + dismissedPixel: GeneralPixel.autofillLoginsSavePasswordInlineDismissed) + } else { + firePixel(for: action, + confirmedPixel: GeneralPixel.autofillLoginsSaveLoginInlineConfirmed, + dismissedPixel: GeneralPixel.autofillLoginsSaveLoginInlineDismissed) + } + } + } + } + + private func firePixel(for action: Action, confirmedPixel: PixelKitEventV2, dismissedPixel: PixelKitEventV2) { + let pixel = action == .confirmed ? confirmedPixel : dismissedPixel + PixelKit.fire(pixel) + } + } diff --git a/DuckDuckGo/Statistics/GeneralPixel.swift b/DuckDuckGo/Statistics/GeneralPixel.swift index d5846e38a7..fd7e8dafa3 100644 --- a/DuckDuckGo/Statistics/GeneralPixel.swift +++ b/DuckDuckGo/Statistics/GeneralPixel.swift @@ -40,11 +40,27 @@ enum GeneralPixel: PixelKitEventV2 { case formAutofilled(kind: FormAutofillKind) case autofillItemSaved(kind: FormAutofillKind) + case autofillLoginsSaveLoginInlineDisplayed + case autofillLoginsSaveLoginInlineConfirmed + case autofillLoginsSaveLoginInlineDismissed + + case autofillLoginsSavePasswordInlineDisplayed + case autofillLoginsSavePasswordInlineConfirmed + case autofillLoginsSavePasswordInlineDismissed + case autofillLoginsSaveLoginModalExcludeSiteConfirmed case autofillLoginsSettingsResetExcludedDisplayed case autofillLoginsSettingsResetExcludedConfirmed case autofillLoginsSettingsResetExcludedDismissed + case autofillLoginsUpdatePasswordInlineDisplayed + case autofillLoginsUpdatePasswordInlineConfirmed + case autofillLoginsUpdatePasswordInlineDismissed + + case autofillLoginsUpdateUsernameInlineDisplayed + case autofillLoginsUpdateUsernameInlineConfirmed + case autofillLoginsUpdateUsernameInlineDismissed + case bitwardenPasswordAutofilled case bitwardenPasswordSaved @@ -361,6 +377,20 @@ enum GeneralPixel: PixelKitEventV2 { case .autofillItemSaved(kind: let kind): return "m_mac_save_\(kind)" + case .autofillLoginsSaveLoginInlineDisplayed: + return "m_mac_autofill_logins_save_login_inline_displayed" + case .autofillLoginsSaveLoginInlineConfirmed: + return "m_mac_autofill_logins_save_login_inline_confirmed" + case .autofillLoginsSaveLoginInlineDismissed: + return "m_mac_autofill_logins_save_login_inline_dismissed" + + case .autofillLoginsSavePasswordInlineDisplayed: + return "m_mac_autofill_logins_save_password_inline_displayed" + case .autofillLoginsSavePasswordInlineConfirmed: + return "m_mac_autofill_logins_save_password_inline_confirmed" + case .autofillLoginsSavePasswordInlineDismissed: + return "m_mac_autofill_logins_save_password_inline_dismissed" + case .autofillLoginsSaveLoginModalExcludeSiteConfirmed: return "m_mac_autofill_logins_save_login_exclude_site_confirmed" case .autofillLoginsSettingsResetExcludedDisplayed: @@ -370,6 +400,20 @@ enum GeneralPixel: PixelKitEventV2 { case .autofillLoginsSettingsResetExcludedDismissed: return "m_mac_autofill_settings_reset_excluded_dismissed" + case .autofillLoginsUpdatePasswordInlineDisplayed: + return "m_mac_autofill_logins_update_password_inline_displayed" + case .autofillLoginsUpdatePasswordInlineConfirmed: + return "m_mac_autofill_logins_update_password_inline_confirmed" + case .autofillLoginsUpdatePasswordInlineDismissed: + return "m_mac_autofill_logins_update_password_inline_dismissed" + + case .autofillLoginsUpdateUsernameInlineDisplayed: + return "m_mac_autofill_logins_update_username_inline_displayed" + case .autofillLoginsUpdateUsernameInlineConfirmed: + return "m_mac_autofill_logins_update_username_inline_confirmed" + case .autofillLoginsUpdateUsernameInlineDismissed: + return "m_mac_autofill_logins_update_username_inline_dismissed" + case .bitwardenPasswordAutofilled: return "m_mac_bitwarden_autofill_password" diff --git a/UnitTests/Tab/ViewModel/TabViewModelTests.swift b/UnitTests/Tab/ViewModel/TabViewModelTests.swift index c2e83d6fbb..4828527900 100644 --- a/UnitTests/Tab/ViewModel/TabViewModelTests.swift +++ b/UnitTests/Tab/ViewModel/TabViewModelTests.swift @@ -294,7 +294,7 @@ final class TabViewModelTests: XCTestCase { let filteredCases = DefaultZoomValue.allCases.filter { $0 != AccessibilityPreferences.shared.defaultPageZoom } let randomZoomLevel = filteredCases.randomElement()! AccessibilityPreferences.shared.updateZoomPerWebsite(zoomLevel: randomZoomLevel, url: hostURL) - var tab = Tab(url: url) + let tab = Tab(url: url) var tabVM = TabViewModel(tab: tab) // WHEN From 04bb8e2b1799f0b23fbc94f3466372d62d61d9c5 Mon Sep 17 00:00:00 2001 From: Christopher Brind Date: Thu, 23 May 2024 15:51:35 +0100 Subject: [PATCH 07/11] Add History to iOS (updated UI and rollout) (#2770) Task/Issue URL: https://app.asana.com/0/72649045549333/1205122649889514/f Tech Design URL: CC: **Description**: Tweak suggestions for iOS **Steps to test this PR**: 1. Build and run the app 2. Use history and ensure that it works as before --- DuckDuckGo.xcodeproj/project.pbxproj | 8 +- .../xcshareddata/swiftpm/Package.resolved | 6 +- .../xcschemes/sandbox-test-tool.xcscheme | 2 +- .../DataBrokerProtection/Package.swift | 2 +- .../NetworkProtectionMac/Package.swift | 2 +- LocalPackages/SubscriptionUI/Package.swift | 2 +- .../Model/HistoryCoordinatorTests.swift | 272 ------------------ 7 files changed, 8 insertions(+), 286 deletions(-) delete mode 100644 UnitTests/History/Model/HistoryCoordinatorTests.swift diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 08e3fe56ea..5d81a1af21 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -864,7 +864,6 @@ 3706FE6D293F661700E42796 /* ChromiumBookmarksReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BB99D0C26FE1A83001E4761 /* ChromiumBookmarksReaderTests.swift */; }; 3706FE6E293F661700E42796 /* FirefoxBookmarksReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BB99D0D26FE1A83001E4761 /* FirefoxBookmarksReaderTests.swift */; }; 3706FE6F293F661700E42796 /* LocalStatisticsStoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B117F7C276C0CB5002F3D8C /* LocalStatisticsStoreTests.swift */; }; - 3706FE70293F661700E42796 /* HistoryCoordinatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAEC74B32642C69300C2EFBC /* HistoryCoordinatorTests.swift */; }; 3706FE71293F661700E42796 /* SavedStateMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 378205F52837CBA800D1D4AA /* SavedStateMock.swift */; }; 3706FE72293F661700E42796 /* ClickToLoadTDSTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA8AE769279FBDB20078943E /* ClickToLoadTDSTests.swift */; }; 3706FE73293F661700E42796 /* PermissionManagerMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = B63ED0D926AE7AF400A9DAD1 /* PermissionManagerMock.swift */; }; @@ -1981,7 +1980,6 @@ AAE8B110258A456C00E81239 /* TabPreviewViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAE8B10F258A456C00E81239 /* TabPreviewViewController.swift */; }; AAE99B8927088A19008B6BD9 /* FirePopover.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAE99B8827088A19008B6BD9 /* FirePopover.swift */; }; AAEC74B22642C57200C2EFBC /* HistoryCoordinatingMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAEC74B12642C57200C2EFBC /* HistoryCoordinatingMock.swift */; }; - AAEC74B42642C69300C2EFBC /* HistoryCoordinatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAEC74B32642C69300C2EFBC /* HistoryCoordinatorTests.swift */; }; AAEC74B62642CC6A00C2EFBC /* HistoryStoringMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAEC74B52642CC6A00C2EFBC /* HistoryStoringMock.swift */; }; AAEC74B82642E43800C2EFBC /* HistoryStoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAEC74B72642E43800C2EFBC /* HistoryStoreTests.swift */; }; AAECA42024EEA4AC00EFA63A /* IndexPathExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAECA41F24EEA4AC00EFA63A /* IndexPathExtension.swift */; }; @@ -3754,7 +3752,6 @@ AAE8B10F258A456C00E81239 /* TabPreviewViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabPreviewViewController.swift; sourceTree = ""; }; AAE99B8827088A19008B6BD9 /* FirePopover.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirePopover.swift; sourceTree = ""; }; AAEC74B12642C57200C2EFBC /* HistoryCoordinatingMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryCoordinatingMock.swift; sourceTree = ""; }; - AAEC74B32642C69300C2EFBC /* HistoryCoordinatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryCoordinatorTests.swift; sourceTree = ""; }; AAEC74B52642CC6A00C2EFBC /* HistoryStoringMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryStoringMock.swift; sourceTree = ""; }; AAEC74B72642E43800C2EFBC /* HistoryStoreTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryStoreTests.swift; sourceTree = ""; }; AAECA41F24EEA4AC00EFA63A /* IndexPathExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IndexPathExtension.swift; sourceTree = ""; }; @@ -7626,7 +7623,6 @@ isa = PBXGroup; children = ( AAEC74B12642C57200C2EFBC /* HistoryCoordinatingMock.swift */, - AAEC74B32642C69300C2EFBC /* HistoryCoordinatorTests.swift */, ); path = Model; sourceTree = ""; @@ -10598,7 +10594,6 @@ 028904212A7B25770028369C /* AppConfigurationURLProviderTests.swift in Sources */, 3706FE6F293F661700E42796 /* LocalStatisticsStoreTests.swift in Sources */, 31DC2F232BD6E028001354EF /* DataBrokerPrerequisitesStatusVerifierTests.swift in Sources */, - 3706FE70293F661700E42796 /* HistoryCoordinatorTests.swift in Sources */, 9F3344632BBFBDA40040CBEB /* BookmarksBarVisibilityManagerTests.swift in Sources */, 3706FE71293F661700E42796 /* SavedStateMock.swift in Sources */, 3706FE72293F661700E42796 /* ClickToLoadTDSTests.swift in Sources */, @@ -12012,7 +12007,6 @@ 4BB99D1026FE1A84001E4761 /* FirefoxBookmarksReaderTests.swift in Sources */, 9FBD84702BB3DD8400220859 /* MockAttributionsPixelHandler.swift in Sources */, 4B117F7D276C0CB5002F3D8C /* LocalStatisticsStoreTests.swift in Sources */, - AAEC74B42642C69300C2EFBC /* HistoryCoordinatorTests.swift in Sources */, 378205F62837CBA800D1D4AA /* SavedStateMock.swift in Sources */, 3783F92329432E1800BCA897 /* WebViewTests.swift in Sources */, EA8AE76A279FBDB20078943E /* ClickToLoadTDSTests.swift in Sources */, @@ -13027,7 +13021,7 @@ repositoryURL = "https://github.com/duckduckgo/BrowserServicesKit"; requirement = { kind = exactVersion; - version = 146.0.0; + version = 146.0.1; }; }; 9FF521422BAA8FF300B9819B /* XCRemoteSwiftPackageReference "lottie-spm" */ = { diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 7c7e84edb5..913e081687 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" : "b01a7ba359b650f0c5c3ab00a756e298b1ae650c", - "version" : "146.0.0" + "revision" : "d18f97300d105ec5b8fb5fbe54659c661dea6158", + "version" : "146.0.1" } }, { @@ -174,7 +174,7 @@ { "identity" : "trackerradarkit", "kind" : "remoteSourceControl", - "location" : "https://github.com/duckduckgo/TrackerRadarKit", + "location" : "https://github.com/duckduckgo/TrackerRadarKit.git", "state" : { "revision" : "c01e6a59d000356b58ec77053e0a99d538be56a5", "version" : "2.1.1" diff --git a/DuckDuckGo.xcodeproj/xcshareddata/xcschemes/sandbox-test-tool.xcscheme b/DuckDuckGo.xcodeproj/xcshareddata/xcschemes/sandbox-test-tool.xcscheme index eb7e5e26bb..41730d7069 100644 --- a/DuckDuckGo.xcodeproj/xcshareddata/xcschemes/sandbox-test-tool.xcscheme +++ b/DuckDuckGo.xcodeproj/xcshareddata/xcschemes/sandbox-test-tool.xcscheme @@ -1,7 +1,7 @@ + version = "1.7"> Date: Thu, 23 May 2024 16:00:12 +0100 Subject: [PATCH 08/11] Make profile selector optional (#2811) Task/Issue URL: https://app.asana.com/0/1199230911884351/1207389625725611/f **Description**: Make profile selector optional --- .../Sources/DataBrokerProtection/Model/ExtractedProfile.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/ExtractedProfile.swift b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/ExtractedProfile.swift index aa6158bd24..5bf40900ae 100644 --- a/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/ExtractedProfile.swift +++ b/LocalPackages/DataBrokerProtection/Sources/DataBrokerProtection/Model/ExtractedProfile.swift @@ -19,7 +19,7 @@ import Foundation struct ProfileSelector: Codable { - let selector: String + let selector: String? let findElements: Bool? let afterText: String? let beforeText: String? From 41a9790206f610fc6fb57d0ac8199ac52f45979b Mon Sep 17 00:00:00 2001 From: Brian Hall Date: Thu, 23 May 2024 10:54:56 -0500 Subject: [PATCH 09/11] Bump BSK (#2807) Task/Issue URL: https://app.asana.com/0/608920331025329/1207360142555446/f Tech Design URL: CC: **Description**: **Steps to test this PR**: 1. --- ###### Internal references: [Pull Request Review Checklist](https://app.asana.com/0/1202500774821704/1203764234894239/f) [Software Engineering Expectations](https://app.asana.com/0/59792373528535/199064865822552) [Technical Design Template](https://app.asana.com/0/59792373528535/184709971311943) [Pull Request Documentation](https://app.asana.com/0/1202500774821704/1204012835277482/f) --- DuckDuckGo.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/swiftpm/Package.resolved | 10 +++++----- LocalPackages/DataBrokerProtection/Package.swift | 2 +- LocalPackages/NetworkProtectionMac/Package.swift | 2 +- LocalPackages/SubscriptionUI/Package.swift | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 5d81a1af21..ca8c019e43 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -13021,7 +13021,7 @@ repositoryURL = "https://github.com/duckduckgo/BrowserServicesKit"; requirement = { kind = exactVersion; - version = 146.0.1; + version = 146.1.0; }; }; 9FF521422BAA8FF300B9819B /* XCRemoteSwiftPackageReference "lottie-spm" */ = { diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 913e081687..4a74ac2fd1 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" : "d18f97300d105ec5b8fb5fbe54659c661dea6158", - "version" : "146.0.1" + "revision" : "65f3ccadd0118bcef112e3f5fff03e491b3261cd", + "version" : "146.1.0" } }, { @@ -41,8 +41,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/duckduckgo/content-scope-scripts", "state" : { - "revision" : "bb8e7e62104ed6506c7bfd3ef7aa4aca3686ed4f", - "version" : "5.15.0" + "revision" : "fa861c4eccb21d235e34070b208b78bdc32ece08", + "version" : "5.17.0" } }, { @@ -174,7 +174,7 @@ { "identity" : "trackerradarkit", "kind" : "remoteSourceControl", - "location" : "https://github.com/duckduckgo/TrackerRadarKit.git", + "location" : "https://github.com/duckduckgo/TrackerRadarKit", "state" : { "revision" : "c01e6a59d000356b58ec77053e0a99d538be56a5", "version" : "2.1.1" diff --git a/LocalPackages/DataBrokerProtection/Package.swift b/LocalPackages/DataBrokerProtection/Package.swift index 9b62cd2cb8..5c788ad63f 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: "146.0.1"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "146.1.0"), .package(path: "../SwiftUIExtensions"), .package(path: "../XPCHelper"), ], diff --git a/LocalPackages/NetworkProtectionMac/Package.swift b/LocalPackages/NetworkProtectionMac/Package.swift index 517322718a..24722fef2b 100644 --- a/LocalPackages/NetworkProtectionMac/Package.swift +++ b/LocalPackages/NetworkProtectionMac/Package.swift @@ -31,7 +31,7 @@ let package = Package( .library(name: "NetworkProtectionUI", targets: ["NetworkProtectionUI"]), ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "146.0.1"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "146.1.0"), .package(url: "https://github.com/airbnb/lottie-spm", exact: "4.4.1"), .package(path: "../XPCHelper"), .package(path: "../SwiftUIExtensions"), diff --git a/LocalPackages/SubscriptionUI/Package.swift b/LocalPackages/SubscriptionUI/Package.swift index 925a70c283..a33802799a 100644 --- a/LocalPackages/SubscriptionUI/Package.swift +++ b/LocalPackages/SubscriptionUI/Package.swift @@ -12,7 +12,7 @@ let package = Package( targets: ["SubscriptionUI"]), ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "146.0.1"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "146.1.0"), .package(path: "../SwiftUIExtensions") ], targets: [ From a3d04ea917dab3ded23019cd85cb3d9a5798ec14 Mon Sep 17 00:00:00 2001 From: Sean Barag Date: Thu, 23 May 2024 10:28:44 -0700 Subject: [PATCH 10/11] autofill: don't prefix autofill email pixels with `m.mac.` (#2808) Task/Issue URL: https://app.asana.com/0/1206682621538333/1207380134103391/f Tech Design URL: N/A Pixels fired for email autofill use-cases are intentionally not prefixed with `m.mac.`, though the historical reason isn't clear to me. The migration to PixelKit for pixel management[^1] caused those email-related pixels to be prefixed. Wrap email-related JS pixels in `NonStandardEvent`s to prevent that prefixing, to restore their original intended names. [^1]: d885fd02d (PixelKit adoption (#2557), 2024-04-17) --- DuckDuckGo/Autofill/ContentOverlayViewController.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DuckDuckGo/Autofill/ContentOverlayViewController.swift b/DuckDuckGo/Autofill/ContentOverlayViewController.swift index 63a6a88368..5033f96ac5 100644 --- a/DuckDuckGo/Autofill/ContentOverlayViewController.swift +++ b/DuckDuckGo/Autofill/ContentOverlayViewController.swift @@ -325,7 +325,7 @@ extension ContentOverlayViewController: SecureVaultManagerDelegate { self.emailManager.updateLastUseDate() - PixelKit.fire(GeneralPixel.jsPixel(pixel), withAdditionalParameters: pixelParameters) + PixelKit.fire(NonStandardEvent(GeneralPixel.jsPixel(pixel)), withAdditionalParameters: pixelParameters) } else { PixelKit.fire(GeneralPixel.jsPixel(pixel), withAdditionalParameters: pixel.pixelParameters) } From 22905cde55fb3cb9359046f0d3a5ebebebb4f141 Mon Sep 17 00:00:00 2001 From: amddg44 Date: Thu, 23 May 2024 21:45:08 +0200 Subject: [PATCH 11/11] Autofill engagement KPIs for pixel reporting (#2806) Task/Issue URL: https://app.asana.com/0/72649045549333/1207357107981852/f Tech Design URL: CC: Description: New Autofill engagement KPI pixels --- DuckDuckGo.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/swiftpm/Package.resolved | 4 ++-- DuckDuckGo/Application/AppDelegate.swift | 24 +++++++++++++++++++ .../ContentOverlayViewController.swift | 5 ++++ .../SecureVaultLoginImporter.swift | 4 ++++ DuckDuckGo/Menus/MainMenuActions.swift | 3 +++ .../PasswordManagementViewController.swift | 1 + .../View/SaveCredentialsViewController.swift | 2 ++ .../Statistics/ATB/StatisticsLoader.swift | 1 + DuckDuckGo/Statistics/GeneralPixel.swift | 17 +++++++++++++ .../DataBrokerProtection/Package.swift | 2 +- .../NetworkProtectionMac/Package.swift | 2 +- LocalPackages/SubscriptionUI/Package.swift | 2 +- UnitTests/DataExport/MockSecureVault.swift | 8 +++++++ 14 files changed, 71 insertions(+), 6 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index ca8c019e43..6143c3a653 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -13021,7 +13021,7 @@ repositoryURL = "https://github.com/duckduckgo/BrowserServicesKit"; requirement = { kind = exactVersion; - version = 146.1.0; + version = 146.2.0; }; }; 9FF521422BAA8FF300B9819B /* XCRemoteSwiftPackageReference "lottie-spm" */ = { diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 4a74ac2fd1..6dd57e77a0 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" : "65f3ccadd0118bcef112e3f5fff03e491b3261cd", - "version" : "146.1.0" + "revision" : "e1e436422bc167933baa0f90838958f2ac7119f3", + "version" : "146.2.0" } }, { diff --git a/DuckDuckGo/Application/AppDelegate.swift b/DuckDuckGo/Application/AppDelegate.swift index 2b734be8f9..4ef995e23e 100644 --- a/DuckDuckGo/Application/AppDelegate.swift +++ b/DuckDuckGo/Application/AppDelegate.swift @@ -76,6 +76,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate { let featureFlagger: FeatureFlagger private var appIconChanger: AppIconChanger! private var autoClearHandler: AutoClearHandler! + private(set) var autofillPixelReporter: AutofillPixelReporter? private(set) var syncDataProviders: SyncDataProviders! private(set) var syncService: DDGSyncing? @@ -321,6 +322,8 @@ final class AppDelegate: NSObject, NSApplicationDelegate { #endif setUpAutoClearHandler() + + setUpAutofillPixelReporter() } func applicationDidBecomeActive(_ notification: Notification) { @@ -587,6 +590,27 @@ final class AppDelegate: NSObject, NSApplicationDelegate { } } } + + private func setUpAutofillPixelReporter() { + autofillPixelReporter = AutofillPixelReporter( + userDefaults: .standard, + eventMapping: EventMapping {event, _, params, _ in + switch event { + case .autofillActiveUser: + PixelKit.fire(GeneralPixel.autofillActiveUser) + case .autofillEnabledUser: + PixelKit.fire(GeneralPixel.autofillEnabledUser) + case .autofillOnboardedUser: + PixelKit.fire(GeneralPixel.autofillOnboardedUser) + case .autofillLoginsStacked: + PixelKit.fire(GeneralPixel.autofillLoginsStacked, withAdditionalParameters: params) + case .autofillCreditCardsStacked: + PixelKit.fire(GeneralPixel.autofillCreditCardsStacked, withAdditionalParameters: params) + } + }, + passwordManager: PasswordManagerCoordinator.shared, + installDate: AppDelegate.firstLaunchDate) + } } extension AppDelegate: UNUserNotificationCenterDelegate { diff --git a/DuckDuckGo/Autofill/ContentOverlayViewController.swift b/DuckDuckGo/Autofill/ContentOverlayViewController.swift index 5033f96ac5..12a087e0ad 100644 --- a/DuckDuckGo/Autofill/ContentOverlayViewController.swift +++ b/DuckDuckGo/Autofill/ContentOverlayViewController.swift @@ -296,6 +296,7 @@ extension ContentOverlayViewController: SecureVaultManagerDelegate { public func secureVaultManager(_: SecureVaultManager, didAutofill type: AutofillType, withObjectId objectId: String) { PixelKit.fire(GeneralPixel.formAutofilled(kind: type.formAutofillKind)) + NotificationCenter.default.post(name: .autofillFillEvent, object: nil) if type.formAutofillKind == .password && passwordManagerCoordinator.isEnabled { @@ -326,7 +327,11 @@ extension ContentOverlayViewController: SecureVaultManagerDelegate { self.emailManager.updateLastUseDate() PixelKit.fire(NonStandardEvent(GeneralPixel.jsPixel(pixel)), withAdditionalParameters: pixelParameters) + NotificationCenter.default.post(name: .autofillFillEvent, object: nil) } else { + if pixel.isIdentityPixel { + NotificationCenter.default.post(name: .autofillFillEvent, object: nil) + } PixelKit.fire(GeneralPixel.jsPixel(pixel), withAdditionalParameters: pixel.pixelParameters) } } diff --git a/DuckDuckGo/DataImport/Logins/SecureVault/SecureVaultLoginImporter.swift b/DuckDuckGo/DataImport/Logins/SecureVault/SecureVaultLoginImporter.swift index 193806cef0..eb3cdd03d9 100644 --- a/DuckDuckGo/DataImport/Logins/SecureVault/SecureVaultLoginImporter.swift +++ b/DuckDuckGo/DataImport/Logins/SecureVault/SecureVaultLoginImporter.swift @@ -60,6 +60,10 @@ final class SecureVaultLoginImporter: LoginImporter { } } + if successful.count > 0 { + NotificationCenter.default.post(name: .autofillSaveEvent, object: nil, userInfo: nil) + } + return .init(successful: successful.count, duplicate: duplicates.count, failed: failed.count) } diff --git a/DuckDuckGo/Menus/MainMenuActions.swift b/DuckDuckGo/Menus/MainMenuActions.swift index 103b90bc81..cef3e05a90 100644 --- a/DuckDuckGo/Menus/MainMenuActions.swift +++ b/DuckDuckGo/Menus/MainMenuActions.swift @@ -743,6 +743,9 @@ extension MainViewController { try? vault?.deleteNoteFor(noteId: noteID) } UserDefaults.standard.set(false, forKey: UserDefaultsWrapper.Key.homePageContinueSetUpImport.rawValue) + + let autofillPixelReporter = AutofillPixelReporter(userDefaults: .standard, eventMapping: EventMapping { _, _, _, _ in }, installDate: nil) + autofillPixelReporter.resetStoreDefaults() } @objc func resetBookmarks(_ sender: Any?) { diff --git a/DuckDuckGo/SecureVault/View/PasswordManagementViewController.swift b/DuckDuckGo/SecureVault/View/PasswordManagementViewController.swift index f18a2478de..bb8bd46046 100644 --- a/DuckDuckGo/SecureVault/View/PasswordManagementViewController.swift +++ b/DuckDuckGo/SecureVault/View/PasswordManagementViewController.swift @@ -539,6 +539,7 @@ final class PasswordManagementViewController: NSViewController { refetchWithText(searchField.stringValue) { [weak self] in self?.syncModelsOnCredentials(savedCredentials, select: true) } + NotificationCenter.default.post(name: .autofillSaveEvent, object: nil, userInfo: nil) } else { syncModelsOnCredentials(savedCredentials) } diff --git a/DuckDuckGo/SecureVault/View/SaveCredentialsViewController.swift b/DuckDuckGo/SecureVault/View/SaveCredentialsViewController.swift index e9f565bb9b..0d7bc4bc6d 100644 --- a/DuckDuckGo/SecureVault/View/SaveCredentialsViewController.swift +++ b/DuckDuckGo/SecureVault/View/SaveCredentialsViewController.swift @@ -241,6 +241,8 @@ final class SaveCredentialsViewController: NSViewController { PixelKit.fire(DebugEvent(GeneralPixel.secureVaultError(error: error))) } + NotificationCenter.default.post(name: .autofillSaveEvent, object: nil, userInfo: nil) + evaluateCredentialsAndFirePixels(for: .confirmed, credentials: existingCredentials) PixelKit.fire(GeneralPixel.autofillItemSaved(kind: .password)) diff --git a/DuckDuckGo/Statistics/ATB/StatisticsLoader.swift b/DuckDuckGo/Statistics/ATB/StatisticsLoader.swift index 19e7c70108..1d3cfb1be3 100644 --- a/DuckDuckGo/Statistics/ATB/StatisticsLoader.swift +++ b/DuckDuckGo/Statistics/ATB/StatisticsLoader.swift @@ -167,6 +167,7 @@ final class StatisticsLoader { if let data = response?.data, let atb = try? self.parser.convert(fromJsonData: data) { self.statisticsStore.searchRetentionAtb = atb.version self.storeUpdateVersionIfPresent(atb) + NotificationCenter.default.post(name: .searchDAU, object: nil, userInfo: nil) } completion() diff --git a/DuckDuckGo/Statistics/GeneralPixel.swift b/DuckDuckGo/Statistics/GeneralPixel.swift index fd7e8dafa3..6184263d85 100644 --- a/DuckDuckGo/Statistics/GeneralPixel.swift +++ b/DuckDuckGo/Statistics/GeneralPixel.swift @@ -61,6 +61,12 @@ enum GeneralPixel: PixelKitEventV2 { case autofillLoginsUpdateUsernameInlineConfirmed case autofillLoginsUpdateUsernameInlineDismissed + case autofillActiveUser + case autofillEnabledUser + case autofillOnboardedUser + case autofillLoginsStacked + case autofillCreditCardsStacked + case bitwardenPasswordAutofilled case bitwardenPasswordSaved @@ -414,6 +420,17 @@ enum GeneralPixel: PixelKitEventV2 { case .autofillLoginsUpdateUsernameInlineDismissed: return "m_mac_autofill_logins_update_username_inline_dismissed" + case .autofillActiveUser: + return "m_mac_autofill_activeuser" + case .autofillEnabledUser: + return "m_mac_autofill_enableduser" + case .autofillOnboardedUser: + return "m_mac_autofill_onboardeduser" + case .autofillLoginsStacked: + return "m_mac_autofill_logins_stacked" + case .autofillCreditCardsStacked: + return "m_mac_autofill_creditcards_stacked" + case .bitwardenPasswordAutofilled: return "m_mac_bitwarden_autofill_password" diff --git a/LocalPackages/DataBrokerProtection/Package.swift b/LocalPackages/DataBrokerProtection/Package.swift index 5c788ad63f..1fa39e2681 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: "146.1.0"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "146.2.0"), .package(path: "../SwiftUIExtensions"), .package(path: "../XPCHelper"), ], diff --git a/LocalPackages/NetworkProtectionMac/Package.swift b/LocalPackages/NetworkProtectionMac/Package.swift index 24722fef2b..041e3d6cd1 100644 --- a/LocalPackages/NetworkProtectionMac/Package.swift +++ b/LocalPackages/NetworkProtectionMac/Package.swift @@ -31,7 +31,7 @@ let package = Package( .library(name: "NetworkProtectionUI", targets: ["NetworkProtectionUI"]), ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "146.1.0"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "146.2.0"), .package(url: "https://github.com/airbnb/lottie-spm", exact: "4.4.1"), .package(path: "../XPCHelper"), .package(path: "../SwiftUIExtensions"), diff --git a/LocalPackages/SubscriptionUI/Package.swift b/LocalPackages/SubscriptionUI/Package.swift index a33802799a..57655ffe08 100644 --- a/LocalPackages/SubscriptionUI/Package.swift +++ b/LocalPackages/SubscriptionUI/Package.swift @@ -12,7 +12,7 @@ let package = Package( targets: ["SubscriptionUI"]), ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "146.1.0"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "146.2.0"), .package(path: "../SwiftUIExtensions") ], targets: [ diff --git a/UnitTests/DataExport/MockSecureVault.swift b/UnitTests/DataExport/MockSecureVault.swift index 5f07ac42f2..97cacafde5 100644 --- a/UnitTests/DataExport/MockSecureVault.swift +++ b/UnitTests/DataExport/MockSecureVault.swift @@ -161,6 +161,10 @@ final class MockSecureVault: AutofillSecureVault { return storedCards } + func creditCardsCount() throws -> Int { + return storedCards.count + } + func creditCardFor(id: Int64) throws -> SecureVaultModels.CreditCard? { return storedCards.first { $0.id == id } } @@ -408,6 +412,10 @@ class MockDatabaseProvider: AutofillDatabaseProvider { return Array(_creditCards.values) } + func creditCardsCount() throws -> Int { + return _creditCards.count + } + func creditCardForCardId(_ cardId: Int64) throws -> SecureVaultModels.CreditCard? { return _creditCards[cardId] }