Skip to content

Commit

Permalink
Add rollout for cookie popup management enabled by default (#2373)
Browse files Browse the repository at this point in the history
  • Loading branch information
dus7 authored Feb 5, 2024
1 parent 1211ccd commit b28a385
Show file tree
Hide file tree
Showing 22 changed files with 74 additions and 445 deletions.
3 changes: 3 additions & 0 deletions Core/FeatureFlag.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public enum FeatureFlag: String {
case networkProtectionWaitlistAccess
case networkProtectionWaitlistActive
case subscription
case autoconsentOnByDefault
}

extension FeatureFlag: FeatureFlagSourceProviding {
Expand Down Expand Up @@ -64,6 +65,8 @@ extension FeatureFlag: FeatureFlagSourceProviding {
return .remoteReleasable(.subfeature(AutofillSubfeature.onByDefault))
case .incontextSignup:
return .remoteReleasable(.feature(.incontextSignup))
case .autoconsentOnByDefault:
return .remoteReleasable(.subfeature(AutoconsentSubfeature.onByDefault))
}
}
}
Expand Down
6 changes: 0 additions & 6 deletions Core/PixelEvent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,6 @@ extension Pixel {
case daxDialogsFireEducationShown
case daxDialogsFireEducationConfirmed
case daxDialogsFireEducationCancelled
case daxDialogsAutoconsentShown
case daxDialogsAutoconsentConfirmed
case daxDialogsAutoconsentCancelled

case defaultBrowserButtonPressedSettings

Expand Down Expand Up @@ -624,9 +621,6 @@ extension Pixel.Event {
case .daxDialogsFireEducationShown: return "m_dx_fe_s"
case .daxDialogsFireEducationConfirmed: return "m_dx_fe_co"
case .daxDialogsFireEducationCancelled: return "m_dx_fe_ca"
case .daxDialogsAutoconsentShown: return "m_dax_dialog_autoconsent_shown"
case .daxDialogsAutoconsentConfirmed: return "m_dax_dialog_autoconsent_confirmed"
case .daxDialogsAutoconsentCancelled: return "m_dax_dialog_autoconsent_cancelled"

case .defaultBrowserButtonPressedSettings: return "m_db_s"

Expand Down
1 change: 0 additions & 1 deletion Core/UserDefaultsPropertyWrapper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,6 @@ public struct UserDefaultsWrapper<T> {

case voiceSearchEnabled = "com.duckduckgo.app.voiceSearchEnabled"

case autoconsentPromptSeen = "com.duckduckgo.ios.autoconsentPromptSeen"
case autoconsentEnabled = "com.duckduckgo.ios.autoconsentEnabled"

case shouldScheduleRulesCompilationOnAppLaunch = "com.duckduckgo.ios.shouldScheduleRulesCompilationOnAppLaunch"
Expand Down
44 changes: 0 additions & 44 deletions DuckDuckGo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -105,17 +105,12 @@
1CB7B82123CEA1F800AA24EA /* DateExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CB7B82023CEA1F800AA24EA /* DateExtension.swift */; };
1CB7B82323CEA28300AA24EA /* DateExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CB7B82223CEA28300AA24EA /* DateExtensionTests.swift */; };
1E016AB42949FEB500F21625 /* OmniBarNotificationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E016AB32949FEB500F21625 /* OmniBarNotificationViewModel.swift */; };
1E016AB6294A5EB100F21625 /* CustomDaxDialog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E016AB5294A5EB100F21625 /* CustomDaxDialog.swift */; };
1E05D1D629C46EBB00BF9A1F /* DailyPixel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E05D1D529C46EBB00BF9A1F /* DailyPixel.swift */; };
1E05D1D829C46EDA00BF9A1F /* TimedPixel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E05D1D729C46EDA00BF9A1F /* TimedPixel.swift */; };
1E05D1DB29C47B3300BF9A1F /* DailyPixelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E05D1D929C47B2B00BF9A1F /* DailyPixelTests.swift */; };
1E0A75EA27A2FBD000A2BFB6 /* Downloads.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1E0A75E927A2FBD000A2BFB6 /* Downloads.storyboard */; };
1E162605296840D80004127F /* Triangle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E162604296840D80004127F /* Triangle.swift */; };
1E1626072968413B0004127F /* ViewExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E1626062968413B0004127F /* ViewExtension.swift */; };
1E16260B296845120004127F /* cookie-banner-illustration-animated.json in Resources */ = {isa = PBXBuildFile; fileRef = 1E162609296845120004127F /* cookie-banner-illustration-animated.json */; };
1E16260C296845120004127F /* cookie-banner-illustration-animated-dark.json in Resources */ = {isa = PBXBuildFile; fileRef = 1E16260A296845120004127F /* cookie-banner-illustration-animated-dark.json */; };
1E162610296C5C630004127F /* CustomDaxDialogViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E16260F296C5C630004127F /* CustomDaxDialogViewModel.swift */; };
1E162613296C62820004127F /* CookieConsentDaxDialogViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E162612296C62820004127F /* CookieConsentDaxDialogViewModel.swift */; };
1E162615296D910F0004127F /* cookie-icon-animated-40-dark.json in Resources */ = {isa = PBXBuildFile; fileRef = 1E162614296D910F0004127F /* cookie-icon-animated-40-dark.json */; };
1E1D8B5D2994FFE100C96994 /* AutoconsentMessageProtocolTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E1D8B5C2994FFE100C96994 /* AutoconsentMessageProtocolTests.swift */; };
1E1D8B6129950FD200C96994 /* AutoconsentBackgroundTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E1D8B6029950FD200C96994 /* AutoconsentBackgroundTests.swift */; };
Expand Down Expand Up @@ -1210,17 +1205,12 @@
1CB7B82023CEA1F800AA24EA /* DateExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateExtension.swift; sourceTree = "<group>"; };
1CB7B82223CEA28300AA24EA /* DateExtensionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateExtensionTests.swift; sourceTree = "<group>"; };
1E016AB32949FEB500F21625 /* OmniBarNotificationViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OmniBarNotificationViewModel.swift; sourceTree = "<group>"; };
1E016AB5294A5EB100F21625 /* CustomDaxDialog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomDaxDialog.swift; sourceTree = "<group>"; };
1E05D1D529C46EBB00BF9A1F /* DailyPixel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DailyPixel.swift; sourceTree = "<group>"; };
1E05D1D729C46EDA00BF9A1F /* TimedPixel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimedPixel.swift; sourceTree = "<group>"; };
1E05D1D929C47B2B00BF9A1F /* DailyPixelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DailyPixelTests.swift; sourceTree = "<group>"; };
1E0A75E927A2FBD000A2BFB6 /* Downloads.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Downloads.storyboard; sourceTree = "<group>"; };
1E162604296840D80004127F /* Triangle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Triangle.swift; sourceTree = "<group>"; };
1E1626062968413B0004127F /* ViewExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewExtension.swift; sourceTree = "<group>"; };
1E162609296845120004127F /* cookie-banner-illustration-animated.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "cookie-banner-illustration-animated.json"; sourceTree = "<group>"; };
1E16260A296845120004127F /* cookie-banner-illustration-animated-dark.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "cookie-banner-illustration-animated-dark.json"; sourceTree = "<group>"; };
1E16260F296C5C630004127F /* CustomDaxDialogViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomDaxDialogViewModel.swift; sourceTree = "<group>"; };
1E162612296C62820004127F /* CookieConsentDaxDialogViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CookieConsentDaxDialogViewModel.swift; sourceTree = "<group>"; };
1E162614296D910F0004127F /* cookie-icon-animated-40-dark.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "cookie-icon-animated-40-dark.json"; sourceTree = "<group>"; };
1E1D8B5C2994FFE100C96994 /* AutoconsentMessageProtocolTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoconsentMessageProtocolTests.swift; sourceTree = "<group>"; };
1E1D8B6029950FD200C96994 /* AutoconsentBackgroundTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoconsentBackgroundTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -3095,16 +3085,6 @@
name = AppTPBreakageForm;
sourceTree = "<group>";
};
1E16260029683B4D0004127F /* CustomDaxDialog */ = {
isa = PBXGroup;
children = (
1E1626082968418F0004127F /* Animations */,
1E162611296C62350004127F /* Model */,
1E016AB5294A5EB100F21625 /* CustomDaxDialog.swift */,
);
name = CustomDaxDialog;
sourceTree = "<group>";
};
1E162603296840790004127F /* SwiftUI */ = {
isa = PBXGroup;
children = (
Expand All @@ -3116,24 +3096,6 @@
name = SwiftUI;
sourceTree = "<group>";
};
1E1626082968418F0004127F /* Animations */ = {
isa = PBXGroup;
children = (
1E16260A296845120004127F /* cookie-banner-illustration-animated-dark.json */,
1E162609296845120004127F /* cookie-banner-illustration-animated.json */,
);
name = Animations;
sourceTree = "<group>";
};
1E162611296C62350004127F /* Model */ = {
isa = PBXGroup;
children = (
1E16260F296C5C630004127F /* CustomDaxDialogViewModel.swift */,
1E162612296C62820004127F /* CookieConsentDaxDialogViewModel.swift */,
);
name = Model;
sourceTree = "<group>";
};
1E162616296D962A0004127F /* Model */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -4941,7 +4903,6 @@
F11CEF581EBB66C80088E4D7 /* Tutorials */ = {
isa = PBXGroup;
children = (
1E16260029683B4D0004127F /* CustomDaxDialog */,
858650CF2469BCC100C36F8A /* DaxOnboarding */,
85EE7F53224667C3000FE757 /* WebContainer */,
85C11E4A209084DE00BFFEB4 /* HomeRow */,
Expand Down Expand Up @@ -6085,7 +6046,6 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
1E16260C296845120004127F /* cookie-banner-illustration-animated-dark.json in Resources */,
AA4D6A8D23DE49A5007E8790 /* [email protected] in Resources */,
F47E53DB250A9A1C0037C686 /* Onboarding.xcassets in Resources */,
AA4D6ACC23DE4D27007E8790 /* [email protected] in Resources */,
Expand Down Expand Up @@ -6148,7 +6108,6 @@
85A313972028E78A00327D00 /* release_notes.txt in Resources */,
9865DFFD22A84CF300D27829 /* FavoriteHomeCell.xib in Resources */,
1EE411FE2858B9300003FE64 /* dark-shield.json in Resources */,
1E16260B296845120004127F /* cookie-banner-illustration-animated.json in Resources */,
AA4D6AD323DE4D27007E8790 /* [email protected] in Resources */,
AA4D6AA123DE4CC4007E8790 /* [email protected] in Resources */,
984147A824F0259000362052 /* Onboarding.storyboard in Resources */,
Expand Down Expand Up @@ -6531,7 +6490,6 @@
D664C7C72B289AA200CBFA76 /* PurchaseInProgressView.swift in Sources */,
D6E83C122B1E6AB3006C8AFB /* SettingsView.swift in Sources */,
F1668BCE1E798081008CBA04 /* BookmarksViewController.swift in Sources */,
1E162610296C5C630004127F /* CustomDaxDialogViewModel.swift in Sources */,
8590CB69268A4E190089F6BF /* DebugEtagStorage.swift in Sources */,
D6D12CA62B291CAA0054390C /* AppStoreRestoreFlow.swift in Sources */,
C1CDA3162AFB9C7F006D1476 /* AutofillNeverPromptWebsitesManager.swift in Sources */,
Expand Down Expand Up @@ -6769,7 +6727,6 @@
98DA6ECA2181E41F00E65433 /* ThemeManager.swift in Sources */,
C159DF072A430B60007834BB /* EmailSignupViewController.swift in Sources */,
37A6A8FE2AFD0208008580A3 /* FaviconsFetcherOnboarding.swift in Sources */,
1E016AB6294A5EB100F21625 /* CustomDaxDialog.swift in Sources */,
02341FA42A437999008A1531 /* OnboardingStepView.swift in Sources */,
F1CA3C3B1F045B65005FADB3 /* Authenticator.swift in Sources */,
CBD4F13D279EBFA000B20FD7 /* HomeMessageCollectionViewCell.swift in Sources */,
Expand Down Expand Up @@ -6958,7 +6915,6 @@
3132FA2827A0788400DD7A12 /* PassKitPreviewHelper.swift in Sources */,
8505836C219F424500ED4EDB /* TextFieldWithInsets.swift in Sources */,
CBD4F13F279EBFAF00B20FD7 /* HomeMessageViewModel.swift in Sources */,
1E162613296C62820004127F /* CookieConsentDaxDialogViewModel.swift in Sources */,
1E4DCF4A27B6A38000961E25 /* DownloadListRepresentable.swift in Sources */,
2DC3FC65C6D9DA634426672D /* AutofillNoAuthAvailableView.swift in Sources */,
);
Expand Down
1 change: 0 additions & 1 deletion DuckDuckGo/AppSettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ protocol AppSettings: AnyObject {

func isWidgetInstalled() async -> Bool

var autoconsentPromptSeen: Bool { get set }
var autoconsentEnabled: Bool { get set }

var isSyncBookmarksPaused: Bool { get }
Expand Down
28 changes: 23 additions & 5 deletions DuckDuckGo/AppUserDefaults.swift
Original file line number Diff line number Diff line change
Expand Up @@ -287,12 +287,30 @@ public class AppUserDefaults: AppSettings {
}
}
}

@UserDefaultsWrapper(key: .autoconsentPromptSeen, defaultValue: false)
var autoconsentPromptSeen: Bool


var autoconsentEnabled: Bool {
get {
// Use settings value if present
if let isEnabled = autoconsentEnabledSetting {
return isEnabled
}

// Use onByDefault rollout otherwise
return featureFlagger.isFeatureOn(.autoconsentOnByDefault)
}

set {
autoconsentEnabledSetting = newValue
}
}

// Only for testing and `DebugViewController` purposes
func clearAutoconsentUserSetting() {
autoconsentEnabledSetting = nil
}

@UserDefaultsWrapper(key: .autoconsentEnabled, defaultValue: false)
var autoconsentEnabled: Bool
private var autoconsentEnabledSetting: Bool?

var inspectableWebViewEnabled: Bool {
get {
Expand Down
1 change: 0 additions & 1 deletion DuckDuckGo/Autoconsent/AutoconsentManagement.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ final class AutoconsentManagement {
private init() {}

var sitesNotifiedCache = Set<String>()
var promptLastShown: Date?

func clearCache() {
dispatchPrecondition(condition: .onQueue(.main))
Expand Down
58 changes: 9 additions & 49 deletions DuckDuckGo/Autoconsent/AutoconsentUserScript.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,13 @@ import PrivacyDashboard
// swiftlint:disable file_length

protocol AutoconsentPreferences {
var autoconsentPromptSeen: Bool { get set }
var autoconsentEnabled: Bool { get set }
}

extension AppUserDefaults: AutoconsentPreferences { }

protocol AutoconsentUserScriptDelegate: AnyObject {
func autoconsentUserScript(_ script: AutoconsentUserScript, didUpdateCookieConsentStatus cookieConsentStatus: CookieConsentInfo)
func autoconsentUserScript(_ script: AutoconsentUserScript, didRequestAskingUserForConsent completion: @escaping (Bool) -> Void)
}

protocol UserScriptWithAutoconsent: UserScript {
Expand Down Expand Up @@ -211,6 +209,12 @@ extension AutoconsentUserScript {
}
}

@MainActor
func handlePopupFound(message: WKScriptMessage, replyHandler: @escaping (Any?, String?) -> Void) {
os_log("Autoconsent popup found", log: .autoconsentLog)
replyHandler([ "type": "ok" ], nil) // this is just to prevent a Promise rejection
}

@MainActor
func handleInit(message: WKScriptMessage, replyHandler: @escaping (Any?, String?) -> Void) {
guard let messageData: InitMessage = decodeMessageBody(from: message.body) else {
Expand All @@ -229,7 +233,7 @@ extension AutoconsentUserScript {
return
}

if preferences.autoconsentPromptSeen == true && preferences.autoconsentEnabled == false {
if preferences.autoconsentEnabled == false {
// this will only happen if the user has just declined a prompt in this tab
replyHandler([ "type": "ok" ], nil) // this is just to prevent a Promise rejection
return
Expand Down Expand Up @@ -260,11 +264,9 @@ extension AutoconsentUserScript {
"rules": nil, // rules are bundled with the content script atm
"config": [
"enabled": true,
// if it's the first time, disable autoAction
"autoAction": preferences.autoconsentPromptSeen ? "optOut" : nil,
"autoAction": "optOut",
"disabledCmps": disabledCMPs,
// the very first time (autoconsentEnabled = nil), make sure the popup is visible
"enablePrehide": preferences.autoconsentPromptSeen,
"enablePrehide": true,
"enableCosmeticRules": true,
"detectRetries": 20,
"isMainWorld": false
Expand Down Expand Up @@ -309,31 +311,7 @@ extension AutoconsentUserScript {
replyHandler(nil, "missing frame target")
}
}

@MainActor
func handlePopupFound(message: WKScriptMessage, replyHandler: @escaping (Any?, String?) -> Void) {
guard preferences.autoconsentPromptSeen == false else {
// if feature is already enabled, opt-out will happen automatically
replyHandler([ "type": "ok" ], nil) // this is just to prevent a Promise rejection
return
}

os_log("Prompting user about autoconsent", log: .autoconsentLog, type: .debug)

// if it's the first time, prompt the user and trigger opt-out
if message.webView?.window != nil {
ensurePrompt(callback: { shouldProceed in
if shouldProceed {
Task {
replyHandler([ "type": "optOut" ], nil)
}
}
})
} else {
replyHandler(nil, "missing frame target")
}
}

@MainActor
func handleOptOutResult(message: WKScriptMessage, replyHandler: @escaping (Any?, String?) -> Void) {
guard let messageData: OptOutResultMessage = decodeMessageBody(from: message.body) else {
Expand Down Expand Up @@ -419,24 +397,6 @@ extension AutoconsentUserScript {
refreshDashboardState(consentManaged: true, cosmetic: nil, optoutFailed: false, selftestFailed: messageData.result)
replyHandler([ "type": "ok" ], nil) // this is just to prevent a Promise rejection
}

@MainActor
func ensurePrompt(callback: @escaping (Bool) -> Void) {
let now = Date.init()
guard management.promptLastShown == nil || now > management.promptLastShown!.addingTimeInterval(1) else {
// user said "not now" recently, don't bother asking
os_log("Have a recent user response, canceling prompt", log: .autoconsentLog, type: .debug)
callback(preferences.autoconsentEnabled) // if two prompts were scheduled from the same tab, result could be true
return
}

management.promptLastShown = now
self.delegate?.autoconsentUserScript(self, didRequestAskingUserForConsent: { result in
self.preferences.autoconsentEnabled = result
self.preferences.autoconsentPromptSeen = true
callback(result)
})
}
}

extension NSNotification.Name {
Expand Down
1 change: 0 additions & 1 deletion DuckDuckGo/AutoconsentSettingsViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ final class AutoconsentSettingsViewController: UITableViewController {

@IBAction private func onAutoconsentValueChanged(_ sender: Any) {
appSettings.autoconsentEnabled = autoconsentToggle.isOn
appSettings.autoconsentPromptSeen = true
Pixel.fire(pixel: autoconsentToggle.isOn ? .settingsAutoconsentOn : .settingsAutoconsentOff)
}

Expand Down
Loading

0 comments on commit b28a385

Please sign in to comment.