From 6e27a6d5eb35e05f23f68d457e20c46ec8d884de Mon Sep 17 00:00:00 2001 From: Sam Symons Date: Thu, 7 Mar 2024 20:53:11 -0800 Subject: [PATCH 01/19] Release 7.111.0-1 (#2561) --- DuckDuckGo.xcodeproj/project.pbxproj | 56 ++++++++++++++-------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 126d8db9f4..118bd1271b 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -8210,7 +8210,7 @@ CODE_SIGN_ENTITLEMENTS = PacketTunnelProvider/PacketTunnelProvider.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; @@ -8247,7 +8247,7 @@ CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -8339,7 +8339,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = ShareExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -8367,7 +8367,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -8517,7 +8517,7 @@ CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGo.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_ASSET_PATHS = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -8543,7 +8543,7 @@ CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGo.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; INFOPLIST_FILE = DuckDuckGo/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -8608,7 +8608,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEAD_CODE_STRIPPING = NO; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = Widgets/Info.plist; @@ -8643,7 +8643,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEAD_CODE_STRIPPING = NO; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -8677,7 +8677,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = OpenAction/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -8708,7 +8708,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -8995,7 +8995,7 @@ CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGoAlpha.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_ASSET_PATHS = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -9026,7 +9026,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = ShareExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -9055,7 +9055,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = OpenAction/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -9089,7 +9089,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEAD_CODE_STRIPPING = NO; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = Widgets/Info.plist; @@ -9120,7 +9120,7 @@ CODE_SIGN_ENTITLEMENTS = PacketTunnelProvider/PacketTunnelProviderAlpha.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; @@ -9153,11 +9153,11 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 0; + DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Core/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -9391,7 +9391,7 @@ CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGoAlpha.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_ASSET_PATHS = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -9418,7 +9418,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -9451,7 +9451,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -9489,7 +9489,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEAD_CODE_STRIPPING = NO; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -9525,7 +9525,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -9560,11 +9560,11 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 0; + DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Core/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -9738,11 +9738,11 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 0; + DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Core/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -9771,10 +9771,10 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 0; + DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Core/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; From 9b5a3fee905698389deeba9afff1e44d1c30d997 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mariusz=20=C5=9Apiewak?= Date: Fri, 8 Mar 2024 12:27:36 +0100 Subject: [PATCH 02/19] Fix blank space after URL bar hides (#2549) --- DuckDuckGo/MainViewController.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/DuckDuckGo/MainViewController.swift b/DuckDuckGo/MainViewController.swift index 160c7ee248..9496a1bfaa 100644 --- a/DuckDuckGo/MainViewController.swift +++ b/DuckDuckGo/MainViewController.swift @@ -1550,6 +1550,11 @@ extension MainViewController: BrowserChromeDelegate { // 1.0 - full size, 0.0 - hidden private func updateToolbarConstant(_ ratio: CGFloat) { var bottomHeight = toolbarHeight + if viewCoordinator.addressBarPosition.isBottom { + // When position is set to bottom, contentContainer is pinned to top + // of navigationBarContainer, hence the adjustment. + bottomHeight += viewCoordinator.navigationBarContainer.frame.height + } bottomHeight += view.safeAreaInsets.bottom let multiplier = viewCoordinator.toolbar.isHidden ? 1.0 : 1.0 - ratio viewCoordinator.constraints.toolbarBottom.constant = bottomHeight * multiplier From b3cdaddfb90c8047636f6e48b8859415b33e6c18 Mon Sep 17 00:00:00 2001 From: Federico Cappelli Date: Fri, 8 Mar 2024 12:44:39 +0000 Subject: [PATCH 03/19] Vanilla browser integration (#2550) Task/Issue URL: https://app.asana.com/0/1199230911884351/1206522891480880/f Tech Design URL: https://app.asana.com/0/0/1206524485561895/f **Description**: Adds a vanilla browser to the debug menu. --- DuckDuckGo.xcodeproj/project.pbxproj | 33 ++++++++-- .../xcshareddata/swiftpm/Package.resolved | 9 +++ DuckDuckGo/Debug.storyboard | 66 ++++++++++--------- DuckDuckGo/MainViewController+Segues.swift | 6 +- ...otDebugViewController+VanillaBrowser.swift | 51 ++++++++++++++ DuckDuckGo/RootDebugViewController.swift | 60 +++++++++-------- DuckDuckGo/SettingsLegacyViewProvider.swift | 16 +++-- 7 files changed, 170 insertions(+), 71 deletions(-) create mode 100644 DuckDuckGo/RootDebugViewController+VanillaBrowser.swift diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index e7ac8930e1..67b2dfb269 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -953,6 +953,8 @@ F1CA3C391F045885005FADB3 /* PrivacyUserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1CA3C381F045885005FADB3 /* PrivacyUserDefaults.swift */; }; F1CA3C3B1F045B65005FADB3 /* Authenticator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1CA3C3A1F045B65005FADB3 /* Authenticator.swift */; }; F1CDD3F21F16911700BE0581 /* AboutViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1CDD3F11F16911700BE0581 /* AboutViewController.swift */; }; + F1D43AFA2B99C1D300BAB743 /* BareBonesBrowserKit in Frameworks */ = {isa = PBXBuildFile; productRef = F1D43AF92B99C1D300BAB743 /* BareBonesBrowserKit */; }; + F1D43AFC2B99C56000BAB743 /* RootDebugViewController+VanillaBrowser.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1D43AFB2B99C56000BAB743 /* RootDebugViewController+VanillaBrowser.swift */; }; F1D477C61F2126CC0031ED49 /* OmniBarState.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1D477C51F2126CC0031ED49 /* OmniBarState.swift */; }; F1D477C91F2139410031ED49 /* SmallOmniBarStateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1D477C81F2139410031ED49 /* SmallOmniBarStateTests.swift */; }; F1D477CB1F2149C40031ED49 /* Type.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1D477CA1F2149C40031ED49 /* Type.swift */; }; @@ -2652,6 +2654,7 @@ F1CA3C3A1F045B65005FADB3 /* Authenticator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Authenticator.swift; sourceTree = ""; }; F1CB8EA21F26B39000A7171B /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; F1CDD3F11F16911700BE0581 /* AboutViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AboutViewController.swift; sourceTree = ""; }; + F1D43AFB2B99C56000BAB743 /* RootDebugViewController+VanillaBrowser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RootDebugViewController+VanillaBrowser.swift"; sourceTree = ""; }; F1D477C51F2126CC0031ED49 /* OmniBarState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OmniBarState.swift; sourceTree = ""; }; F1D477C81F2139410031ED49 /* SmallOmniBarStateTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SmallOmniBarStateTests.swift; sourceTree = ""; }; F1D477CA1F2149C40031ED49 /* Type.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Type.swift; sourceTree = ""; }; @@ -2733,6 +2736,7 @@ 0238E44F29C0FAA100615E30 /* FindInPageIOSJSSupport in Frameworks */, 3760DFED299315EF0045A446 /* Waitlist in Frameworks */, B6A26C042B98358B00DF9EAD /* Macros in Frameworks */, + F1D43AFA2B99C1D300BAB743 /* BareBonesBrowserKit in Frameworks */, F143C2EB1E4A4CD400CFDE3A /* Core.framework in Frameworks */, 4B2754EC29E8C7DF00394032 /* Lottie in Frameworks */, 31E69A63280F4CB600478327 /* DuckUI in Frameworks */, @@ -3783,25 +3787,24 @@ 84E341941E2F7EFB00BDBA6F /* DuckDuckGo */ = { isa = PBXGroup; children = ( - CB48D32F2B90CE8500631D8B /* UserBehaviorMonitor */, - EE3B98EA2A9634CC002F63A0 /* DuckDuckGoAlpha.entitlements */, - CB258D1129A4F1BB00DEBA24 /* Configuration */, 6FD1BAE02B87A0E8000C475C /* AdAttribution */, - 1E908BED29827C480008C8F3 /* Autoconsent */, - 3157B43627F4C8380042D3D7 /* Favicons */, AA4D6A8023DE4973007E8790 /* AppIcon */, F1C5ECF31E37812900C599A4 /* Application */, 02025B0A29884CF300E694E7 /* AppTrackingProtection */, 9817C9C121EF58BA00884F65 /* AutoClear */, F15D43211E70849A00BF2CDC /* Autocomplete */, + 1E908BED29827C480008C8F3 /* Autoconsent */, F44D279327F331930037F371 /* Autofill */, F1668BCC1E798025008CBA04 /* Bookmarks */, 9830A05725ED0C5D00DB64DE /* BrowsingMenu */, + CB258D1129A4F1BB00DEBA24 /* Configuration */, B652DF02287C01EE00C12A9C /* ContentBlocking */, D6E0C1812B7A2B0700D5E1E9 /* DesktopDownloads */, 310D09192799EF5C00DC0060 /* Downloads */, F143C2C51E4A08F300CFDE3A /* DuckDuckGo.entitlements */, + EE3B98EA2A9634CC002F63A0 /* DuckDuckGoAlpha.entitlements */, C159DF052A430B36007834BB /* EmailProtection */, + 3157B43627F4C8380042D3D7 /* Favicons */, 839F119520DBC489007CD8C2 /* Feedback */, 85F2FFFE2215C163006BB258 /* FindInPage */, F13B4BF31F18C73A00814661 /* Home */, @@ -3813,17 +3816,18 @@ 85AE668C20971FCA0014CF04 /* Notifications */, F1C4A70C1E5771F800A6CA1B /* OmniBar */, F1AE54DB1F0425BB00D9A700 /* Privacy */, - D664C7922B289AA000CBFA76 /* Subscription */, F1DF09502B039E6E008CC908 /* PrivacyDashboard */, 02ECEC602A965074009F0654 /* PrivacyInfo.xcprivacy */, C1B7B51D28941F160098FD6A /* RemoteMessaging */, F1AB2B401E3F75A000868554 /* Settings */, 0A6CC0EE23904D5400E4F627 /* Settings.bundle */, + D664C7922B289AA000CBFA76 /* Subscription */, 85F98F8C296F0ED100742F4A /* Sync */, F13B4BF41F18C74500814661 /* Tabs */, F1386BA21E6846320062FC3C /* TabSwitcher */, 98F3A1D6217B36EE0011A0D4 /* Themes */, F11CEF581EBB66C80088E4D7 /* Tutorials */, + CB48D32F2B90CE8500631D8B /* UserBehaviorMonitor */, F1D796ED1E7AE4090019D451 /* UserInterface */, 84E341E31E2FC0E400BDBA6F /* UserInterfaceResources */, 3151F0E827357F8F00226F58 /* VoiceSearch */, @@ -3968,6 +3972,7 @@ 4B0295182537BC6700E00CEF /* ConfigurationDebugViewController.swift */, 858566FA252E55D6007501B8 /* ImageCacheDebugViewController.swift */, 8590CB66268A2E520089F6BF /* RootDebugViewController.swift */, + F1D43AFB2B99C56000BAB743 /* RootDebugViewController+VanillaBrowser.swift */, 8590CB68268A4E190089F6BF /* DebugEtagStorage.swift */, 1EDE39D12705D4A100C99C72 /* FileSizeDebugViewController.swift */, 983D71B02A286E810072E26D /* SyncDebugViewController.swift */, @@ -5792,6 +5797,7 @@ 0238E44E29C0FAA100615E30 /* FindInPageIOSJSSupport */, 4B2754EB29E8C7DF00394032 /* Lottie */, B6A26C032B98358B00DF9EAD /* Macros */, + F1D43AF92B99C1D300BAB743 /* BareBonesBrowserKit */, ); productName = DuckDuckGo; productReference = 84E341921E2F7EFB00BDBA6F /* DuckDuckGo.app */; @@ -6104,6 +6110,7 @@ 4B2754EA29E8C7DF00394032 /* XCRemoteSwiftPackageReference "lottie-ios" */, 854007E52B57FB020001BD98 /* XCRemoteSwiftPackageReference "ZIPFoundation" */, B6F997C22B8F374300476735 /* XCRemoteSwiftPackageReference "apple-toolbox" */, + F1D43AF82B99C1D300BAB743 /* XCRemoteSwiftPackageReference "BareBonesBrowser" */, ); productRefGroup = 84E341931E2F7EFB00BDBA6F /* Products */; projectDirPath = ""; @@ -6830,6 +6837,7 @@ F17922DE1E7192E6006E3D97 /* SuggestionTableViewCell.swift in Sources */, 85DB12ED2A1FED0C000A4A72 /* AppDelegate+AppDeepLinks.swift in Sources */, 98DA6ECA2181E41F00E65433 /* ThemeManager.swift in Sources */, + F1D43AFC2B99C56000BAB743 /* RootDebugViewController+VanillaBrowser.swift in Sources */, C159DF072A430B60007834BB /* EmailSignupViewController.swift in Sources */, 37A6A8FE2AFD0208008580A3 /* FaviconsFetcherOnboarding.swift in Sources */, 02341FA42A437999008A1531 /* OnboardingStepView.swift in Sources */, @@ -10064,6 +10072,14 @@ version = 2.7.0; }; }; + F1D43AF82B99C1D300BAB743 /* XCRemoteSwiftPackageReference "BareBonesBrowser" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/duckduckgo/BareBonesBrowser.git"; + requirement = { + kind = exactVersion; + version = 0.1.0; + }; + }; F42D541B29DCA40B004C4FF1 /* XCRemoteSwiftPackageReference "DesignResourcesKit" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/duckduckgo/DesignResourcesKit"; @@ -10334,6 +10350,11 @@ package = 98A16C2928A11BDE00A6C003 /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; productName = TestUtils; }; + F1D43AF92B99C1D300BAB743 /* BareBonesBrowserKit */ = { + isa = XCSwiftPackageProductDependency; + package = F1D43AF82B99C1D300BAB743 /* XCRemoteSwiftPackageReference "BareBonesBrowser" */; + productName = BareBonesBrowserKit; + }; F42D541C29DCA40B004C4FF1 /* DesignResourcesKit */ = { isa = XCSwiftPackageProductDependency; package = F42D541B29DCA40B004C4FF1 /* XCRemoteSwiftPackageReference "DesignResourcesKit" */; diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 9e06e3edb7..d6347676ad 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -9,6 +9,15 @@ "version" : "2.0.0" } }, + { + "identity" : "barebonesbrowser", + "kind" : "remoteSourceControl", + "location" : "https://github.com/duckduckgo/BareBonesBrowser.git", + "state" : { + "revision" : "31e5bfedc3c2ca005640c4bf2b6959d69b0e18b9", + "version" : "0.1.0" + } + }, { "identity" : "bloom_cpp", "kind" : "remoteSourceControl", diff --git a/DuckDuckGo/Debug.storyboard b/DuckDuckGo/Debug.storyboard index fdab02c1d8..d3940730a7 100644 --- a/DuckDuckGo/Debug.storyboard +++ b/DuckDuckGo/Debug.storyboard @@ -1,9 +1,9 @@ - + - + @@ -21,14 +21,23 @@ - - + + + + + + + + + + + - - - - - + @@ -62,7 +68,7 @@ - + @@ -82,7 +88,7 @@ - + @@ -102,7 +108,7 @@ - + @@ -122,7 +128,7 @@ - + @@ -142,7 +148,7 @@ - + @@ -162,7 +168,7 @@ - + @@ -182,7 +188,7 @@ - + @@ -202,7 +208,7 @@ - + @@ -222,7 +228,7 @@ - + @@ -234,7 +240,7 @@ - + @@ -243,7 +249,7 @@ - + @@ -252,7 +258,7 @@ - + @@ -261,7 +267,7 @@ - + @@ -837,34 +843,34 @@ - + - + - + - + diff --git a/DuckDuckGo/MainViewController+Segues.swift b/DuckDuckGo/MainViewController+Segues.swift index c352df8fb2..c0e6351e14 100644 --- a/DuckDuckGo/MainViewController+Segues.swift +++ b/DuckDuckGo/MainViewController+Segues.swift @@ -245,7 +245,8 @@ extension MainViewController { let legacyViewProvider = SettingsLegacyViewProvider(syncService: syncService, syncDataProviders: syncDataProviders, appSettings: appSettings, - bookmarksDatabase: bookmarksDatabase) + bookmarksDatabase: bookmarksDatabase, + tabManager: tabManager) #if SUBSCRIPTION let settingsViewModel = SettingsViewModel(legacyViewProvider: legacyViewProvider, accountManager: AccountManager()) #else @@ -273,7 +274,8 @@ extension MainViewController { RootDebugViewController(coder: coder, sync: self.syncService, bookmarksDatabase: self.bookmarksDatabase, - internalUserDecider: AppDependencyProvider.shared.internalUserDecider) + internalUserDecider: AppDependencyProvider.shared.internalUserDecider, + tabManager: self.tabManager) } let controller = ThemableNavigationController(rootViewController: settings) diff --git a/DuckDuckGo/RootDebugViewController+VanillaBrowser.swift b/DuckDuckGo/RootDebugViewController+VanillaBrowser.swift new file mode 100644 index 0000000000..58c6c2c5d6 --- /dev/null +++ b/DuckDuckGo/RootDebugViewController+VanillaBrowser.swift @@ -0,0 +1,51 @@ +// +// RootDebugViewController+VanillaBrowser.swift +// DuckDuckGo +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation +import BareBonesBrowserKit +import SwiftUI +import WebKit +import Core +import Common + +extension RootDebugViewController { + + fileprivate static let ddgURL = URL(string: "https://duckduckgo.com/")! + @objc func openVanillaBrowser(_ sender: Any?) { + let homeURL = tabManager?.current()?.tabModel.link?.url ?? RootDebugViewController.ddgURL + openVanillaBrowser(url: homeURL) + } + + static var webViewConfiguration: WKWebViewConfiguration = { + let configuration = WKWebViewConfiguration() + configuration.websiteDataStore = WKWebsiteDataStore.nonPersistent() + configuration.processPool = WKProcessPool() + return configuration + }() + + fileprivate func openVanillaBrowser(url: URL) { + os_log(.debug, "Vanilla Browser open URL %s", url.absoluteString) + let browserView = BareBonesBrowserView(initialURL: url, + homeURL: RootDebugViewController.ddgURL, + uiDelegate: nil, + configuration: Self.webViewConfiguration, + userAgent: DefaultUserAgentManager.duckDuckGoUserAgent) + present(controller: UIHostingController(rootView: browserView), fromView: self.view) + } +} diff --git a/DuckDuckGo/RootDebugViewController.swift b/DuckDuckGo/RootDebugViewController.swift index 12ad6a0618..58d2cff77a 100644 --- a/DuckDuckGo/RootDebugViewController.swift +++ b/DuckDuckGo/RootDebugViewController.swift @@ -35,6 +35,7 @@ class RootDebugViewController: UITableViewController { case crashMemory = 667 case toggleInspectableWebViews = 668 case toggleInternalUserState = 669 + case openVanillaBrowser = 670 } @IBOutlet weak var shareButton: UIBarButtonItem! @@ -48,22 +49,26 @@ class RootDebugViewController: UITableViewController { private var bookmarksDatabase: CoreDataDatabase? private var sync: DDGSyncing? private var internalUserDecider: DefaultInternalUserDecider? + var tabManager: TabManager? init?(coder: NSCoder, sync: DDGSyncing, bookmarksDatabase: CoreDataDatabase, - internalUserDecider: InternalUserDecider) { + internalUserDecider: InternalUserDecider, + tabManager: TabManager) { self.sync = sync self.bookmarksDatabase = bookmarksDatabase self.internalUserDecider = internalUserDecider as? DefaultInternalUserDecider + self.tabManager = tabManager super.init(coder: coder) } - func configure(sync: DDGSyncing, bookmarksDatabase: CoreDataDatabase, internalUserDecider: InternalUserDecider) { + func configure(sync: DDGSyncing, bookmarksDatabase: CoreDataDatabase, internalUserDecider: InternalUserDecider, tabManager: TabManager) { self.sync = sync self.bookmarksDatabase = bookmarksDatabase self.internalUserDecider = internalUserDecider as? DefaultInternalUserDecider + self.tabManager = tabManager } required init?(coder: NSCoder) { @@ -106,37 +111,38 @@ class RootDebugViewController: UITableViewController { } override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - if tableView.cellForRow(at: indexPath)?.tag == Row.crashFatalError.rawValue { - fatalError(#function) - } - if tableView.cellForRow(at: indexPath)?.tag == Row.crashMemory.rawValue { - var arrays = [String]() - while 1 != 2 { - arrays.append(UUID().uuidString) - } - } - - if let cell = tableView.cellForRow(at: indexPath), cell.tag == Row.toggleInspectableWebViews.rawValue { + defer { tableView.deselectRow(at: indexPath, animated: true) - - let defaults = AppUserDefaults() - defaults.inspectableWebViewEnabled.toggle() - cell.accessoryType = defaults.inspectableWebViewEnabled ? .checkmark : .none - NotificationCenter.default.post(Notification(name: AppUserDefaults.Notifications.inspectableWebViewsToggled)) } - if let cell = tableView.cellForRow(at: indexPath), cell.tag == Row.toggleInternalUserState.rawValue { - tableView.deselectRow(at: indexPath, animated: true) - - let newState = !(internalUserDecider?.isInternalUser ?? false) - internalUserDecider?.debugSetInternalUserState(newState) - cell.accessoryType = newState ? .checkmark : .none - NotificationCenter.default.post(Notification(name: AppUserDefaults.Notifications.inspectableWebViewsToggled)) + if let rowTag = tableView.cellForRow(at: indexPath)?.tag, + let row = Row(rawValue: rowTag), + let cell = tableView.cellForRow(at: indexPath) { + + switch row { + case .crashFatalError: + fatalError(#function) + case .crashMemory: + var arrays = [String]() + while 1 != 2 { + arrays.append(UUID().uuidString) + } + case .toggleInspectableWebViews: + let defaults = AppUserDefaults() + defaults.inspectableWebViewEnabled.toggle() + cell.accessoryType = defaults.inspectableWebViewEnabled ? .checkmark : .none + NotificationCenter.default.post(Notification(name: AppUserDefaults.Notifications.inspectableWebViewsToggled)) + case .toggleInternalUserState: + let newState = !(internalUserDecider?.isInternalUser ?? false) + internalUserDecider?.debugSetInternalUserState(newState) + cell.accessoryType = newState ? .checkmark : .none + NotificationCenter.default.post(Notification(name: AppUserDefaults.Notifications.inspectableWebViewsToggled)) + case .openVanillaBrowser: + openVanillaBrowser(nil) + } } - } - } extension RootDebugViewController: DiagnosticReportDataSourceDelegate { diff --git a/DuckDuckGo/SettingsLegacyViewProvider.swift b/DuckDuckGo/SettingsLegacyViewProvider.swift index 025139dc8c..2bdd9740bd 100644 --- a/DuckDuckGo/SettingsLegacyViewProvider.swift +++ b/DuckDuckGo/SettingsLegacyViewProvider.swift @@ -27,20 +27,23 @@ import Persistence import Common class SettingsLegacyViewProvider: ObservableObject { - + let syncService: DDGSyncing let syncDataProviders: SyncDataProviders let appSettings: AppSettings let bookmarksDatabase: CoreDataDatabase - - init(syncService: DDGSyncing, + let tabManager: TabManager + + init(syncService: any DDGSyncing, syncDataProviders: SyncDataProviders, - appSettings: AppSettings, - bookmarksDatabase: CoreDataDatabase) { + appSettings: any AppSettings, + bookmarksDatabase: CoreDataDatabase, + tabManager: TabManager) { self.syncService = syncService self.syncDataProviders = syncDataProviders self.appSettings = appSettings self.bookmarksDatabase = bookmarksDatabase + self.tabManager = tabManager } enum LegacyView { @@ -104,7 +107,8 @@ class SettingsLegacyViewProvider: ObservableObject { if let viewController = storyboard.instantiateViewController(withIdentifier: "DebugMenu") as? RootDebugViewController { viewController.configure(sync: syncService, bookmarksDatabase: bookmarksDatabase, - internalUserDecider: AppDependencyProvider.shared.internalUserDecider) + internalUserDecider: AppDependencyProvider.shared.internalUserDecider, + tabManager: tabManager) return viewController } return UIViewController() From cb0877d0fdf3a06a80893a23277f81acb0481002 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mariusz=20=C5=9Apiewak?= Date: Fri, 8 Mar 2024 16:30:26 +0100 Subject: [PATCH 04/19] Revert "Report Apple Ad attribution using pixel (#2510)" (#2562) --- Core/Pixel.swift | 9 - Core/PixelEvent.swift | 5 - Core/UserDefaultsPropertyWrapper.swift | 3 +- DuckDuckGo.xcodeproj/project.pbxproj | 36 --- .../AdAttribution/AdAttributionFetcher.swift | 159 -------------- .../AdAttributionPixelReporter.swift | 96 -------- .../AdAttributionReporterStorage.swift | 38 ---- DuckDuckGo/AppDelegate.swift | 8 - .../AdAttributionFetcherTests.swift | 183 ---------------- .../AdAttributionPixelReporterTests.swift | 206 ------------------ 10 files changed, 1 insertion(+), 742 deletions(-) delete mode 100644 DuckDuckGo/AdAttribution/AdAttributionFetcher.swift delete mode 100644 DuckDuckGo/AdAttribution/AdAttributionPixelReporter.swift delete mode 100644 DuckDuckGo/AdAttribution/AdAttributionReporterStorage.swift delete mode 100644 DuckDuckGoTests/AdAttributionFetcherTests.swift delete mode 100644 DuckDuckGoTests/AdAttributionPixelReporterTests.swift diff --git a/Core/Pixel.swift b/Core/Pixel.swift index edf6d6611b..33b3ca2214 100644 --- a/Core/Pixel.swift +++ b/Core/Pixel.swift @@ -127,15 +127,6 @@ public struct PixelParameters { public static let returnUserErrorCode = "error_code" public static let returnUserOldATB = "old_atb" public static let returnUserNewATB = "new_atb" - - // Ad Attribution - public static let adAttributionOrgID = "org_id" - public static let adAttributionCampaignID = "campaign_id" - public static let adAttributionConversionType = "conversion_type" - public static let adAttributionAdGroupID = "ad_group_id" - public static let adAttributionCountryOrRegion = "country_or_region" - public static let adAttributionKeywordID = "keyword_id" - public static let adAttributionAdID = "ad_id" } public struct PixelValues { diff --git a/Core/PixelEvent.swift b/Core/PixelEvent.swift index 8925cb5716..a4e38df468 100644 --- a/Core/PixelEvent.swift +++ b/Core/PixelEvent.swift @@ -541,8 +541,6 @@ extension Pixel { case appRatingPromptFetchError - case appleAdAttribution - case userBehaviorReloadTwice case userBehaviorReloadAndRestart case userBehaviorReloadAndFireButton @@ -1059,9 +1057,6 @@ extension Pixel.Event { case .debugReturnUserUpdateATB: return "m_debug_return_user_update_atb" case .appRatingPromptFetchError: return "m_d_app_rating_prompt_fetch_error" - - // MARK: - Apple Ad Attribution - case .appleAdAttribution: return "m_apple-ad-attribution" // MARK: - User behavior case .userBehaviorReloadTwice: return "m_reload-twice" diff --git a/Core/UserDefaultsPropertyWrapper.swift b/Core/UserDefaultsPropertyWrapper.swift index f0515137b2..9ab2131af7 100644 --- a/Core/UserDefaultsPropertyWrapper.swift +++ b/Core/UserDefaultsPropertyWrapper.swift @@ -124,10 +124,9 @@ public struct UserDefaultsWrapper { case subscriptionIsActive = "com.duckduckgo.ios.subscruption.isActive" - case appleAdAttributionReportCompleted = "com.duckduckgo.ios.appleAdAttributionReport.completed" - case didRefreshTimestamp = "com.duckduckgo.ios.userBehavior.didRefreshTimestamp" case didBurnTimestamp = "com.duckduckgo.ios.userBehavior.didBurnTimestamp" + } private let key: Key diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 118bd1271b..ed21691428 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -309,12 +309,7 @@ 4BFB911B29B7D9530014D4B7 /* AppTrackingProtectionStoringModelPerformanceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BFB911A29B7D9530014D4B7 /* AppTrackingProtectionStoringModelPerformanceTests.swift */; }; 6AC6DAB328804F97002723C0 /* BarsAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AC6DAB228804F97002723C0 /* BarsAnimator.swift */; }; 6AC98419288055C1005FA9CA /* BarsAnimatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AC98418288055C1005FA9CA /* BarsAnimatorTests.swift */; }; - 6FD1BAE42B87A107000C475C /* AdAttributionPixelReporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FD1BAE12B87A107000C475C /* AdAttributionPixelReporter.swift */; }; - 6FD1BAE52B87A107000C475C /* AdAttributionReporterStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FD1BAE22B87A107000C475C /* AdAttributionReporterStorage.swift */; }; - 6FD1BAE62B87A107000C475C /* AdAttributionFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FD1BAE32B87A107000C475C /* AdAttributionFetcher.swift */; }; - 6FD3AEE32B8F4EEB0060FCCC /* AdAttributionPixelReporterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FD3AEE12B8DFBB80060FCCC /* AdAttributionPixelReporterTests.swift */; }; 6FDA1FB32B59584400AC962A /* AddressDisplayHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FDA1FB22B59584400AC962A /* AddressDisplayHelper.swift */; }; - 6FF915822B88E07A0042AC87 /* AdAttributionFetcherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FF915802B88E0750042AC87 /* AdAttributionFetcherTests.swift */; }; 83004E802193BB8200DA013C /* WKNavigationExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83004E7F2193BB8200DA013C /* WKNavigationExtension.swift */; }; 83004E862193E5ED00DA013C /* TabViewControllerBrowsingMenuExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83004E852193E5ED00DA013C /* TabViewControllerBrowsingMenuExtension.swift */; }; 83004E882193E8C700DA013C /* TabViewControllerLongPressMenuExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83004E872193E8C700DA013C /* TabViewControllerLongPressMenuExtension.swift */; }; @@ -1404,12 +1399,7 @@ 6AC6DAB228804F97002723C0 /* BarsAnimator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BarsAnimator.swift; sourceTree = ""; }; 6AC98418288055C1005FA9CA /* BarsAnimatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BarsAnimatorTests.swift; sourceTree = ""; }; 6FB030C7234331B400A10DB9 /* Configuration.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Configuration.xcconfig; path = Configuration/Configuration.xcconfig; sourceTree = ""; }; - 6FD1BAE12B87A107000C475C /* AdAttributionPixelReporter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AdAttributionPixelReporter.swift; path = AdAttribution/AdAttributionPixelReporter.swift; sourceTree = ""; }; - 6FD1BAE22B87A107000C475C /* AdAttributionReporterStorage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AdAttributionReporterStorage.swift; path = AdAttribution/AdAttributionReporterStorage.swift; sourceTree = ""; }; - 6FD1BAE32B87A107000C475C /* AdAttributionFetcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AdAttributionFetcher.swift; path = AdAttribution/AdAttributionFetcher.swift; sourceTree = ""; }; - 6FD3AEE12B8DFBB80060FCCC /* AdAttributionPixelReporterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdAttributionPixelReporterTests.swift; sourceTree = ""; }; 6FDA1FB22B59584400AC962A /* AddressDisplayHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddressDisplayHelper.swift; sourceTree = ""; }; - 6FF915802B88E0750042AC87 /* AdAttributionFetcherTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdAttributionFetcherTests.swift; sourceTree = ""; }; 83004E7F2193BB8200DA013C /* WKNavigationExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WKNavigationExtension.swift; sourceTree = ""; }; 83004E832193E14C00DA013C /* UIAlertControllerExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = UIAlertControllerExtension.swift; path = ../Core/UIAlertControllerExtension.swift; sourceTree = ""; }; 83004E852193E5ED00DA013C /* TabViewControllerBrowsingMenuExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabViewControllerBrowsingMenuExtension.swift; sourceTree = ""; }; @@ -3555,25 +3545,6 @@ name = VPN; sourceTree = ""; }; - 6FD1BAE02B87A0E8000C475C /* AdAttribution */ = { - isa = PBXGroup; - children = ( - 6FD1BAE32B87A107000C475C /* AdAttributionFetcher.swift */, - 6FD1BAE12B87A107000C475C /* AdAttributionPixelReporter.swift */, - 6FD1BAE22B87A107000C475C /* AdAttributionReporterStorage.swift */, - ); - name = AdAttribution; - sourceTree = ""; - }; - 6FF9157F2B88E04F0042AC87 /* AdAttribution */ = { - isa = PBXGroup; - children = ( - 6FF915802B88E0750042AC87 /* AdAttributionFetcherTests.swift */, - 6FD3AEE12B8DFBB80060FCCC /* AdAttributionPixelReporterTests.swift */, - ); - name = AdAttribution; - sourceTree = ""; - }; 830FA79B1F8E81FB00FCE105 /* ContentBlocker */ = { isa = PBXGroup; children = ( @@ -3759,7 +3730,6 @@ CB48D32F2B90CE8500631D8B /* UserBehaviorMonitor */, EE3B98EA2A9634CC002F63A0 /* DuckDuckGoAlpha.entitlements */, CB258D1129A4F1BB00DEBA24 /* Configuration */, - 6FD1BAE02B87A0E8000C475C /* AdAttribution */, 1E908BED29827C480008C8F3 /* Autoconsent */, 3157B43627F4C8380042D3D7 /* Favicons */, AA4D6A8023DE4973007E8790 /* AppIcon */, @@ -4966,7 +4936,6 @@ F12D98401F266B30003C2EE3 /* DuckDuckGo */ = { isa = PBXGroup; children = ( - 6FF9157F2B88E04F0042AC87 /* AdAttribution */, CB48D3342B90CEBD00631D8B /* UserBehaviorMonitor */, F17669A21E411D63003D3222 /* Application */, 026F08B629B7DC130079B9DF /* AppTrackingProtection */, @@ -6516,7 +6485,6 @@ 319A371028299A850079FBCE /* PasswordHider.swift in Sources */, 982C87C42255559A00919035 /* UITableViewCellExtension.swift in Sources */, B623C1C42862CD670043013E /* WKDownloadSession.swift in Sources */, - 6FD1BAE42B87A107000C475C /* AdAttributionPixelReporter.swift in Sources */, EEFD562F2A65B6CA00DAEC48 /* NetworkProtectionInviteViewModel.swift in Sources */, 1E8AD1D927C4FEC100ABA377 /* DownloadsListSectioningHelper.swift in Sources */, 1E4DCF4827B6A35400961E25 /* DownloadsListModel.swift in Sources */, @@ -6693,7 +6661,6 @@ 31CB4251273AF50700FA0F3F /* SpeechRecognizerProtocol.swift in Sources */, 319A37172829C8AD0079FBCE /* UITableViewExtension.swift in Sources */, 85EE7F59224673C5000FE757 /* WebContainerNavigationController.swift in Sources */, - 6FD1BAE52B87A107000C475C /* AdAttributionReporterStorage.swift in Sources */, D68A21462B7EC16200BB372E /* SubscriptionExternalLinkViewModel.swift in Sources */, F4C9FBF528340DDA002281CC /* AutofillInterfaceEmailTruncator.swift in Sources */, 1E016AB42949FEB500F21625 /* OmniBarNotificationViewModel.swift in Sources */, @@ -6914,7 +6881,6 @@ 1E4FAA6627D8DFC800ADC5B3 /* CompleteDownloadRowViewModel.swift in Sources */, D6D95CE12B6D52DA00960317 /* RootPresentationMode.swift in Sources */, 83004E862193E5ED00DA013C /* TabViewControllerBrowsingMenuExtension.swift in Sources */, - 6FD1BAE62B87A107000C475C /* AdAttributionFetcher.swift in Sources */, EE01EB402AFBD0000096AAC9 /* NetworkProtectionVPNSettingsViewModel.swift in Sources */, EE72CA852A862D000043B5B3 /* NetworkProtectionDebugViewController.swift in Sources */, C18ED43A2AB6F77600BF3805 /* AutofillSettingsEnableFooterView.swift in Sources */, @@ -6993,7 +6959,6 @@ CBDD5DDF29A6736A00832877 /* APIHeadersTests.swift in Sources */, 986B45D0299E30A50089D2D7 /* BookmarkEntityTests.swift in Sources */, B6AD9E3828D4512E0019CDE9 /* EmbeddedTrackerDataTests.swift in Sources */, - 6FF915822B88E07A0042AC87 /* AdAttributionFetcherTests.swift in Sources */, 1E722729292EB24D003B5F53 /* AppSettingsMock.swift in Sources */, 8536A1C8209AF2410050739E /* MockVariantManager.swift in Sources */, C1B7B53428944EFA0098FD6A /* CoreDataTestUtilities.swift in Sources */, @@ -7070,7 +7035,6 @@ 9847C00527A41A0A00DB07AA /* WebViewTestHelper.swift in Sources */, 3170048227A9504F00C03F35 /* DownloadMocks.swift in Sources */, 317045C02858C6B90016ED1F /* AutofillInterfaceEmailTruncatorTests.swift in Sources */, - 6FD3AEE32B8F4EEB0060FCCC /* AdAttributionPixelReporterTests.swift in Sources */, 987130C6294AAB9F00AB05E0 /* BookmarkListViewModelTests.swift in Sources */, F1134ED21F40EF3A00B73467 /* JsonTestDataLoader.swift in Sources */, 4B83397129AC18C9003F7EA9 /* AppTrackingProtectionStoringModelTests.swift in Sources */, diff --git a/DuckDuckGo/AdAttribution/AdAttributionFetcher.swift b/DuckDuckGo/AdAttribution/AdAttributionFetcher.swift deleted file mode 100644 index a9d3d31a80..0000000000 --- a/DuckDuckGo/AdAttribution/AdAttributionFetcher.swift +++ /dev/null @@ -1,159 +0,0 @@ -// -// AdAttributionFetcher.swift -// DuckDuckGo -// -// Copyright © 2024 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import AdServices -import Common - -protocol AdAttributionFetcher { - func fetch() async -> AdServicesAttributionResponse? -} - -/// Fetches ad attribution data for from Apple. -/// -/// DuckDuckGo uses the AdServices framework to fetch and monitor anonymous install attribution data from Apple. No personally identifiable data is involved. -/// DuckDuckGo does not use the App Tracking Transparency framework at any point, and only uses the “standard” attribution payload. -/// See https://developer.apple.com/documentation/adservices/aaattribution/attributiontoken()#Attribution-payload-descriptions for details. -struct DefaultAdAttributionFetcher: AdAttributionFetcher { - - typealias TokenGetter = () throws -> String - - private let tokenGetter: TokenGetter - private let urlSession: URLSession - private let retryInterval: TimeInterval - - init(tokenGetter: @escaping TokenGetter = Self.fetchAttributionToken, - urlSession: URLSession = .shared, - retryInterval: TimeInterval = .seconds(5)) { - self.tokenGetter = tokenGetter - self.urlSession = urlSession - self.retryInterval = retryInterval - } - - func fetch() async -> AdServicesAttributionResponse? { - guard #available(iOS 14.3, *) else { - return nil - } - - var lastToken: String? - - for _ in 0.. AdServicesAttributionResponse { - let request = createAttributionDataRequest(with: token) - let (data, response) = try await urlSession.data(for: request) - - guard let response = response as? HTTPURLResponse else { - throw AdAttributionFetcherError.invalidResponse - } - - switch response.statusCode { - case 200: - let decoder = JSONDecoder() - let decoded = try decoder.decode(AdServicesAttributionResponse.self, from: data) - - return decoded - case 400: - throw AdAttributionFetcherError.invalidToken - case 404: - throw AdAttributionFetcherError.invalidResponse - default: - throw AdAttributionFetcherError.unknown - } - } - - private func createAttributionDataRequest(with token: String) -> URLRequest { - var request = URLRequest(url: Constant.attributionServiceURL) - request.httpMethod = "POST" - request.setValue("text/plain", forHTTPHeaderField: "Content-Type") - request.httpBody = token.data(using: .utf8) - - return request - } - - private struct Constant { - static let attributionServiceURL = URL(string: "https://api-adservices.apple.com/api/v1/")! - static let maxRetries = 3 - } -} - -extension AdAttributionFetcher { - static func fetchAttributionToken() throws -> String { - if #available(iOS 14.3, *) { - return try AAAttribution.attributionToken() - } else { - throw AdAttributionFetcherError.attributionUnsupported - } - } -} - -struct AdServicesAttributionResponse: Decodable { - let attribution: Bool - let orgId: Int? - let campaignId: Int? - let conversionType: String? - let adGroupId: Int? - let countryOrRegion: String? - let keywordId: Int? - let adId: Int? -} - -enum AdAttributionFetcherError: Error { - case attributionUnsupported - case invalidResponse - case invalidToken - case unknown - - var allowsRetry: Bool { - switch self { - case .invalidToken, .invalidResponse: - return true - case .unknown, .attributionUnsupported: - return false - } - } -} diff --git a/DuckDuckGo/AdAttribution/AdAttributionPixelReporter.swift b/DuckDuckGo/AdAttribution/AdAttributionPixelReporter.swift deleted file mode 100644 index 4ea0faa969..0000000000 --- a/DuckDuckGo/AdAttribution/AdAttributionPixelReporter.swift +++ /dev/null @@ -1,96 +0,0 @@ -// -// AdAttributionPixelReporter.swift -// DuckDuckGo -// -// Copyright © 2024 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import Foundation -import Core - -protocol PixelFiring { - static func fire(pixel: Pixel.Event, withAdditionalParameters params: [String: String]) async throws -} - -final class AdAttributionPixelReporter { - - static var shared = AdAttributionPixelReporter() - - private var fetcherStorage: AdAttributionReporterStorage - private let attributionFetcher: AdAttributionFetcher - private let pixelFiring: PixelFiring.Type - - init(fetcherStorage: AdAttributionReporterStorage = UserDefaultsAdAttributionReporterStorage(), - attributionFetcher: AdAttributionFetcher = DefaultAdAttributionFetcher(), - pixelFiring: PixelFiring.Type = Pixel.self) { - self.fetcherStorage = fetcherStorage - self.attributionFetcher = attributionFetcher - self.pixelFiring = pixelFiring - } - - @discardableResult - func reportAttributionIfNeeded() async -> Bool { - guard await fetcherStorage.wasAttributionReportSuccessful == false else { - return false - } - - if let attributionData = await self.attributionFetcher.fetch() { - if attributionData.attribution { - let parameters = self.pixelParametersForAttribution(attributionData) - do { - try await pixelFiring.fire(pixel: .appleAdAttribution, withAdditionalParameters: parameters) - } catch { - return false - } - } - - await fetcherStorage.markAttributionReportSuccessful() - - return true - } - - return false - } - - private func pixelParametersForAttribution(_ attribution: AdServicesAttributionResponse) -> [String: String] { - var params: [String: String] = [:] - - params[PixelParameters.adAttributionAdGroupID] = attribution.adGroupId.map(String.init) - params[PixelParameters.adAttributionOrgID] = attribution.orgId.map(String.init) - params[PixelParameters.adAttributionCampaignID] = attribution.campaignId.map(String.init) - params[PixelParameters.adAttributionConversionType] = attribution.conversionType - params[PixelParameters.adAttributionAdGroupID] = attribution.adGroupId.map(String.init) - params[PixelParameters.adAttributionCountryOrRegion] = attribution.countryOrRegion - params[PixelParameters.adAttributionKeywordID] = attribution.keywordId.map(String.init) - params[PixelParameters.adAttributionAdID] = attribution.adId.map(String.init) - - return params - } -} - -extension Pixel: PixelFiring { - static func fire(pixel: Event, withAdditionalParameters params: [String: String]) async throws { - - try await withCheckedThrowingContinuation { (continuation: CheckedContinuation) in - Pixel.fire(pixel: pixel, withAdditionalParameters: params) { error in - if let error { - continuation.resume(throwing: error) - } else { - continuation.resume() - } - } - } - } -} diff --git a/DuckDuckGo/AdAttribution/AdAttributionReporterStorage.swift b/DuckDuckGo/AdAttribution/AdAttributionReporterStorage.swift deleted file mode 100644 index c0085e05f9..0000000000 --- a/DuckDuckGo/AdAttribution/AdAttributionReporterStorage.swift +++ /dev/null @@ -1,38 +0,0 @@ -// -// AdAttributionReporterStorage.swift -// DuckDuckGo -// -// Copyright © 2024 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import Core -import Foundation - -protocol AdAttributionReporterStorage { - var wasAttributionReportSuccessful: Bool { get async } - - func markAttributionReportSuccessful() async -} - -final class UserDefaultsAdAttributionReporterStorage: AdAttributionReporterStorage { - @MainActor - @UserDefaultsWrapper(key: .appleAdAttributionReportCompleted, defaultValue: false) - var wasAttributionReportSuccessful: Bool - - @MainActor - func markAttributionReportSuccessful() async { - wasAttributionReportSuccessful = true - } -} diff --git a/DuckDuckGo/AppDelegate.swift b/DuckDuckGo/AppDelegate.swift index 3bd12868ed..1f712dd2c6 100644 --- a/DuckDuckGo/AppDelegate.swift +++ b/DuckDuckGo/AppDelegate.swift @@ -335,8 +335,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate { clearDebugWaitlistState() - reportAdAttribution() - AppDependencyProvider.shared.userBehaviorMonitor.handleAction(.reopenApp) return true @@ -389,12 +387,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } #endif - private func reportAdAttribution() { - Task.detached(priority: .background) { - await AdAttributionPixelReporter.shared.reportAttributionIfNeeded() - } - } - func applicationDidBecomeActive(_ application: UIApplication) { guard !testing else { return } diff --git a/DuckDuckGoTests/AdAttributionFetcherTests.swift b/DuckDuckGoTests/AdAttributionFetcherTests.swift deleted file mode 100644 index 28b9c94541..0000000000 --- a/DuckDuckGoTests/AdAttributionFetcherTests.swift +++ /dev/null @@ -1,183 +0,0 @@ -// -// AdAttributionFetcherTests.swift -// DuckDuckGo -// -// Copyright © 2024 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import XCTest - -@testable import DuckDuckGo -@testable import TestUtils - -final class AdAttributionFetcherTests: XCTestCase { - - private let mockSession: URLSession = { - let configuration = URLSessionConfiguration.default - configuration.protocolClasses = [MockURLProtocol.self] - let session = URLSession(configuration: configuration) - - return session - }() - - override func setUpWithError() throws { - MockURLProtocol.requestHandler = MockURLProtocol.defaultHandler - } - - override func tearDownWithError() throws { - MockURLProtocol.requestHandler = nil - } - - func testMakesRequestWithToken() async throws { - let testToken = "foo" - let sut = DefaultAdAttributionFetcher(tokenGetter: { testToken }, urlSession: mockSession, retryInterval: .leastNonzeroMagnitude) - - _ = await sut.fetch() - - let requestStream = try XCTUnwrap(MockURLProtocol.lastRequest?.httpBodyStream) - let requestBody = try Data(reading: requestStream) - - XCTAssertEqual(String(data: requestBody, encoding: .utf8), testToken) - } - - func testRetriesRequest() async throws { - let testToken = "foo" - let sut = DefaultAdAttributionFetcher(tokenGetter: { testToken }, urlSession: mockSession, retryInterval: .leastNonzeroMagnitude) - let retryExpectation = XCTestExpectation() - retryExpectation.expectedFulfillmentCount = 3 - retryExpectation.assertForOverFulfill = true - - MockURLProtocol.requestHandler = { request in - retryExpectation.fulfill() - let handler = MockURLProtocol.handler(with: 404) - return try handler(request) - } - - _ = await sut.fetch() - - let requestStream = try XCTUnwrap(MockURLProtocol.lastRequest?.httpBodyStream) - let requestBody = try Data(reading: requestStream) - - XCTAssertEqual(String(data: requestBody, encoding: .utf8), testToken) - - await fulfillment(of: [retryExpectation]) - } - - func testRefreshesTokenOnRetry() async throws { - let retryExpectation = XCTestExpectation() - retryExpectation.expectedFulfillmentCount = 3 - retryExpectation.assertForOverFulfill = true - - let refreshExpectation = XCTestExpectation() - - let testToken = "foo" - let sut = DefaultAdAttributionFetcher(tokenGetter: { - refreshExpectation.fulfill() - return testToken - }, urlSession: mockSession, retryInterval: .leastNonzeroMagnitude) - - MockURLProtocol.requestHandler = { request in - retryExpectation.fulfill() - let handler = MockURLProtocol.handler(with: 400) - return try handler(request) - } - - _ = await sut.fetch() - - let requestStream = try XCTUnwrap(MockURLProtocol.lastRequest?.httpBodyStream) - let requestBody = try Data(reading: requestStream) - - XCTAssertEqual(String(data: requestBody, encoding: .utf8), testToken) - - await fulfillment(of: [retryExpectation]) - } - - func testDoesNotRetry_WhenUnrecoverable() async throws { - let testToken = "foo" - let sut = DefaultAdAttributionFetcher(tokenGetter: { testToken }, urlSession: mockSession, retryInterval: .leastNonzeroMagnitude) - let noRetryExpectation = XCTestExpectation() - noRetryExpectation.expectedFulfillmentCount = 1 - noRetryExpectation.assertForOverFulfill = true - - MockURLProtocol.requestHandler = { request in - noRetryExpectation.fulfill() - let handler = MockURLProtocol.handler(with: 500) - return try handler(request) - } - - _ = await sut.fetch() - - let requestStream = try XCTUnwrap(MockURLProtocol.lastRequest?.httpBodyStream) - let requestBody = try Data(reading: requestStream) - - XCTAssertEqual(String(data: requestBody, encoding: .utf8), testToken) - - await fulfillment(of: [noRetryExpectation]) - } - - func testRespectsRetryInterval() async throws { - let testToken = "foo" - let sut = DefaultAdAttributionFetcher(tokenGetter: { testToken }, urlSession: mockSession, retryInterval: .milliseconds(30)) - - MockURLProtocol.requestHandler = { request in - let handler = MockURLProtocol.handler(with: 404) - return try handler(request) - } - - let startTime = Date() - _ = await sut.fetch() - - XCTAssertGreaterThanOrEqual(Date().timeIntervalSince(startTime), .milliseconds(90)) - } -} - -private extension MockURLProtocol { - typealias RequestHandler = (URLRequest) throws -> (HTTPURLResponse, Data?) - - static func handler(with statusCode: Int, data: Data? = nil) -> RequestHandler { - return { request in - (HTTPURLResponse(url: request.url!, statusCode: statusCode, httpVersion: nil, headerFields: nil)!, data) - } - } - - static let defaultHandler = handler(with: 300) -} - -private extension Data { - init(reading input: InputStream, size: Int = 1024) throws { - self.init() - input.open() - defer { - input.close() - } - - let bufferSize = size - let buffer = UnsafeMutablePointer.allocate(capacity: bufferSize) - defer { - buffer.deallocate() - } - while input.hasBytesAvailable { - let read = input.read(buffer, maxLength: bufferSize) - if read < 0 { - // Stream error occured - throw input.streamError! - } else if read == 0 { - // EOF - break - } - self.append(buffer, count: read) - } - } -} diff --git a/DuckDuckGoTests/AdAttributionPixelReporterTests.swift b/DuckDuckGoTests/AdAttributionPixelReporterTests.swift deleted file mode 100644 index 97eef8546e..0000000000 --- a/DuckDuckGoTests/AdAttributionPixelReporterTests.swift +++ /dev/null @@ -1,206 +0,0 @@ -// -// AdAttributionPixelReporterTests.swift -// DuckDuckGo -// -// Copyright © 2024 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import Core -import XCTest - -@testable import DuckDuckGo - -final class AdAttributionPixelReporterTests: XCTestCase { - - private var attributionFetcher: AdAttributionFetcherMock! - private var fetcherStorage: AdAttributionReporterStorageMock! - - override func setUpWithError() throws { - attributionFetcher = AdAttributionFetcherMock() - fetcherStorage = AdAttributionReporterStorageMock() - } - - override func tearDownWithError() throws { - attributionFetcher = nil - fetcherStorage = nil - PixelFiringMock.tearDown() - } - - func testReportsAttribution() async { - let sut = createSUT() - attributionFetcher.fetchResponse = AdServicesAttributionResponse(attribution: true) - - let result = await sut.reportAttributionIfNeeded() - - XCTAssertEqual(PixelFiringMock.lastPixel, .appleAdAttribution) - XCTAssertTrue(result) - } - - func testReportsOnce() async { - let sut = createSUT() - attributionFetcher.fetchResponse = AdServicesAttributionResponse(attribution: true) - - await fetcherStorage.markAttributionReportSuccessful() - let result = await sut.reportAttributionIfNeeded() - - XCTAssertNil(PixelFiringMock.lastPixel) - XCTAssertFalse(result) - } - - func testPixelname() async { - let sut = createSUT() - attributionFetcher.fetchResponse = AdServicesAttributionResponse(attribution: true) - - let result = await sut.reportAttributionIfNeeded() - - XCTAssertEqual(PixelFiringMock.lastPixel?.name, "m_apple-ad-attribution") - XCTAssertTrue(result) - } - - func testPixelAttributesNaming() async throws { - let sut = createSUT() - attributionFetcher.fetchResponse = AdServicesAttributionResponse(attribution: true) - - await sut.reportAttributionIfNeeded() - - let pixelAttributes = try XCTUnwrap(PixelFiringMock.lastParams) - - XCTAssertEqual(pixelAttributes["org_id"], "1") - XCTAssertEqual(pixelAttributes["campaign_id"], "2") - XCTAssertEqual(pixelAttributes["conversion_type"], "conversionType") - XCTAssertEqual(pixelAttributes["ad_group_id"], "3") - XCTAssertEqual(pixelAttributes["country_or_region"], "countryOrRegion") - XCTAssertEqual(pixelAttributes["keyword_id"], "4") - XCTAssertEqual(pixelAttributes["ad_id"], "5") - } - - func testPixelAttributes_WhenPartialAttributionData() async throws { - let sut = createSUT() - attributionFetcher.fetchResponse = AdServicesAttributionResponse( - attribution: true, - orgId: 1, - campaignId: 2, - conversionType: "conversionType", - adGroupId: nil, - countryOrRegion: nil, - keywordId: nil, - adId: nil - ) - - await sut.reportAttributionIfNeeded() - - let pixelAttributes = try XCTUnwrap(PixelFiringMock.lastParams) - - XCTAssertEqual(pixelAttributes["org_id"], "1") - XCTAssertEqual(pixelAttributes["campaign_id"], "2") - XCTAssertEqual(pixelAttributes["conversion_type"], "conversionType") - XCTAssertNil(pixelAttributes["ad_group_id"]) - XCTAssertNil(pixelAttributes["country_or_region"]) - XCTAssertNil(pixelAttributes["keyword_id"]) - XCTAssertNil(pixelAttributes["ad_id"]) - } - - func testPixelNotFiredAndMarksReport_WhenAttributionFalse() async { - let sut = createSUT() - attributionFetcher.fetchResponse = AdServicesAttributionResponse(attribution: false) - - let result = await sut.reportAttributionIfNeeded() - - XCTAssertNil(PixelFiringMock.lastPixel) - XCTAssertTrue(fetcherStorage.wasAttributionReportSuccessful) - XCTAssertTrue(result) - } - - func testPixelNotFiredAndReportNotMarked_WhenAttributionUnavailable() async { - let sut = createSUT() - attributionFetcher.fetchResponse = nil - - let result = await sut.reportAttributionIfNeeded() - - XCTAssertNil(PixelFiringMock.lastPixel) - XCTAssertFalse(fetcherStorage.wasAttributionReportSuccessful) - XCTAssertFalse(result) - } - - func testDoesNotMarkSuccessful_WhenPixelFiringFailed() async { - let sut = createSUT() - attributionFetcher.fetchResponse = AdServicesAttributionResponse(attribution: true) - PixelFiringMock.expectedFireError = NSError(domain: "PixelFailure", code: 1) - - let result = await sut.reportAttributionIfNeeded() - - XCTAssertFalse(fetcherStorage.wasAttributionReportSuccessful) - XCTAssertFalse(result) - } - - private func createSUT() -> AdAttributionPixelReporter { - AdAttributionPixelReporter(fetcherStorage: fetcherStorage, - attributionFetcher: attributionFetcher, - pixelFiring: PixelFiringMock.self) - } -} - -class AdAttributionReporterStorageMock: AdAttributionReporterStorage { - func markAttributionReportSuccessful() async { - wasAttributionReportSuccessful = true - } - - private(set) var wasAttributionReportSuccessful: Bool = false -} - -class AdAttributionFetcherMock: AdAttributionFetcher { - var fetchResponse: AdServicesAttributionResponse? - func fetch() async -> AdServicesAttributionResponse? { - fetchResponse - } -} - -final actor PixelFiringMock: PixelFiring { - static var expectedFireError: Error? - - static var lastParams: [String: String]? - static var lastPixel: Pixel.Event? - static func fire(pixel: Pixel.Event, withAdditionalParameters params: [String: String]) async throws { - lastParams = params - lastPixel = pixel - - if let expectedFireError { - throw expectedFireError - } - } - - static func tearDown() { - lastParams = nil - lastPixel = nil - expectedFireError = nil - } - - private init() {} -} - -extension AdServicesAttributionResponse { - init(attribution: Bool) { - self.init( - attribution: attribution, - orgId: 1, - campaignId: 2, - conversionType: "conversionType", - adGroupId: 3, - countryOrRegion: "countryOrRegion", - keywordId: 4, - adId: 5 - ) - } -} From c5f08434c634e96245512eb0000b27da35e3bfa1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mariusz=20=C5=9Apiewak?= Date: Fri, 8 Mar 2024 16:41:49 +0100 Subject: [PATCH 05/19] Release 7.111.0-2 (#2563) --- DuckDuckGo.xcodeproj/project.pbxproj | 56 ++++++++++++++-------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index ed21691428..e1228c49d7 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -8174,7 +8174,7 @@ CODE_SIGN_ENTITLEMENTS = PacketTunnelProvider/PacketTunnelProvider.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; @@ -8211,7 +8211,7 @@ CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -8303,7 +8303,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = ShareExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -8331,7 +8331,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -8481,7 +8481,7 @@ CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGo.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_ASSET_PATHS = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -8507,7 +8507,7 @@ CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGo.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; INFOPLIST_FILE = DuckDuckGo/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -8572,7 +8572,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEAD_CODE_STRIPPING = NO; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = Widgets/Info.plist; @@ -8607,7 +8607,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEAD_CODE_STRIPPING = NO; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -8641,7 +8641,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = OpenAction/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -8672,7 +8672,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -8959,7 +8959,7 @@ CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGoAlpha.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_ASSET_PATHS = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -8990,7 +8990,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = ShareExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -9019,7 +9019,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = OpenAction/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -9053,7 +9053,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEAD_CODE_STRIPPING = NO; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = Widgets/Info.plist; @@ -9084,7 +9084,7 @@ CODE_SIGN_ENTITLEMENTS = PacketTunnelProvider/PacketTunnelProviderAlpha.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; @@ -9117,11 +9117,11 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; + DYLIB_CURRENT_VERSION = 2; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Core/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -9355,7 +9355,7 @@ CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGoAlpha.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_ASSET_PATHS = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -9382,7 +9382,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -9415,7 +9415,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -9453,7 +9453,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEAD_CODE_STRIPPING = NO; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -9489,7 +9489,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -9524,11 +9524,11 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; + DYLIB_CURRENT_VERSION = 2; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Core/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -9702,11 +9702,11 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; + DYLIB_CURRENT_VERSION = 2; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Core/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -9735,10 +9735,10 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; + DYLIB_CURRENT_VERSION = 2; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Core/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; From 9beb215a40cdd7de2760bddb9c3c64438fec3e22 Mon Sep 17 00:00:00 2001 From: Daniel Bernal Date: Fri, 8 Mar 2024 18:26:46 +0100 Subject: [PATCH 06/19] 18. Subscription Entitlements caching (#2556) Task/Issue URL: https://app.asana.com/0/1204099484721401/1206574474409573/f Description: This PR just bumps BSK (No changes required on iOS) MainActor change has no relation to subscriptions - iOS app was broken in main because of a change in BSK, this fixes that --- DuckDuckGo.xcodeproj/project.pbxproj | 2 +- .../project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 4 ++-- DuckDuckGo/SettingsViewModel.swift | 4 +++- DuckDuckGo/SubscriptionDebugViewController.swift | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index bdda85746f..87aa6f9bf9 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -10017,7 +10017,7 @@ repositoryURL = "https://github.com/DuckDuckGo/BrowserServicesKit"; requirement = { kind = exactVersion; - version = 119.0.1; + version = 119.1.0; }; }; B6F997C22B8F374300476735 /* XCRemoteSwiftPackageReference "apple-toolbox" */ = { diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index d6347676ad..0529cdbeb6 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" : "a32ff4084e910354bdd948812793de6b04804a1b", - "version" : "119.0.1" + "revision" : "38f51b83343d533dab2b9280a9618462149be4c8", + "version" : "119.1.0" } }, { diff --git a/DuckDuckGo/SettingsViewModel.swift b/DuckDuckGo/SettingsViewModel.swift index 1e31387cd3..50ee464566 100644 --- a/DuckDuckGo/SettingsViewModel.swift +++ b/DuckDuckGo/SettingsViewModel.swift @@ -357,7 +357,7 @@ extension SettingsViewModel { cacheSubscriptionState(active: true) // Check entitlements and update UI accordingly - let entitlements: [AccountManager.Entitlement] = [.identityTheftRestoration, .dataBrokerProtection, .networkProtection] + let entitlements: [Entitlement.ProductName] = [.identityTheftRestoration, .dataBrokerProtection, .networkProtection] for entitlement in entitlements { if case .success = await AccountManager().hasEntitlement(for: entitlement) { switch entitlement { @@ -367,6 +367,8 @@ extension SettingsViewModel { self.shouldShowDBP = true case .networkProtection: self.shouldShowNetP = true + case .unknown: + return } } } diff --git a/DuckDuckGo/SubscriptionDebugViewController.swift b/DuckDuckGo/SubscriptionDebugViewController.swift index 27c4b67c96..7fd5c9a032 100644 --- a/DuckDuckGo/SubscriptionDebugViewController.swift +++ b/DuckDuckGo/SubscriptionDebugViewController.swift @@ -238,7 +238,7 @@ final class SubscriptionDebugViewController: UITableViewController { showAlert(title: "Not authenticated", message: "No authenticated user found! - Subscription not available") return } - let entitlements: [AccountManager.Entitlement] = [.networkProtection, .dataBrokerProtection, .identityTheftRestoration] + let entitlements: [Entitlement.ProductName] = [.networkProtection, .dataBrokerProtection, .identityTheftRestoration] for entitlement in entitlements { if case let .success(result) = await AccountManager().hasEntitlement(for: entitlement) { let resultSummary = "Entitlement check for \(entitlement.rawValue): \(result)" From 92ee914eec16e1fc33af6e3082b8ef55fa1bc89f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 8 Mar 2024 18:19:53 +0000 Subject: [PATCH 07/19] Bump submodules/privacy-reference-tests from `40ce868` to `a603ff9` (#2500) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- submodules/privacy-reference-tests | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submodules/privacy-reference-tests b/submodules/privacy-reference-tests index 6b7ad1e7f1..a603ff9af2 160000 --- a/submodules/privacy-reference-tests +++ b/submodules/privacy-reference-tests @@ -1 +1 @@ -Subproject commit 6b7ad1e7f15270f9dfeb58a272199f4d57c3eb22 +Subproject commit a603ff9af22ca3ff7ce2e7ffbfe18c447d9f23e8 From 072cbaf06c5d6d35d7ef795411f7acacfa75dc7d Mon Sep 17 00:00:00 2001 From: Sam Symons Date: Sun, 10 Mar 2024 15:06:33 -0700 Subject: [PATCH 08/19] Fix VPN view model memory leak (#2570) Task/Issue URL: https://app.asana.com/0/414235014887631/1206803287661273/f Tech Design URL: CC: @graeme Description: This PR fixes a view model memory leak in the VPN code. --- DuckDuckGo/NetworkProtectionStatusViewModel.swift | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/DuckDuckGo/NetworkProtectionStatusViewModel.swift b/DuckDuckGo/NetworkProtectionStatusViewModel.swift index ab3ca1f57b..eb603f6162 100644 --- a/DuckDuckGo/NetworkProtectionStatusViewModel.swift +++ b/DuckDuckGo/NetworkProtectionStatusViewModel.swift @@ -37,7 +37,6 @@ final class NetworkProtectionStatusViewModel: ObservableObject { private let serverInfoObserver: ConnectionServerInfoObserver private let errorObserver: ConnectionErrorObserver private var cancellables: Set = [] - private var delayedToggleReenableCancellable: Cancellable? // MARK: Error @@ -158,12 +157,11 @@ final class NetworkProtectionStatusViewModel: ObservableObject { // Each event cancels the previous delayed publisher $shouldDisableToggle .filter { $0 } - .map { - Just(!$0) - .delay(for: 2.0, scheduler: DispatchQueue.main) - .assign(to: \.shouldDisableToggle, onWeaklyHeld: self) + .map { _ -> AnyPublisher in + Just(false).delay(for: 2.0, scheduler: DispatchQueue.main).eraseToAnyPublisher() } - .assign(to: \.delayedToggleReenableCancellable, onWeaklyHeld: self) + .switchToLatest() + .assign(to: \.shouldDisableToggle, onWeaklyHeld: self) .store(in: &cancellables) } From 0576c795c450986503afabb6297b3128b01beb8b Mon Sep 17 00:00:00 2001 From: Anh Do <18567+quanganhdo@users.noreply.github.com> Date: Sun, 10 Mar 2024 23:31:21 -0400 Subject: [PATCH 09/19] Move vpnFirstEnabled and networkPathChange out of VPNSettings (#2560) --- DuckDuckGo.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/swiftpm/Package.resolved | 4 ++-- DuckDuckGo/Feedback/VPNMetadataCollector.swift | 7 +++++-- DuckDuckGo/NetworkProtectionTunnelController.swift | 2 +- .../NetworkProtectionPacketTunnelProvider.swift | 6 +++--- 5 files changed, 12 insertions(+), 9 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 87aa6f9bf9..2700fe8674 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -10017,7 +10017,7 @@ repositoryURL = "https://github.com/DuckDuckGo/BrowserServicesKit"; requirement = { kind = exactVersion; - version = 119.1.0; + version = 120.0.0; }; }; B6F997C22B8F374300476735 /* XCRemoteSwiftPackageReference "apple-toolbox" */ = { diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 0529cdbeb6..4afb646162 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" : "38f51b83343d533dab2b9280a9618462149be4c8", - "version" : "119.1.0" + "revision" : "cea7c43e5ab1d7484ab29abeda429350ed8a7dc1", + "version" : "120.0.0" } }, { diff --git a/DuckDuckGo/Feedback/VPNMetadataCollector.swift b/DuckDuckGo/Feedback/VPNMetadataCollector.swift index 06ee3a44c2..9d04ef794f 100644 --- a/DuckDuckGo/Feedback/VPNMetadataCollector.swift +++ b/DuckDuckGo/Feedback/VPNMetadataCollector.swift @@ -99,13 +99,16 @@ final class DefaultVPNMetadataCollector: VPNMetadataCollector { private let statusObserver: ConnectionStatusObserver private let serverInfoObserver: ConnectionServerInfoObserver private let settings: VPNSettings + private let defaults: UserDefaults init(statusObserver: ConnectionStatusObserver = ConnectionStatusObserverThroughSession(), serverInfoObserver: ConnectionServerInfoObserver = ConnectionServerInfoObserverThroughSession(), - settings: VPNSettings = .init(defaults: .networkProtectionGroupDefaults)) { + settings: VPNSettings = .init(defaults: .networkProtectionGroupDefaults), + defaults: UserDefaults = .networkProtectionGroupDefaults) { self.statusObserver = statusObserver self.serverInfoObserver = serverInfoObserver self.settings = settings + self.defaults = defaults } func collectMetadata() async -> VPNMetadata { @@ -149,7 +152,7 @@ final class DefaultVPNMetadataCollector: VPNMetadataCollector { dateFormatter.timeZone = TimeZone(secondsFromGMT: 0) dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss" - let networkPathChange = settings.networkPathChange + let networkPathChange = defaults.networkPathChange let lastPathChange = String(describing: networkPathChange) var lastPathChangeDate = "unknown" diff --git a/DuckDuckGo/NetworkProtectionTunnelController.swift b/DuckDuckGo/NetworkProtectionTunnelController.swift index 2ba3add399..24e993cb86 100644 --- a/DuckDuckGo/NetworkProtectionTunnelController.swift +++ b/DuckDuckGo/NetworkProtectionTunnelController.swift @@ -146,7 +146,7 @@ final class NetworkProtectionTunnelController: TunnelController { try tunnelManager.connection.startVPNTunnel(options: options) UniquePixel.fire(pixel: .networkProtectionNewUser) { error in guard error != nil else { return } - VPNSettings(defaults: .networkProtectionGroupDefaults).vpnFirstEnabled = Pixel.Event.networkProtectionNewUser.lastFireDate( + UserDefaults.networkProtectionGroupDefaults.vpnFirstEnabled = Pixel.Event.networkProtectionNewUser.lastFireDate( uniquePixelStorage: UniquePixel.storage ) } diff --git a/PacketTunnelProvider/NetworkProtection/NetworkProtectionPacketTunnelProvider.swift b/PacketTunnelProvider/NetworkProtection/NetworkProtectionPacketTunnelProvider.swift index 4a20ed31eb..f4a05f4059 100644 --- a/PacketTunnelProvider/NetworkProtection/NetworkProtectionPacketTunnelProvider.swift +++ b/PacketTunnelProvider/NetworkProtection/NetworkProtectionPacketTunnelProvider.swift @@ -38,12 +38,12 @@ final class NetworkProtectionPacketTunnelProvider: PacketTunnelProvider { // MARK: - PacketTunnelProvider.Event reporting private static var packetTunnelProviderEvents: EventMapping = .init { event, _, _, _ in - let settings = VPNSettings(defaults: .networkProtectionGroupDefaults) + let defaults = UserDefaults.networkProtectionGroupDefaults switch event { case .userBecameActive: DailyPixel.fire(pixel: .networkProtectionActiveUser, - withAdditionalParameters: ["cohort": UniquePixel.dateString(for: settings.vpnFirstEnabled)]) + withAdditionalParameters: ["cohort": UniquePixel.dateString(for: defaults.vpnFirstEnabled)]) case .reportConnectionAttempt(attempt: let attempt): switch attempt { case .connecting: @@ -60,7 +60,7 @@ final class NetworkProtectionPacketTunnelProvider: PacketTunnelProvider { case .failureRecovered: DailyPixel.fireDailyAndCount(pixel: .networkProtectionTunnelFailureRecovered) case .networkPathChanged(let newPath): - settings.apply(change: .setNetworkPathChange(newPath)) + defaults.updateNetworkPath(with: newPath) } case .reportLatency(result: let result): switch result { From 861dd2c6bc5c9fe44872409dd5de6425d9399d2d Mon Sep 17 00:00:00 2001 From: Elle Sullivan Date: Mon, 11 Mar 2024 09:44:58 +0000 Subject: [PATCH 10/19] Update SwiftSoup and Kingfisher versions (#2566) --- DuckDuckGo.xcodeproj/project.pbxproj | 2 +- .../project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 2700fe8674..a8dc2102a3 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -10033,7 +10033,7 @@ repositoryURL = "https://github.com/scinfu/SwiftSoup"; requirement = { kind = exactVersion; - version = 2.7.0; + version = 2.7.1; }; }; F1D43AF82B99C1D300BAB743 /* XCRemoteSwiftPackageReference "BareBonesBrowser" */ = { diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 4afb646162..5a5344ce43 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -167,8 +167,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/scinfu/SwiftSoup", "state" : { - "revision" : "f83c097597094a04124eb6e0d1e894d24129af87", - "version" : "2.7.0" + "revision" : "1d39e56d364cba79ce43b341f9661b534cccb18d", + "version" : "2.7.1" } }, { From 3e69aa52386ee609e2fb53bb2dd14bfdd0c8063a Mon Sep 17 00:00:00 2001 From: Federico Cappelli Date: Mon, 11 Mar 2024 10:49:48 +0000 Subject: [PATCH 11/19] Subscription pro pixels (#2531) Task/Issue URL: https://app.asana.com/0/1200019156869587/1205469290776415/f Description: Pixels added to all privacy pro subscription flows. --- Core/AppLastCompiledRulesStore.swift | 20 +- .../AppPrivacyConfigurationDataProvider.swift | 2 +- Core/AppTrackerDataSetProvider.swift | 2 +- .../AppTrackingProtectionAllowlistModel.swift | 30 +- Core/AppTrackingProtectionFeedbackModel.swift | 6 +- Core/AppTrackingProtectionListViewModel.swift | 22 +- Core/AtbAndVariantCleanup.swift | 4 +- Core/BookmarkObjects.swift | 12 +- Core/BookmarksCachingSearch.swift | 62 ++-- Core/BookmarksCoreDataStorage.swift | 90 +++--- Core/BookmarksExporter.swift | 2 +- Core/BookmarksModelsErrorHandling.swift | 20 +- Core/Configuration.swift | 2 +- Core/ContentBlockerRulesLists.swift | 12 +- Core/ContentBlocking.swift | 47 ++- Core/CookieStorage.swift | 34 +-- Core/DDGPersistenceContainer.swift | 4 +- Core/DailyPixel.swift | 22 +- Core/Database.swift | 12 +- Core/Debounce.swift | 8 +- Core/DebugUserScript.swift | 28 +- Core/DefaultVariantManager.swift | 38 +-- Core/EtagStorage.swift | 14 +- Core/FaviconSourcesProvider.swift | 20 +- Core/FaviconUserScript.swift | 18 +- Core/FileStore.swift | 16 +- Core/HTTPSUpgradeParser.swift | 6 +- Core/HistoryManager.swift | 2 +- Core/Instruments.swift | 16 +- Core/InternalUserStore.swift | 2 +- Core/LegacyBookmarksStoreMigration.swift | 62 ++-- Core/Link.swift | 6 +- Core/LocaleExtension.swift | 2 +- Core/LoginFormDetectionUserScript.swift | 14 +- Core/NSAttributedStringExtension.swift | 6 +- Core/NSManagedObjectContextExtension.swift | 8 +- Core/NavigatorSharePatchUserScript.swift | 10 +- Core/NotFoundCachingDownloader.swift | 4 +- Core/Pixel.swift | 38 +-- Core/PixelEvent.swift | 281 +++++++++++------- Core/PreserveLogins.swift | 10 +- Core/PrivacyFeatures.swift | 2 +- Core/ReturnUserMeasurement.swift | 2 +- Core/SchemeHandler.swift | 10 +- Core/StatisticsLoader.swift | 30 +- Core/StatisticsUserDefaults.swift | 4 +- Core/StorageCache.swift | 8 +- Core/StringExtension.swift | 8 +- Core/TabInstrumentation.swift | 36 +-- Core/TextFieldWithInsets.swift | 4 +- Core/TextSizeUserScript.swift | 12 +- Core/TimeIntervalExtension.swift | 2 +- Core/TimedPixel.swift | 8 +- Core/UIColorExtension.swift | 36 +-- Core/UIKeyCommandExtension.swift | 4 +- Core/UIViewControllerExtension.swift | 12 +- Core/UIViewExtension.swift | 4 +- Core/URLFileExtension.swift | 4 +- Core/UserAgentManager.swift | 49 ++- Core/UserDefaultsExtension.swift | 2 +- Core/UserDefaultsPropertyWrapper.swift | 28 +- Core/WebCacheManager.swift | 32 +- Core/global.swift | 4 +- DuckDuckGo.xcodeproj/project.pbxproj | 2 + .../xcschemes/DuckDuckGo-Alpha.xcscheme | 4 +- DuckDuckGo/AppDelegate.swift | 21 ++ DuckDuckGo/SettingsViewModel.swift | 1 + ...scriptionPagesUseSubscriptionFeature.swift | 31 +- .../SubscriptionPagesUserScript.swift | 3 + .../SubscriptionEmailViewModel.swift | 5 +- .../ViewModel/SubscriptionFlowViewModel.swift | 41 ++- .../ViewModel/SubscriptionITPViewModel.swift | 1 + .../ViewModel/SubscriptionPIRViewModel.swift | 3 + .../SubscriptionRestoreViewModel.swift | 10 +- .../SubscriptionSettingsViewModel.swift | 3 +- .../Views/PurchaseInProgressView.swift | 15 +- .../Views/SubscriptionEmailView.swift | 10 + .../Views/SubscriptionFlowView.swift | 44 +-- .../Views/SubscriptionITPView.swift | 9 + .../Views/SubscriptionPIRView.swift | 13 +- .../Views/SubscriptionRestoreView.swift | 26 +- .../Views/SubscriptionSettingsView.swift | 30 +- DuckDuckGo/VPNWaitlistView.swift | 7 + submodules/privacy-reference-tests | 2 +- 84 files changed, 916 insertions(+), 680 deletions(-) diff --git a/Core/AppLastCompiledRulesStore.swift b/Core/AppLastCompiledRulesStore.swift index 05a65d3cb4..100d52239e 100644 --- a/Core/AppLastCompiledRulesStore.swift +++ b/Core/AppLastCompiledRulesStore.swift @@ -22,12 +22,12 @@ import BrowserServicesKit import TrackerRadarKit struct AppLastCompiledRules: LastCompiledRules, Codable { - + var name: String var trackerData: TrackerData var etag: String var identifier: ContentBlockerRulesIdentifier - + } protocol Storage { @@ -36,7 +36,7 @@ protocol Storage { } struct LastCompiledRulesStorage: Storage { - + private enum Const { static let filename = "LastCompiledRules" static let path = FileManager @@ -44,7 +44,7 @@ struct LastCompiledRulesStorage: Storage { .containerURL(forSecurityApplicationGroupIdentifier: ContentBlockerStoreConstants.groupName)! .appendingPathComponent(filename) } - + func persist(_ data: Data) -> Bool { do { try data.write(to: Const.path, options: .atomic) @@ -53,7 +53,7 @@ struct LastCompiledRulesStorage: Storage { return false } } - + var data: Data? { do { return try Data(contentsOf: Const.path) @@ -61,17 +61,17 @@ struct LastCompiledRulesStorage: Storage { return nil } } - + } final class AppLastCompiledRulesStore: LastCompiledRulesStore { - + private var storage: Storage init(with storage: Storage = LastCompiledRulesStorage()) { self.storage = storage } - + var rules: [LastCompiledRules] { guard let data = storage.data, @@ -88,10 +88,10 @@ final class AppLastCompiledRulesStore: LastCompiledRulesStore { etag: rules.etag, identifier: rules.identifier) } - + if !rules.isEmpty, let encodedRules = try? JSONEncoder().encode(rules) { _ = storage.persist(encodedRules) } } - + } diff --git a/Core/AppPrivacyConfigurationDataProvider.swift b/Core/AppPrivacyConfigurationDataProvider.swift index 37ce38ac13..4f17bd9b2e 100644 --- a/Core/AppPrivacyConfigurationDataProvider.swift +++ b/Core/AppPrivacyConfigurationDataProvider.swift @@ -39,7 +39,7 @@ final public class AppPrivacyConfigurationDataProvider: EmbeddedDataProvider { if let url = Bundle.main.url(forResource: "ios-config", withExtension: "json") { return url } - + return Bundle(for: self).url(forResource: "ios-config", withExtension: "json")! } diff --git a/Core/AppTrackerDataSetProvider.swift b/Core/AppTrackerDataSetProvider.swift index e4041d3cad..cf23ed6b99 100644 --- a/Core/AppTrackerDataSetProvider.swift +++ b/Core/AppTrackerDataSetProvider.swift @@ -39,7 +39,7 @@ final public class AppTrackerDataSetProvider: EmbeddedDataProvider { if let url = Bundle.main.url(forResource: "trackerData", withExtension: "json") { return url } - + return Bundle(for: Self.self).url(forResource: "trackerData", withExtension: "json")! } diff --git a/Core/AppTrackingProtectionAllowlistModel.swift b/Core/AppTrackingProtectionAllowlistModel.swift index 0698cfba3f..cdbd6f1917 100644 --- a/Core/AppTrackingProtectionAllowlistModel.swift +++ b/Core/AppTrackingProtectionAllowlistModel.swift @@ -24,28 +24,28 @@ public class AppTrackingProtectionAllowlistModel { public static let groupID = "\(Global.groupIdPrefix).apptp" public static let fileName = "appTPallowlist" } - + private let filename: String - + lazy private var allowlistUrl: URL? = { let groupContainerUrl = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: Constants.groupID) return groupContainerUrl?.appendingPathComponent(self.filename, conformingTo: .text) }() - + var allowedDomains: Set - + public init(filename: String = Constants.fileName) { self.allowedDomains = Set() self.filename = filename - + readFromFile() } - + func writeToFile() { guard let allowlistUrl = allowlistUrl else { fatalError("Unable to get file location") } - + // Write the allowlist as a textfile with one domain per line do { let string = allowedDomains.joined(separator: "\n") @@ -59,7 +59,7 @@ public class AppTrackingProtectionAllowlistModel { print(error.localizedDescription) } } - + public func readFromFile() { guard let allowlistUrl = allowlistUrl else { fatalError("Unable to get file location") @@ -67,7 +67,7 @@ public class AppTrackingProtectionAllowlistModel { guard FileManager.default.fileExists(atPath: allowlistUrl.path) else { return } - + // Read allowlist from file. Break the string into array then cast to a set. do { let strData = try String(contentsOf: allowlistUrl) @@ -77,30 +77,30 @@ public class AppTrackingProtectionAllowlistModel { print(error.localizedDescription) } } - + public func allow(domain: String) { allowedDomains.insert(domain) writeToFile() } - + public func contains(domain: String) -> Bool { var check = domain while check.contains(".") { if allowedDomains.contains(check) { return true } - + check = String(check.components(separatedBy: ".").dropFirst().joined(separator: ".")) } - + return false } - + public func remove(domain: String) { allowedDomains.remove(domain) writeToFile() } - + public func clearList() { allowedDomains.removeAll() writeToFile() diff --git a/Core/AppTrackingProtectionFeedbackModel.swift b/Core/AppTrackingProtectionFeedbackModel.swift index bfa9243050..98a26c8b81 100644 --- a/Core/AppTrackingProtectionFeedbackModel.swift +++ b/Core/AppTrackingProtectionFeedbackModel.swift @@ -41,19 +41,19 @@ public class AppTrackingProtectionFeedbackModel: ObservableObject { return [] } } - + public func sendReport(appName: String, category: String, description: String) { let date = Calendar.current.date(byAdding: .minute, value: -10, to: Date())! let trackers = trackers(moreRecentThan: date) let trackersString = trackers.map { $0.domain }.joined(separator: ",") - + let parameters = [ "appName": appName, "category": category, "description": description, "blockedTrackers": trackersString ] - + Pixel.fire(pixel: .appTPBreakageReport, withAdditionalParameters: parameters) } diff --git a/Core/AppTrackingProtectionListViewModel.swift b/Core/AppTrackingProtectionListViewModel.swift index 7ff61c28e1..c77ed22884 100644 --- a/Core/AppTrackingProtectionListViewModel.swift +++ b/Core/AppTrackingProtectionListViewModel.swift @@ -33,7 +33,7 @@ public class AppTrackingProtectionListViewModel: NSObject, ObservableObject, NSF @Published public var debugModeEnabled = false @Published public var isOnboarding = false - + // We only want to show "Manage Trackers" and "Report an issue" if the user has enabled AppTP at least once @UserDefaultsWrapper(key: .appTPUsed, defaultValue: false) public var appTPUsed { @@ -49,7 +49,7 @@ public class AppTrackingProtectionListViewModel: NSObject, ObservableObject, NSF queue.maxConcurrentOperationCount = 1 return queue }() - + private let relativeFormatter: DateFormatter = { let formatter = DateFormatter() formatter.timeStyle = .none @@ -57,13 +57,13 @@ public class AppTrackingProtectionListViewModel: NSObject, ObservableObject, NSF formatter.doesRelativeDateFormatting = true return formatter }() - + private let relativeTimeFormatter: RelativeDateTimeFormatter = { let formatter = RelativeDateTimeFormatter() formatter.unitsStyle = .short return formatter }() - + private let dateFormatter: DateFormatter = { let formatter = DateFormatter() formatter.dateFormat = "MMMM dd" @@ -76,26 +76,26 @@ public class AppTrackingProtectionListViewModel: NSObject, ObservableObject, NSF formatter.timeStyle = .medium return formatter }() - + private let inputFormatter: DateFormatter = { let formatter = DateFormatter() formatter.dateFormat = "yyyy-MM-dd" return formatter }() - + public func formattedDate(_ sectionName: String) -> String { guard let date = inputFormatter.date(from: sectionName) else { return "Invalid Date" } - + let relativeDate = relativeFormatter.string(from: date) if relativeDate.rangeOfCharacter(from: .decimalDigits) != nil { return dateFormatter.string(from: date) } - + return relativeDate } - + /// Returns a relative datestring for the given timestamp. e.g. "5 min. ago" /// If the timestamp is within 1 second of the current time this function will return `nil` /// A `nil` return value should be considered "just now". @@ -108,7 +108,7 @@ public class AppTrackingProtectionListViewModel: NSObject, ObservableObject, NSF // return nil here and replace it with UserText on the view side. return nil } - + return relativeTimeFormatter.localizedString(for: timestamp, relativeTo: Date()) } @@ -138,7 +138,7 @@ public class AppTrackingProtectionListViewModel: NSObject, ObservableObject, NSF self.context.stalenessInterval = 0 super.init() - + self.isOnboarding = !appTPUsed setupFetchedResultsController() diff --git a/Core/AtbAndVariantCleanup.swift b/Core/AtbAndVariantCleanup.swift index 7d0cb0c63e..05824b853c 100644 --- a/Core/AtbAndVariantCleanup.swift +++ b/Core/AtbAndVariantCleanup.swift @@ -25,14 +25,14 @@ public class AtbAndVariantCleanup { static func cleanup(statisticsStorage: StatisticsStore = StatisticsUserDefaults(), variantManager: VariantManager = DefaultVariantManager()) { - + guard let variant = statisticsStorage.variant else { return } // clean up ATB if let atb = statisticsStorage.atb, atb.hasSuffix(variant) { statisticsStorage.atb = String(atb.dropLast(variant.count)) } - + // remove existing variant if not in an active experiment if variantManager.currentVariant == nil { statisticsStorage.variant = nil diff --git a/Core/BookmarkObjects.swift b/Core/BookmarkObjects.swift index f6e66ae86f..53719f279b 100644 --- a/Core/BookmarkObjects.swift +++ b/Core/BookmarkObjects.swift @@ -33,22 +33,22 @@ private struct Constants { public protocol Bookmark: BookmarkItem { var url: URL? { get set } - + var displayTitle: String? { get } } public extension Bookmark { - + var displayTitle: String? { let host = url?.host?.droppingWwwPrefix() ?? url?.absoluteString - + var displayTitle = (title?.isEmpty ?? true) ? host : title - + if let url = url, url.isDuckDuckGo, let title = displayTitle, title.hasSuffix(Constants.ddgSuffix) { displayTitle = String(title.dropLast(Constants.ddgSuffix.count)) } - + return displayTitle } } @@ -58,7 +58,7 @@ public protocol BookmarkFolder: BookmarkItem { } public extension BookmarkFolder { - + var numberOfChildrenDeep: Int { guard let children = children else { return 0 } return children.reduce(children.count) { $0 + (($1 as? BookmarkFolder)?.numberOfChildrenDeep ?? 0) } diff --git a/Core/BookmarksCachingSearch.swift b/Core/BookmarksCachingSearch.swift index 09247b71c6..abbaaff361 100644 --- a/Core/BookmarksCachingSearch.swift +++ b/Core/BookmarksCachingSearch.swift @@ -37,30 +37,30 @@ public protocol BookmarksStringSearchResult { } public protocol BookmarksSearchStore { - + var dataDidChange: AnyPublisher { get } - + func bookmarksAndFavorites(completion: @escaping ([BookmarksCachingSearch.ScoredBookmark]) -> Void) } public class CoreDataBookmarksSearchStore: BookmarksSearchStore { - + private let bookmarksStore: CoreDataDatabase - + private let subject = PassthroughSubject() public var dataDidChange: AnyPublisher - + public init(bookmarksStore: CoreDataDatabase) { self.bookmarksStore = bookmarksStore self.dataDidChange = self.subject.eraseToAnyPublisher() - + registerForCoreDataStorageNotifications() } - + public func bookmarksAndFavorites(completion: @escaping ([BookmarksCachingSearch.ScoredBookmark]) -> Void) { - + let context = bookmarksStore.makeContext(concurrencyType: .privateQueueConcurrencyType) - + let fetchRequest = NSFetchRequest(entityName: "BookmarkEntity") fetchRequest.predicate = NSPredicate( format: "%K = false AND %K == NO", @@ -72,7 +72,7 @@ public class CoreDataBookmarksSearchStore: BookmarksSearchStore { #keyPath(BookmarkEntity.url), #keyPath(BookmarkEntity.objectID)] fetchRequest.relationshipKeyPathsForPrefetching = [#keyPath(BookmarkEntity.favoriteFolders)] - + context.perform { let result = try? context.fetch(fetchRequest) as? [[String: Any]] @@ -83,14 +83,14 @@ public class CoreDataBookmarksSearchStore: BookmarksSearchStore { } } } - + private func registerForCoreDataStorageNotifications() { NotificationCenter.default.addObserver(self, selector: #selector(coreDataDidSave), name: NSManagedObjectContext.didSaveObjectsNotification, object: nil) } - + @objc func coreDataDidSave(notification: Notification) { guard let externalContext = notification.object as? NSManagedObjectContext, externalContext.persistentStoreCoordinator == bookmarksStore.coordinator else { return } @@ -106,20 +106,20 @@ public class BookmarksCachingSearch: BookmarksStringSearch { public let url: URL public let isFavorite: Bool var score: Int - + init(objectID: NSManagedObjectID, title: String, url: URL, isFavorite: Bool) { self.objectID = objectID self.title = title self.url = url self.isFavorite = isFavorite - + if isFavorite { score = 0 } else { score = -1 } } - + init?(bookmark: [String: Any]) { guard let title = bookmark[#keyPath(BookmarkEntity.title)] as? String, let urlString = bookmark[#keyPath(BookmarkEntity.url)] as? String, @@ -127,7 +127,7 @@ public class BookmarksCachingSearch: BookmarksStringSearch { let objectID = bookmark[#keyPath(BookmarkEntity.objectID)] as? NSManagedObjectID else { return nil } - + self.init(objectID: objectID, title: title, url: url, @@ -138,7 +138,7 @@ public class BookmarksCachingSearch: BookmarksStringSearch { return Self.init(objectID: objectID, title: title, url: url, isFavorite: !isFavorite) } } - + private let bookmarksStore: BookmarksSearchStore private var cancellable: AnyCancellable? @@ -147,17 +147,17 @@ public class BookmarksCachingSearch: BookmarksStringSearch { self.cancellable = bookmarksStore.dataDidChange.sink { [weak self] _ in self?.refreshCache() } - + loadCache() } public var hasData: Bool { return cachedBookmarksAndFavorites.count > 0 } - + private var cachedBookmarksAndFavorites = [ScoredBookmark]() private var cacheLoadedCondition = RunLoop.ResumeCondition() - + private func loadCache() { bookmarksStore.bookmarksAndFavorites { result in self.cachedBookmarksAndFavorites = result @@ -166,7 +166,7 @@ public class BookmarksCachingSearch: BookmarksStringSearch { } } } - + private var bookmarksAndFavorites: [ScoredBookmark] { RunLoop.current.run(until: cacheLoadedCondition) return cachedBookmarksAndFavorites @@ -182,25 +182,25 @@ public class BookmarksCachingSearch: BookmarksStringSearch { private func score(query: String, input: [ScoredBookmark]) -> [ScoredBookmark] { let query = query.lowercased() let tokens = query.split(separator: " ").filter { !$0.isEmpty }.map { String($0).lowercased() } - + var input = input var result = [ScoredBookmark]() - + for index in 0.. 1 { var matchesAllTokens = true for token in tokens { @@ -210,11 +210,11 @@ public class BookmarksCachingSearch: BookmarksStringSearch { break } } - + if matchesAllTokens { // Score tokenized matches input[index].score += 10 - + // Boost score if first token matches: if let firstToken = tokens.first { // domain - high score boost if domain.starts(with: firstToken) { @@ -242,9 +242,9 @@ public class BookmarksCachingSearch: BookmarksStringSearch { guard hasData else { return [] } - + let bookmarks = bookmarksAndFavorites - + let trimmed = query.trimmingWhitespace() var finalResult = self.score(query: trimmed, input: bookmarks) finalResult = finalResult.sorted { $0.score > $1.score } diff --git a/Core/BookmarksCoreDataStorage.swift b/Core/BookmarksCoreDataStorage.swift index a0428fa7a4..8844e0b7b5 100644 --- a/Core/BookmarksCoreDataStorage.swift +++ b/Core/BookmarksCoreDataStorage.swift @@ -22,10 +22,10 @@ import CoreData import Bookmarks public class LegacyBookmarksCoreDataStorage { - + private let storeLoadedCondition = RunLoop.ResumeCondition() internal var persistentContainer: NSPersistentContainer - + public lazy var viewContext: NSManagedObjectContext = { RunLoop.current.run(until: storeLoadedCondition) let context = persistentContainer.viewContext @@ -33,7 +33,7 @@ public class LegacyBookmarksCoreDataStorage { context.name = Constants.viewContextName return context }() - + public func getTemporaryPrivateContext() -> NSManagedObjectContext { RunLoop.current.run(until: storeLoadedCondition) let context = persistentContainer.newBackgroundContext() @@ -41,10 +41,10 @@ public class LegacyBookmarksCoreDataStorage { context.name = Constants.privateContextName return context } - + private var cachedReadOnlyTopLevelBookmarksFolder: BookmarkFolderManagedObject? private var cachedReadOnlyTopLevelFavoritesFolder: BookmarkFolderManagedObject? - + internal static var managedObjectModel: NSManagedObjectModel { let coreBundle = Bundle(identifier: "com.duckduckgo.mobile.ios.Core")! guard let managedObjectModel = NSManagedObjectModel.mergedModel(from: [coreBundle]) else { @@ -52,41 +52,41 @@ public class LegacyBookmarksCoreDataStorage { } return managedObjectModel } - + private var storeDescription: NSPersistentStoreDescription { return NSPersistentStoreDescription(url: storeURL) } - + public static var defaultStoreURL: URL { let containerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: BookmarksDatabase.Constants.bookmarksGroupID)! return containerURL.appendingPathComponent("\(Constants.databaseName).sqlite") } - + private let storeURL: URL - + public init?(storeURL: URL = defaultStoreURL, createIfNeeded: Bool = false) { if !FileManager.default.fileExists(atPath: storeURL.path), createIfNeeded == false { return nil } - + self.storeURL = storeURL - + persistentContainer = NSPersistentContainer(name: Constants.databaseName, managedObjectModel: Self.managedObjectModel) persistentContainer.persistentStoreDescriptions = [storeDescription] } - + public func removeStore() { - + typealias StoreInfo = (url: URL?, type: String) - + do { var storesToDelete = [StoreInfo]() for store in persistentContainer.persistentStoreCoordinator.persistentStores { storesToDelete.append((url: store.url, type: store.type)) try persistentContainer.persistentStoreCoordinator.remove(store) } - + for (url, type) in storesToDelete { if let url = url { try persistentContainer.persistentStoreCoordinator.destroyPersistentStore(at: url, @@ -97,21 +97,21 @@ public class LegacyBookmarksCoreDataStorage { Pixel.fire(pixel: .bookmarksMigrationCouldNotRemoveOldStore, error: error) } - + try? FileManager.default.removeItem(atPath: storeURL.path) try? FileManager.default.removeItem(atPath: storeURL.path.appending("-wal")) try? FileManager.default.removeItem(atPath: storeURL.path.appending("-shm")) } - + public func loadStoreAndCaches(andMigrate handler: @escaping (NSManagedObjectContext) -> Void = { _ in }) { - + loadStore(andMigrate: handler) - + RunLoop.current.run(until: storeLoadedCondition) cacheReadOnlyTopLevelBookmarksFolder() cacheReadOnlyTopLevelFavoritesFolder() } - + internal func loadStore(andMigrate handler: @escaping (NSManagedObjectContext) -> Void = { _ in }) { persistentContainer = NSPersistentContainer(name: Constants.databaseName, managedObjectModel: Self.managedObjectModel) @@ -120,7 +120,7 @@ public class LegacyBookmarksCoreDataStorage { if let error = error { fatalError("Unable to load persistent stores: \(error)") } - + let context = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType) context.persistentStoreCoordinator = self.persistentContainer.persistentStoreCoordinator context.name = "Migration" @@ -154,14 +154,14 @@ public class LegacyBookmarksCoreDataStorage { // MARK: public interface extension LegacyBookmarksCoreDataStorage { - + public var topLevelBookmarksFolder: BookmarkFolderManagedObject? { guard let folder = cachedReadOnlyTopLevelBookmarksFolder else { return nil } return folder } - + public var topLevelFavoritesFolder: BookmarkFolderManagedObject? { guard let folder = cachedReadOnlyTopLevelFavoritesFolder else { return nil @@ -185,7 +185,7 @@ extension LegacyBookmarksCoreDataStorage { case favorite case bookmark } - + /* This function will return nil if the database desired structure is not met i.e: If there are more than one root level folder OR @@ -193,16 +193,16 @@ extension LegacyBookmarksCoreDataStorage { */ internal func fetchReadOnlyTopLevelFolder(withFolderType folderType: TopLevelFolderType) -> BookmarkFolderManagedObject? { - + var folder: BookmarkFolderManagedObject? - + viewContext.performAndWait { let fetchRequest = NSFetchRequest(entityName: Constants.folderClassName) fetchRequest.predicate = NSPredicate(format: "%K == nil AND %K == %@", #keyPath(BookmarkManagedObject.parent), #keyPath(BookmarkManagedObject.isFavorite), NSNumber(value: folderType == .favorite)) - + let results = try? viewContext.fetch(fetchRequest) guard (results?.count ?? 0) == 1, let fetchedFolder = results?.first else { @@ -213,11 +213,11 @@ extension LegacyBookmarksCoreDataStorage { } return folder } - + internal func cacheReadOnlyTopLevelBookmarksFolder() { guard let folder = fetchReadOnlyTopLevelFolder(withFolderType: .bookmark) else { fixFolderDataStructure(withFolderType: .bookmark) - + // https://app.asana.com/0/414709148257752/1202779945035904/f guard let fixedFolder = fetchReadOnlyTopLevelFolder(withFolderType: .bookmark) else { Pixel.fire(pixel: .debugCouldNotFixBookmarkFolder) @@ -229,11 +229,11 @@ extension LegacyBookmarksCoreDataStorage { } self.cachedReadOnlyTopLevelBookmarksFolder = folder } - + internal func cacheReadOnlyTopLevelFavoritesFolder() { guard let folder = fetchReadOnlyTopLevelFolder(withFolderType: .favorite) else { fixFolderDataStructure(withFolderType: .favorite) - + // https://app.asana.com/0/414709148257752/1202779945035904/f guard let fixedFolder = fetchReadOnlyTopLevelFolder(withFolderType: .favorite) else { Pixel.fire(pixel: .debugCouldNotFixFavoriteFolder) @@ -266,25 +266,25 @@ extension LegacyBookmarksCoreDataStorage { // This is a temporary workaround, do not use the following functions for anything else extension LegacyBookmarksCoreDataStorage { - + private func deleteExtraOrphanedFolders(_ orphanedFolders: [BookmarkFolderManagedObject], onContext context: NSManagedObjectContext, withFolderType folderType: TopLevelFolderType) { let count = orphanedFolders.count let pixelParam = [PixelParameters.bookmarkErrorOrphanedFolderCount: "\(count)"] - + if folderType == .favorite { Pixel.fire(pixel: .debugFavoriteOrphanFolderNew, withAdditionalParameters: pixelParam) } else { Pixel.fire(pixel: .debugBookmarkOrphanFolderNew, withAdditionalParameters: pixelParam) } - + // Sort all orphaned folders by number of children let sorted = orphanedFolders.sorted { ($0.children?.count ?? 0) > ($1.children?.count ?? 0) } - + // Get the folder with the highest number of children let folderWithMoreChildren = sorted.first - + // Separate the other folders let otherFolders = sorted.suffix(from: 1) @@ -297,7 +297,7 @@ extension LegacyBookmarksCoreDataStorage { context.delete(folder) } } - + /* Top level (orphaned) folders need to match its type i.e: Favorites and Bookmarks each have their own root folder @@ -318,7 +318,7 @@ extension LegacyBookmarksCoreDataStorage { bookmarksFetchRequest.returnsObjectsAsFaults = false let bookmarks = try? context.fetch(bookmarksFetchRequest) - + if bookmarks?.count ?? 0 > 0 { if folderType == .favorite { Pixel.fire(pixel: .debugMissingTopFolderFixHasFavorites) @@ -326,7 +326,7 @@ extension LegacyBookmarksCoreDataStorage { Pixel.fire(pixel: .debugMissingTopFolderFixHasBookmarks) } } - + // Create root folder for the specified folder type let bookmarksFolder: BookmarkFolderManagedObject if folderType == .favorite { @@ -334,31 +334,31 @@ extension LegacyBookmarksCoreDataStorage { } else { bookmarksFolder = Self.rootFolderManagedObject(context) } - + // Assign all bookmarks to the parent folder bookmarks?.forEach { $0.parent = bookmarksFolder } } - + internal func fixFolderDataStructure(withFolderType folderType: TopLevelFolderType) { let privateContext = getTemporaryPrivateContext() - + privateContext.performAndWait { let fetchRequest = NSFetchRequest(entityName: Constants.folderClassName) fetchRequest.predicate = NSPredicate(format: "%K == nil AND %K == %@", #keyPath(BookmarkManagedObject.parent), #keyPath(BookmarkManagedObject.isFavorite), NSNumber(value: folderType == .favorite)) - + let results = try? privateContext.fetch(fetchRequest) - + if let orphanedFolders = results, orphanedFolders.count > 1 { deleteExtraOrphanedFolders(orphanedFolders, onContext: privateContext, withFolderType: folderType) } else { createMissingTopLevelFolder(onContext: privateContext, withFolderType: folderType) } - + do { try privateContext.save() } catch { diff --git a/Core/BookmarksExporter.swift b/Core/BookmarksExporter.swift index 294b59a076..5a46ec1749 100644 --- a/Core/BookmarksExporter.swift +++ b/Core/BookmarksExporter.swift @@ -42,7 +42,7 @@ public struct BookmarksExporter { func exportBookmarksToContent() throws -> String { var content = [Template.header] - + let context = coreDataStorage.makeContext(concurrencyType: .mainQueueConcurrencyType) guard let rootFolder = BookmarkUtils.fetchRootFolder(context) else { throw BookmarksExporterError.brokenDatabaseStructure diff --git a/Core/BookmarksModelsErrorHandling.swift b/Core/BookmarksModelsErrorHandling.swift index b60ecdd4d8..3539714f78 100644 --- a/Core/BookmarksModelsErrorHandling.swift +++ b/Core/BookmarksModelsErrorHandling.swift @@ -32,7 +32,7 @@ public class BookmarksModelsErrorHandling: EventMapping { var domainEvent: Pixel.Event? var params = [String: String]() switch event { - + case .bookmarkFolderExpected: domainEvent = .bookmarkFolderExpected case .bookmarksListIndexNotMatchingBookmark: @@ -49,12 +49,12 @@ public class BookmarksModelsErrorHandling: EventMapping { domainEvent = .indexOutOfRange(model) case .saveFailed(let model): domainEvent = .saveFailed(model) - + if let error = error as? NSError { let processedErrors = CoreDataErrorsParser.parse(error: error) params = processedErrors.errorPixelParameters } - + case .missingParent(let object): domainEvent = .missingParent(object) } @@ -68,14 +68,14 @@ public class BookmarksModelsErrorHandling: EventMapping { } } } - + override init(mapping: @escaping EventMapping.Mapping) { fatalError("Use init()") } } public extension BookmarkEditorViewModel { - + convenience init(editingEntityID: NSManagedObjectID, bookmarksDatabase: CoreDataDatabase, favoritesDisplayMode: FavoritesDisplayMode, @@ -84,9 +84,9 @@ public extension BookmarkEditorViewModel { bookmarksDatabase: bookmarksDatabase, favoritesDisplayMode: favoritesDisplayMode, errorEvents: BookmarksModelsErrorHandling(syncService: syncService)) - + } - + convenience init(creatingFolderWithParentID parentFolderID: NSManagedObjectID?, bookmarksDatabase: CoreDataDatabase, favoritesDisplayMode: FavoritesDisplayMode, @@ -99,7 +99,7 @@ public extension BookmarkEditorViewModel { } public extension BookmarkListViewModel { - + convenience init(bookmarksDatabase: CoreDataDatabase, parentID: NSManagedObjectID?, favoritesDisplayMode: FavoritesDisplayMode, @@ -112,14 +112,14 @@ public extension BookmarkListViewModel { } public extension FavoritesListViewModel { - + convenience init(bookmarksDatabase: CoreDataDatabase, favoritesDisplayMode: FavoritesDisplayMode) { self.init(bookmarksDatabase: bookmarksDatabase, errorEvents: BookmarksModelsErrorHandling(), favoritesDisplayMode: favoritesDisplayMode) } } public extension MenuBookmarksViewModel { - + convenience init(bookmarksDatabase: CoreDataDatabase, syncService: DDGSyncing?) { self.init(bookmarksDatabase: bookmarksDatabase, errorEvents: BookmarksModelsErrorHandling(syncService: syncService)) } diff --git a/Core/Configuration.swift b/Core/Configuration.swift index 2cd73a6266..e7c9dcb631 100644 --- a/Core/Configuration.swift +++ b/Core/Configuration.swift @@ -33,5 +33,5 @@ public extension Configuration { case .FBConfig: return "FBConfig" } } - + } diff --git a/Core/ContentBlockerRulesLists.swift b/Core/ContentBlockerRulesLists.swift index b7de2e2d63..89407284fc 100644 --- a/Core/ContentBlockerRulesLists.swift +++ b/Core/ContentBlockerRulesLists.swift @@ -20,17 +20,17 @@ import BrowserServicesKit public final class ContentBlockerRulesLists: DefaultContentBlockerRulesListsSource { - + private let adClickAttribution: AdClickAttributionFeature - + init(trackerDataManager: TrackerDataManager, adClickAttribution: AdClickAttributionFeature) { self.adClickAttribution = adClickAttribution super.init(trackerDataManager: trackerDataManager) } - + public override var contentBlockerRulesLists: [ContentBlockerRulesList] { var result = super.contentBlockerRulesLists - + if adClickAttribution.isEnabled, let tdsRulesIndex = result.firstIndex(where: { $0.name == Constants.trackerDataSetRulesListName }) { let tdsRules = result[tdsRulesIndex] @@ -42,8 +42,8 @@ public final class ContentBlockerRulesLists: DefaultContentBlockerRulesListsSour result.append(splitRules.1) } } - + return result } - + } diff --git a/Core/ContentBlocking.swift b/Core/ContentBlocking.swift index 2767e48e4d..43b5348b42 100644 --- a/Core/ContentBlocking.swift +++ b/Core/ContentBlocking.swift @@ -22,7 +22,6 @@ import BrowserServicesKit import Combine import Common - public final class ContentBlocking { public static let shared = ContentBlocking() @@ -84,25 +83,25 @@ public final class ContentBlocking { switch event { case .trackerDataParseFailed: domainEvent = .trackerDataParseFailed - + case .trackerDataReloadFailed: domainEvent = .trackerDataReloadFailed - + case .trackerDataCouldNotBeLoaded: domainEvent = .trackerDataCouldNotBeLoaded - + case .privacyConfigurationReloadFailed: domainEvent = .privacyConfigurationReloadFailed - + case .privacyConfigurationParseFailed: domainEvent = .privacyConfigurationParseFailed - + case .privacyConfigurationCouldNotBeLoaded: domainEvent = .privacyConfigurationCouldNotBeLoaded - + case .contentBlockingCompilationFailed(let listName, let component): let defaultTDSListName = DefaultContentBlockerRulesListsSource.Constants.trackerDataSetRulesListName - + let listType: Pixel.Event.CompileRulesListType switch listName { case defaultTDSListName: @@ -116,11 +115,11 @@ public final class ContentBlocking { } domainEvent = .contentBlockingCompilationFailed(listType: listType, component: component) - + case .contentBlockingCompilationTime: domainEvent = .contentBlockingCompilationTime } - + if let error = error { Pixel.fire(pixel: domainEvent, error: error, @@ -132,7 +131,7 @@ public final class ContentBlocking { includedParameters: [], onComplete: onComplete) } - + } public func makeAdClickAttributionDetection(tld: TLD) -> AdClickAttributionDetection { @@ -142,7 +141,7 @@ public final class ContentBlocking { errorReporting: attributionDebugEvents, log: .adAttributionLog) } - + public func makeAdClickAttributionLogic(tld: TLD) -> AdClickAttributionLogic { AdClickAttributionLogic(featureConfig: adClickAttribution, rulesProvider: adClickAttributionRulesProvider, @@ -151,7 +150,7 @@ public final class ContentBlocking { errorReporting: attributionDebugEvents, log: .adAttributionLog) } - + private let attributionEvents = EventMapping { event, _, parameters, _ in var shouldIncludeAppVersion = true let domainEvent: Pixel.Event @@ -164,10 +163,10 @@ public final class ContentBlocking { domainEvent = .adClickAttributionPageLoads shouldIncludeAppVersion = false } - + Pixel.fire(pixel: domainEvent, withAdditionalParameters: parameters ?? [:], includedParameters: shouldIncludeAppVersion ? [.appVersion] : []) } - + private let attributionDebugEvents = EventMapping { event, _, _, _ in let domainEvent: Pixel.Event switch event { @@ -192,29 +191,29 @@ public final class ContentBlocking { case .adAttributionLogicWrongVendorOnFailedCompilation: domainEvent = .adAttributionLogicWrongVendorOnFailedCompilation } - + Pixel.fire(pixel: domainEvent, includedParameters: []) } - + } public class DomainsProtectionUserDefaultsStore: DomainsProtectionStore { - + private struct Keys { static let unprotectedDomains = "com.duckduckgo.contentblocker.whitelist" static let trackerList = "com.duckduckgo.trackerList" } - + private let suiteName: String - + public init(suiteName: String = ContentBlockerStoreConstants.groupName) { self.suiteName = suiteName } - + private var userDefaults: UserDefaults? { return UserDefaults(suiteName: suiteName) } - + public private(set) var unprotectedDomains: Set { get { guard let data = userDefaults?.data(forKey: Keys.unprotectedDomains) else { return Set() } @@ -229,13 +228,13 @@ public class DomainsProtectionUserDefaultsStore: DomainsProtectionStore { userDefaults?.set(data, forKey: Keys.unprotectedDomains) } } - + public func disableProtection(forDomain domain: String) { var domains = unprotectedDomains domains.insert(domain) unprotectedDomains = domains } - + public func enableProtection(forDomain domain: String) { var domains = unprotectedDomains domains.remove(domain) diff --git a/Core/CookieStorage.swift b/Core/CookieStorage.swift index 0349d9841a..0cfc79c970 100644 --- a/Core/CookieStorage.swift +++ b/Core/CookieStorage.swift @@ -34,7 +34,7 @@ public class CookieStorage { } private var userDefaults: UserDefaults - + var isConsumed: Bool { get { return userDefaults.bool(forKey: Keys.consumed, defaultValue: false) @@ -43,7 +43,7 @@ public class CookieStorage { userDefaults.set(newValue, forKey: Keys.consumed) } } - + /// Use the `updateCookies` function rather than the setter which is only visible for testing. var cookies: [HTTPCookie] { get { @@ -54,17 +54,17 @@ public class CookieStorage { cookieData.forEach({ properties[HTTPCookiePropertyKey(rawValue: $0.key)] = $0.value }) - + if let cookie = HTTPCookie(properties: properties) { os_log("read cookie %s %s %s", log: .generalLog, type: .debug, cookie.domain, cookie.name, cookie.value) storedCookies.append(cookie) } } } - + return storedCookies } - + set { var cookies = [[String: Any?]]() newValue.forEach { cookie in @@ -91,16 +91,16 @@ public class CookieStorage { case different case notConsumed } - + /// Update ALL cookies. The absence of cookie domains here indicateds they have been removed by the website, so be sure to call this with all cookies that might need to be persisted even if those websites have not been visited yet. @discardableResult func updateCookies(_ cookies: [HTTPCookie], keepingPreservedLogins preservedLogins: PreserveLogins) -> CookieDomainsOnUpdateDiagnostic { guard isConsumed else { return .notConsumed } - + isConsumed = false - + let persisted = self.cookies - + func cookiesByDomain(_ cookies: [HTTPCookie]) -> [String: [HTTPCookie]] { var byDomain = [String: [HTTPCookie]]() cookies.forEach { cookie in @@ -110,7 +110,7 @@ public class CookieStorage { } return byDomain } - + let updatedCookiesByDomain = cookiesByDomain(cookies) var persistedCookiesByDomain = cookiesByDomain(persisted) @@ -119,16 +119,16 @@ public class CookieStorage { updatedDomains: updatedCookiesByDomain.keys.sorted(), persistedDomains: persistedCookiesByDomain.keys.sorted() ) - + let cookieDomains = Set(updatedCookiesByDomain.keys.map { $0 } + persistedCookiesByDomain.keys.map { $0 }) - + cookieDomains.forEach { persistedCookiesByDomain[$0] = updatedCookiesByDomain[$0] } - + persistedCookiesByDomain.keys.forEach { guard !URL.isDuckDuckGo(domain: $0) else { return } // DDG cookies are for SERP settings only - + if !preservedLogins.isAllowed(cookieDomain: $0) { persistedCookiesByDomain.removeValue(forKey: $0) } @@ -137,10 +137,10 @@ public class CookieStorage { let now = Date() self.cookies = persistedCookiesByDomain.map { $0.value }.joined().compactMap { $0 } .filter { $0.expiresDate == nil || $0.expiresDate! > now } - + return diagnosticResult } - + private func evaluateDomains(updatedDomains: [String], persistedDomains: [String]) -> CookieDomainsOnUpdateDiagnostic { if persistedDomains.isEmpty { return .empty @@ -152,5 +152,5 @@ public class CookieStorage { return .different } } - + } diff --git a/Core/DDGPersistenceContainer.swift b/Core/DDGPersistenceContainer.swift index ef61f85edc..5bec649281 100644 --- a/Core/DDGPersistenceContainer.swift +++ b/Core/DDGPersistenceContainer.swift @@ -41,7 +41,7 @@ public class DDGPersistenceContainer { private static func createPersistenceStoreCoordinator(name: String, model: NSManagedObjectModel) -> NSPersistentStoreCoordinator? { guard let storeURL = storeURL(for: name) else { return nil } - + let persistenceStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: model) let options = [ NSMigratePersistentStoresAutomaticallyOption: true, NSInferMappingModelAutomaticallyOption: true ] @@ -53,7 +53,7 @@ public class DDGPersistenceContainer { return persistenceStoreCoordinator } - + public static func storeURL(for name: String) -> URL? { let fileManager = FileManager.default guard let docsDir = fileManager.urls(for: .documentDirectory, in: .userDomainMask).last else { return nil } diff --git a/Core/DailyPixel.swift b/Core/DailyPixel.swift index ef878eae28..e257b9828b 100644 --- a/Core/DailyPixel.swift +++ b/Core/DailyPixel.swift @@ -30,17 +30,17 @@ import Foundation /// In those scenarios a 'DailyPixelError' is returned denoting the reason. /// public final class DailyPixel { - + public enum Error: Swift.Error { - + case alreadyFired - + } - + private enum Constant { - + static let dailyPixelStorageIdentifier = "com.duckduckgo.daily.pixel.storage" - + } private static let storage: UserDefaults = UserDefaults(suiteName: Constant.dailyPixelStorageIdentifier)! @@ -53,7 +53,7 @@ public final class DailyPixel { withAdditionalParameters params: [String: String] = [:], includedParameters: [Pixel.QueryParameters] = [.atb, .appVersion], onComplete: @escaping (Swift.Error?) -> Void = { _ in }) { - + if !pixel.hasBeenFiredToday(dailyPixelStorage: storage) { Pixel.fire(pixel: pixel, withAdditionalParameters: params, @@ -96,20 +96,20 @@ public final class DailyPixel { onComplete: onCountComplete ) } - + private static func updatePixelLastFireDate(pixel: Pixel.Event) { storage.set(Date(), forKey: pixel.name) } - + } private extension Pixel.Event { - + func hasBeenFiredToday(dailyPixelStorage: UserDefaults) -> Bool { if let lastFireDate = dailyPixelStorage.object(forKey: name) as? Date { return Date().isSameDay(lastFireDate) } return false } - + } diff --git a/Core/Database.swift b/Core/Database.swift index 0c682643c3..a9fc351606 100644 --- a/Core/Database.swift +++ b/Core/Database.swift @@ -23,17 +23,17 @@ import CoreData import Persistence public class Database { - + fileprivate struct Constants { static let databaseGroupID = "\(Global.groupIdPrefix).database" - + static let databaseName = "Database" } - + public static let shared = makeCoreDataDatabase() - + static func makeCoreDataDatabase() -> CoreDataDatabase { - + guard let appRatingModel = CoreDataDatabase.loadModel(from: .main, named: "AppRatingPrompt"), let remoteMessagingModel = CoreDataDatabase.loadModel(from: .main, named: "RemoteMessaging"), let managedObjectModel = NSManagedObjectModel(byMerging: [appRatingModel, @@ -41,7 +41,7 @@ public class Database { HTTPSUpgrade.managedObjectModel]) else { fatalError("No DB scheme found") } - + let url = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: Database.Constants.databaseGroupID)! return CoreDataDatabase(name: Constants.databaseName, containerLocation: url, diff --git a/Core/Debounce.swift b/Core/Debounce.swift index b96204614b..6994c3dda3 100644 --- a/Core/Debounce.swift +++ b/Core/Debounce.swift @@ -20,17 +20,17 @@ import Foundation public class Debounce { - + private let queue: DispatchQueue private let interval: TimeInterval - + private var currentWorkItem = DispatchWorkItem(block: {}) - + public init(queue: DispatchQueue, seconds: TimeInterval) { self.queue = queue self.interval = seconds } - + public func schedule(_ block: @escaping (() -> Void)) { currentWorkItem.cancel() currentWorkItem = DispatchWorkItem(block: { block() }) diff --git a/Core/DebugUserScript.swift b/Core/DebugUserScript.swift index d3c83f93f7..1c7ee0ea47 100644 --- a/Core/DebugUserScript.swift +++ b/Core/DebugUserScript.swift @@ -22,47 +22,47 @@ import WebKit import UserScript public class DebugUserScript: NSObject, UserScript { - + struct MessageNames { - + static let signpost = "signpost" static let log = "log" - + } - + public lazy var source: String = { return "" }() - + public var injectionTime: WKUserScriptInjectionTime = .atDocumentStart - + public var forMainFrameOnly: Bool = false - + public var messageNames: [String] = [ MessageNames.signpost, MessageNames.log ] - + public weak var instrumentation: TabInstrumentation? public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { switch message.name { - + case MessageNames.signpost: handleSignpost(message: message) - + case MessageNames.log: handleLog(message: message) - + default: break } } - + private func handleLog(message: WKScriptMessage) { os_log("%s", log: .generalLog, type: .debug, String(describing: message.body)) } - + private func handleSignpost(message: WKScriptMessage) { guard let dict = message.body as? [String: Any], let event = dict["event"] as? String else { return } - + if event == "Request Allowed" { if let elapsedTimeInMs = dict["time"] as? Double, let url = dict["url"] as? String { diff --git a/Core/DefaultVariantManager.swift b/Core/DefaultVariantManager.swift index 3072c39ab7..b17b8de45d 100644 --- a/Core/DefaultVariantManager.swift +++ b/Core/DefaultVariantManager.swift @@ -31,7 +31,7 @@ extension FeatureName { } public struct VariantIOS: Variant { - + struct When { static let always = { return true } static let padDevice = { return UIDevice.current.userInterfaceIdiom == .pad } @@ -39,7 +39,7 @@ public struct VariantIOS: Variant { static let inRequiredCountry = { return ["AU", "AT", "DK", "FI", "FR", "DE", "IT", "IE", "NZ", "NO", "ES", "SE", "GB"] .contains(where: { Locale.current.regionCode == $0 }) } - + static let inEnglish = { return Locale.current.languageCode == "en" } static let iOS15 = { () -> Bool in @@ -80,23 +80,23 @@ public struct VariantIOS: Variant { } public protocol VariantRNG { - + func nextInt(upperBound: Int) -> Int - + } public class DefaultVariantManager: VariantManager { - + public var currentVariant: Variant? { let variantName = ProcessInfo.processInfo.environment["VARIANT", default: storage.variant ?? "" ] return variants.first(where: { $0.name == variantName }) } - + private let variants: [Variant] private let storage: StatisticsStore private let rng: VariantRNG private let returningUserMeasurement: ReturnUserMeasurement - + init(variants: [Variant], storage: StatisticsStore, rng: VariantRNG, @@ -120,30 +120,30 @@ public class DefaultVariantManager: VariantManager { public func isSupported(feature: FeatureName) -> Bool { return currentVariant?.features.contains(feature) ?? false } - + public func assignVariantIfNeeded(_ newInstallCompletion: (VariantManager) -> Void) { guard !storage.hasInstallStatistics else { os_log("no new variant needed for existing user", log: .generalLog, type: .debug) return } - + if let variant = currentVariant { os_log("already assigned variant: %s", log: .generalLog, type: .debug, String(describing: variant)) return } - + guard let variant = selectVariant() else { os_log("Failed to assign variant", log: .generalLog, type: .debug) - + // it's possible this failed because there are none to assign, we should still let new install logic execute _ = newInstallCompletion(self) return } - + storage.variant = variant.name newInstallCompletion(self) } - + private func selectVariant() -> Variant? { if returningUserMeasurement.isReturningUser { return VariantIOS.returningUser @@ -151,7 +151,7 @@ public class DefaultVariantManager: VariantManager { let totalWeight = variants.reduce(0, { $0 + $1.weight }) let randomPercent = rng.nextInt(upperBound: totalWeight) - + var runningTotal = 0 for variant in variants { runningTotal += variant.weight @@ -159,19 +159,19 @@ public class DefaultVariantManager: VariantManager { return variant.isIncluded() ? variant : nil } } - + return nil } - + } public class Arc4RandomUniformVariantRNG: VariantRNG { - + public init() { } - + public func nextInt(upperBound: Int) -> Int { // swiftlint:disable:next legacy_random return Int(arc4random_uniform(UInt32(upperBound))) } - + } diff --git a/Core/EtagStorage.swift b/Core/EtagStorage.swift index b3644ee70d..3b02fe96e7 100644 --- a/Core/EtagStorage.swift +++ b/Core/EtagStorage.swift @@ -22,26 +22,26 @@ import Foundation import Configuration public protocol BlockerListETagStorage { - + func saveEtag(_ etag: String, for configuration: Configuration) func loadEtag(for configuration: Configuration) -> String? - + } public struct UserDefaultsETagStorage: BlockerListETagStorage { - + private let defaults = UserDefaults(suiteName: "com.duckduckgo.blocker-list.etags") - + public init() { } - + public func loadEtag(for configuration: Configuration) -> String? { let etag = defaults?.string(forKey: configuration.storeKey) os_log("stored etag for %s %s", log: .generalLog, type: .debug, configuration.storeKey, etag ?? "nil") return etag } - + public func saveEtag(_ etag: String, for configuration: Configuration) { defaults?.set(etag, forKey: configuration.storeKey) } - + } diff --git a/Core/FaviconSourcesProvider.swift b/Core/FaviconSourcesProvider.swift index 17e3f38ade..4690e805bb 100644 --- a/Core/FaviconSourcesProvider.swift +++ b/Core/FaviconSourcesProvider.swift @@ -20,35 +20,35 @@ import Foundation protocol FaviconSourcesProvider { - + func mainSource(forDomain: String) -> URL? - + func additionalSources(forDomain: String) -> [URL] - + } class DefaultFaviconSourcesProvider: FaviconSourcesProvider { - + enum ImageNames: String { - + case appleTouch = "apple-touch-icon.png" case favicon = "favicon.ico" - + } - + func mainSource(forDomain domain: String) -> URL? { return imageSource(forDomain: domain, imageName: ImageNames.appleTouch, secure: true) } - + func additionalSources(forDomain domain: String) -> [URL] { return [ imageSource(forDomain: domain, imageName: .favicon, secure: true), imageSource(forDomain: domain, imageName: .favicon, secure: false) ].compactMap { $0 } } - + private func imageSource(forDomain domain: String, imageName: ImageNames, secure: Bool) -> URL? { return URL(string: (secure ? "https" : "http") + "://" + domain + "/" + imageName.rawValue) } - + } diff --git a/Core/FaviconUserScript.swift b/Core/FaviconUserScript.swift index 6b7d48ae8d..b3563bfa4e 100644 --- a/Core/FaviconUserScript.swift +++ b/Core/FaviconUserScript.swift @@ -27,7 +27,7 @@ public protocol FaviconUserScriptDelegate: NSObjectProtocol { } public class FaviconUserScript: NSObject, UserScript { - + public var source: String = """ (function() { @@ -35,7 +35,7 @@ public class FaviconUserScript: NSObject, UserScript { function getFavicon() { return findFavicons()[0]; }; - + function findFavicons() { var selectors = [ @@ -50,7 +50,7 @@ public class FaviconUserScript: NSObject, UserScript { var icons = document.head.querySelectorAll(selector); for (var i = 0; i < icons.length; i++) { var href = icons[i].href; - + // Exclude SVGs since we can't handle them if (href.indexOf("svg") >= 0 || (icons[i].type && icons[i].type.indexOf("svg") >= 0)) { continue; @@ -72,15 +72,15 @@ public class FaviconUserScript: NSObject, UserScript { }) (); """ - + public var injectionTime: WKUserScriptInjectionTime = .atDocumentEnd - + public var forMainFrameOnly: Bool = true - + public var messageNames: [String] = ["faviconFound"] - + public weak var delegate: FaviconUserScriptDelegate? - + public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { let url: URL? @@ -93,5 +93,5 @@ public class FaviconUserScript: NSObject, UserScript { let host = message.messageHost delegate?.faviconUserScript(self, didRequestUpdateFaviconForHost: host, withUrl: url) } - + } diff --git a/Core/FileStore.swift b/Core/FileStore.swift index 04fe3f41fb..5696711dbf 100644 --- a/Core/FileStore.swift +++ b/Core/FileStore.swift @@ -21,11 +21,11 @@ import Foundation import Configuration public class FileStore { - + private let groupIdentifier: String = ContentBlockerStoreConstants.groupName public init() { } - + public func persist(_ data: Data, for configuration: Configuration) throws { do { try data.write(to: persistenceLocation(for: configuration), options: .atomic) @@ -34,26 +34,26 @@ public class FileStore { throw error } } - + func removeData(forFile file: String) -> Bool { var fileUrl = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: groupIdentifier) fileUrl = fileUrl!.appendingPathComponent(file) guard let fileUrl = fileUrl else { return false } guard FileManager.default.fileExists(atPath: fileUrl.path) else { return true } - + do { try FileManager.default.removeItem(at: fileUrl) } catch { return false } - + return true } - + public func loadAsString(for configuration: Configuration) -> String? { try? String(contentsOf: persistenceLocation(for: configuration)) } - + public func loadAsData(for configuration: Configuration) -> Data? { do { return try Data(contentsOf: persistenceLocation(for: configuration)) @@ -65,7 +65,7 @@ public class FileStore { return nil } } - + func hasData(for configuration: Configuration) -> Bool { FileManager.default.fileExists(atPath: persistenceLocation(for: configuration).path) } diff --git a/Core/HTTPSUpgradeParser.swift b/Core/HTTPSUpgradeParser.swift index 85dbd9cee7..1d144d390e 100644 --- a/Core/HTTPSUpgradeParser.swift +++ b/Core/HTTPSUpgradeParser.swift @@ -21,7 +21,7 @@ import Foundation import BrowserServicesKit public class HTTPSUpgradeParser { - + static func convertExcludedDomainsData(_ data: Data) throws -> [String] { do { let decoder = JSONDecoder() @@ -32,7 +32,7 @@ public class HTTPSUpgradeParser { throw JsonError.typeMismatch } } - + static func convertBloomFilterSpecification(fromJSONData data: Data) throws -> HTTPSBloomFilterSpecification { do { let decoder = JSONDecoder() @@ -43,5 +43,5 @@ public class HTTPSUpgradeParser { throw JsonError.typeMismatch } } - + } diff --git a/Core/HistoryManager.swift b/Core/HistoryManager.swift index 957a8d327e..49b2049f78 100644 --- a/Core/HistoryManager.swift +++ b/Core/HistoryManager.swift @@ -33,5 +33,5 @@ class HistoryManager { func isHistoryFeatureEnabled() -> Bool { return privacyConfig.isEnabled(featureKey: .history) && variantManager.isSupported(feature: .history) } - + } diff --git a/Core/Instruments.swift b/Core/Instruments.swift index 54a61ee677..5a059931dd 100644 --- a/Core/Instruments.swift +++ b/Core/Instruments.swift @@ -21,28 +21,28 @@ import Foundation import os.signpost public class Instruments { - + public enum TimedEvent: String { case fetchingContentBlockerData - + case loadingDisconnectMeStore case loadingEasylistStore - + case tabInitialisation - + case clearingData case injectScripts } - + static public let shared = Instruments() - + static var eventsLog = OSLog(subsystem: "com.duckduckgo.instrumentation", category: "Events") public func startTimedEvent(_ event: TimedEvent, info: String? = nil) -> Any? { if #available(iOSApplicationExtension 12.0, *) { let id = OSSignpostID(log: Instruments.eventsLog) - + os_signpost(.begin, log: Instruments.eventsLog, name: "Timed Event", @@ -52,7 +52,7 @@ public class Instruments { } return nil } - + public func endTimedEvent(for spid: Any?, result: String? = nil) { if #available(iOSApplicationExtension 12.0, *), let id = spid as? OSSignpostID { diff --git a/Core/InternalUserStore.swift b/Core/InternalUserStore.swift index 5d58edb0a7..ba0b3210a2 100644 --- a/Core/InternalUserStore.swift +++ b/Core/InternalUserStore.swift @@ -23,7 +23,7 @@ import BrowserServicesKit public class InternalUserStore: InternalUserStoring { public init() { } - + @UserDefaultsWrapper(key: .featureFlaggingDidVerifyInternalUser, defaultValue: false) public var isInternalUser: Bool } diff --git a/Core/LegacyBookmarksStoreMigration.swift b/Core/LegacyBookmarksStoreMigration.swift index dfd6a4c371..0cb3d417ac 100644 --- a/Core/LegacyBookmarksStoreMigration.swift +++ b/Core/LegacyBookmarksStoreMigration.swift @@ -22,12 +22,12 @@ import CoreData import Bookmarks public class LegacyBookmarksStoreMigration { - + internal enum LegacyTopLevelFolderType { case favorite case bookmark } - + public static func migrate(from legacyStorage: LegacyBookmarksCoreDataStorage?, to context: NSManagedObjectContext) { if let legacyStorage = legacyStorage { @@ -50,37 +50,37 @@ public class LegacyBookmarksStoreMigration { } } } - + private static func fetchTopLevelFolder(_ folderType: LegacyTopLevelFolderType, in context: NSManagedObjectContext) -> [BookmarkFolderManagedObject] { - + let fetchRequest = NSFetchRequest(entityName: LegacyBookmarksCoreDataStorage.Constants.folderClassName) fetchRequest.predicate = NSPredicate(format: "%K == nil AND %K == %@", #keyPath(BookmarkManagedObject.parent), #keyPath(BookmarkManagedObject.isFavorite), NSNumber(value: folderType == .favorite)) - + guard let results = try? context.fetch(fetchRequest) else { return [] } - + // In case of corruption, we can cat more than one 'root' return results } // swiftlint:disable cyclomatic_complexity // swiftlint:disable function_body_length - + private static func migrate(source: NSManagedObjectContext, destination: NSManagedObjectContext) { - + // Do not migrate more than once guard BookmarkUtils.fetchRootFolder(destination) == nil else { Pixel.fire(pixel: .bookmarksMigrationAlreadyPerformed) return } - + BookmarkUtils.prepareFoldersStructure(in: destination) - + guard let newRoot = BookmarkUtils.fetchRootFolder(destination), let newFavoritesRoot = BookmarkUtils.fetchFavoritesFolder(withUUID: FavoritesFolderID.unified.rawValue, in: destination), let newMobileFavoritesRoot = BookmarkUtils.fetchFavoritesFolder(withUUID: FavoritesFolderID.mobile.rawValue, in: destination) else { @@ -88,37 +88,37 @@ public class LegacyBookmarksStoreMigration { Thread.sleep(forTimeInterval: 2) fatalError("Could not write to Bookmarks DB") } - + // Fetch all 'roots' in case we had some kind of inconsistency and duplicated objects let bookmarkRoots = fetchTopLevelFolder(.bookmark, in: source) let favoriteRoots = fetchTopLevelFolder(.favorite, in: source) - + var index = 0 var folderMap = [NSManagedObjectID: BookmarkEntity]() - + var favoritesToMigrate = [BookmarkItemManagedObject]() var bookmarksToMigrate = [BookmarkItemManagedObject]() - + // Map old roots to new one, prepare list of top level bookmarks to migrate for folder in favoriteRoots { folderMap[folder.objectID] = newRoot - + favoritesToMigrate.append(contentsOf: folder.children?.array as? [BookmarkItemManagedObject] ?? []) } - + for folder in bookmarkRoots { folderMap[folder.objectID] = newRoot - + bookmarksToMigrate.append(contentsOf: folder.children?.array as? [BookmarkItemManagedObject] ?? []) } - + var urlToBookmarkMap = [URL: BookmarkEntity]() - + // Iterate over bookmarks to migrate while index < bookmarksToMigrate.count { - + let objectToMigrate = bookmarksToMigrate[index] - + guard let parent = objectToMigrate.parent, let newParent = folderMap[parent.objectID], let title = objectToMigrate.title else { @@ -126,37 +126,37 @@ public class LegacyBookmarksStoreMigration { index += 1 continue } - + if let folder = objectToMigrate as? BookmarkFolderManagedObject { let newFolder = BookmarkEntity.makeFolder(title: title, parent: newParent, context: destination) folderMap[folder.objectID] = newFolder - + if let children = folder.children?.array as? [BookmarkItemManagedObject] { bookmarksToMigrate.append(contentsOf: children) } - + } else if let bookmark = objectToMigrate as? BookmarkManagedObject, let url = bookmark.url { - + let newBookmark = BookmarkEntity.makeBookmark(title: title, url: url.absoluteString, parent: newParent, context: destination) - + urlToBookmarkMap[url] = newBookmark } - + index += 1 } - + // Process favorites starting from the last one, so we preserve the order while adding at begining for favorite in favoritesToMigrate.reversed() { guard let favorite = favorite as? BookmarkManagedObject, let title = favorite.title, let url = favorite.url else { continue } - + let bookmark = { if let existingBookmark = urlToBookmarkMap[url] { return existingBookmark @@ -173,12 +173,12 @@ public class LegacyBookmarksStoreMigration { bookmark.addToFavorites(insertAt: 0, favoritesRoot: newMobileFavoritesRoot) } - + do { try destination.save(onErrorFire: .bookmarksMigrationFailed) } catch { destination.reset() - + BookmarkUtils.prepareLegacyFoldersStructure(in: destination) do { try destination.save(onErrorFire: .bookmarksMigrationCouldNotPrepareDatabaseOnFailedMigration) diff --git a/Core/Link.swift b/Core/Link.swift index 0dcbf45e17..cb868d81f9 100644 --- a/Core/Link.swift +++ b/Core/Link.swift @@ -20,7 +20,7 @@ import Foundation public class Link: NSObject, NSCoding { - + struct Constants { static let ddgSuffix = " at DuckDuckGo" } @@ -30,11 +30,11 @@ public class Link: NSObject, NSCoding { static let url = "url" static let localPath = "localPath" } - + public let title: String? public let url: URL public let localFileURL: URL? - + public var displayTitle: String { let host = url.host?.droppingWwwPrefix() ?? url.absoluteString diff --git a/Core/LocaleExtension.swift b/Core/LocaleExtension.swift index cfbb0126bb..3cb01d997c 100644 --- a/Core/LocaleExtension.swift +++ b/Core/LocaleExtension.swift @@ -20,7 +20,7 @@ import Foundation extension Locale { - + public var isRegionInEurope: Bool { ["AD", "AL", "AT", "AZ", "BA", "BE", "BG", "BY", "CH", "CY", "CZ", "DE", "DK", "EE", "ES", "FI", "FR", "GE", "GI", "GR", "HR", "HU", "IE", "IS", "IT", "KZ", "LI", "LT", "LU", "LV", diff --git a/Core/LoginFormDetectionUserScript.swift b/Core/LoginFormDetectionUserScript.swift index 67c43f2d43..81a0fbbec0 100644 --- a/Core/LoginFormDetectionUserScript.swift +++ b/Core/LoginFormDetectionUserScript.swift @@ -21,9 +21,9 @@ import WebKit import UserScript public protocol LoginFormDetectionDelegate: NSObjectProtocol { - + func loginFormDetectionUserScriptDetectedLoginForm(_ script: LoginFormDetectionUserScript) - + } public class LoginFormDetectionUserScript: NSObject, UserScript { @@ -33,15 +33,15 @@ public class LoginFormDetectionUserScript: NSObject, UserScript { "$IS_DEBUG$": isDebugBuild ? "true" : "false" ]) }() - + public var injectionTime: WKUserScriptInjectionTime = .atDocumentStart - + public var forMainFrameOnly: Bool = false - + public var messageNames: [String] = [ "loginFormDetected" ] - + public weak var delegate: LoginFormDetectionDelegate? - + public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { delegate?.loginFormDetectionUserScriptDetectedLoginForm(self) } diff --git a/Core/NSAttributedStringExtension.swift b/Core/NSAttributedStringExtension.swift index 5454e14ac0..f0a3a61be7 100644 --- a/Core/NSAttributedStringExtension.swift +++ b/Core/NSAttributedStringExtension.swift @@ -27,15 +27,15 @@ extension NSAttributedString { mutableText.mutableString.setString(text) return mutableText } - + public var font: UIFont? { return attributes(at: 0, effectiveRange: nil)[.font] as? UIFont } - + public func stringWithFontSize(_ size: CGFloat) -> NSAttributedString? { guard let font = font else { return nil } let newFont = font.withSize(size) - + let newString = NSMutableAttributedString(attributedString: self) newString.setAttributes([.font: newFont], range: string.fullRange) return newString diff --git a/Core/NSManagedObjectContextExtension.swift b/Core/NSManagedObjectContextExtension.swift index d23551da22..27b6236d77 100644 --- a/Core/NSManagedObjectContextExtension.swift +++ b/Core/NSManagedObjectContextExtension.swift @@ -21,7 +21,7 @@ import CoreData import Persistence extension Array where Element == CoreDataErrorsParser.ErrorInfo { - + var errorPixelParameters: [String: String] { let params: [String: String] if let first = first { @@ -38,18 +38,18 @@ extension Array where Element == CoreDataErrorsParser.ErrorInfo { } extension NSManagedObjectContext { - + public func save(onErrorFire event: Pixel.Event) throws { do { try save() } catch { let nsError = error as NSError let processedErrors = CoreDataErrorsParser.parse(error: nsError) - + Pixel.fire(pixel: event, error: error, withAdditionalParameters: processedErrors.errorPixelParameters) - + throw error } } diff --git a/Core/NavigatorSharePatchUserScript.swift b/Core/NavigatorSharePatchUserScript.swift index 6f600cfafb..80b875ce14 100644 --- a/Core/NavigatorSharePatchUserScript.swift +++ b/Core/NavigatorSharePatchUserScript.swift @@ -25,15 +25,15 @@ public class NavigatorSharePatchUserScript: NSObject, UserScript { public var source: String { return Self.loadJS("navigatorsharepatch", from: Bundle.core) } - + public var injectionTime: WKUserScriptInjectionTime = .atDocumentStart - + public var forMainFrameOnly: Bool = false - + public var messageNames: [String] = [] - + public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { - + } } diff --git a/Core/NotFoundCachingDownloader.swift b/Core/NotFoundCachingDownloader.swift index edbfd59402..c9ba6daa67 100644 --- a/Core/NotFoundCachingDownloader.swift +++ b/Core/NotFoundCachingDownloader.swift @@ -40,7 +40,7 @@ class NotFoundCachingDownloader: ImageDownloader { if shouldDownload(url) { return super.downloadImage(with: url, options: options, completionHandler: completionHandler) } - + completionHandler?(.failure(.requestError(reason: .emptyRequest))) return nil } @@ -49,7 +49,7 @@ class NotFoundCachingDownloader: ImageDownloader { guard let hashedKey = FaviconsHelper.defaultResource(forDomain: domain, sourcesProvider: sourcesProvider)?.cacheKey else { return } notFoundCache[hashedKey] = Date().timeIntervalSince1970 } - + func shouldDownload(_ url: URL, referenceDate: Date = Date()) -> Bool { guard let domain = url.host else { return false } return shouldDownload(forDomain: domain, referenceDate: referenceDate) diff --git a/Core/Pixel.swift b/Core/Pixel.swift index 33b3ca2214..0e30542c6b 100644 --- a/Core/Pixel.swift +++ b/Core/Pixel.swift @@ -27,22 +27,22 @@ public struct PixelParameters { public static let duration = "dur" static let test = "test" public static let appVersion = "appVersion" - + public static let autocompleteBookmarkCapable = "bc" public static let autocompleteIncludedLocalResults = "sb" - + public static let originatedFromMenu = "om" - + public static let applicationState = "as" public static let dataAvailability = "dp" - + static let errorCode = "e" static let errorDomain = "d" static let errorDescription = "de" static let errorCount = "c" static let underlyingErrorCode = "ue" static let underlyingErrorDomain = "ud" - + static let coreDataErrorCode = "coreDataCode" static let coreDataErrorDomain = "coreDataDomain" static let coreDataErrorEntity = "coreDataEntity" @@ -62,12 +62,12 @@ public struct PixelParameters { static let clearWebDataTimedOut = "cd" public static let tabPreviewCountDelta = "cd" - + public static let etag = "et" public static let emailCohort = "cohort" public static let emailLastUsed = "duck_address_last_used" - + // Cookie clearing public static let storeInitialCount = "store_initial_count" public static let storeProtectedCount = "store_protected_count" @@ -78,27 +78,27 @@ public struct PixelParameters { public static let storageAfterDeletionCount = "storage_after_deletion_count" public static let storeAfterDeletionDiffCount = "store_after_deletion_diff_count" public static let storageAfterDeletionDiffCount = "storage_after_deletion_diff_count" - + public static let tabsModelCount = "tabs_model_count" public static let tabControllerCacheCount = "tab_controller_cache_count" - + public static let count = "count" public static let textSizeInitial = "text_size_initial" public static let textSizeUpdated = "text_size_updated" - + public static let canAutoPreviewMIMEType = "can_auto_preview_mime_type" public static let mimeType = "mime_type" public static let fileSizeGreaterThan10MB = "file_size_greater_than_10mb" public static let downloadListCount = "download_list_count" - + public static let bookmarkCount = "bco" - + public static let isBackgrounded = "is_backgrounded" public static let isDataProtected = "is_data_protected" - + public static let isInternalUser = "is_internal_user" - + // Email manager public static let emailKeychainAccessType = "access_type" public static let emailKeychainError = "error" @@ -122,7 +122,7 @@ public struct PixelParameters { public static let function = "function" public static let line = "line" public static let reason = "reason" - + // Return user public static let returnUserErrorCode = "error_code" public static let returnUserOldATB = "old_atb" @@ -150,10 +150,10 @@ public class Pixel { case atb case appVersion } - + private init() { } - + public static func fire(pixel: Pixel.Event, forDeviceType deviceType: UIUserInterfaceIdiom? = UIDevice.current.userInterfaceIdiom, withAdditionalParameters params: [String: String] = [:], @@ -222,11 +222,11 @@ public class Pixel { onComplete(error) } } - + } extension Pixel { - + public static func fire(pixel: Pixel.Event, error: Error?, includedParameters: [QueryParameters] = [.appVersion], diff --git a/Core/PixelEvent.swift b/Core/PixelEvent.swift index 0814b1a731..796409c66b 100644 --- a/Core/PixelEvent.swift +++ b/Core/PixelEvent.swift @@ -24,9 +24,8 @@ import Configuration import DDGSync import NetworkProtection -// swiftlint:disable file_length extension Pixel { - + // swiftlint:disable:next type_body_length public enum Event { @@ -79,7 +78,7 @@ extension Pixel { case addressBarShare case addressBarSettings - + case shareSheetResultSuccess case shareSheetResultFail case shareSheetActivityCopy @@ -89,7 +88,7 @@ extension Pixel { case shareSheetActivityPrint case shareSheetActivityAddToReadingList case shareSheetActivityOther - + case tabBarBackPressed case tabBarForwardPressed case bookmarksButtonPressed @@ -106,7 +105,7 @@ extension Pixel { case feedbackPositive case feedbackNegativePrefix(category: String) - + case brokenSiteReport case daxDialogsSerp @@ -119,7 +118,7 @@ extension Pixel { case daxDialogsFireEducationShown case daxDialogsFireEducationConfirmed case daxDialogsFireEducationCancelled - + case defaultBrowserButtonPressedSettings case widgetsOnboardingCTAPressed @@ -166,14 +165,14 @@ extension Pixel { case downloadAttemptToOpenBLOBviaJS case jsAlertShown - + case featureFlaggingInternalUserAuthenticated case autofillLoginsSaveLoginModalDisplayed case autofillLoginsSaveLoginModalConfirmed case autofillLoginsSaveLoginModalDismissed case autofillLoginsSaveLoginModalExcludeSiteConfirmed - + case autofillLoginsSavePasswordModalDisplayed case autofillLoginsSavePasswordModalConfirmed case autofillLoginsSavePasswordModalDismissed @@ -185,7 +184,7 @@ extension Pixel { case autofillLoginsUpdateUsernameModalDisplayed case autofillLoginsUpdateUsernameModalConfirmed case autofillLoginsUpdateUsernameModalDismissed - + case autofillLoginsFillLoginInlineManualDisplayed case autofillLoginsFillLoginInlineManualConfirmed case autofillLoginsFillLoginInlineManualDismissed @@ -203,18 +202,18 @@ extension Pixel { case autofillLoginsFillLoginInlineDisablePromptShown case autofillLoginsFillLoginInlineDisablePromptAutofillKept case autofillLoginsFillLoginInlineDisablePromptAutofillDisabled - + case autofillSettingsOpened case autofillLoginsSettingsEnabled case autofillLoginsSettingsDisabled case autofillLoginsSettingsResetExcludedDisplayed case autofillLoginsSettingsResetExcludedConfirmed case autofillLoginsSettingsResetExcludedDismissed - + case autofillLoginsPasswordGenerationPromptDisplayed case autofillLoginsPasswordGenerationPromptConfirmed case autofillLoginsPasswordGenerationPromptDismissed - + case autofillJSPixelFired(_ pixel: AutofillUserScript.JSPixel) case secureVaultError @@ -238,13 +237,13 @@ extension Pixel { // MARK: AppTP case appTPBreakageReport - + case appTPFailedToCreateProxyServer case appTPFailedToSetTunnelNetworkSettings case appTPFailedToAccessPreferences case appTPFailedToAccessPreferencesDuringSetup case appTPFailedToStartTunnel - + case appTPVPNDisconnect case appTPVPNMemoryWarning case appTPVPNMemoryCritical @@ -260,9 +259,9 @@ extension Pixel { case appTPDBFeedbackTrackerFetchFailed case appTPDBTrackerStoreFailure case appTPCouldNotLoadDatabase - + // MARK: Network Protection - + case networkProtectionActiveUser case networkProtectionNewUser @@ -281,27 +280,27 @@ extension Pixel { case networkProtectionEnableAttemptConnecting case networkProtectionEnableAttemptSuccess case networkProtectionEnableAttemptFailure - + case networkProtectionTunnelFailureDetected case networkProtectionTunnelFailureRecovered - + case networkProtectionLatency(quality: NetworkProtectionLatencyMonitor.ConnectionQuality) case networkProtectionLatencyError - + case networkProtectionEnabledOnSearch - + case networkProtectionBreakageReport case networkProtectionRekeyAttempt case networkProtectionRekeyFailure case networkProtectionRekeyCompleted - + case networkProtectionTunnelConfigurationNoServerRegistrationInfo case networkProtectionTunnelConfigurationCouldNotSelectClosestServer case networkProtectionTunnelConfigurationCouldNotGetPeerPublicKey case networkProtectionTunnelConfigurationCouldNotGetPeerHostName case networkProtectionTunnelConfigurationCouldNotGetInterfaceAddressRange - + case networkProtectionClientFailedToFetchServerList case networkProtectionClientFailedToParseServerListResponse case networkProtectionClientFailedToEncodeRegisterKeyRequest @@ -314,38 +313,38 @@ extension Pixel { case networkProtectionClientFailedToRedeemInviteCode case networkProtectionClientFailedToParseRedeemResponse case networkProtectionClientInvalidAuthToken - + case networkProtectionServerListStoreFailedToEncodeServerList case networkProtectionServerListStoreFailedToDecodeServerList case networkProtectionServerListStoreFailedToWriteServerList case networkProtectionServerListStoreFailedToReadServerList - + case networkProtectionKeychainErrorFailedToCastKeychainValueToData case networkProtectionKeychainReadError case networkProtectionKeychainWriteError case networkProtectionKeychainUpdateError case networkProtectionKeychainDeleteError - + case networkProtectionWireguardErrorCannotLocateTunnelFileDescriptor case networkProtectionWireguardErrorInvalidState case networkProtectionWireguardErrorFailedDNSResolution case networkProtectionWireguardErrorCannotSetNetworkSettings case networkProtectionWireguardErrorCannotStartWireguardBackend - + case networkProtectionFailedToLoadFromPreferences case networkProtectionFailedToSaveToPreferences case networkProtectionActivationRequestFailed case networkProtectionFailedToStartTunnel - + case networkProtectionDisconnected - + case networkProtectionNoAuthTokenFoundError - + case networkProtectionMemoryWarning case networkProtectionMemoryCritical - + case networkProtectionUnhandledError - + case networkProtectionWaitlistUserActive case networkProtectionSettingsRowDisplayed case networkProtectionWaitlistIntroScreenDisplayed @@ -354,14 +353,14 @@ extension Pixel { case networkProtectionWaitlistNotificationShown case networkProtectionWaitlistNotificationLaunched case networkProtectionWaitlistRetriedInviteCodeRedemption - + case networkProtectionGeoswitchingOpened case networkProtectionGeoswitchingSetNearest case networkProtectionGeoswitchingSetCustom case networkProtectionGeoswitchingNoLocations - + // MARK: remote messaging pixels - + case remoteMessageShown case remoteMessageShownUnique case remoteMessageDismissed @@ -369,10 +368,10 @@ extension Pixel { case remoteMessagePrimaryActionClicked case remoteMessageSecondaryActionClicked case remoteMessageSheet - + // MARK: debug pixels case dbCrashDetected - + case dbMigrationError case dbRemovalError case dbDestroyError @@ -388,7 +387,7 @@ extension Pixel { case dbRemoteMessagingUpdateMessageStatusError case dbRemoteMessagingDeleteScheduledMessageError case dbLocalAuthenticationError - + case configurationFetchInfo case trackerDataParseFailed @@ -405,11 +404,11 @@ extension Pixel { case contentBlockingCompilationTime case ampBlockingRulesCompilationFailed - + case webKitDidTerminate case webKitTerminationDidReloadCurrentTab case webKitDidTerminateDuringWarmup - + case backgroundTaskSubmissionFailed case blankOverlayNotDismissed @@ -426,10 +425,10 @@ extension Pixel { case compilationResult(result: CompileRulesResult, waitTime: CompileRulesWaitTime, appState: AppState) case emailAutofillKeychainError - + case adAttributionGlobalAttributedRulesDoNotExist case adAttributionCompilationFailedForAttributedRulesList - + case adAttributionLogicUnexpectedStateOnInheritedAttribution case adAttributionLogicUnexpectedStateOnRulesCompiled case adAttributionLogicUnexpectedStateOnRulesCompilationFailed @@ -452,18 +451,18 @@ extension Pixel { case debugMissingTopFolderFixHasBookmarks case debugCantSaveBookmarkFix - + case debugCannotClearObservationsDatabase case debugWebsiteDataStoresNotClearedMultiple case debugWebsiteDataStoresNotClearedOne case debugCookieCleanupError - + case debugBookmarksMigratedMoreThanOnce - + // Return user measurement case debugReturnUserAddATB case debugReturnUserUpdateATB - + // Errors from Bookmarks Module case bookmarkFolderExpected case bookmarksListIndexNotMatchingBookmark @@ -483,7 +482,7 @@ extension Pixel { case bookmarksMigrationCouldNotPrepareDatabaseOnFailedMigration case bookmarksMigrationCouldNotRemoveOldStore case bookmarksMigrationCouldNotPrepareMultipleFavoriteFolders - + case syncSignupDirect case syncSignupConnect case syncLogin @@ -498,7 +497,7 @@ extension Pixel { case syncCredentialsCountLimitExceededDaily case syncBookmarksRequestSizeLimitExceededDaily case syncCredentialsRequestSizeLimitExceededDaily - + case syncSentUnauthenticatedRequest case syncMetadataCouldNotLoadDatabase case syncBookmarksFailed @@ -513,22 +512,23 @@ extension Pixel { case syncRemoveDeviceError case syncDeleteAccountError case syncLoginExistingAccountError + case syncWrongEnvironment case swipeTabsUsed case swipeTabsUsedDaily - + case bookmarksCleanupFailed case bookmarksCleanupAttemptedWhileSyncWasEnabled case favoritesCleanupFailed case bookmarksFaviconsFetcherStateStoreInitializationFailed case bookmarksFaviconsFetcherFailed - + case credentialsDatabaseCleanupFailed case credentialsCleanupAttemptedWhileSyncWasEnabled - + case invalidPayload(Configuration) - + case emailIncontextPromptDisplayed case emailIncontextPromptConfirmed case emailIncontextPromptDismissed @@ -537,9 +537,9 @@ extension Pixel { case emailIncontextModalDismissed case emailIncontextModalExitEarly case emailIncontextModalExitEarlyContinue - + case compilationFailed - + case appRatingPromptFetchError case userBehaviorReloadTwice @@ -549,6 +549,38 @@ extension Pixel { case userBehaviorReloadAndTogglePrivacyControls case userBehaviorFireButtonAndRestart case userBehaviorFireButtonAndTogglePrivacyControls + + // MARK: Privacy pro + case privacyProSubscriptionActive + case privacyProOfferScreenImpression + case privacyProPurchaseAttempt + case privacyProPurchaseFailure + case privacyProPurchaseFailureStoreError + case privacyProPurchaseFailureBackendError + case privacyProPurchaseFailureAccountNotCreated + case privacyProPurchaseSuccess + case privacyProRestorePurchaseOfferPageEntry + case privacyProRestorePurchaseEmailStart + case privacyProRestorePurchaseStoreStart + case privacyProRestorePurchaseEmailSuccess + case privacyProRestorePurchaseStoreSuccess + case privacyProRestorePurchaseStoreFailureNotFound + case privacyProRestorePurchaseStoreFailureOther + case privacyProRestoreAfterPurchaseAttempt + case privacyProSubscriptionActivated + case privacyProWelcomeAddDevice + case privacyProSettingsAddDevice + case privacyProAddDeviceEnterEmail + case privacyProWelcomeVPN + case privacyProWelcomePersonalInformationRemoval + case privacyProWelcomeIdentityRestoration + case privacyProSubscriptionSettings + case privacyProVPNSettings + case privacyProPersonalInformationRemovalSettings + case privacyProIdentityRestorationSettings + case privacyProSubscriptionManagementEmail + case privacyProSubscriptionManagementPlanBilling + case privacyProSubscriptionManagementRemoval } } @@ -562,14 +594,14 @@ extension Pixel.Event { case .appLaunch: return "ml" case .refreshPressed: return "m_r" case .pullToRefresh: return "m_pull-to-reload" - + case .forgetAllPressedBrowsing: return "mf_bp" case .forgetAllPressedTabSwitching: return "mf_tp" case .forgetAllExecuted: return "mf" case .forgetAllDataCleared: return "mf_dc" case .privacyDashboardOpened: return "mp" - + case .dashboardProtectionAllowlistAdd: return "mp_wla" case .dashboardProtectionAllowlistRemove: return "mp_wlr" @@ -582,7 +614,7 @@ extension Pixel.Event { case .settingsDoNotSellShown: return "ms_dns" case .settingsDoNotSellOn: return "ms_dns_on" case .settingsDoNotSellOff: return "ms_dns_off" - + case .settingsAutoconsentShown: return "m_settings_autoconsent_shown" case .settingsAutoconsentOn: return "m_settings_autoconsent_on" case .settingsAutoconsentOff: return "m_settings_autoconsent_off" @@ -604,9 +636,9 @@ extension Pixel.Event { case .browsingMenuReportBrokenSite: return "mb_rb" case .browsingMenuFireproof: return "mb_f" case .browsingMenuAutofill: return "m_nav_autofill_menu_item_pressed" - + case .browsingMenuShare: return "m_browsingmenu_share" - + case .addressBarShare: return "m_addressbar_share" case .addressBarSettings: return "m_addressbar_settings" case .shareSheetResultSuccess: return "m_sharesheet_result_success" @@ -618,7 +650,7 @@ extension Pixel.Event { case .shareSheetActivityPrint: return "m_sharesheet_activity_print" case .shareSheetActivityAddToReadingList: return "m_sharesheet_activity_addtoreadinglist" case .shareSheetActivityOther: return "m_sharesheet_activity_other" - + case .tabBarBackPressed: return "mt_bk" case .tabBarForwardPressed: return "mt_fw" case .bookmarksButtonPressed: return "mt_bm" @@ -632,10 +664,10 @@ extension Pixel.Event { case .autocompleteSelectedLocal: return "m_au_l" case .autocompleteSelectedRemote: return "m_au_r" - + case .feedbackPositive: return "mfbs_positive_submit" case .feedbackNegativePrefix(category: let category): return "mfbs_negative_\(category)" - + case .brokenSiteReport: return "epbf" case .daxDialogsSerp: return "m_dx_s" @@ -693,28 +725,28 @@ extension Pixel.Event { case .downloadsSharingPredownloadedLocalFile: return "m_downloads_sharing_predownloaded_local_file" case .downloadAttemptToOpenBLOBviaJS: return "m_download_attempt_to_open_blob_js" - + case .jsAlertShown: return "m_js_alert_shown" - + case .featureFlaggingInternalUserAuthenticated: return "m_internal-user_authenticated" - + case .autofillLoginsSaveLoginModalDisplayed: return "m_autofill_logins_save_login_inline_displayed" case .autofillLoginsSaveLoginModalConfirmed: return "m_autofill_logins_save_login_inline_confirmed" case .autofillLoginsSaveLoginModalDismissed: return "m_autofill_logins_save_login_inline_dismissed" case .autofillLoginsSaveLoginModalExcludeSiteConfirmed: return "m_autofill_logins_save_login_exclude_site_confirmed" - + case .autofillLoginsSavePasswordModalDisplayed: return "m_autofill_logins_save_password_inline_displayed" case .autofillLoginsSavePasswordModalConfirmed: return "m_autofill_logins_save_password_inline_confirmed" case .autofillLoginsSavePasswordModalDismissed: return "m_autofill_logins_save_password_inline_dismissed" - + case .autofillLoginsUpdatePasswordModalDisplayed: return "m_autofill_logins_update_password_inline_displayed" case .autofillLoginsUpdatePasswordModalConfirmed: return "m_autofill_logins_update_password_inline_confirmed" case .autofillLoginsUpdatePasswordModalDismissed: return "m_autofill_logins_update_password_inline_dismissed" - + case .autofillLoginsUpdateUsernameModalDisplayed: return "m_autofill_logins_update_username_inline_displayed" case .autofillLoginsUpdateUsernameModalConfirmed: return "m_autofill_logins_update_username_inline_confirmed" case .autofillLoginsUpdateUsernameModalDismissed: return "m_autofill_logins_update_username_inline_dismissed" - + case .autofillLoginsFillLoginInlineManualDisplayed: return "m_autofill_logins_fill_login_inline_manual_displayed" case .autofillLoginsFillLoginInlineManualConfirmed: return "m_autofill_logins_fill_login_inline_manual_confirmed" case .autofillLoginsFillLoginInlineManualDismissed: return "m_autofill_logins_fill_login_inline_manual_dismissed" @@ -745,11 +777,11 @@ extension Pixel.Event { case .autofillLoginsSettingsResetExcludedDisplayed: return "m_autofill_settings_reset_excluded_displayed" case .autofillLoginsSettingsResetExcludedConfirmed: return "m_autofill_settings_reset_excluded_confirmed" case .autofillLoginsSettingsResetExcludedDismissed: return "m_autofill_settings_reset_excluded_dismissed" - + case .autofillLoginsPasswordGenerationPromptDisplayed: return "m_autofill_logins_password_generation_prompt_displayed" case .autofillLoginsPasswordGenerationPromptConfirmed: return "m_autofill_logins_password_generation_prompt_confirmed" case .autofillLoginsPasswordGenerationPromptDismissed: return "m_autofill_logins_password_generation_prompt_dismissed" - + case .autofillJSPixelFired(let pixel): return "m_ios_\(pixel.pixelName)" @@ -760,19 +792,19 @@ extension Pixel.Event { case .secureVaultIsEnabledCheckedWhenEnabledAndDataProtected: return "m_secure-vault_is-enabled-checked_when-enabled-and-data-protected" - // MARK: Ad Click Attribution pixels + // MARK: Ad Click Attribution pixels case .adClickAttributionDetected: return "m_ad_click_detected" case .adClickAttributionActive: return "m_ad_click_active" case .adClickAttributionPageLoads: return "m_pageloads_with_ad_attribution" - // MARK: SERP pixels + // MARK: SERP pixels case .serpRequerySame: return "rq_0" case .serpRequeryNew: return "rq_1" - // MARK: AppTP pixels - + // MARK: AppTP pixels + case .appTPBreakageReport: return "m_apptp_breakage_report" case .appTPFailedToCreateProxyServer: return "m_apptp_failed_to_create_proxy_server" case .appTPFailedToSetTunnelNetworkSettings: return "m_apptp_failed_to_set_tunnel_network_settings" @@ -782,7 +814,7 @@ extension Pixel.Event { case .appTPVPNDisconnect: return "m_apptp_vpn_disconnect" case .appTPVPNMemoryWarning: return "m_apptp_vpn_memory_warning" case .appTPVPNMemoryCritical: return "m_apptp_vpn_memory_critical" - + case .appTPBlocklistParseFailed: return "m_apptp_blocklist_parse_failed" case .appTPActiveUser: return "m_apptp_active_user" case .appTPDBLocationFailed: return "m_apptp_db_location_not_found" @@ -793,9 +825,9 @@ extension Pixel.Event { case .appTPDBFeedbackTrackerFetchFailed: return "m_apptp_db_feedback_tracker_fetch_failed" case .appTPDBTrackerStoreFailure: return "m_apptp_db_tracker_store_failure" case .appTPCouldNotLoadDatabase: return "m_apptp_could_not_load_database" - - // MARK: Network Protection pixels - + + // MARK: Network Protection pixels + case .networkProtectionActiveUser: return "m_netp_daily_active_d" case .networkProtectionNewUser: return "m_netp_daily_active_u" case .networkProtectionControllerStartAttempt: return "m_netp_controller_start_attempt" @@ -862,7 +894,7 @@ extension Pixel.Event { case .networkProtectionMemoryWarning: return "m_netp_vpn_memory_warning" case .networkProtectionMemoryCritical: return "m_netp_vpn_memory_critical" case .networkProtectionUnhandledError: return "m_netp_unhandled_error" - + case .networkProtectionWaitlistUserActive: return "m_netp_waitlist_user_active" case .networkProtectionSettingsRowDisplayed: return "m_netp_waitlist_settings_entry_viewed" case .networkProtectionWaitlistIntroScreenDisplayed: return "m_netp_waitlist_intro_screen_viewed" @@ -871,14 +903,14 @@ extension Pixel.Event { case .networkProtectionWaitlistNotificationShown: return "m_netp_waitlist_notification_shown" case .networkProtectionWaitlistNotificationLaunched: return "m_netp_waitlist_notification_launched" case .networkProtectionWaitlistRetriedInviteCodeRedemption: return "m_netp_waitlist_retried_invite_code_redemption" - + case .networkProtectionGeoswitchingOpened: return "m_netp_imp_geoswitching" case .networkProtectionGeoswitchingSetNearest: return "m_netp_ev_geoswitching_set_nearest" case .networkProtectionGeoswitchingSetCustom: return "m_netp_ev_geoswitching_set_custom" case .networkProtectionGeoswitchingNoLocations: return "m_netp_ev_geoswitching_no_locations" - - // MARK: remote messaging pixels - + + // MARK: remote messaging pixels + case .remoteMessageShown: return "m_remote_message_shown" case .remoteMessageShownUnique: return "m_remote_message_shown_unique" case .remoteMessageDismissed: return "m_remote_message_dismissed" @@ -886,9 +918,9 @@ extension Pixel.Event { case .remoteMessagePrimaryActionClicked: return "m_remote_message_primary_action_clicked" case .remoteMessageSecondaryActionClicked: return "m_remote_message_secondary_action_clicked" case .remoteMessageSheet: return "m_remote_message_sheet" - - // MARK: debug pixels - + + // MARK: debug pixels + case .dbCrashDetected: return "m_d_crash" case .dbMigrationError: return "m_d_dbme" case .dbRemovalError: return "m_d_dbre" @@ -905,9 +937,9 @@ extension Pixel.Event { case .dbRemoteMessagingUpdateMessageStatusError: return "m_d_db_rm_update_message_status" case .dbRemoteMessagingDeleteScheduledMessageError: return "m_d_db_rm_delete_scheduled_message" case .dbLocalAuthenticationError: return "m_d_local_auth_error" - + case .debugBookmarksMigratedMoreThanOnce: return "m_debug_bookmarks_migrated-more-than-once" - + case .configurationFetchInfo: return "m_d_cfgfetch" case .trackerDataParseFailed: return "m_d_tds_p" @@ -924,7 +956,7 @@ extension Pixel.Event { case .contentBlockingCompilationTime: return "m_content_blocking_compilation_time" case .ampBlockingRulesCompilationFailed: return "m_debug_amp_rules_compilation_failed" - + case .webKitDidTerminate: return "m_d_wkt" case .webKitDidTerminateDuringWarmup: return "m_d_webkit-terminated-during-warmup" case .webKitTerminationDidReloadCurrentTab: return "m_d_wktct" @@ -946,25 +978,25 @@ extension Pixel.Event { return "m_compilation_result_\(result)_time_\(waitTime)_state_\(appState)" case .emailAutofillKeychainError: return "m_email_autofill_keychain_error" - + case .debugBookmarkOrphanFolderNew: return "m_d_bookmark_orphan_folder_new" case .debugBookmarkTopLevelMissingNew: return "m_d_bookmark_top_level_missing_new" case .debugCouldNotFixBookmarkFolder: return "m_d_cannot_fix_bookmark_folder" case .debugMissingTopFolderFixHasBookmarks: return "m_d_missing_top_folder_has_bookmarks" - + case .debugFavoriteOrphanFolderNew: return "m_d_favorite_orphan_folder_new" case .debugFavoriteTopLevelMissingNew: return "m_d_favorite_top_level_missing_new" case .debugCouldNotFixFavoriteFolder: return "m_d_cannot_fix_favorite_folder" case .debugMissingTopFolderFixHasFavorites: return "m_d_missing_top_folder_has_favorites" case .debugCantSaveBookmarkFix: return "m_d_cant_save_bookmark_fix" - + case .debugCannotClearObservationsDatabase: return "m_d_cannot_clear_observations_database" case .debugWebsiteDataStoresNotClearedMultiple: return "m_d_wkwebsitedatastoresnotcleared_multiple" case .debugWebsiteDataStoresNotClearedOne: return "m_d_wkwebsitedatastoresnotcleared_one" case .debugCookieCleanupError: return "m_d_cookie-cleanup-error" - - // MARK: Ad Attribution + + // MARK: Ad Attribution case .adAttributionGlobalAttributedRulesDoNotExist: return "m_attribution_global_attributed_rules_do_not_exist" case .adAttributionCompilationFailedForAttributedRulesList: return "m_attribution_compilation_failed_for_attributed_rules_list" @@ -997,7 +1029,7 @@ extension Pixel.Event { return "m_d_bookmarks_migration_could_not_prepare_database_on_failed_migration" case .bookmarksMigrationCouldNotRemoveOldStore: return "m_d_bookmarks_migration_could_not_remove_old_store" case .bookmarksMigrationCouldNotPrepareMultipleFavoriteFolders: return "m_d_bookmarks_migration_could_not_prepare_multiple_favorite_folders" - + case .syncSignupDirect: return "m_sync_signup_direct" case .syncSignupConnect: return "m_sync_signup_connect" case .syncLogin: return "m_sync_login" @@ -1012,7 +1044,7 @@ extension Pixel.Event { case .syncCredentialsCountLimitExceededDaily: return "m_d_sync_credentials_count_limit_exceeded_daily" case .syncBookmarksRequestSizeLimitExceededDaily: return "m_d_sync_bookmarks_request_size_limit_exceeded_daily" case .syncCredentialsRequestSizeLimitExceededDaily: return "m_d_sync_credentials_request_size_limit_exceeded_daily" - + case .syncSentUnauthenticatedRequest: return "m_d_sync_sent_unauthenticated_request" case .syncMetadataCouldNotLoadDatabase: return "m_d_sync_metadata_could_not_load_database" case .syncBookmarksFailed: return "m_d_sync_bookmarks_failed" @@ -1032,19 +1064,19 @@ extension Pixel.Event { case .swipeTabsUsed: return "m_swipe-tabs-used" case .swipeTabsUsedDaily: return "m_swipe-tabs-used-daily" - + case .bookmarksCleanupFailed: return "m_d_bookmarks_cleanup_failed" case .bookmarksCleanupAttemptedWhileSyncWasEnabled: return "m_d_bookmarks_cleanup_attempted_while_sync_was_enabled" case .favoritesCleanupFailed: return "m_d_favorites_cleanup_failed" case .bookmarksFaviconsFetcherStateStoreInitializationFailed: return "m_d_bookmarks_favicons_fetcher_state_store_initialization_failed" case .bookmarksFaviconsFetcherFailed: return "m_d_bookmarks_favicons_fetcher_failed" - + case .credentialsDatabaseCleanupFailed: return "m_d_credentials_database_cleanup_failed_2" case .credentialsCleanupAttemptedWhileSyncWasEnabled: return "m_d_credentials_cleanup_attempted_while_sync_was_enabled" - + case .invalidPayload(let configuration): return "m_d_\(configuration.rawValue)_invalid_payload".lowercased() - - // MARK: - InContext Email Protection + + // MARK: - InContext Email Protection case .emailIncontextPromptDisplayed: return "m_email_incontext_prompt_displayed" case .emailIncontextPromptConfirmed: return "m_email_incontext_prompt_confirmed" case .emailIncontextPromptDismissed: return "m_email_incontext_prompt_dismissed" @@ -1053,15 +1085,15 @@ extension Pixel.Event { case .emailIncontextModalDismissed: return "m_email_incontext_modal_dismissed" case .emailIncontextModalExitEarly: return "m_email_incontext_modal_exit_early" case .emailIncontextModalExitEarlyContinue: return "m_email_incontext_modal_exit_early_continue" - + case .compilationFailed: return "m_d_compilation_failed" - // MARK: - Return user measurement + // MARK: - Return user measurement case .debugReturnUserAddATB: return "m_debug_return_user_add_atb" case .debugReturnUserUpdateATB: return "m_debug_return_user_update_atb" - + case .appRatingPromptFetchError: return "m_d_app_rating_prompt_fetch_error" - // MARK: - User behavior + // MARK: - User behavior case .userBehaviorReloadTwice: return "m_reload-twice" case .userBehaviorReloadAndRestart: return "m_reload-and-restart" case .userBehaviorReloadAndFireButton: return "m_reload-and-fire-button" @@ -1069,12 +1101,43 @@ extension Pixel.Event { case .userBehaviorReloadAndTogglePrivacyControls: return "m_reload-and-toggle-privacy-controls" case .userBehaviorFireButtonAndRestart: return "m_fire-button-and-restart" case .userBehaviorFireButtonAndTogglePrivacyControls: return "m_fire-button-and-toggle-privacy-controls" + + // MARK: Privacy pro + case .privacyProSubscriptionActive: return "m_privacy-pro_app_subscription_active" + case .privacyProOfferScreenImpression: return "m_privacy-pro_offer_screen_impression" + case .privacyProPurchaseAttempt: return "m_privacy-pro_terms-conditions_subscribe_click" + case .privacyProPurchaseFailure: return "m_privacy-pro_app_subscription-purchase_failure_other" + case .privacyProPurchaseFailureStoreError: return "m_privacy-pro_app_subscription-purchase_failure_store" + case .privacyProPurchaseFailureAccountNotCreated: return "m_privacy-pro_app_subscription-purchase_failure_backend" + case .privacyProPurchaseFailureBackendError: return "m_privacy-pro_app_subscription-purchase_failure_account-creation" + case .privacyProPurchaseSuccess: return "m_privacy-pro_app_subscription-purchase_success" + case .privacyProRestorePurchaseOfferPageEntry: return "m_privacy-pro_offer_restore-purchase_click" + case .privacyProRestorePurchaseEmailStart: return "m_privacy-pro_activate-subscription_enter-email_click" + case .privacyProRestorePurchaseStoreStart: return "m_privacy-pro_activate-subscription_restore-purchase_click" + case .privacyProRestorePurchaseEmailSuccess: return "m_privacy-pro_app_subscription-restore-using-email_success" + case .privacyProRestorePurchaseStoreSuccess: return "m_privacy-pro_app_subscription-restore-using-store_success" + case .privacyProRestorePurchaseStoreFailureNotFound: return "m_privacy-pro_app_subscription-restore-using-store_failure_not-found" + case .privacyProRestorePurchaseStoreFailureOther: return "m_privacy-pro_app_subscription-restore-using-store_failure_other" + case .privacyProRestoreAfterPurchaseAttempt: return "m_privacy-pro_app_subscription-restore-after-purchase-attempt_success" + case .privacyProSubscriptionActivated: return "m_privacy-pro_app_subscription_activated_u" + case .privacyProWelcomeAddDevice: return "m_privacy-pro_welcome_add-device_click_u" + case .privacyProSettingsAddDevice: return "m_privacy-pro_settings_add-device_click" + case .privacyProAddDeviceEnterEmail: return "m_privacy-pro_add-device_enter-email_click" + case .privacyProWelcomeVPN: return "m_privacy-pro_welcome_vpn_click_u" + case .privacyProWelcomePersonalInformationRemoval: return "m_privacy-pro_welcome_personal-information-removal_click_u" + case .privacyProWelcomeIdentityRestoration: return "m_privacy-pro_welcome_identity-theft-restoration_click_u" + case .privacyProSubscriptionSettings: return "m_privacy-pro_settings_screen_impression" + case .privacyProVPNSettings: return "m_privacy-pro_app-settings_vpn_click" + case .privacyProPersonalInformationRemovalSettings: return "m_privacy-pro_app-settings_personal-information-removal_click" + case .privacyProIdentityRestorationSettings: return "m_privacy-pro_app-settings_identity-theft-restoration_click" + case .privacyProSubscriptionManagementEmail: return "m_privacy-pro_manage-email_edit_click" + case .privacyProSubscriptionManagementPlanBilling: return "m_privacy-pro_settings_change-plan-or-billing_click" + case .privacyProSubscriptionManagementRemoval: return "m_privacy-pro_settings_remove-from-device_click" } - } - } +// swiftlint:disable file_length extension Pixel.Event { public enum CompileRulesWaitTime: String, CustomStringConvertible { @@ -1128,9 +1191,9 @@ extension Pixel.Event { case regular } - - public enum CompileRulesListType: String, CustomStringConvertible { + public enum CompileRulesListType: String, CustomStringConvertible { + public var description: String { rawValue } case tds diff --git a/Core/PreserveLogins.swift b/Core/PreserveLogins.swift index 2c747a4abd..6ce320e0ef 100644 --- a/Core/PreserveLogins.swift +++ b/Core/PreserveLogins.swift @@ -20,13 +20,13 @@ import Foundation public class PreserveLogins { - + public struct Notifications { public static let loginDetectionStateChanged = Foundation.Notification.Name("com.duckduckgo.ios.PreserveLogins.loginDetectionStateChanged") } - + public static let shared = PreserveLogins() - + @UserDefaultsWrapper(key: .preserveLoginsAllowedDomains, defaultValue: []) private(set) public var allowedDomains: [String] @@ -36,7 +36,7 @@ public class PreserveLogins { NotificationCenter.default.post(name: Notifications.loginDetectionStateChanged, object: nil) } } - + public func addToAllowed(domain: String) { allowedDomains += [domain] } @@ -55,7 +55,7 @@ public class PreserveLogins { public func clearAll() { allowedDomains = [] } - + public func isAllowed(fireproofDomain domain: String) -> Bool { return allowedDomains.contains(domain) } diff --git a/Core/PrivacyFeatures.swift b/Core/PrivacyFeatures.swift index 977a5d94f9..422484ce5b 100644 --- a/Core/PrivacyFeatures.swift +++ b/Core/PrivacyFeatures.swift @@ -58,5 +58,5 @@ public final class PrivacyFeatures { } public static let httpsUpgrade = HTTPSUpgrade(store: httpsUpgradeStore, privacyManager: ContentBlocking.shared.privacyConfigurationManager) - + } diff --git a/Core/ReturnUserMeasurement.swift b/Core/ReturnUserMeasurement.swift index f4a9507a08..ba33d836ab 100644 --- a/Core/ReturnUserMeasurement.swift +++ b/Core/ReturnUserMeasurement.swift @@ -105,7 +105,7 @@ class KeychainReturnUserMeasurement: ReturnUserMeasurement { kSecClass as String: secClassCFString, kSecMatchLimit as String: kSecMatchLimitOne, kSecReturnAttributes as String: true, // Needs to be true or returns nothing. - kSecReturnRef as String: true, + kSecReturnRef as String: true ] var returnArrayRef: CFTypeRef? let status = SecItemCopyMatching(query as CFDictionary, &returnArrayRef) diff --git a/Core/SchemeHandler.swift b/Core/SchemeHandler.swift index 75acbae352..605e5c58df 100644 --- a/Core/SchemeHandler.swift +++ b/Core/SchemeHandler.swift @@ -21,13 +21,13 @@ import Foundation import BrowserServicesKit public class SchemeHandler { - + public enum Action: Equatable { case open case askForConfirmation case cancel } - + public enum SchemeType: Equatable { case navigational case external(Action) @@ -51,14 +51,14 @@ public class SchemeHandler { case shortcutsProduction = "shortcuts-production" case workflow } - + private enum BlockedScheme: String { case appleDataDetectors = "x-apple-data-detectors" } - + public static func schemeType(for url: URL) -> SchemeType { guard let schemeString = url.scheme else { return .unknown } - + guard BlockedScheme(rawValue: schemeString) == nil else { return .external(.cancel) } diff --git a/Core/StatisticsLoader.swift b/Core/StatisticsLoader.swift index 8b7a6e481c..3572d40dc0 100644 --- a/Core/StatisticsLoader.swift +++ b/Core/StatisticsLoader.swift @@ -23,21 +23,21 @@ import BrowserServicesKit import Networking public class StatisticsLoader { - + public typealias Completion = (() -> Void) - + public static let shared = StatisticsLoader() - + private let statisticsStore: StatisticsStore private let returnUserMeasurement: ReturnUserMeasurement private let parser = AtbParser() - + init(statisticsStore: StatisticsStore = StatisticsUserDefaults(), returnUserMeasurement: ReturnUserMeasurement = KeychainReturnUserMeasurement()) { self.statisticsStore = statisticsStore self.returnUserMeasurement = returnUserMeasurement } - + public func load(completion: @escaping Completion = {}) { if statisticsStore.hasInstallStatistics { completion() @@ -45,18 +45,18 @@ public class StatisticsLoader { } requestInstallStatistics(completion: completion) } - + private func requestInstallStatistics(completion: @escaping Completion = {}) { let configuration = APIRequest.Configuration(url: .atb) let request = APIRequest(configuration: configuration, urlSession: .session()) - + request.fetch { response, error in if let error = error { os_log("Initial atb request failed with error %s", log: .generalLog, type: .debug, error.localizedDescription) completion() return } - + if let data = response?.data, let atb = try? self.parser.convert(fromJsonData: data) { self.requestExti(atb: atb, completion: completion) } else { @@ -64,14 +64,14 @@ public class StatisticsLoader { } } } - + private func requestExti(atb: Atb, completion: @escaping Completion = {}) { let installAtb = atb.version + (statisticsStore.variant ?? "") let url = URL.makeExtiURL(atb: installAtb) - + let configuration = APIRequest.Configuration(url: url) let request = APIRequest(configuration: configuration, urlSession: .session()) - + request.fetch { _, error in if let error = error { os_log("Exti request failed with error %s", log: .generalLog, type: .debug, error.localizedDescription) @@ -84,7 +84,7 @@ public class StatisticsLoader { completion() } } - + public func refreshSearchRetentionAtb(completion: @escaping Completion = {}) { guard let url = StatisticsDependentURLFactory(statisticsStore: statisticsStore).makeSearchAtbURL() else { requestInstallStatistics(completion: completion) @@ -93,7 +93,7 @@ public class StatisticsLoader { let configuration = APIRequest.Configuration(url: url) let request = APIRequest(configuration: configuration, urlSession: .session()) - + request.fetch { response, error in if let error = error { os_log("Search atb request failed with error %s", log: .generalLog, type: .debug, error.localizedDescription) @@ -107,7 +107,7 @@ public class StatisticsLoader { completion() } } - + public func refreshAppRetentionAtb(completion: @escaping Completion = {}) { guard let url = StatisticsDependentURLFactory(statisticsStore: statisticsStore).makeAppAtbURL() else { requestInstallStatistics(completion: completion) @@ -116,7 +116,7 @@ public class StatisticsLoader { let configuration = APIRequest.Configuration(url: url) let request = APIRequest(configuration: configuration, urlSession: .session()) - + request.fetch { response, error in if let error = error { os_log("App atb request failed with error %s", log: .generalLog, type: .debug, error.localizedDescription) diff --git a/Core/StatisticsUserDefaults.swift b/Core/StatisticsUserDefaults.swift index 290db449d0..0ff51cc809 100644 --- a/Core/StatisticsUserDefaults.swift +++ b/Core/StatisticsUserDefaults.swift @@ -56,7 +56,7 @@ public class StatisticsUserDefaults: StatisticsStore { userDefaults?.setValue(newValue, forKey: Keys.atb) } } - + public var installDate: Date? { get { guard let interval = userDefaults?.double(forKey: Keys.installDate), interval > 0 else { @@ -77,7 +77,7 @@ public class StatisticsUserDefaults: StatisticsStore { userDefaults?.setValue(newValue, forKey: Keys.searchRetentionAtb) } } - + public var appRetentionAtb: String? { get { return userDefaults?.string(forKey: Keys.appRetentionAtb) ?? atb diff --git a/Core/StorageCache.swift b/Core/StorageCache.swift index 27ea656435..1336fdfef0 100644 --- a/Core/StorageCache.swift +++ b/Core/StorageCache.swift @@ -22,15 +22,15 @@ import BrowserServicesKit import Common public class StorageCache { - + public let tld: TLD - + public init() { tld = TLD() } - + public init(tld: TLD) { self.tld = tld } - + } diff --git a/Core/StringExtension.swift b/Core/StringExtension.swift index f0ad33ba90..b79146a7ea 100644 --- a/Core/StringExtension.swift +++ b/Core/StringExtension.swift @@ -26,7 +26,7 @@ extension String { public func format(arguments: CVarArg...) -> String { return String(format: self, arguments: arguments) } - + public func sha256() -> String { if let stringData = self.data(using: String.Encoding.utf8) { return stringData.sha256 @@ -40,17 +40,17 @@ extension String { verticalOffset: CGFloat = 0.0) -> NSAttributedString? { let components = self.components(separatedBy: placeholder) guard components.count > 1 else { return nil } - + let attachment = NSTextAttachment() attachment.image = image attachment.bounds = CGRect(x: 0, y: verticalOffset, width: image.size.width, height: image.size.height) let attachmentString = NSAttributedString(attachment: attachment) - + let paddingAttachment = NSTextAttachment() paddingAttachment.bounds = CGRect(x: 0, y: 0, width: horizontalPadding, height: 0) let startPadding = NSAttributedString(attachment: paddingAttachment) let endPadding = NSAttributedString(attachment: paddingAttachment) - + let firstString = NSMutableAttributedString(string: components[0]) for component in components.dropFirst() { let endString = NSMutableAttributedString(string: component) diff --git a/Core/TabInstrumentation.swift b/Core/TabInstrumentation.swift index 86c40b5c3e..039623cb46 100644 --- a/Core/TabInstrumentation.swift +++ b/Core/TabInstrumentation.swift @@ -21,31 +21,31 @@ import Foundation import os.signpost public class TabInstrumentation { - + static let tabsLog = OSLog(subsystem: "com.duckduckgo.instrumentation", category: "TabInstrumentation") - + static var tabMaxIdentifier: UInt64 = 0 - + private var siteLoadingSPID: Any? private var currentURL: String? private var currentTabIdentifier: UInt64 - + public init() { type(of: self).tabMaxIdentifier += 1 currentTabIdentifier = type(of: self).tabMaxIdentifier } - + private var tabInitSPID: Any? - + public func willPrepareWebView() { tabInitSPID = Instruments.shared.startTimedEvent(.tabInitialisation, info: "Tab-\(currentTabIdentifier)") } - + public func didPrepareWebView() { Instruments.shared.endTimedEvent(for: tabInitSPID) } - + public func willLoad(url: URL) { currentURL = url.absoluteString if #available(iOSApplicationExtension 12.0, *) { @@ -58,7 +58,7 @@ public class TabInstrumentation { "Loading URL: %@ in %llu", url.absoluteString, currentTabIdentifier) } } - + public func didLoadURL() { if #available(iOSApplicationExtension 12.0, *), let id = siteLoadingSPID as? OSSignpostID { @@ -69,42 +69,42 @@ public class TabInstrumentation { "Loading Finished: %{private}@", "T") } } - + // MARK: - JS events - + public func request(url: String, allowedIn timeInMs: Double) { request(url: url, isTracker: false, blocked: false, in: timeInMs) } - + public func tracker(url: String, allowedIn timeInMs: Double, reason: String?) { request(url: url, isTracker: true, blocked: false, reason: reason ?? "?", in: timeInMs) } - + public func tracker(url: String, blockedIn timeInMs: Double) { request(url: url, isTracker: true, blocked: true, in: timeInMs) } - + private func request(url: String, isTracker: Bool, blocked: Bool, reason: String = "", in timeInMs: Double) { if #available(iOSApplicationExtension 12.0, *) { let currentURL = self.currentURL ?? "unknown" let requestType = isTracker ? "Tracker" : "Regular" let status = blocked ? "Blocked" : "Allowed" - + // 0 is treated as 1ms let timeInNS: UInt64 = timeInMs.asNanos - + os_log(.debug, log: type(of: self).tabsLog, "[%@] Request: %@ - %@ - %@ (%@) in %llu", currentURL, url, requestType, status, reason, timeInNS) } } - + public func jsEvent(name: String, executedIn timeInMs: Double) { if #available(iOSApplicationExtension 12.0, *) { let currentURL = self.currentURL ?? "unknown" // 0 is treated as 1ms let timeInNS: UInt64 = timeInMs.asNanos - + os_log(.debug, log: type(of: self).tabsLog, "[%@] JSEvent: %@ executedIn: %llu", currentURL, name, timeInNS) diff --git a/Core/TextFieldWithInsets.swift b/Core/TextFieldWithInsets.swift index 11a378026e..c367c82f9e 100644 --- a/Core/TextFieldWithInsets.swift +++ b/Core/TextFieldWithInsets.swift @@ -21,7 +21,7 @@ import UIKit @IBDesignable class TextFieldWithInsets: UITextField { - + var onCopyAction: ((UITextField) -> Void)? @IBInspectable var leftInset: CGFloat = 0 @@ -52,7 +52,7 @@ class TextFieldWithInsets: UITextField { let height = bounds.size.height - topInset - bottomInset return CGRect(x: x, y: y, width: width, height: height) } - + override func copy(_ sender: Any?) { if let action = onCopyAction { action(self) diff --git a/Core/TextSizeUserScript.swift b/Core/TextSizeUserScript.swift index a24d9aa6d1..a246d48e63 100644 --- a/Core/TextSizeUserScript.swift +++ b/Core/TextSizeUserScript.swift @@ -22,25 +22,25 @@ import WebKit import UserScript public class TextSizeUserScript: NSObject, UserScript { - + public static let knownDynamicTypeExceptions: [String] = ["wikipedia.org"] public var textSizeAdjustmentInPercents: Int = 100 - + public var source: String { TextSizeUserScript.makeSource(for: textSizeAdjustmentInPercents) } public var injectionTime: WKUserScriptInjectionTime = .atDocumentStart public var forMainFrameOnly: Bool = false public var messageNames: [String] = [] - + public init(textSizeAdjustmentInPercents: Int) { self.textSizeAdjustmentInPercents = textSizeAdjustmentInPercents } public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { } - + fileprivate static func makeSource(for textSizeAdjustmentInPercents: Int) -> String { let dynamicTypeScalePercentage = UIFontMetrics.default.scaledValue(for: 100) - + return loadJS("textsize", from: Bundle.core, withReplacements: [ "$KNOWN_DYNAMIC_TYPE_EXCEPTIONS$": knownDynamicTypeExceptions.joined(separator: "\n"), "$TEXT_SIZE_ADJUSTMENT_IN_PERCENTS$": "\(textSizeAdjustmentInPercents)", @@ -50,7 +50,7 @@ public class TextSizeUserScript: NSObject, UserScript { } public extension WKWebView { - + func adjustTextSize(_ percentage: Int) { let jsString = TextSizeUserScript.makeSource(for: percentage) evaluateJavaScript(jsString, completionHandler: nil) diff --git a/Core/TimeIntervalExtension.swift b/Core/TimeIntervalExtension.swift index 825bd80577..00f8492ee4 100644 --- a/Core/TimeIntervalExtension.swift +++ b/Core/TimeIntervalExtension.swift @@ -20,7 +20,7 @@ import Foundation extension TimeInterval { - + // MARK: - Computed Type Properties internal static var secondsPerDay: Double { return 24 * 60 * 60 } internal static var secondsPerHour: Double { return 60 * 60 } diff --git a/Core/TimedPixel.swift b/Core/TimedPixel.swift index e51591f80b..8f9afb9d45 100644 --- a/Core/TimedPixel.swift +++ b/Core/TimedPixel.swift @@ -20,20 +20,20 @@ import Foundation public class TimedPixel { - + let pixel: Pixel.Event let date: Date - + public init(_ pixel: Pixel.Event, date: Date = Date()) { self.pixel = pixel self.date = date } - + public func fire(_ fireDate: Date = Date(), withAdditionalParameters params: [String: String] = [:]) { let duration = String(fireDate.timeIntervalSince(date)) var newParams = params newParams[PixelParameters.duration] = duration Pixel.fire(pixel: pixel, withAdditionalParameters: newParams) } - + } diff --git a/Core/UIColorExtension.swift b/Core/UIColorExtension.swift index 20a49393d1..66cfb360b6 100644 --- a/Core/UIColorExtension.swift +++ b/Core/UIColorExtension.swift @@ -28,7 +28,7 @@ extension UIColor { public static var nearlyBlackLight: UIColor { return UIColor(red: 51.0 / 255.0, green: 51.0 / 255.0, blue: 51.0 / 255.0, alpha: 1.0) } - + public static var nearlyBlack: UIColor { return UIColor(red: 34.0 / 255.0, green: 34.0 / 255.0, blue: 34.0 / 255.0, alpha: 1.0) } @@ -36,11 +36,11 @@ extension UIColor { public static var charcoalGrey: UIColor { return UIColor(red: 68.0 / 255.0, green: 68.0 / 255.0, blue: 68.0 / 255.0, alpha: 1.0) } - + public static var greyishBrown: UIColor { return UIColor(red: 85.0 / 255.0, green: 85.0 / 255.0, blue: 85.0 / 255.0, alpha: 1.0) } - + public static var greyishBrown2: UIColor { return UIColor(red: 102.0 / 255.0, green: 102.0 / 255.0, blue: 102.0 / 255.0, alpha: 1.0) } @@ -48,19 +48,19 @@ extension UIColor { public static var greyish: UIColor { return UIColor(red: 170.0 / 255.0, green: 170.0 / 255.0, blue: 170.0 / 255.0, alpha: 1.0) } - + public static var greyish2: UIColor { return UIColor(red: 153.0 / 255.0, green: 153.0 / 255.0, blue: 153.0 / 255.0, alpha: 1.0) } - + public static var greyish3: UIColor { return UIColor(red: 136.0 / 255.0, green: 136.0 / 255.0, blue: 136.0 / 255.0, alpha: 1.0) } - + public static var lightGreyish: UIColor { return UIColor(red: 234.0 / 255.0, green: 234.0 / 255.0, blue: 234.0 / 255.0, alpha: 1.0) } - + public static var gray20: UIColor { return UIColor(red: 238.0 / 255.0, green: 238.0 / 255.0, blue: 238.0 / 255.0, alpha: 1.0) } @@ -72,7 +72,7 @@ extension UIColor { public static var darkGreyish: UIColor { return UIColor(red: 73.0 / 255.0, green: 73.0 / 255.0, blue: 73.0 / 255.0, alpha: 1.0) } - + public static var lightMercury: UIColor { return UIColor(red: 204.0 / 255.0, green: 204.0 / 255.0, blue: 204.0 / 255.0, alpha: 1.0) } @@ -84,39 +84,39 @@ extension UIColor { public static var cornflowerBlue: UIColor { return UIColor(red: 103.0 / 255.0, green: 143.0 / 255.0, blue: 255.0 / 255.0, alpha: 1.0) } - + public static var cornflowerDark: UIColor { return UIColor(red: 80.0 / 255.0, green: 120.0 / 255.0, blue: 233.0 / 255.0, alpha: 1.0) } - + public static var skyBlue: UIColor { return UIColor(red: 66.0 / 255.0, green: 191.0 / 255.0, blue: 254.0 / 255.0, alpha: 1.0) } - + public static var skyBlueLight: UIColor { return UIColor(red: 120.0 / 255.0, green: 210.0 / 255.0, blue: 255.0 / 255.0, alpha: 1.0) } - + public static var midGreen: UIColor { return UIColor(red: 63.0 / 255.0, green: 161.0 / 255.0, blue: 64.0 / 255.0, alpha: 1.0) } - + public static var orange: UIColor { return UIColor(red: 222.0 / 255.0, green: 88.0 / 255.0, blue: 51.0 / 255.0, alpha: 1.0) } - + public static var orangeLight: UIColor { return UIColor(red: 255.0 / 255.0, green: 135.0 / 255.0, blue: 75.0 / 255.0, alpha: 1.0) } - + public static var nearlyWhiteLight: UIColor { return UIColor(red: 250.0 / 255.0, green: 250.0 / 255.0, blue: 250.0 / 255.0, alpha: 1.0) } - + public static var nearlyWhite: UIColor { return UIColor(red: 245.0 / 255.0, green: 245.0 / 255.0, blue: 245.0 / 255.0, alpha: 1.0) } - + public static var destructive: UIColor { return UIColor.systemRed } @@ -124,7 +124,7 @@ extension UIColor { public static var yellow60: UIColor { return UIColor(hex: "F9BE1A") } - + } extension UIColor { diff --git a/Core/UIKeyCommandExtension.swift b/Core/UIKeyCommandExtension.swift index 1615f0f876..45767ea865 100644 --- a/Core/UIKeyCommandExtension.swift +++ b/Core/UIKeyCommandExtension.swift @@ -20,9 +20,9 @@ import Foundation public extension UIKeyCommand { - + static let inputBackspace = String(UnicodeScalar(8)) static let inputTab = String(UnicodeScalar(9)) static let inputEnter = String(UnicodeScalar(13)) - + } diff --git a/Core/UIViewControllerExtension.swift b/Core/UIViewControllerExtension.swift index 4e91074719..4e787b286b 100644 --- a/Core/UIViewControllerExtension.swift +++ b/Core/UIViewControllerExtension.swift @@ -21,11 +21,11 @@ import UIKit import Core extension UIViewController { - + var isSmall: Bool { return view.frame.height <= 568 } - + var isPad: Bool { return UIDevice.current.userInterfaceIdiom == .pad } @@ -77,7 +77,7 @@ extension UIViewController { } present(controller, animated: true, completion: nil) } - + public func installChildViewController(_ childController: UIViewController) { addChild(childController) childController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight] @@ -114,7 +114,7 @@ extension Core.Bookmark { // Unfortuntely required to make methods available to objc extension Core.BookmarkManagedObject: UIActivityItemSource { - + @objc public func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any { (self as Bookmark).activityViewControllerPlaceholderItem(activityViewController) } @@ -141,12 +141,12 @@ extension Core.Link: UIActivityItemSource { public func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? { - + // We don't want to save localPath to favorites or bookmarks if let localFileURL = localFileURL, activityType != .saveBookmarkInDuckDuckGo, activityType != .saveFavoriteInDuckDuckGo { - + return localFileURL } return url.removingInternalSearchParameters() diff --git a/Core/UIViewExtension.swift b/Core/UIViewExtension.swift index d1cea20ad8..6b7084ce58 100644 --- a/Core/UIViewExtension.swift +++ b/Core/UIViewExtension.swift @@ -73,7 +73,7 @@ extension UIView { view.removeFromSuperview() } } - + @MainActor public func createImageSnapshot(inBounds bounds: CGRect? = nil) -> UIImage? { let bounds = bounds ?? self.frame @@ -85,5 +85,5 @@ extension UIView { UIGraphicsEndImageContext() return image } - + } diff --git a/Core/URLFileExtension.swift b/Core/URLFileExtension.swift index 286a728c7b..ccc4a4913e 100644 --- a/Core/URLFileExtension.swift +++ b/Core/URLFileExtension.swift @@ -26,14 +26,14 @@ extension URL { public var creation: Date? { (try? resourceValues(forKeys: [.creationDateKey]))?.creationDate } - + /// The time at which the resource was most recently accessed. /// This key corresponds to an Date value, or nil if the volume doesn't support access dates. /// When you set the contentAccessDateKey for a resource, also set contentModificationDateKey in the same call to the setResourceValues(_:) method. Otherwise, the file system may set the contentAccessDateKey value to the current contentModificationDateKey value. public var contentAccess: Date? { (try? resourceValues(forKeys: [.contentAccessDateKey]))?.contentAccessDate } - + public var fileSize: Int? { return (try? resourceValues(forKeys: [.fileSizeKey]))?.fileSize } diff --git a/Core/UserAgentManager.swift b/Core/UserAgentManager.swift index 32f90094d2..6ecc0c7013 100644 --- a/Core/UserAgentManager.swift +++ b/Core/UserAgentManager.swift @@ -32,32 +32,31 @@ public protocol UserAgentManager { func update(webView: WKWebView, isDesktop: Bool, url: URL?) func userAgent(isDesktop: Bool) -> String - + } public class DefaultUserAgentManager: UserAgentManager { - + public static let shared: UserAgentManager = DefaultUserAgentManager() private var userAgent = UserAgent() - + init() { prepareUserAgent() } - + private func prepareUserAgent() { let webview = WKWebView() webview.load(URLRequest.developerInitiated(#URL("about:blank"))) - getDefaultAgent(webView: webview) { [weak self] agent in // Reference webview instance to keep it in scope and allow UA to be returned _ = webview - + guard let defaultAgent = agent else { return } self?.userAgent = UserAgent(defaultAgent: defaultAgent) } } - + public func userAgent(isDesktop: Bool) -> String { return userAgent.agent(forUrl: nil, isDesktop: isDesktop) } @@ -70,21 +69,21 @@ public class DefaultUserAgentManager: UserAgentManager { let agent = userAgent.agent(forUrl: url, isDesktop: isDesktop) webView.customUserAgent = agent } - + private func getDefaultAgent(webView: WKWebView, completion: @escaping (String?) -> Void) { webView.evaluateJavaScript("navigator.userAgent") { (result, _) in let agent = result as? String completion(agent) } } - + public static var duckDuckGoUserAgent: String { duckduckGoUserAgent(for: AppVersion.shared) } - + public static func duckduckGoUserAgent(for appVersion: AppVersion) -> String { let osVersion = UIDevice.current.systemVersion return "ddg_ios/\(appVersion.versionAndBuildNumber) (\(appVersion.identifier); iOS \(osVersion))" } - + } struct UserAgent { @@ -97,7 +96,7 @@ struct UserAgent { case brand } - + private enum Constants { // swiftlint:disable line_length static let fallbackWekKitVersion = "605.1.15" @@ -105,7 +104,7 @@ struct UserAgent { static let fallbackDefaultAgent = "Mozilla/5.0 (iPhone; CPU iPhone OS 13_5 like Mac OS X) AppleWebKit/\(fallbackWekKitVersion) (KHTML, like Gecko) Mobile/15E148" static let desktopPrefixComponent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15)" static let fallbackVersionComponent = "Version/13.1.1" - + static let uaOmitSitesConfigKey = "omitApplicationSites" static let uaOmitDomainConfigKey = "domain" @@ -120,13 +119,13 @@ struct UserAgent { static let uaStateKey = "state" // swiftlint:enable line_length } - + private struct Regex { static let suffix = "(AppleWebKit/.*) Mobile" static let webKitVersion = "AppleWebKit/([^ ]+) " static let osVersion = " OS ([0-9_]+)" } - + private let baseAgent: String private let baseDesktopAgent: String private let version: String @@ -146,11 +145,11 @@ struct UserAgent { brandComponent = UserAgent.createBrandComponent(withVersion: version) self.statistics = statistics } - + private func omitApplicationSites(forConfig config: PrivacyConfiguration) -> [String] { let uaSettings = config.settings(for: .customUserAgent) let omitApplicationObjs = uaSettings[Constants.uaOmitSitesConfigKey] as? [[String: String]] ?? [] - + return omitApplicationObjs.map { $0[Constants.uaOmitDomainConfigKey] ?? "" } } @@ -346,11 +345,11 @@ struct UserAgent { private static func createSafariComponent(fromAgent agent: String) -> String { let regex = try? NSRegularExpression(pattern: Regex.webKitVersion) let match = regex?.firstMatch(in: agent, options: [], range: NSRange(location: 0, length: agent.count)) - + guard let range = match?.range(at: 1) else { return Constants.fallbackSafariComponent } - + let version = (agent as NSString).substring(with: range) return "Safari/\(version)" } @@ -360,29 +359,29 @@ struct UserAgent { private static func createBaseAgent(fromAgent agent: String, versionComponent: String) -> String { var agentComponents = agent.split(separator: " ") - + guard !agentComponents.isEmpty else { return agent } - + agentComponents.insert(.init(versionComponent), at: agentComponents.endIndex - 1) return agentComponents.joined(separator: " ") } - + private static func createBaseDesktopAgent(fromAgent agent: String, versionComponent: String) -> String { let regex = try? NSRegularExpression(pattern: Regex.suffix) let match = regex?.firstMatch(in: agent, options: [], range: NSRange(location: 0, length: agent.count)) - + guard let range = match?.range(at: 1) else { return createBaseDesktopAgent(fromAgent: Constants.fallbackDefaultAgent, versionComponent: versionComponent) } - + let suffix = (agent as NSString).substring(with: range) return "\(Constants.desktopPrefixComponent) \(suffix) \(versionComponent)" } - + } private extension StatisticsStore { diff --git a/Core/UserDefaultsExtension.swift b/Core/UserDefaultsExtension.swift index eeb1bea03b..f8ff4afeaf 100644 --- a/Core/UserDefaultsExtension.swift +++ b/Core/UserDefaultsExtension.swift @@ -21,7 +21,7 @@ import Foundation extension UserDefaults { public static var app = UserDefaults.standard - + public func bool(forKey key: String, defaultValue: Bool) -> Bool { return object(forKey: key) as? Bool ?? defaultValue } diff --git a/Core/UserDefaultsPropertyWrapper.swift b/Core/UserDefaultsPropertyWrapper.swift index 9ab2131af7..bb95c04c97 100644 --- a/Core/UserDefaultsPropertyWrapper.swift +++ b/Core/UserDefaultsPropertyWrapper.swift @@ -32,10 +32,10 @@ public struct UserDefaultsWrapper { case favorites = "com.duckduckgo.ios.home.favorites" case keyboardOnNewTab = "com.duckduckgo.ios.keyboard.newtab" case keyboardOnAppLaunch = "com.duckduckgo.ios.keyboard.applaunch" - + case gridViewEnabled = "com.duckduckgo.ios.tabs.grid" case gridViewSeen = "com.duckduckgo.ios.tabs.seen" - + case preserveLoginsAllowedDomains = "com.duckduckgo.ios.PreserveLogins.userDecision.allowedDomains2" case preserveLoginsDetectionEnabled = "com.duckduckgo.ios.PreserveLogins.detectionEnabled" case preserveLoginsLegacyAllowedDomains = "com.duckduckgo.ios.PreserveLogins.userDecision.allowedDomains" @@ -55,7 +55,7 @@ public struct UserDefaultsWrapper { case faviconTabsCacheNeedsCleanup = "com.duckduckgo.ios.favicons.tabsCacheNeedsCleanup" case legacyCovidInfo = "com.duckduckgo.ios.home.covidInfo" - + case lastConfigurationRefreshDate = "com.duckduckgo.ios.lastConfigurationRefreshDate" case lastConfigurationUpdateDate = "com.duckduckgo.ios.lastConfigurationUpdateDate" case lastRemoteMessagingRefreshDate = "com.duckduckgo.ios.lastRemoteMessagingRefreshDate" @@ -73,7 +73,7 @@ public struct UserDefaultsWrapper { case emailWaitlistShouldReceiveNotifications = "com.duckduckgo.ios.showWaitlistNotification" case unseenDownloadsAvailable = "com.duckduckgo.app.unseenDownloadsAvailable" - + case lastCompiledRules = "com.duckduckgo.app.lastCompiledRules" case autofillSaveModalRejectionCount = "com.duckduckgo.ios.autofillSaveModalRejectionCount" @@ -85,9 +85,9 @@ public struct UserDefaultsWrapper { // .v2 suffix added to fix https://app.asana.com/0/547792610048271/1206524375402369/f case featureFlaggingDidVerifyInternalUser = "com.duckduckgo.app.featureFlaggingDidVerifyInternalUser.v2" - + case voiceSearchEnabled = "com.duckduckgo.app.voiceSearchEnabled" - + case autoconsentEnabled = "com.duckduckgo.ios.autoconsentEnabled" case shouldScheduleRulesCompilationOnAppLaunch = "com.duckduckgo.ios.shouldScheduleRulesCompilationOnAppLaunch" @@ -119,9 +119,9 @@ public struct UserDefaultsWrapper { case bookmarksLastGoodVersion = "com.duckduckgo.ios.bookmarksLastGoodVersion" case bookmarksMigrationVersion = "com.duckduckgo.ios.bookmarksMigrationVersion" - + case privacyConfigCustomURL = "com.duckduckgo.ios.privacyConfigCustomURL" - + case subscriptionIsActive = "com.duckduckgo.ios.subscruption.isActive" case didRefreshTimestamp = "com.duckduckgo.ios.userBehavior.didRefreshTimestamp" @@ -146,11 +146,11 @@ public struct UserDefaultsWrapper { if let storedValue = container.object(forKey: key.rawValue) as? T { return storedValue } - + if setIfEmpty { container.set(defaultValue, forKey: key.rawValue) } - + return defaultValue } set { @@ -164,13 +164,13 @@ public struct UserDefaultsWrapper { } private protocol AnyOptional { - + var isNil: Bool { get } - + } extension Optional: AnyOptional { - + var isNil: Bool { self == nil } - + } diff --git a/Core/WebCacheManager.swift b/Core/WebCacheManager.swift index bfaa47d0dd..7974cca03f 100644 --- a/Core/WebCacheManager.swift +++ b/Core/WebCacheManager.swift @@ -34,27 +34,27 @@ extension WKWebsiteDataStore { } extension HTTPCookie { - + func matchesDomain(_ domain: String) -> Bool { return self.domain == domain || (self.domain.hasPrefix(".") && domain.hasSuffix(self.domain)) } - + } @MainActor public class WebCacheManager { - + public static var shared = WebCacheManager() - + private init() { } - + /// We save cookies from the current container rather than copying them to a new container because /// the container only persists cookies to disk when the web view is used. If the user presses the fire button /// twice then the fire proofed cookies will be lost and the user will be logged out any sites they're logged in to. public func consumeCookies(cookieStorage: CookieStorage = CookieStorage(), httpCookieStore: WKHTTPCookieStore) async { guard !cookieStorage.isConsumed else { return } - + let cookies = cookieStorage.cookies var consumedCookiesCount = 0 for cookie in cookies { @@ -63,10 +63,10 @@ public class WebCacheManager { } cookieStorage.isConsumed = true } - + public func removeCookies(forDomains domains: [String], dataStore: WKWebsiteDataStore) async { - + let timeoutTask = Task.detached { try? await Task.sleep(interval: 5.0) if !Task.isCancelled { @@ -75,7 +75,7 @@ public class WebCacheManager { ]) } } - + let cookieStore = dataStore.httpCookieStore let cookies = await cookieStore.allCookies() for cookie in cookies where domains.contains(where: { cookie.matchesDomain($0) }) { @@ -83,7 +83,7 @@ public class WebCacheManager { } timeoutTask.cancel() } - + public func clear(cookieStorage: CookieStorage = CookieStorage(), logins: PreserveLogins = PreserveLogins.shared, dataStoreIdManager: DataStoreIdManager = .shared) async { @@ -92,17 +92,17 @@ public class WebCacheManager { if #available(iOS 17, *), dataStoreIdManager.hasId { cookiesToUpdate += await containerBasedClearing(storeIdManager: dataStoreIdManager) ?? [] } - + // Perform legacy clearing to migrate to new container cookiesToUpdate += await legacyDataClearing() ?? [] cookieStorage.updateCookies(cookiesToUpdate, keepingPreservedLogins: logins) } - + } extension WebCacheManager { - + @available(iOS 17, *) private func checkForLeftBehindDataStores() async { let ids = await WKWebsiteDataStore.allDataStoreIdentifiers @@ -119,17 +119,17 @@ extension WebCacheManager { var dataStore: WKWebsiteDataStore? = WKWebsiteDataStore(forIdentifier: containerId) let cookies = await dataStore?.httpCookieStore.allCookies() dataStore = nil - + let uuids = await WKWebsiteDataStore.allDataStoreIdentifiers for uuid in uuids { try? await WKWebsiteDataStore.remove(forIdentifier: uuid) } await checkForLeftBehindDataStores() - + storeIdManager.allocateNewContainerId() return cookies } - + private func legacyDataClearing() async -> [HTTPCookie]? { let timeoutTask = Task.detached { try? await Task.sleep(interval: 5.0) diff --git a/Core/global.swift b/Core/global.swift index 1a0a11d364..07c90fd66d 100644 --- a/Core/global.swift +++ b/Core/global.swift @@ -39,9 +39,9 @@ public struct Global { public class CoreModule { } extension Bundle { - + public static var core: Bundle { return Bundle(for: CoreModule.self) } - + } diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index a8dc2102a3..ac4bdd1f2e 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -5121,9 +5121,11 @@ 85CA53A724BB342B00A6288C /* Favicons */, EE50052C29C3692700AE0773 /* FeatureFlags */, 8512BCBF2061B6110085E862 /* global.swift */, + 858479C72B8792C900D156C1 /* History */, F143C2E71E4A4CD400CFDE3A /* Info.plist */, 98B001AE251EABB40090EC07 /* InfoPlist.strings */, F18608DE1E5E648100361C30 /* Javascript */, + EE7A92852AC6DE2500832A36 /* NetworkProtection */, CBAA195B27C3982A00A4BD49 /* PrivacyFeatures.swift */, CBAA195627BFDD9800A4BD49 /* SmarterEncryption */, F1134EA71F3E2B3500B73467 /* Statistics */, diff --git a/DuckDuckGo.xcodeproj/xcshareddata/xcschemes/DuckDuckGo-Alpha.xcscheme b/DuckDuckGo.xcodeproj/xcshareddata/xcschemes/DuckDuckGo-Alpha.xcscheme index 7138aee59b..3d469bdc41 100644 --- a/DuckDuckGo.xcodeproj/xcshareddata/xcschemes/DuckDuckGo-Alpha.xcscheme +++ b/DuckDuckGo.xcodeproj/xcshareddata/xcschemes/DuckDuckGo-Alpha.xcscheme @@ -38,7 +38,9 @@ ignoresPersistentStateOnLaunch = "NO" debugDocumentVersioning = "YES" debugServiceExtension = "internal" - allowLocationSimulation = "YES"> + allowLocationSimulation = "YES" + consoleMode = "0" + structuredConsoleMode = "1"> Subfeature.Handler? { + + os_log("WebView handler: %s", log: .subscription, type: .debug, methodName) + switch methodName { case Handlers.getSubscription: return getSubscription case Handlers.setSubscription: return setSubscription case Handlers.backToSettings: return backToSettings case Handlers.getSubscriptionOptions: return getSubscriptionOptions case Handlers.subscriptionSelected: return subscriptionSelected - case Handlers.activateSubscription: return activateSubscription + case Handlers.activateSubscription: + Pixel.fire(pixel: .privacyProRestorePurchaseOfferPageEntry) + return activateSubscription case Handlers.featureSelected: return featureSelected default: return nil } } - /// Values that the Frontend can use to determine the current state. // swiftlint:disable nesting @@ -173,7 +178,7 @@ final class SubscriptionPagesUseSubscriptionFeature: Subfeature, ObservableObjec case .success(let subscriptionOptions): return subscriptionOptions case .failure: - os_log(.error, log: .subscription, "Failed to obtain subscription options") + os_log("Failed to obtain subscription options", log: .subscription, type: .error) setTransactionError(.failedToGetSubscriptionOptions) return nil } @@ -181,9 +186,11 @@ final class SubscriptionPagesUseSubscriptionFeature: Subfeature, ObservableObjec } } + // swiftlint:disable:next function_body_length func subscriptionSelected(params: Any, original: WKScriptMessage) async -> Encodable? { await withTransactionInProgress { + DailyPixel.fireDailyAndCount(pixel: .privacyProPurchaseAttempt) setTransactionError(nil) setTransactionStatus(.purchasing) resetSubscriptionFlow() @@ -218,6 +225,11 @@ final class SubscriptionPagesUseSubscriptionFeature: Subfeature, ObservableObjec switch error { case .cancelledByUser: setTransactionError(.cancelledByUser) + case .accountCreationFailed: + setTransactionError(.accountCreationFailed) + case .activeSubscriptionAlreadyPresent: + setTransactionError(.hasActiveSubscription) + Pixel.fire(pixel: .privacyProRestoreAfterPurchaseAttempt) default: setTransactionError(.purchaseFailed) } @@ -230,6 +242,8 @@ final class SubscriptionPagesUseSubscriptionFeature: Subfeature, ObservableObjec switch await AppStorePurchaseFlow.completeSubscriptionPurchase(with: purchaseTransactionJWS, subscriptionAppGroup: Bundle.main.appGroup(bundle: .subs)) { case .success(let purchaseUpdate): + DailyPixel.fireDailyAndCount(pixel: .privacyProPurchaseSuccess) + UniquePixel.fire(pixel: .privacyProSubscriptionActivated) await pushPurchaseUpdate(originalMessage: message, purchaseUpdate: purchaseUpdate) case .failure: setTransactionError(.missingEntitlements) @@ -253,7 +267,7 @@ final class SubscriptionPagesUseSubscriptionFeature: Subfeature, ObservableObjec accountManager.storeAuthToken(token: authToken) accountManager.storeAccount(token: accessToken, email: accountDetails.email, externalID: accountDetails.externalID) } else { - os_log(.error, log: .subscription, "Failed to obtain subscription options") + os_log("Failed to obtain subscription options", log: .subscription, type: .error) setTransactionError(.failedToSetSubscription) } @@ -281,11 +295,11 @@ final class SubscriptionPagesUseSubscriptionFeature: Subfeature, ObservableObjec emailActivationComplete = true case .failure: - os_log(.error, log: .subscription, "Failed to restore subscription from Email") + os_log("Failed to restore subscription from Email", log: .subscription, type: .error) setTransactionError(.failedToRestoreFromEmail) } } else { - os_log(.error, log: .subscription, "General error. Could not get account Details") + os_log("General error. Could not get account Details", log: .subscription, type: .error) setTransactionError(.generalError) } return nil @@ -360,6 +374,5 @@ final class SubscriptionPagesUseSubscriptionFeature: Subfeature, ObservableObjec } } -// swiftlint:enable type_body_length #endif diff --git a/DuckDuckGo/Subscription/UserScripts/SubscriptionPagesUserScript.swift b/DuckDuckGo/Subscription/UserScripts/SubscriptionPagesUserScript.swift index 81633fa54c..c3f52e4c40 100644 --- a/DuckDuckGo/Subscription/UserScripts/SubscriptionPagesUserScript.swift +++ b/DuckDuckGo/Subscription/UserScripts/SubscriptionPagesUserScript.swift @@ -25,6 +25,7 @@ import Combine import Foundation import WebKit import UserScript +import Core /// /// The user script that will be the broker for all subscription features @@ -62,8 +63,10 @@ extension SubscriptionPagesUserScript: WKScriptMessageHandlerWithReply { } extension SubscriptionPagesUserScript: WKScriptMessageHandler { + public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { // unsupported + os_log("SubscriptionPagesUserScript sent an unsupported message: %s", log: .generalLog, type: .fault, message.messageName) } } diff --git a/DuckDuckGo/Subscription/ViewModel/SubscriptionEmailViewModel.swift b/DuckDuckGo/Subscription/ViewModel/SubscriptionEmailViewModel.swift index 52d746ec87..6cb2ea102f 100644 --- a/DuckDuckGo/Subscription/ViewModel/SubscriptionEmailViewModel.swift +++ b/DuckDuckGo/Subscription/ViewModel/SubscriptionEmailViewModel.swift @@ -52,9 +52,9 @@ final class SubscriptionEmailViewModel: ObservableObject { subscriptionExpired, generalError } - + private var cancellables = Set() - + init(userScript: SubscriptionPagesUserScript = SubscriptionPagesUserScript(), subFeature: SubscriptionPagesUseSubscriptionFeature = SubscriptionPagesUseSubscriptionFeature(), accountManager: AccountManager = AccountManager()) { @@ -115,6 +115,7 @@ final class SubscriptionEmailViewModel: ObservableObject { } private func completeActivation() { + DailyPixel.fireDailyAndCount(pixel: .privacyProRestorePurchaseEmailSuccess) subFeature.emailActivationComplete = false activateSubscription = true } diff --git a/DuckDuckGo/Subscription/ViewModel/SubscriptionFlowViewModel.swift b/DuckDuckGo/Subscription/ViewModel/SubscriptionFlowViewModel.swift index 623d191afb..6d0cb332d7 100644 --- a/DuckDuckGo/Subscription/ViewModel/SubscriptionFlowViewModel.swift +++ b/DuckDuckGo/Subscription/ViewModel/SubscriptionFlowViewModel.swift @@ -124,10 +124,13 @@ final class SubscriptionFlowViewModel: ObservableObject { if value != nil { switch value?.feature { case FeatureName.netP: + UniquePixel.fire(pixel: .privacyProWelcomeVPN) self?.selectedFeature = .netP case FeatureName.itr: + UniquePixel.fire(pixel: .privacyProWelcomePersonalInformationRemoval) self?.selectedFeature = .itr case FeatureName.dbp: + UniquePixel.fire(pixel: .privacyProWelcomeIdentityRestoration) self?.selectedFeature = .dbp default: break @@ -151,28 +154,63 @@ final class SubscriptionFlowViewModel: ObservableObject { } + // swiftlint:disable:next cyclomatic_complexity private func handleTransactionError(error: SubscriptionPagesUseSubscriptionFeature.UseSubscriptionError) { + + var isStoreError = false + var isBackendError = false + switch error { - case .purchaseFailed: + isStoreError = true transactionError = .purchaseFailed case .missingEntitlements: + isBackendError = true transactionError = .missingEntitlements case .failedToGetSubscriptionOptions: + isStoreError = true transactionError = .failedToGetSubscriptionOptions case .failedToSetSubscription: + isBackendError = true transactionError = .failedToSetSubscription + case .failedToRestoreFromEmail, .failedToRestoreFromEmailSubscriptionInactive: + isBackendError = true + transactionError = .generalError case .failedToRestorePastPurchase: + isStoreError = true transactionError = .failedToRestorePastPurchase + case .subscriptionNotFound: + isStoreError = true + transactionError = .generalError case .subscriptionExpired: + isStoreError = true transactionError = .subscriptionExpired case .hasActiveSubscription: + isStoreError = true + isBackendError = true transactionError = .hasActiveSubscription case .cancelledByUser: transactionError = nil + case .accountCreationFailed: + DailyPixel.fireDailyAndCount(pixel: .privacyProPurchaseFailureAccountNotCreated) + transactionError = .generalError default: transactionError = .generalError } + + if isStoreError { + DailyPixel.fireDailyAndCount(pixel: .privacyProPurchaseFailureStoreError) + } + + if isBackendError { + DailyPixel.fireDailyAndCount(pixel: .privacyProPurchaseFailureBackendError) + } + + if let transactionError, + transactionError != .hasActiveSubscription && transactionError != .cancelledByUser { + // The observer of `transactionError` does the same calculation, if the error is anything else than .hasActiveSubscription then shows a "Something went wrong" alert + DailyPixel.fireDailyAndCount(pixel: .privacyProPurchaseFailure) + } } private func setupWebViewObservers() async { @@ -255,6 +293,5 @@ final class SubscriptionFlowViewModel: ObservableObject { func navigateBack() async { await webViewModel.navigationCoordinator.goBack() } - } #endif diff --git a/DuckDuckGo/Subscription/ViewModel/SubscriptionITPViewModel.swift b/DuckDuckGo/Subscription/ViewModel/SubscriptionITPViewModel.swift index 04432065ec..6e613491a8 100644 --- a/DuckDuckGo/Subscription/ViewModel/SubscriptionITPViewModel.swift +++ b/DuckDuckGo/Subscription/ViewModel/SubscriptionITPViewModel.swift @@ -138,6 +138,7 @@ final class SubscriptionITPViewModel: ObservableObject { func initializeView() { webViewModel.navigationCoordinator.navigateTo(url: manageITPURL ) Task { await setupSubscribers() } + Pixel.fire(pixel: .privacyProIdentityRestorationSettings) } private func downloadAttachment(from url: URL) async { diff --git a/DuckDuckGo/Subscription/ViewModel/SubscriptionPIRViewModel.swift b/DuckDuckGo/Subscription/ViewModel/SubscriptionPIRViewModel.swift index 2ce1c2674b..75f6cf72ea 100644 --- a/DuckDuckGo/Subscription/ViewModel/SubscriptionPIRViewModel.swift +++ b/DuckDuckGo/Subscription/ViewModel/SubscriptionPIRViewModel.swift @@ -26,5 +26,8 @@ final class SubscriptionPIRViewModel: ObservableObject { var viewTitle = UserText.subscriptionTitle + func onAppear() { + Pixel.fire(pixel: .privacyProPersonalInformationRemovalSettings) + } } #endif diff --git a/DuckDuckGo/Subscription/ViewModel/SubscriptionRestoreViewModel.swift b/DuckDuckGo/Subscription/ViewModel/SubscriptionRestoreViewModel.swift index 5ad61c5b80..3da604761f 100644 --- a/DuckDuckGo/Subscription/ViewModel/SubscriptionRestoreViewModel.swift +++ b/DuckDuckGo/Subscription/ViewModel/SubscriptionRestoreViewModel.swift @@ -52,10 +52,10 @@ final class SubscriptionRestoreViewModel: ObservableObject { self.purchaseManager = purchaseManager self.accountManager = accountManager self.isAddingDevice = isAddingDevice - initializeView() } func initializeView() { + Pixel.fire(pixel: .privacyProSettingsAddDevice) subscriptionEmail = accountManager.email if accountManager.isUserAuthenticated { isAddingDevice = true @@ -89,6 +89,12 @@ final class SubscriptionRestoreViewModel: ObservableObject { default: activationResult = .error } + + if activationResult == .notFound { + DailyPixel.fireDailyAndCount(pixel: .privacyProRestorePurchaseStoreFailureNotFound) + } else { + DailyPixel.fireDailyAndCount(pixel: .privacyProRestorePurchaseStoreFailureOther) + } } @MainActor @@ -98,10 +104,12 @@ final class SubscriptionRestoreViewModel: ObservableObject { @MainActor func restoreAppstoreTransaction() { + DailyPixel.fireDailyAndCount(pixel: .privacyProRestorePurchaseStoreStart) Task { activationResult = .unknown do { try await subFeature.restoreAccountFromAppStorePurchase() + DailyPixel.fireDailyAndCount(pixel: .privacyProRestorePurchaseStoreSuccess) activationResult = .activated } catch let error { if let specificError = error as? SubscriptionPagesUseSubscriptionFeature.UseSubscriptionError { diff --git a/DuckDuckGo/Subscription/ViewModel/SubscriptionSettingsViewModel.swift b/DuckDuckGo/Subscription/ViewModel/SubscriptionSettingsViewModel.swift index f52e6f75c4..5c65f88e98 100644 --- a/DuckDuckGo/Subscription/ViewModel/SubscriptionSettingsViewModel.swift +++ b/DuckDuckGo/Subscription/ViewModel/SubscriptionSettingsViewModel.swift @@ -23,6 +23,7 @@ import StoreKit #if SUBSCRIPTION import Subscription +import Core @available(iOS 15.0, *) final class SubscriptionSettingsViewModel: ObservableObject { @@ -133,7 +134,5 @@ final class SubscriptionSettingsViewModel: ObservableObject { subscriptionUpdateTimer?.invalidate() signOutObserver = nil } - - } #endif diff --git a/DuckDuckGo/Subscription/Views/PurchaseInProgressView.swift b/DuckDuckGo/Subscription/Views/PurchaseInProgressView.swift index 2793d9c043..fcb83893f1 100644 --- a/DuckDuckGo/Subscription/Views/PurchaseInProgressView.swift +++ b/DuckDuckGo/Subscription/Views/PurchaseInProgressView.swift @@ -83,10 +83,11 @@ struct ViewHeightKey: PreferenceKey { } } -struct PurchaseInProgressView_Previews: PreviewProvider { - static var previews: some View { - PurchaseInProgressView(status: "Completing Purchase... ") - .previewLayout(.sizeThatFits) - .padding() - } -} +// Commented out because CI fails if a SwiftUI preview is enabled https://app.asana.com/0/414709148257752/1206774081310425/f +// struct PurchaseInProgressView_Previews: PreviewProvider { +// static var previews: some View { +// PurchaseInProgressView(status: "Completing Purchase... ") +// .previewLayout(.sizeThatFits) +// .padding() +// } +// } diff --git a/DuckDuckGo/Subscription/Views/SubscriptionEmailView.swift b/DuckDuckGo/Subscription/Views/SubscriptionEmailView.swift index d720aa434f..8712560f36 100644 --- a/DuckDuckGo/Subscription/Views/SubscriptionEmailView.swift +++ b/DuckDuckGo/Subscription/Views/SubscriptionEmailView.swift @@ -20,6 +20,7 @@ #if SUBSCRIPTION import SwiftUI import Foundation +import Core @available(iOS 15.0, *) struct SubscriptionEmailView: View { @@ -74,4 +75,13 @@ struct SubscriptionEmailView: View { } + +// Commented out because CI fails if a SwiftUI preview is enabled https://app.asana.com/0/414709148257752/1206774081310425/f +// @available(iOS 15.0, *) +// struct SubscriptionEmailView_Previews: PreviewProvider { +// static var previews: some View { +// SubscriptionEmailView() +// } +// } + #endif diff --git a/DuckDuckGo/Subscription/Views/SubscriptionFlowView.swift b/DuckDuckGo/Subscription/Views/SubscriptionFlowView.swift index 0637e2c18f..85e820fd3a 100644 --- a/DuckDuckGo/Subscription/Views/SubscriptionFlowView.swift +++ b/DuckDuckGo/Subscription/Views/SubscriptionFlowView.swift @@ -21,6 +21,7 @@ import SwiftUI import Foundation import DesignResourcesKit +import Core @available(iOS 15.0, *) struct SubscriptionFlowView: View { @@ -28,10 +29,11 @@ struct SubscriptionFlowView: View { @Environment(\.dismiss) var dismiss @StateObject var viewModel = SubscriptionFlowViewModel() @State private var shouldShowNavigationBar = false - @State private var isActive: Bool = false + @State private var isActive = false @State private var transactionError: SubscriptionFlowViewModel.SubscriptionPurchaseError? - @State private var shouldPresentError: Bool = false - + @State private var shouldPresentError = false + @State private var isFirstOnAppear = true + enum Constants { static let daxLogo = "Home" static let daxLogoSize: CGFloat = 24.0 @@ -65,7 +67,7 @@ struct SubscriptionFlowView: View { .tint(Color(designSystemColor: .textPrimary)) .environment(\.rootPresentationMode, self.$isActive) } - + @ViewBuilder private var dismissButton: some View { Button(action: { viewModel.finalizeSubscriptionFlow() }, label: { Text(UserText.subscriptionCloseButton) }) @@ -125,10 +127,10 @@ struct SubscriptionFlowView: View { viewModel.shouldDismissView = false } } - + .onChange(of: viewModel.userTappedRestoreButton) { _ in - isActive = true - viewModel.userTappedRestoreButton = false + isActive = true + viewModel.userTappedRestoreButton = false } .onChange(of: viewModel.transactionError) { value in @@ -139,6 +141,12 @@ struct SubscriptionFlowView: View { } .onAppear(perform: { + + if isFirstOnAppear && !viewModel.activateSubscriptionOnLoad { + isFirstOnAppear = false + Pixel.fire(pixel: .privacyProOfferScreenImpression) + } + setUpAppearances() Task { await viewModel.initializeViewData() } @@ -169,7 +177,6 @@ struct SubscriptionFlowView: View { private func getAlert() -> Alert { switch transactionError { - case .hasActiveSubscription: Alert( title: Text(UserText.subscriptionFoundTitle), @@ -190,32 +197,26 @@ struct SubscriptionFlowView: View { } ) } - } - - + @ViewBuilder private var webView: some View { ZStack(alignment: .top) { - // Restore View Hidden Link NavigationLink(destination: SubscriptionRestoreView(), isActive: $isActive) { EmptyView() }.isDetailLink(false) - - + AsyncHeadlessWebView(viewModel: viewModel.webViewModel) .background() if viewModel.transactionStatus != .idle { PurchaseInProgressView(status: getTransactionStatus()) } - } } - - + private func setUpAppearances() { let navAppearance = UINavigationBar.appearance() navAppearance.backgroundColor = UIColor(designSystemColor: .background) @@ -225,4 +226,13 @@ struct SubscriptionFlowView: View { } } + +// Commented out because CI fails if a SwiftUI preview is enabled https://app.asana.com/0/414709148257752/1206774081310425/f +// @available(iOS 15.0, *) +// struct SubscriptionFlowView_Previews: PreviewProvider { +// static var previews: some View { +// SubscriptionFlowView() +// } +// } + #endif diff --git a/DuckDuckGo/Subscription/Views/SubscriptionITPView.swift b/DuckDuckGo/Subscription/Views/SubscriptionITPView.swift index 6111e75246..32f0f60d0e 100644 --- a/DuckDuckGo/Subscription/Views/SubscriptionITPView.swift +++ b/DuckDuckGo/Subscription/Views/SubscriptionITPView.swift @@ -163,4 +163,13 @@ struct SubscriptionITPView: View { navAppearance.tintColor = UIColor(designSystemColor: .textPrimary) } } + +// Commented out because CI fails if a SwiftUI preview is enabled https://app.asana.com/0/414709148257752/1206774081310425/f +// @available(iOS 15.0, *) +// struct SubscriptionITPView_Previews: PreviewProvider { +// static var previews: some View { +// SubscriptionITPView() +// } +// } + #endif diff --git a/DuckDuckGo/Subscription/Views/SubscriptionPIRView.swift b/DuckDuckGo/Subscription/Views/SubscriptionPIRView.swift index 10072d9f8a..52f04501f9 100644 --- a/DuckDuckGo/Subscription/Views/SubscriptionPIRView.swift +++ b/DuckDuckGo/Subscription/Views/SubscriptionPIRView.swift @@ -64,7 +64,9 @@ struct SubscriptionPIRView: View { } .edgesIgnoringSafeArea(.all) - } + }.onAppear(perform: { + viewModel.onAppear() + }) } private var header: some View { @@ -195,7 +197,14 @@ struct SubscriptionPIRView: View { .daxBodyRegular() .tint(Color(designSystemColor: .textPrimary)) } - } +// Commented out because CI fails if a SwiftUI preview is enabled https://app.asana.com/0/414709148257752/1206774081310425/f +// @available(iOS 15.0, *) +// struct SubscriptionPIRView_Previews: PreviewProvider { +// static var previews: some View { +// SubscriptionPIRView() +// } +// } + #endif diff --git a/DuckDuckGo/Subscription/Views/SubscriptionRestoreView.swift b/DuckDuckGo/Subscription/Views/SubscriptionRestoreView.swift index ac67117332..7185163b3f 100644 --- a/DuckDuckGo/Subscription/Views/SubscriptionRestoreView.swift +++ b/DuckDuckGo/Subscription/Views/SubscriptionRestoreView.swift @@ -20,6 +20,7 @@ import Foundation import SwiftUI import DesignResourcesKit +import Core #if SUBSCRIPTION @available(iOS 15.0, *) @@ -120,13 +121,20 @@ struct SubscriptionRestoreView: View { .daxSubheadRegular() .foregroundColor(Color(designSystemColor: .textSecondary)) getCellButton(buttonText: UserText.subscriptionActivateEmailButton, - action: buttonAction) + action: { + DailyPixel.fireDailyAndCount(pixel: .privacyProRestorePurchaseEmailStart) + DailyPixel.fire(pixel: .privacyProWelcomeAddDevice) + buttonAction() + }) } else if viewModel.subscriptionEmail == nil { Text(UserText.subscriptionAddDeviceEmailDescription) .daxSubheadRegular() .foregroundColor(Color(designSystemColor: .textSecondary)) getCellButton(buttonText: UserText.subscriptionRestoreAddEmailButton, - action: buttonAction) + action: { + Pixel.fire(pixel: .privacyProAddDeviceEnterEmail) + buttonAction() + }) } else { Text(viewModel.subscriptionEmail ?? "").daxSubheadSemibold() Text(UserText.subscriptionManageEmailDescription) @@ -134,7 +142,10 @@ struct SubscriptionRestoreView: View { .foregroundColor(Color(designSystemColor: .textSecondary)) HStack { getCellButton(buttonText: UserText.subscriptionManageEmailButton, - action: buttonAction) + action: { + Pixel.fire(pixel: .privacyProSubscriptionManagementEmail) + buttonAction() + }) } } }) @@ -292,4 +303,13 @@ struct SubscriptionRestoreView_Previews: PreviewProvider { .previewDevice("iPhone 12") } } + +// Commented out because CI fails if a SwiftUI preview is enabled https://app.asana.com/0/414709148257752/1206774081310425/f +// @available(iOS 15.0, *) +// struct SubscriptionRestoreView_Previews: PreviewProvider { +// static var previews: some View { +// SubscriptionRestoreView() +// } +// } + #endif diff --git a/DuckDuckGo/Subscription/Views/SubscriptionSettingsView.swift b/DuckDuckGo/Subscription/Views/SubscriptionSettingsView.swift index 2f6a93f513..76abb5875a 100644 --- a/DuckDuckGo/Subscription/Views/SubscriptionSettingsView.swift +++ b/DuckDuckGo/Subscription/Views/SubscriptionSettingsView.swift @@ -20,6 +20,7 @@ import Foundation import SwiftUI import DesignResourcesKit +import Core class SceneEnvironment: ObservableObject { weak var windowScene: UIWindowScene? @@ -33,7 +34,8 @@ struct SubscriptionSettingsView: View { @Environment(\.dismiss) var dismiss @StateObject var viewModel = SubscriptionSettingsViewModel() @StateObject var sceneEnvironment = SceneEnvironment() - + @State var isFirstOnAppear = true + @ViewBuilder private var optionsView: some View { List { @@ -56,7 +58,10 @@ struct SubscriptionSettingsView: View { .daxBodyRegular() .foregroundColor(Color.init(designSystemColor: .accent)) }, - action: { Task { viewModel.manageSubscription() } }, + action: { + Pixel.fire(pixel: .privacyProSubscriptionManagementPlanBilling) + Task { viewModel.manageSubscription() } + }, isButton: true) } @@ -68,7 +73,7 @@ struct SubscriptionSettingsView: View { .daxBodyRegular() }) } - + SettingsCustomCell(content: { Text(UserText.subscriptionRemoveFromDevice) .daxBodyRegular() @@ -105,6 +110,7 @@ struct SubscriptionSettingsView: View { primaryButton: .cancel(Text(UserText.subscriptionRemoveCancel)) { }, secondaryButton: .destructive(Text(UserText.subscriptionRemove)) { + Pixel.fire(pixel: .privacyProSubscriptionManagementRemoval) viewModel.removeSubscription() presentationMode.wrappedValue.dismiss() } @@ -124,10 +130,13 @@ struct SubscriptionSettingsView: View { } else { optionsView } - } + }.onAppear(perform: { + if isFirstOnAppear { + isFirstOnAppear = false + Pixel.fire(pixel: .privacyProSubscriptionSettings) + } + }) .navigationBarTitleDisplayMode(.inline) - - } } @@ -148,4 +157,13 @@ struct SubscriptionSettingsView_Previews: PreviewProvider { // .preferredColorScheme(.dark) } } + +// Commented out because CI fails if a SwiftUI preview is enabled https://app.asana.com/0/414709148257752/1206774081310425/f +// @available(iOS 15.0, *) +// struct SubscriptionSettingsView_Previews: PreviewProvider { +// static var previews: some View { +// SubscriptionSettingsView() +// } +// } + #endif diff --git a/DuckDuckGo/VPNWaitlistView.swift b/DuckDuckGo/VPNWaitlistView.swift index 9f912e48b6..22bd2c6b45 100644 --- a/DuckDuckGo/VPNWaitlistView.swift +++ b/DuckDuckGo/VPNWaitlistView.swift @@ -113,6 +113,13 @@ struct VPNWaitlistSignUpView: View { } +@available(iOS 15.0, *) +struct VPNWaitlistSignUpView_Previews: PreviewProvider { + static var previews: some View { + VPNWaitlistSignUpView(requestInFlight: false) { _ in } + } +} + // MARK: - Joined Waitlist Views @available(iOS 15.0, *) diff --git a/submodules/privacy-reference-tests b/submodules/privacy-reference-tests index a603ff9af2..40ce86837d 160000 --- a/submodules/privacy-reference-tests +++ b/submodules/privacy-reference-tests @@ -1 +1 @@ -Subproject commit a603ff9af22ca3ff7ce2e7ffbfe18c447d9f23e8 +Subproject commit 40ce86837def0adbf558f00ed0531ab4df5839a8 From b22a76879faa2ffca012c3a96080a358f13f9da4 Mon Sep 17 00:00:00 2001 From: Christopher Brind Date: Mon, 11 Mar 2024 11:57:38 +0000 Subject: [PATCH 12/19] update metadata (#2571) Task/Issue URL: https://app.asana.com/0/0/1206806894867215/f Tech Design URL: CC: Description: Updates Metadata --- fastlane/metadata/cs/description.txt | 2 -- fastlane/metadata/de-DE/description.txt | 3 --- fastlane/metadata/en-CA/description.txt | 6 ++--- fastlane/metadata/en-GB/description.txt | 6 ++--- fastlane/metadata/en-US/description.txt | 8 +++--- fastlane/metadata/es-ES/description.txt | 3 --- fastlane/metadata/fi/description.txt | 3 --- fastlane/metadata/fr-FR/description.txt | 2 -- fastlane/metadata/it/description.txt | 2 -- fastlane/metadata/nl-NL/description.txt | 2 -- fastlane/metadata/nl-NL/it/description.txt | 27 ------------------- fastlane/metadata/nl-NL/it/marketing_url.txt | 1 - fastlane/metadata/nl-NL/it/name.txt | 1 - fastlane/metadata/nl-NL/it/privacy_url.txt | 1 - .../metadata/nl-NL/it/promotional_text.txt | 1 - fastlane/metadata/nl-NL/it/subtitle.txt | 1 - fastlane/metadata/nl-NL/it/support_url.txt | 1 - fastlane/metadata/no/description.txt | 5 +--- fastlane/metadata/pl/description.txt | 2 -- fastlane/metadata/pt-PT/description.txt | 4 +-- fastlane/metadata/ro/description.txt | 2 -- fastlane/metadata/ru/description.txt | 4 --- fastlane/metadata/sv/description.txt | 5 +--- fastlane/metadata/tr/description.txt | 3 --- 24 files changed, 10 insertions(+), 85 deletions(-) delete mode 100644 fastlane/metadata/nl-NL/it/description.txt delete mode 100644 fastlane/metadata/nl-NL/it/marketing_url.txt delete mode 100644 fastlane/metadata/nl-NL/it/name.txt delete mode 100644 fastlane/metadata/nl-NL/it/privacy_url.txt delete mode 100644 fastlane/metadata/nl-NL/it/promotional_text.txt delete mode 100644 fastlane/metadata/nl-NL/it/subtitle.txt delete mode 100644 fastlane/metadata/nl-NL/it/support_url.txt diff --git a/fastlane/metadata/cs/description.txt b/fastlane/metadata/cs/description.txt index 9cb9498c21..9d1c85dd9c 100644 --- a/fastlane/metadata/cs/description.txt +++ b/fastlane/metadata/cs/description.txt @@ -20,7 +20,5 @@ KAŽDODENNÍ OCHRANA SOUKROMÍ • Ukaž, jak to vidíš se svým soukromím, pomocí Globální kontroly ochrany osobních údajů (GPC). - Další informace najdeš na https://help.duckduckgo.com/duckduckgo-help-pages/privacy/gpc/. -Poznámka k naší ochraně před načítáním trackerů třetích stran po kliknutí na reklamu na DuckDuckGo: Tato naše ochrana jde vysoko nad rámec toho, co běžně nabízí většina oblíbených prohlížečů, ale neustále pracujeme na tom, aby byla ještě komplexnější. Aktuálně je to tak, že když chce inzerent na svých webových stránkách zjišťovat konverze z našich soukromých reklam ve vyhledávání DuckDuckGo, naše ochrana před načítáním trackerů třetích stran nezastaví načítání požadavků bat.bing.com na webových stránkách inzerenta po kliknutí na reklamu na DuckDuckGo, ale na všech ostatních místech tyto požadavky zablokujeme. Je to proto, že soukromé reklamy ve vyhledávání na DuckDuckGo provozujeme ve spolupráci se společností Microsoft. V rámci tohoto partnerství je zobrazování reklam na DuckDuckGo anonymní a společnost Microsoft se zavázala, že naše uživatele nebude po kliknutí na reklamy profilovat. - Další informace o tomto tématu a bezplatné ochraně před trackery si můžeš přečíst na adrese https://help.duckduckgo.com/privacy/web-tracking-protections. diff --git a/fastlane/metadata/de-DE/description.txt b/fastlane/metadata/de-DE/description.txt index b9dfbca593..aa3e6d5c92 100644 --- a/fastlane/metadata/de-DE/description.txt +++ b/fastlane/metadata/de-DE/description.txt @@ -1,4 +1,3 @@ - Die DuckDuckGo-App bietet den umfassendsten Online-Datenschutz mit nur einem Knopfdruck. Mit einem einzigen Download erhältst du einen neuen Browser für die alltägliche Nutzung, der nahtlosen Schutz beim Suchen und Surfen bietet sowie Zugriff auf Tracking-Schutz beim Empfang von E-Mails. Viele dieser Schutzmaßnahmen werden in den meisten gängigen Browsern standardmäßig nicht angeboten. HIGHLIGHTS DER FUNKTION @@ -17,8 +16,6 @@ DATENSCHUTZKONTROLLE FÜR JEDEN TAG • Signalisiere deine Privatsphäre-Präferenz mit Global Privacy Control (GPC) – GPC ist in unsere App integriert und hilft dir, deine Widerspruchsrechte automatisch auszuüben, indem du Websites darüber informierst, dass sie deine persönlichen Daten nicht verkaufen oder weitergeben dürfen. Ob deine Rechtsansprüche (z. B. aktuelle oder zukünftige CCPA- oder DSGVO-Anforderungen) auf diese Weise durchgesetzt werden können, hängt von den Gesetzen in deiner Gerichtsbarkeit ab. -3rd-Party Tracker Loading Protection nach Klicken auf DuckDuckGo-Werbung geht weit über das hinaus, was die meisten gängigen Browser standardmäßig bieten. Wir arbeiten ständig daran, die Funktion noch umfassender zu gestalten. Wenn ein Werbetreibender die Konvertierung auf seiner eigenen Website für DuckDuckGo Private Search-Werbung ermitteln möchte, verhindert 3rd-Party Tracker Loading Protection nicht, dass bat.bing.com-Anfragen auf der Website des Werbetreibenden geladen werden, nachdem die DuckDuckGo-Werbung angeklickt wurde. Diese Anfragen werden jedoch in allen anderen Zusammenhängen gestoppt. Das liegt daran, dass DuckDuckGo Private Search-Werbung in Partnerschaft mit Microsoft erfolgt. Im Rahmen dieser Zusammenarbeit ist das Ansehen von Werbung auf DuckDuckGo anonym. Zudem hat sich Microsoft verpflichtet, keine Profile der Anzeigenklicks unserer Nutzer zu erstellen. - Weitere Informationen hierzu sowie zu Tracking Protection unter https://help.duckduckgo.com/privacy/web-tracking-protections/ Du musst nicht warten, um dir deine Privatsphäre zurückzuholen. Schließ dich den Millionen von Menschen an, die DuckDuckGo nutzen, und schütze viele deiner täglichen Online-Aktivitäten mit einer einzigen App. Das ist Datenschutz, vereinfacht. diff --git a/fastlane/metadata/en-CA/description.txt b/fastlane/metadata/en-CA/description.txt index 36d8d4e3dd..5b53dce2e6 100644 --- a/fastlane/metadata/en-CA/description.txt +++ b/fastlane/metadata/en-CA/description.txt @@ -1,4 +1,4 @@ -The DuckDuckGo browser provides the most comprehensive online privacy protection in one app. Unlike most popular browsers, it has powerful privacy protections by default, including our search engine that doesn’t track your history and over a dozen other built-in protections. Millions of people use DuckDuckGo as their go-to browser to protect their everyday online activities, from searching to browsing, emailing, and more. +DuckDuckGo is a free browser that provides the most comprehensive online privacy protection in one app. Unlike most popular browsers, it has powerful privacy protections by default, including our search engine that doesn’t track your history and over a dozen other built-in protections. Millions of people use DuckDuckGo as their go-to browser to protect their everyday online activities, from searching to browsing, emailing, and more.   FEATURE HIGHLIGHTS • Search Privately by Default - DuckDuckGo Private Search comes built-in, so you can easily search the web without being tracked. @@ -20,9 +20,7 @@ EVERYDAY PRIVACY CONTROLS   • Signal Your Privacy Preference with Global Privacy Control (GPC) - Built in to our app, GPC intends to help you express your opt-out rights automatically by telling websites not to sell or share your personal information. Whether it can be used to enforce your legal rights (for example, current or future CCPA, GDPR requirements) depends on the laws in your jurisdiction.   -Note about our 3rd-Party Tracker Loading Protection following DuckDuckGo ad clicks: Our 3rd-Party Tracker Protection goes above and beyond what you get in most popular browsers by default, but we’re constantly working to make it more comprehensive. Currently, if an advertiser wants to detect conversions on their own website for our private DuckDuckGo search ads, our 3rd-Party Tracker Loading Protection will not stop bat.bing.com requests from loading on the advertiser’s website following DuckDuckGo ad clicks, but those requests are stopped in all other contexts. This is because DuckDuckGo private search advertising is in partnership with Microsoft. As part of that partnership, viewing ads on DuckDuckGo is anonymous and Microsoft has committed to not profile our users’ ad clicks. -  -Read more about this and our free Tracking Protections at https://help.duckduckgo.com/privacy/web-tracking-protections +Read more about our free Tracking Protections at https://help.duckduckgo.com/privacy/web-tracking-protections You don't need to wait to take back your privacy. Join the millions of people using DuckDuckGo and protect many of your everyday online activities with one app. It's privacy, simplified. diff --git a/fastlane/metadata/en-GB/description.txt b/fastlane/metadata/en-GB/description.txt index c0444a66a2..2a1b6998f0 100644 --- a/fastlane/metadata/en-GB/description.txt +++ b/fastlane/metadata/en-GB/description.txt @@ -1,4 +1,4 @@ -The DuckDuckGo browser provides the most comprehensive online privacy protection in one app. Unlike most popular browsers, it has powerful privacy protections by default, including our search engine that doesn’t track your history and over a dozen other built-in protections. Millions of people use DuckDuckGo as their go-to browser to protect their everyday online activities, from searching to browsing, emailing, and more. +DuckDuckGo is a free browser that provides the most comprehensive online privacy protection in one app. Unlike most popular browsers, it has powerful privacy protections by default, including our search engine that doesn’t track your history and over a dozen other built-in protections. Millions of people use DuckDuckGo as their go-to browser to protect their everyday online activities, from searching to browsing, emailing, and more. FEATURE HIGHLIGHTS • Search Privately by Default — DuckDuckGo Private Search comes built-in, so you can easily search the web without being tracked. @@ -20,9 +20,7 @@ EVERYDAY PRIVACY CONTROLS • Signal Your Privacy Preference with Global Privacy Control (GPC) — built-into our app, GPC intends to help you express your opt-out rights automatically by telling websites not to sell or share your personal information. Whether it can be used to enforce your legal rights (for example, current or future CCPA, GDPR requirements) depends on the laws in your jurisdiction. -Note about our 3rd-Party Tracker Loading Protection following DuckDuckGo ad clicks: Our 3rd-Party Tracker Protection goes above and beyond what you get in most popular browsers by default, but we’re constantly working to make it more comprehensive. Currently, if an advertiser wants to detect conversions on their own website for our private DuckDuckGo search ads, our 3rd-Party Tracker Loading Protection will not stop bat.bing.com requests from loading on the advertiser’s website following DuckDuckGo ad clicks, but those requests are stopped in all other contexts. This is because DuckDuckGo private search advertising is in partnership with Microsoft. As part of that partnership, viewing ads on DuckDuckGo is anonymous and Microsoft has committed to not profile our users’ ad clicks. - -Read more about this and our Tracking Protections at https://help.duckduckgo.com/privacy/web-tracking-protections/ +Read more about our free Tracking Protections at https://help.duckduckgo.com/privacy/web-tracking-protections/ You don't need to wait to take back your privacy. Join the millions of people using DuckDuckGo and protect many of your everyday online activities with one app. It's privacy, simplified. diff --git a/fastlane/metadata/en-US/description.txt b/fastlane/metadata/en-US/description.txt index 61bf4cae26..e16d809a31 100644 --- a/fastlane/metadata/en-US/description.txt +++ b/fastlane/metadata/en-US/description.txt @@ -1,4 +1,4 @@ -The DuckDuckGo browser provides the most comprehensive online privacy protection in one app. Unlike most popular browsers, it has powerful privacy protections by default, including our search engine that doesn’t track your history and over a dozen other built-in protections. Millions of people use DuckDuckGo as their go-to browser to protect their everyday online activities, from searching to browsing, emailing, and more. +DuckDuckGo is a free browser that provides the most comprehensive online privacy protection in one app. Unlike most popular browsers, it has powerful privacy protections by default, including our search engine that doesn’t track your history and over a dozen other built-in protections. Millions of people use DuckDuckGo as their go-to browser to protect their everyday online activities, from searching to browsing, emailing, and more.   FEATURE HIGHLIGHTS • Search Privately by Default - DuckDuckGo Private Search comes built-in, so you can easily search the web without being tracked. @@ -19,10 +19,8 @@ EVERYDAY PRIVACY CONTROLS • Tap Fire Button, Burn Data - Clear your tabs and browsing data fast with our Fire Button.   • Signal Your Privacy Preference with Global Privacy Control (GPC) - Built in to our app, GPC intends to help you express your opt-out rights automatically by telling websites not to sell or share your personal information. Whether it can be used to enforce your legal rights (for example, current or future CCPA, GDPR requirements) depends on the laws in your jurisdiction. -  -Note about our 3rd-Party Tracker Loading Protection following DuckDuckGo ad clicks: Our 3rd-Party Tracker Protection goes above and beyond what you get in most popular browsers by default, but we’re constantly working to make it more comprehensive. Currently, if an advertiser wants to detect conversions on their own website for our private DuckDuckGo search ads, our 3rd-Party Tracker Loading Protection will not stop bat.bing.com requests from loading on the advertiser’s website following DuckDuckGo ad clicks, but those requests are stopped in all other contexts. This is because DuckDuckGo private search advertising is in partnership with Microsoft. As part of that partnership, viewing ads on DuckDuckGo is anonymous and Microsoft has committed to not profile our users’ ad clicks. -  -Read more about this and our free Tracking Protections at https://help.duckduckgo.com/privacy/web-tracking-protections + +Read more about our free Tracking Protections at https://help.duckduckgo.com/privacy/web-tracking-protections You don't need to wait to take back your privacy. Join the millions of people using DuckDuckGo and protect many of your everyday online activities with one app. It's privacy, simplified. diff --git a/fastlane/metadata/es-ES/description.txt b/fastlane/metadata/es-ES/description.txt index af71cefc1d..5eba4a2f86 100644 --- a/fastlane/metadata/es-ES/description.txt +++ b/fastlane/metadata/es-ES/description.txt @@ -1,4 +1,3 @@ - La aplicación DuckDuckGo ofrece la protección de la privacidad en línea más completa con solo pulsar un botón. Con una sola descarga, obtienes un nuevo navegador de uso cotidiano que ofrece una protección perfecta mientras realizas búsquedas y navegas y acceso a protecciones de rastreo cuando recibes correo electrónico. Muchas de estas protecciones no se ofrecen en la mayoría de navegadores por defecto. FUNCIONES DESTACADAS @@ -17,8 +16,6 @@ CONTROLES DE PRIVACIDAD COTIDIANOS • Marca tus preferencias de privacidad con Global Privacy Control (GPC): integrado en la aplicación, GPC te ayuda a expresar tus derechos de exclusión voluntaria automáticamente para indicar a los sitios web que no pueden vender o compartir tu información personal. El hecho de que se pueda utilizar para hacer valer tus derechos (por ejemplo, la CCPA actual o futura, los requisitos del RGPD) depende de las leyes de tu jurisdicción. -Nota sobre nuestra protección de carga de rastreadores de terceros tras los clics de los anuncios de DuckDuckGo: nuestra protección de carga de rastreadores de terceros va más allá de lo que obtienes en la mayoría de los navegadores populares por defecto, pero estamos trabajando constantemente para hacerla aún más completa. Actualmente, si un anunciante desea detectar conversiones en su propio sitio web para nuestros anuncios privados de búsqueda de DuckDuckGo, nuestra protección de carga de rastreadores de terceros no impedirá que las solicitudes de bat.bing.com se carguen del sitio web del anunciante tras los clics en anuncios de DuckDuckGo, pero esas solicitudes se detienen en todos los demás contextos. Esto se debe a que la publicidad de DuckDuckGo Private Search está asociada con Microsoft. Como parte de esa asociación, la visualización de anuncios en DuckDuckGo es anónima y Microsoft se ha comprometido a no perfilar los clics en los anuncios de nuestros usuarios. - Lee más sobre esto y sobre nuestras protecciones de rastreo en https://help.duckduckgo.com/privacy/web-tracking-protections/ No tienes que esperar para recuperar tu privacidad. Únete a los millones de personas que usan DuckDuckGo y protege muchas de tus actividades cotidianas en línea con una sola aplicación. Es tan solo privacidad simplificada. diff --git a/fastlane/metadata/fi/description.txt b/fastlane/metadata/fi/description.txt index 6738709a7c..b3fe33787b 100644 --- a/fastlane/metadata/fi/description.txt +++ b/fastlane/metadata/fi/description.txt @@ -1,4 +1,3 @@ - DuckDuckGo-sovellus tarjoaa kattavimman yksityisyydensuojan verkossa napin painalluksella. Yhdellä latauksella saat uuden arkiselaimen, joka tarjoaa saumattoman suojan, kun haet ja selaat, ja seurantasuojaukset, kun vastaanotat sähköposteja. Monia näistä suojauksista ei tarjota oletusarvoisesti suosituimmissa selaimissa. OMINAISUUKSIEN KOHOKOHDAT  @@ -17,8 +16,6 @@ JOKAPÄIVÄISTÄ YKSITYISYYDEN HALLINTAA • Ilmoita yksityisyysasetuksestasi Global Privacy Control (GPC) -toiminnolla – sovellukseemme sisäänrakennettu GPC auttaa sinua ilmaisemaan kieltäytymisoikeutesi automaattisesti käskemällä sivustoja olemaan myymättä tai jakamatta henkilötietojasi. Se, voidaanko sitä käyttää laillisten oikeuksiesi (esimerkiksi nykyisen tai tulevan CCPA:n tai yleisen tietosuoja-asetuksen vaatimusten) täytäntöönpanoon, riippuu oman lainkäyttöalueesi laeista.  -Huomautus kolmannen osapuolen seurantalaitteiden lataussuojauksesta DuckDuckGo-mainosnapsautusten jälkeen: Kolmannen osapuolen seurantasuojauksemme ylittää sen, mitä saat suosituimmissa selaimissa oletusarvoisesti, mutta pyrimme jatkuvasti tekemään siitä kattavamman. Tällä hetkellä, jos mainostaja haluaa havaita yksityisten DuckDuckGo-hakumainosten konversioita omalla sivustollaan, kolmannen osapuolen seurantalaitteiden lataussuojaus ei estä bat.bing.com-pyyntöjen lataamista mainostajan verkkosivustolle DuckDuckGo-mainosklikkausten jälkeen, mutta pyynnöt pysäytetään kaikissa muissa yhteyksissä. Tämä johtuu siitä, että DuckDuckGon Private Search -haun mainonta tapahtuu yhteistyössä Microsoftin kanssa. Osana kumppanuutta, mainosten katselu DuckDuckGossa on nimetöntä, ja Microsoft on sitoutunut olemaan profiloimamatta käyttäjiemme mainosnapsautuksia.    - Lue lisää tästä ja jäljityssuojastamme osoitteessa https://help.duckduckgo.com/privacy/web-tracking-protections/.  Sinun ei tarvitse odottaa tietosuojasi takaisinottoa. Liity miljoonien DuckDuckGo-käyttäjien joukkoon ja suojaa monia päivittäisiä verkkotoimintojasi yhdellä sovelluksella. Se on tietosuojaa, yksinkertaistettuna.   diff --git a/fastlane/metadata/fr-FR/description.txt b/fastlane/metadata/fr-FR/description.txt index 92fada92bd..9ea936cf54 100644 --- a/fastlane/metadata/fr-FR/description.txt +++ b/fastlane/metadata/fr-FR/description.txt @@ -16,8 +16,6 @@ CONTRÔLES QUOTIDIENS DE LA CONFIDENTIALITÉ • Signalez vos préférences en matière de confidentialité avec le Global Privacy Control (GPC) – intégré à notre application, le GPC vous aide à formuler automatiquement vos droits de refus en indiquant aux sites Web de ne pas vendre ou partager vos informations personnelles. Le fait qu'il puisse être utilisé pour faire valoir vos droits légaux (par exemple, CCPA actuel ou futur, conditions du RGPD) dépend des lois de votre juridiction.  -Remarque à propos de notre « 3rd-Party Tracker Loading Protection » suivant les clics sur les publicités DuckDuckGo – notre protection contre les traqueurs tiers va bien au-delà de ce que vous proposent par défaut les navigateurs les plus populaires, mais nous nous efforçons constamment de l'enrichir. À l'heure actuelle, si un annonceur souhaite détecter les conversions sur son propre site Web pour nos publicités de recherche privée DuckDuckGo, notre protection contre le chargement de traqueurs tiers n'empêchera pas les requêtes bat.bing.com de se charger sur le site Web de l'annonceur après les clics sur les publicités DuckDuckGo, mais ces requêtes sont bloquées dans tous les autres contextes. Cela s'explique par le fait que la publicité de recherche privée DuckDuckGo a un partenariat avec Microsoft. Dans le cadre de ce partenariat, le visionnage des publicités sur DuckDuckGo est anonyme et Microsoft s'est engagé à ne pas profiler les clics sur les publicités de nos utilisateurs.    - Découvrez-en plus à ce sujet et sur nos protections contre le suivi sur https://help.duckduckgo.com/privacy/web-tracking-protections/  Inutile d'attendre pour reprendre le contrôle de votre vie privée. La confidentialité, simplifiée.   \ No newline at end of file diff --git a/fastlane/metadata/it/description.txt b/fastlane/metadata/it/description.txt index bc48c3f911..03dc40d5a6 100644 --- a/fastlane/metadata/it/description.txt +++ b/fastlane/metadata/it/description.txt @@ -20,8 +20,6 @@ CONTROLLI QUOTIDIANI SULLA PRIVACY • Global Privacy Control (GPC) per comunicare le proprie preferenze in materia di privacy - Per saperne di più, visita https://help.duckduckgo.com/duckduckgo-help-pages/privacy/gpc/ -Nota sulla nostra protezione dal caricamento dei sistemi di tracciamento di terze parti a seguito dei clic sugli annunci di DuckDuckGo: la nostra protezione dai sistemi di tracciamento di terze parti è superiore a quella disponibile per impostazione predefinita nei browser più diffusi, ma stiamo lavorando costantemente per renderla più completa. Attualmente, se un inserzionista desidera rilevare le conversioni sul proprio sito Web per i nostri annunci di ricerca privati di DuckDuckGo, la nostra protezione dal caricamento del sistemi di tracciamento di terze parti non interrompe il caricamento delle richieste di bat.bing.com sul sito Web dell'inserzionista in seguito ai clic sugli annunci di DuckDuckGo, ma tali richieste vengono interrotte in tutti gli altri contesti. Questo perché la pubblicità Private Search di DuckDuckGo è in collaborazione con Microsoft. Nell'ambito di questa partnership, la visualizzazione degli annunci su DuckDuckGo è anonima e Microsoft si è impegnata a non profilare i clic degli annunci dei nostri utenti. - Maggiori informazioni su questo e sulle nostre protezioni gratuite dal tracciamento sono disponibili all'indirizzo https://help.duckduckgo.com/privacy/web-tracking-protections diff --git a/fastlane/metadata/nl-NL/description.txt b/fastlane/metadata/nl-NL/description.txt index c1b4c2dba5..3c05387304 100644 --- a/fastlane/metadata/nl-NL/description.txt +++ b/fastlane/metadata/nl-NL/description.txt @@ -20,6 +20,4 @@ PRIVACYCONTROLES VOOR ELKE DAG • Geef je privacyvoorkeuren aan met Global Privacy Control (GPC) - Meer informatie vind je op https://help.duckduckgo.com/duckduckgo-help-pages/privacy/gpc/ -Opmerking over het volgen van klikken op DuckDuckGo-advertenties door onze functie voor het beschermen tegen trackers van derden: onze bescherming tegen trackers van derden gaat verder dan wat de meeste populaire browsers je standaard bieden, maar we werken voortdurend aan verdere uitbreiding ervan. Als een adverteerder conversies voor onze DuckDuckGo-advertenties bij privézoekopdrachten op zijn eigen website wil detecteren, zal onze bescherming tegen trackers van derden niet voorkomen dat bat.bing.com-verzoeken op de website van de adverteerder worden geladen nadat er op DuckDuckGo-advertenties is geklikt. Die verzoeken worden echter gestopt in alle andere contexten. Dat komt doordat voor de advertenties bij privézoekopdrachten in DuckDuckGo wordt samengewerkt met Microsoft. Het bekijken van advertenties op DuckDuckGo is als onderdeel van die samenwerking anoniem. Ook heeft Microsoft afgesproken om de advertentieklikken van onze gebruikers niet te gebruiken om profielen te maken. - Lees meer over deze functie en onze gratis trackingbescherming op https://help.duckduckgo.com/privacy/web-tracking-protections diff --git a/fastlane/metadata/nl-NL/it/description.txt b/fastlane/metadata/nl-NL/it/description.txt deleted file mode 100644 index bc48c3f911..0000000000 --- a/fastlane/metadata/nl-NL/it/description.txt +++ /dev/null @@ -1,27 +0,0 @@ -L'app DuckDuckGo offre protezioni della privacy online complete con la semplice pressione di un pulsante. Con un solo download gratuito avrai un browser Internet privato per uso quotidiano che offre una protezione perfetta durante la ricerca e la navigazione e l'accesso alla protezione del tracciamento per le e-mail che ricevi e le app che usi. Molte di queste protezioni non sono offerte automaticamente dai browser web più famosi. - -FUNZIONALITÀ PRINCIPALI -• Private Search disponibile automaticamente - DuckDuckGo Private Search integrato. - -• Blocco dei cookie di tracciamento - Impedisce il tracciamento alla maggior parte dei cookie di terze parti durante la navigazione online da un sito web all'altro. - -• Blocco della maggior parte dei sistemi di tracciamento prima che vengano caricati - Impedisce alle aziende di raccogliere e utilizzare i dati personali raccolti dai sistemi di tracciamento nascosti nei siti web. - -• Crittografia imposta - Obbliga molti siti visitati a utilizzare automaticamente una connessione crittografata (HTTPS). - -• Blocco dei sistemi di tracciamento e-mail (Beta) - Crea un indirizzo e-mail @duck.com personale e usa Email Protection per bloccare la maggior parte dei sistemi di tracciamento della elettronica, nascondere l'indirizzo esistente e inoltrare i messaggi alla posta in arrivo. - -• Elusione del fingerprinting - Aiuta a impedire alle aziende di creare un identificatore univoco per l'utente. - -Offriamo molte protezioni non disponibili nella maggior parte dei browser Internet (anche i browser in incognito), inclusa la protezione dal tracciamento dei link, dal monitoraggio AMP di Google e altro ancora. - -CONTROLLI QUOTIDIANI SULLA PRIVACY -• Fire Button per l'eliminazione dei dati - Cancella rapidamente le schede e i dati di navigazione con il nostro Fire Button. - -• Global Privacy Control (GPC) per comunicare le proprie preferenze in materia di privacy - Per saperne di più, visita https://help.duckduckgo.com/duckduckgo-help-pages/privacy/gpc/ - -Nota sulla nostra protezione dal caricamento dei sistemi di tracciamento di terze parti a seguito dei clic sugli annunci di DuckDuckGo: la nostra protezione dai sistemi di tracciamento di terze parti è superiore a quella disponibile per impostazione predefinita nei browser più diffusi, ma stiamo lavorando costantemente per renderla più completa. Attualmente, se un inserzionista desidera rilevare le conversioni sul proprio sito Web per i nostri annunci di ricerca privati di DuckDuckGo, la nostra protezione dal caricamento del sistemi di tracciamento di terze parti non interrompe il caricamento delle richieste di bat.bing.com sul sito Web dell'inserzionista in seguito ai clic sugli annunci di DuckDuckGo, ma tali richieste vengono interrotte in tutti gli altri contesti. Questo perché la pubblicità Private Search di DuckDuckGo è in collaborazione con Microsoft. Nell'ambito di questa partnership, la visualizzazione degli annunci su DuckDuckGo è anonima e Microsoft si è impegnata a non profilare i clic degli annunci dei nostri utenti. - -Maggiori informazioni su questo e sulle nostre protezioni gratuite dal tracciamento sono disponibili all'indirizzo https://help.duckduckgo.com/privacy/web-tracking-protections - - diff --git a/fastlane/metadata/nl-NL/it/marketing_url.txt b/fastlane/metadata/nl-NL/it/marketing_url.txt deleted file mode 100644 index 07b7d171d9..0000000000 --- a/fastlane/metadata/nl-NL/it/marketing_url.txt +++ /dev/null @@ -1 +0,0 @@ -https://duckduckgo.com/ diff --git a/fastlane/metadata/nl-NL/it/name.txt b/fastlane/metadata/nl-NL/it/name.txt deleted file mode 100644 index d9601ee135..0000000000 --- a/fastlane/metadata/nl-NL/it/name.txt +++ /dev/null @@ -1 +0,0 @@ -DuckDuckGo Private Browser diff --git a/fastlane/metadata/nl-NL/it/privacy_url.txt b/fastlane/metadata/nl-NL/it/privacy_url.txt deleted file mode 100644 index 910a16c034..0000000000 --- a/fastlane/metadata/nl-NL/it/privacy_url.txt +++ /dev/null @@ -1 +0,0 @@ -https://duckduckgo.com/privacy diff --git a/fastlane/metadata/nl-NL/it/promotional_text.txt b/fastlane/metadata/nl-NL/it/promotional_text.txt deleted file mode 100644 index f253621fcf..0000000000 --- a/fastlane/metadata/nl-NL/it/promotional_text.txt +++ /dev/null @@ -1 +0,0 @@ -La privacy semplificata. Un'unica app ricca di funzionalità e svariati metodi per proteggere la privacy. \ No newline at end of file diff --git a/fastlane/metadata/nl-NL/it/subtitle.txt b/fastlane/metadata/nl-NL/it/subtitle.txt deleted file mode 100644 index df074ced85..0000000000 --- a/fastlane/metadata/nl-NL/it/subtitle.txt +++ /dev/null @@ -1 +0,0 @@ -La privacy semplificata. \ No newline at end of file diff --git a/fastlane/metadata/nl-NL/it/support_url.txt b/fastlane/metadata/nl-NL/it/support_url.txt deleted file mode 100644 index a741075b71..0000000000 --- a/fastlane/metadata/nl-NL/it/support_url.txt +++ /dev/null @@ -1 +0,0 @@ -https://duckduckgo.com/feedback diff --git a/fastlane/metadata/no/description.txt b/fastlane/metadata/no/description.txt index 6af807818e..e9546ff48d 100644 --- a/fastlane/metadata/no/description.txt +++ b/fastlane/metadata/no/description.txt @@ -1,4 +1,3 @@ - DuckDuckGo-appen gir det mest omfattende personvernet på nettet med et trykk på en knapp. Med én nedlasting får du en hverdagsnettleser som tilbyr sømløs beskyttelse mens du søker og surfer samt tilgang til sporingsbeskyttelse på e-poster du får. Mange av disse beskyttelsene tilbys ikke som standard i de fleste populære nettleserne.  HØYDEPUNKTER  @@ -19,9 +18,7 @@ Vi har også mange beskyttelser som ikke er tilgjengelige i de fleste nettlesere PERSONVERNKONTROLLER I HVERDAGEN • Trykk på Fire Button-knappen og brenn data – tøm faner og nettleserdata med Fire Button. -• Signaliser personvernreferansene dine med GPC (Global Privacy Control) – som er innebygd i appen og uttrykker automatisk reservasjonsretten din ved å gi beskjed til nettstedene om at de ikke kan selge eller dele personopplysningene dine. Om dette kan brukes til å håndheve dine juridiske rettigheter (for eksempel i henhold til nåværende eller fremtidige CCPA- og GDPR-krav), avhenger av lovene i din jurisdiksjon.  - -Merknad om vår beskyttelse mot tredjeparts sporingslasting etter annonseklikk på DuckDuckGo: I utgangspunktet går vår beskyttelse mot tredjepartssporing langt utover hva du får i de fleste populære nettlesere, men vi jobber uavlatelig med å gjøre det mer omfattende. For øyeblikket er det slik at hvis en annonsør vil registrere konverteringer på sitt eget nettsted for våre private DuckDuckGo-søkeannonser, forhindrer ikke beskyttelsen mot tredjeparts sporingslasting bat.bing.com-forespørsler i å lastes på annonsørens nettsted etter DuckDuckGo-annonseklikk, men disse forespørslene stoppes i alle andre sammenhenger. Dette er fordi DuckDuckGos Private Search-annonsering skjer i samarbeid med Microsoft. Dette samarbeidet innebærer at du ser annonser anonymt på DuckDuckGo, og Microsoft har forpliktet seg til å ikke profilere brukernes annonseklikk.   +• Signaliser personvernreferansene dine med GPC (Global Privacy Control) – som er innebygd i appen og uttrykker automatisk reservasjonsretten din ved å gi beskjed til nettstedene om at de ikke kan selge eller dele personopplysningene dine. Om dette kan brukes til å håndheve dine juridiske rettigheter (for eksempel i henhold til nåværende eller fremtidige CCPA- og GDPR-krav), avhenger av lovene i din jurisdiksjon.     Les mer om dette og sporingsbeskyttelsene våre på https://help.duckduckgo.com/privacy/web-tracking-protections/   diff --git a/fastlane/metadata/pl/description.txt b/fastlane/metadata/pl/description.txt index 52b811bf02..e7f7073a49 100644 --- a/fastlane/metadata/pl/description.txt +++ b/fastlane/metadata/pl/description.txt @@ -20,7 +20,5 @@ CODZIENNA KONTROLA PRYWATNOŚCI • Zasygnalizuj swoje preferencje dotyczące prywatności za pomocą funkcji GPC (Global Privacy Control) - Więcej informacji znajdziesz na stronie https://help.duckduckgo.com/duckduckgo-help-pages/privacy/gpc/ -Informacja o naszym systemie ochrony przed wczytywaniem reklam przez strony trzecie po kliknięciu reklamy DuckDuckGo: nasza funkcja ochrony przed ładowaniem przez strony trzecie wykracza poza to, co jest domyślnie dostępne w większości popularnych przeglądarek, jednak nieustannie pracujemy nad tym, aby była ona jeszcze bardziej wszechstronna. Obecnie, jeśli reklamodawca chce wykryć konwersje na swojej stronie internetowej dla naszych prywatnych reklam wyszukiwania DuckDuckGo, nasza ochrona przed ładowaniem przez trackery stron trzecich nie blokuje ładowania zapytań bat.bing.com na stronie reklamodawcy po kliknięciu reklamy DuckDuckGo, ale zapytania te są blokowane we wszystkich innych kontekstach. Dzieje się tak dlatego, że reklama w wyszukiwarce prywatnej DuckDuckGo współpracuje z firmą Microsoft. W ramach tego partnerstwa oglądanie reklam w DuckDuckGo jest anonimowe, a firma Microsoft zobowiązała się nie profilować kliknięć reklam przez naszych użytkowników. - Więcej informacji na ten temat oraz na temat naszej bezpłatnej ochrony przed śledzeniem można znaleźć na stronie https://help.duckduckgo.com/privacy/web-tracking-protections diff --git a/fastlane/metadata/pt-PT/description.txt b/fastlane/metadata/pt-PT/description.txt index 880030ff87..6a03db6606 100644 --- a/fastlane/metadata/pt-PT/description.txt +++ b/fastlane/metadata/pt-PT/description.txt @@ -1,4 +1,3 @@ - A aplicação DuckDuckGo oferece-te a proteção de privacidade online mais abrangente com um toque num botão. Com uma transferência, obténs um novo navegador para o dia a dia que oferece proteção contínua enquanto pesquisas e navegas, e acesso a proteções contra rastreamento para os e-mails. Muitas dessas proteções não são oferecidas na maioria dos navegadores populares por predefinição. DESTAQUES DAS FUNCIONALIDADES  @@ -16,8 +15,7 @@ CONTROLOS DE PRIVACIDADE PARA O DIA A DIA • Toca no Fire Button, limpa os dados – Fecha os separadores e limpa os dados de navegação com o Fire Button. • Sinaliza a tua preferência de privacidade com o Global Privacy Control (GPC) – O GPC está incorporado na nossa aplicação e ajuda-te a expressares os teus direitos de exclusão automaticamente ao indicar aos sites que não vendam nem partilhem as tuas informações pessoais. Dependendo das leis da tua jurisdição, pode ser possível utilizá-lo para fazer valer os teus direitos legais (por exemplo, os requisitos atuais e futuros da CCPA e do RGPD).  - -Nota sobre a nossa 3rd-Party Tracker Loading Protection após cliques em anúncios da DuckDuckGo: a nossa 3rd-Party Tracker Loading Protection vai muito além do que a maioria dos navegadores populares oferecem por predefinição, mas estamos sempre a tentar melhorá-la. Atualmente, se um anunciante quiser detetar conversões no respetivo site para os nossos anúncios de pesquisa privados do DuckDuckGo, a 3rd-Party Tracker Loading Protection não impedirá o carregamento dos pedidos a stop.bat.bing.com no site do anunciante após cliques em anúncios do DuckDuckGo, mas esses pedidos serão travados em todos os outros contextos. Tal acontece porque a publicidade de pesquisa privada do DuckDuckGo é uma parceria com a Microsoft. No âmbito dessa parceria, a visualização de anúncios no DuckDuckGo é anónima e a Microsoft comprometeu-se a não criar perfis com os cliques em anúncios dos nossos utilizadores.    +    Obtém mais informações sobre estes anúncios e as nossas proteções contra o rastreamento em https://help.duckduckgo.com/privacy/web-tracking-protections/   Não precisa de esperar para recuperar a sua privacidade. Junte-se aos milhões de pessoas que usam o DuckDuckGo e proteja muitas das suas atividades online diárias com uma aplicação. É privacidade, simplificada.   \ No newline at end of file diff --git a/fastlane/metadata/ro/description.txt b/fastlane/metadata/ro/description.txt index c83a7e612e..14c6ebfd0f 100644 --- a/fastlane/metadata/ro/description.txt +++ b/fastlane/metadata/ro/description.txt @@ -20,8 +20,6 @@ CONTROALE DE CONFIDENȚIALITATE ZILNICE • Semnalează-ți preferințele în materie de confidențialitate cu Controlul global al confidențialității (GPC) Află mai multe la adresa https://help.duckduckgo.com/duckduckgo-help-pages/privacy/gpc/ -Notă despre protecția noastră împotriva încărcării tehnologiilor de urmărire terțe după ce ai făcut clic pe reclame DuckDuckGo: Protecția noastră împotriva tehnologiilor de urmărire terțe depășește cu mult ceea ce primești în mod implicit de la cele mai populare browsere și lucrăm constant pentru a o face chiar și mai cuprinzătoare. În prezent, dacă un agent de publicitate dorește să detecteze conversiile pe propriul site pentru reclamele noastre private la căutările pe DuckDuckGo, protecția noastră împotriva încărcării tehnologiilor de urmărire terțe nu va opri încărcarea solicitărilor bat.bing.com pe site-ul agenției de publicitate în urma clicurilor pe reclame DuckDuckGo, dar aceste solicitări sunt oprite în toate celelalte contexte. Acest lucru se datorează faptului că reclamele în cazul căutării private DuckDuckGo sunt oferite în parteneriat cu Microsoft. Ca parte a acestui parteneriat, vizualizarea anunțurilor pe DuckDuckGo este anonimă, iar Microsoft s-a angajat să nu profileze clicurile pe anunțuri ale utilizatorilor noștri. - Citește mai multe despre acest lucru și despre instrumentele noastre gratuite de protecție împotriva urmăririi pe https://help.duckduckgo.com/privacy/web-tracking-protections diff --git a/fastlane/metadata/ru/description.txt b/fastlane/metadata/ru/description.txt index 6e28acf372..7140d77c4b 100644 --- a/fastlane/metadata/ru/description.txt +++ b/fastlane/metadata/ru/description.txt @@ -1,5 +1,3 @@ - - Приложение DuckDuckGo обеспечивает наиболее комплексную защиту конфиденциальности онлайн одним нажатием кнопки. Всего одна загрузка — и ваш новый браузер будет незаметно защищать вас во время поиска и просмотра сайтов и даже блокировать отслеживание входящей почты. Защита конфиденциальности онлайн напоминает защиту своего дома. Если запереть только входную дверь, это не помешает настойчивым злоумышленникам проникнуть внутрь, особенно если вы оставили заднюю дверь и окна открытыми, а запасной ключ — под ковриком. Вот почему мы предлагаем несколько типов защиты конфиденциальности, многие из которых по умолчанию не доступны в большинстве популярных браузеров.   @@ -19,8 +17,6 @@ • Кнопка «Тревога» для удаления данных — теперь закрыть вкладки и очистить данные браузера можно нажатием специальной кнопки «Тревога». • Определите устраивающие вас параметры конфиденциальности с помощью Глобального контроля конфиденциальности (GPC) — в приложение уже встроен GPC, который автоматически выбирает отказ сайтам в праве продавать или передавать вашу персональную информацию. Возможность использования такого контроля для принудительного соблюдения ваших законных прав (согласно требований, например, CCPA и GDPR в действующей или последующей редакции) зависит от законодательства в месте вашего пребывания.  - -Защита от загрузки сторонних трекеров после нажатия рекламных объявлений в DuckDuckGo намного превосходит то, что предлагает большинство популярных браузеров по умолчанию. Тем не менее, мы постоянно работаем над расширением такой защиты. В настоящее время она не предотвращает загрузку запросов bat.bing.com на сайте рекламодателя после перехода на него по рекламному объявлению в DuckDuckGo, когда рекламодатель отслеживает конверсию на своем сайте. Однако такие запросы блокируются во всех других случаях. Это связано с тем, что реклама в поиске DuckDuckGo ведется на условиях партнерства с Microsoft. В рамках этого партнерства просмотр рекламы на DuckDuckGo является анонимным, так как Microsoft обязалась не профилировать наших пользователей при нажатии ими объявлений.    Читайте подробнее о средствах защиты от отслеживания на странице https://help.duckduckgo.com/privacy/web-tracking-protections/.   diff --git a/fastlane/metadata/sv/description.txt b/fastlane/metadata/sv/description.txt index 85c726e5dc..c17da96d4a 100644 --- a/fastlane/metadata/sv/description.txt +++ b/fastlane/metadata/sv/description.txt @@ -1,4 +1,3 @@ - DuckDuckGo-appen ger det mest omfattande integritetsskyddet online med ett enda knapptryck. Med en nedladdning får du en ny vardagswebbläsare som erbjuder sömlöst skydd medan du söker och surfar. Du får även tillgång till spårningsskydd för inkommande e-postmeddelanden. Många av dessa skydd erbjuds inte som standard i de populäraste webbläsarna. FUNKTIONER I FOKUS  @@ -17,9 +16,7 @@ FUNKTIONER I FOKUS  INTEGRITETSKONTROLLER VARJE DAG • Tryck på brännarknappen, bränn data – Rensa dina flikar och webbläsardata med vår brännarknapp. -• Ange dina integritetsinställningar med Global Privacy Control (GPC) – GPC är inbyggd i vår app och är till för att hjälpa dig att automatiskt utnyttja din rätt att välja genom att tala om för webbplatser att de inte får sälja eller dela din personliga information. Huruvida det kan användas för att genomdriva dina juridiska rättigheter (till exempel nuvarande eller framtida CCPA- eller GDPR-krav) beror på lagarna i din jurisdiktion.  - -Anmärkning gällande vårt tredjepartsskydd för inläsning av spårare efter DuckDuckGo-annonsklick: Vårt skydd för spårare från tredje part ger dig mer än vad du får som standard i de flesta populära webbläsare, men vi arbetar ständigt för att utöka det. Om en annonsör vill upptäcka konverteringar på sin egen webbplats för våra privata DuckDuckGo-sökannonser stoppar vårt skydd för laddning av spårare från tredje part inte bat.bing.com-förfrågningar från att läsas in på annonsörens webbplats efter DuckDuckGo-annonsklick, men dessa förfrågningar stoppas i alla andra sammanhang. Detta beror på att DuckDuckGo:s privata sökannonsering sker i samarbete med Microsoft. Enligt denna partnerskapsöverenskommelse är du anonym när du ser annonser på DuckDuckGo, och Microsoft har åtagit sig att inte profilera våra användares annonsklick.    +• Ange dina integritetsinställningar med Global Privacy Control (GPC) – GPC är inbyggd i vår app och är till för att hjälpa dig att automatiskt utnyttja din rätt att välja genom att tala om för webbplatser att de inte får sälja eller dela din personliga information. Huruvida det kan användas för att genomdriva dina juridiska rättigheter (till exempel nuvarande eller framtida CCPA- eller GDPR-krav) beror på lagarna i din jurisdiktion.   Läs mer om detta och vårt skydd mot spårning på https://help.duckduckgo.com/privacy/web-tracking-protections/  diff --git a/fastlane/metadata/tr/description.txt b/fastlane/metadata/tr/description.txt index 60b4d757f1..fa283ba4a0 100644 --- a/fastlane/metadata/tr/description.txt +++ b/fastlane/metadata/tr/description.txt @@ -1,4 +1,3 @@ - DuckDuckGo uygulaması, bir düğmeye basarak en kapsamlı çevrimiçi gizlilik korumasını sağlar. Tek bir indirme ile, arama yaparken ve tarayıcıda gezinirken sürekli bir koruma ve aldığınız e-postalar için izleme korumalarına erişim sağlar. Bu korumaların çoğu en popüler tarayıcılarda varsayılan olarak sunulmaz. ÖNE ÇIKAN ÖZELLİKLER  @@ -21,8 +20,6 @@ GÜNLÜK GİZLİLİK KONTROLLERİ • Küresel Gizlilik Kontrolü (GPC) ile Gizlilik Tercihinizi Belirtin - uygulamamızda yerleşik olarak bulunan GPC, web sitelerine kişisel bilgilerinizi satmamalarını veya paylaşmamalarını söyleyerek devre dışı bırakma haklarınızı otomatik olarak belirtmenize yardımcı olmayı amaçlamaktadır. Yasal haklarınızı uygulamak için kullanılıp kullanılamayacağı (örneğin, mevcut veya gelecekteki CCPA, GDPR gereklilikleri) yargı alanınızdaki yasalara bağlıdır.  -DuckDuckGo reklam tıklamalarını takip eden Üçüncü Taraf İzleyici Yükleme Korumamız hakkında bilgi: Üçüncü Taraf İzleyici Korumamız, varsayılan olarak en popüler tarayıcılara göre çok daha fazlasını yapar; ancak bunu daha kapsamlı hale getirmek için sürekli olarak çalışmaya devam ediyoruz. Şu anda, bir reklam veren özel DuckDuckGo arama reklamlarımız için kendi web sitesinde dönüşümleri tespit etmek isterse, Üçüncü Taraf İzleyici Yükleme Korumamız, DuckDuckGo reklam tıklamalarını takiben bat.bing.com isteklerinin reklam verenin web sitesinde yüklenmesini durdurmaz; ancak bu istekler diğer tüm koşullarda durdurulur. Bunun nedeni, DuckDuckGo özel arama reklamlarının Microsoft ile ortak olmasıdır. Bu ortaklığın bir parçası olarak, DuckDuckGo'da reklamlar anonim olarak görüntülenir ve Microsoft, kullanıcılarımızın reklam tıklamaları için profil oluşturmamayı taahhüt etmiştir.    - Bu konu ve İzlenme Korumalarımız hakkında daha fazla bilgi okumak için tıklayın: https://help.duckduckgo.com/privacy/web-tracking-protections/  Gizliliğinizi geri almak için beklemenize gerek yok. DuckDuckGo kullanan milyonlarca insana katılın ve tek bir uygulama ile internetteki günlük etkinliklerinizin çoğunu koruyun. Gizlilik, basitleştirildi.   \ No newline at end of file From e0971496db13f82cd416a060bac4d0e2d157f037 Mon Sep 17 00:00:00 2001 From: Dax Mobile <44842493+daxmobile@users.noreply.github.com> Date: Mon, 11 Mar 2024 13:42:52 +0100 Subject: [PATCH 13/19] Update autoconsent to v10.2.0 (#2554) Co-authored-by: muodov --- DuckDuckGo/Autoconsent/autoconsent-bundle.js | 2 +- package-lock.json | 8 ++++---- package.json | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/DuckDuckGo/Autoconsent/autoconsent-bundle.js b/DuckDuckGo/Autoconsent/autoconsent-bundle.js index 7775dafeb4..9463dd5805 100644 --- a/DuckDuckGo/Autoconsent/autoconsent-bundle.js +++ b/DuckDuckGo/Autoconsent/autoconsent-bundle.js @@ -1 +1 @@ -!function(){"use strict";var e=class e{static setBase(t){e.base=t}static findElement(t,o=null,c=!1){let i=null;return i=null!=o?Array.from(o.querySelectorAll(t.selector)):null!=e.base?Array.from(e.base.querySelectorAll(t.selector)):Array.from(document.querySelectorAll(t.selector)),null!=t.textFilter&&(i=i.filter((e=>{const o=e.textContent.toLowerCase();if(Array.isArray(t.textFilter)){let e=!1;for(const c of t.textFilter)if(-1!==o.indexOf(c.toLowerCase())){e=!0;break}return e}if(null!=t.textFilter)return-1!==o.indexOf(t.textFilter.toLowerCase())}))),null!=t.styleFilters&&(i=i.filter((e=>{const o=window.getComputedStyle(e);let c=!0;for(const e of t.styleFilters){const t=o[e.option];c=e.negated?c&&t!==e.value:c&&t===e.value}return c}))),null!=t.displayFilter&&(i=i.filter((e=>t.displayFilter?0!==e.offsetHeight:0===e.offsetHeight))),null!=t.iframeFilter&&(i=i.filter((()=>t.iframeFilter?window.location!==window.parent.location:window.location===window.parent.location))),null!=t.childFilter&&(i=i.filter((o=>{const c=e.base;e.setBase(o);const i=e.find(t.childFilter);return e.setBase(c),null!=i.target}))),c?i:(i.length>1&&console.warn("Multiple possible targets: ",i,t,o),i[0])}static find(t,o=!1){const c=[];if(null!=t.parent){const i=e.findElement(t.parent,null,o);if(null!=i){if(i instanceof Array)return i.forEach((i=>{const n=e.findElement(t.target,i,o);n instanceof Array?n.forEach((e=>{c.push({parent:i,target:e})})):c.push({parent:i,target:n})})),c;{const n=e.findElement(t.target,i,o);n instanceof Array?n.forEach((e=>{c.push({parent:i,target:e})})):c.push({parent:i,target:n})}}}else{const i=e.findElement(t.target,null,o);i instanceof Array?i.forEach((e=>{c.push({parent:null,target:e})})):c.push({parent:null,target:i})}return 0===c.length&&c.push({parent:null,target:null}),o?c:(1!==c.length&&console.warn("Multiple results found, even though multiple false",c),c[0])}};e.base=null;var t=e;function o(e){const o=t.find(e);return"css"===e.type?!!o.target:"checkbox"===e.type?!!o.target&&o.target.checked:void 0}async function c(e,a){switch(e.type){case"click":return async function(e){const o=t.find(e);null!=o.target&&o.target.click();return n(i)}(e);case"list":return async function(e,t){for(const o of e.actions)await c(o,t)}(e,a);case"consent":return async function(e,t){for(const i of e.consents){const e=-1!==t.indexOf(i.type);if(i.matcher&&i.toggleAction){o(i.matcher)!==e&&await c(i.toggleAction)}else e?await c(i.trueAction):await c(i.falseAction)}}(e,a);case"ifcss":return async function(e,o){const i=t.find(e);i.target?e.falseAction&&await c(e.falseAction,o):e.trueAction&&await c(e.trueAction,o)}(e,a);case"waitcss":return async function(e){await new Promise((o=>{let c=e.retries||10;const i=e.waitTime||250,n=()=>{const a=t.find(e);(e.negated&&a.target||!e.negated&&!a.target)&&c>0?(c-=1,setTimeout(n,i)):o()};n()}))}(e);case"foreach":return async function(e,o){const i=t.find(e,!0),n=t.base;for(const n of i)n.target&&(t.setBase(n.target),await c(e.action,o));t.setBase(n)}(e,a);case"hide":return async function(e){const o=t.find(e);o.target&&o.target.classList.add("Autoconsent-Hidden")}(e);case"slide":return async function(e){const o=t.find(e),c=t.find(e.dragTarget);if(o.target){const e=o.target.getBoundingClientRect(),t=c.target.getBoundingClientRect();let i=t.top-e.top,n=t.left-e.left;"y"===this.config.axis.toLowerCase()&&(n=0),"x"===this.config.axis.toLowerCase()&&(i=0);const a=window.screenX+e.left+e.width/2,s=window.screenY+e.top+e.height/2,r=e.left+e.width/2,l=e.top+e.height/2,p=document.createEvent("MouseEvents");p.initMouseEvent("mousedown",!0,!0,window,0,a,s,r,l,!1,!1,!1,!1,0,o.target);const d=document.createEvent("MouseEvents");d.initMouseEvent("mousemove",!0,!0,window,0,a+n,s+i,r+n,l+i,!1,!1,!1,!1,0,o.target);const u=document.createEvent("MouseEvents");u.initMouseEvent("mouseup",!0,!0,window,0,a+n,s+i,r+n,l+i,!1,!1,!1,!1,0,o.target),o.target.dispatchEvent(p),await this.waitTimeout(10),o.target.dispatchEvent(d),await this.waitTimeout(10),o.target.dispatchEvent(u)}}(e);case"close":return async function(){window.close()}();case"wait":return async function(e){await n(e.waitTime)}(e);case"eval":return async function(e){return console.log("eval!",e.code),new Promise((t=>{try{e.async?(window.eval(e.code),setTimeout((()=>{t(window.eval("window.__consentCheckResult"))}),e.timeout||250)):t(window.eval(e.code))}catch(o){console.warn("eval error",o,e.code),t(!1)}}))}(e);default:throw"Unknown action type: "+e.type}}var i=0;function n(e){return new Promise((t=>{setTimeout((()=>{t()}),e)}))}function a(){return crypto&&void 0!==crypto.randomUUID?crypto.randomUUID():Math.random().toString()}var s=class{constructor(e,t=1e3){this.id=e,this.promise=new Promise(((e,t)=>{this.resolve=e,this.reject=t})),this.timer=window.setTimeout((()=>{this.reject(new Error("timeout"))}),t)}},r={pending:new Map,sendContentMessage:null};var l={EVAL_0:()=>console.log(1),EVAL_CONSENTMANAGER_1:()=>window.__cmp&&"object"==typeof __cmp("getCMPData"),EVAL_CONSENTMANAGER_2:()=>!__cmp("consentStatus").userChoiceExists,EVAL_CONSENTMANAGER_3:()=>__cmp("setConsent",0),EVAL_CONSENTMANAGER_4:()=>__cmp("setConsent",1),EVAL_CONSENTMANAGER_5:()=>__cmp("consentStatus").userChoiceExists,EVAL_COOKIEBOT_1:()=>!!window.Cookiebot,EVAL_COOKIEBOT_2:()=>!window.Cookiebot.hasResponse&&!0===window.Cookiebot.dialog?.visible,EVAL_COOKIEBOT_3:()=>window.Cookiebot.withdraw()||!0,EVAL_COOKIEBOT_4:()=>window.Cookiebot.hide()||!0,EVAL_COOKIEBOT_5:()=>!0===window.Cookiebot.declined,EVAL_KLARO_1:()=>{const e=globalThis.klaroConfig||globalThis.klaro?.getManager&&globalThis.klaro.getManager().config;if(!e)return!0;const t=(e.services||e.apps).filter((e=>!e.required)).map((e=>e.name));if(klaro&&klaro.getManager){const e=klaro.getManager();return t.every((t=>!e.consents[t]))}if(klaroConfig&&"cookie"===klaroConfig.storageMethod){const e=klaroConfig.cookieName||klaroConfig.storageName,o=JSON.parse(decodeURIComponent(document.cookie.split(";").find((t=>t.trim().startsWith(e))).split("=")[1]));return Object.keys(o).filter((e=>t.includes(e))).every((e=>!1===o[e]))}},EVAL_ONETRUST_1:()=>window.OnetrustActiveGroups.split(",").filter((e=>e.length>0)).length<=1,EVAL_TRUSTARC_TOP:()=>window&&window.truste&&"0"===window.truste.eu.bindMap.prefCookie,EVAL_ADROLL_0:()=>!document.cookie.includes("__adroll_fpc"),EVAL_ALMACMP_0:()=>document.cookie.includes('"name":"Google","consent":false'),EVAL_AFFINITY_SERIF_COM_0:()=>document.cookie.includes("serif_manage_cookies_viewed")&&!document.cookie.includes("serif_allow_analytics"),EVAL_ARBEITSAGENTUR_TEST:()=>document.cookie.includes("cookie_consent=denied"),EVAL_AXEPTIO_0:()=>document.cookie.includes("axeptio_authorized_vendors=%2C%2C"),EVAL_BAHN_TEST:()=>1===utag.gdpr.getSelectedCategories().length,EVAL_BING_0:()=>document.cookie.includes("AL=0")&&document.cookie.includes("AD=0")&&document.cookie.includes("SM=0"),EVAL_BLOCKSY_0:()=>document.cookie.includes("blocksy_cookies_consent_accepted=no"),EVAL_BORLABS_0:()=>!JSON.parse(decodeURIComponent(document.cookie.split(";").find((e=>-1!==e.indexOf("borlabs-cookie"))).split("=",2)[1])).consents.statistics,EVAL_BUNDESREGIERUNG_DE_0:()=>document.cookie.match("cookie-allow-tracking=0"),EVAL_CANVA_0:()=>!document.cookie.includes("gtm_fpc_engagement_event"),EVAL_CC_BANNER2_0:()=>!!document.cookie.match(/sncc=[^;]+D%3Dtrue/),EVAL_CLICKIO_0:()=>document.cookie.includes("__lxG__consent__v2_daisybit="),EVAL_CLINCH_0:()=>document.cookie.includes("ctc_rejected=1"),EVAL_COOKIECONSENT2_TEST:()=>document.cookie.includes("cc_cookie="),EVAL_COOKIECONSENT3_TEST:()=>document.cookie.includes("cc_cookie="),EVAL_COINBASE_0:()=>JSON.parse(decodeURIComponent(document.cookie.match(/cm_(eu|default)_preferences=([0-9a-zA-Z\\{\\}\\[\\]%:]*);?/)[2])).consent.length<=1,EVAL_COMPLIANZ_BANNER_0:()=>document.cookie.includes("cmplz_banner-status=dismissed"),EVAL_COOKIE_LAW_INFO_0:()=>CLI.disableAllCookies()||CLI.reject_close()||!0,EVAL_COOKIE_LAW_INFO_1:()=>-1===document.cookie.indexOf("cookielawinfo-checkbox-non-necessary=yes"),EVAL_COOKIE_LAW_INFO_DETECT:()=>!!window.CLI,EVAL_COOKIE_MANAGER_POPUP_0:()=>!1===JSON.parse(document.cookie.split(";").find((e=>e.trim().startsWith("CookieLevel"))).split("=")[1]).social,EVAL_COOKIEALERT_0:()=>document.querySelector("body").removeAttribute("style")||!0,EVAL_COOKIEALERT_1:()=>document.querySelector("body").removeAttribute("style")||!0,EVAL_COOKIEALERT_2:()=>!0===window.CookieConsent.declined,EVAL_COOKIEFIRST_0:()=>{return!1===(e=JSON.parse(decodeURIComponent(document.cookie.split(";").find((e=>-1!==e.indexOf("cookiefirst"))).trim()).split("=")[1])).performance&&!1===e.functional&&!1===e.advertising;var e},EVAL_COOKIEFIRST_1:()=>document.querySelectorAll("button[data-cookiefirst-accent-color=true][role=checkbox]:not([disabled])").forEach((e=>"true"==e.getAttribute("aria-checked")&&e.click()))||!0,EVAL_COOKIEINFORMATION_0:()=>CookieInformation.declineAllCategories()||!0,EVAL_COOKIEINFORMATION_1:()=>CookieInformation.submitAllCategories()||!0,EVAL_COOKIEINFORMATION_2:()=>document.cookie.includes("CookieInformationConsent="),EVAL_COOKIEYES_0:()=>document.cookie.includes("advertisement:no"),EVAL_DAILYMOTION_0:()=>!!document.cookie.match("dm-euconsent-v2"),EVAL_DNDBEYOND_TEST:()=>document.cookie.includes("cookie-consent=denied"),EVAL_DSGVO_0:()=>!document.cookie.includes("sp_dsgvo_cookie_settings"),EVAL_DUNELM_0:()=>document.cookie.includes("cc_functional=0")&&document.cookie.includes("cc_targeting=0"),EVAL_ETSY_0:()=>document.querySelectorAll(".gdpr-overlay-body input").forEach((e=>{e.checked=!1}))||!0,EVAL_ETSY_1:()=>document.querySelector(".gdpr-overlay-view button[data-wt-overlay-close]").click()||!0,EVAL_EU_COOKIE_COMPLIANCE_0:()=>-1===document.cookie.indexOf("cookie-agreed=2"),EVAL_EU_COOKIE_LAW_0:()=>!document.cookie.includes("euCookie"),EVAL_EZOIC_0:()=>ezCMP.handleAcceptAllClick(),EVAL_EZOIC_1:()=>!!document.cookie.match(/ez-consent-tcf/),EVAL_GOOGLE_0:()=>!!document.cookie.match(/SOCS=CAE/),EVAL_HEMA_TEST_0:()=>document.cookie.includes("cookies_rejected=1"),EVAL_IUBENDA_0:()=>document.querySelectorAll(".purposes-item input[type=checkbox]:not([disabled])").forEach((e=>{e.checked&&e.click()}))||!0,EVAL_IUBENDA_1:()=>!!document.cookie.match(/_iub_cs-\d+=/),EVAL_IWINK_TEST:()=>document.cookie.includes("cookie_permission_granted=no"),EVAL_JQUERY_COOKIEBAR_0:()=>!document.cookie.includes("cookies-state=accepted"),EVAL_MEDIAVINE_0:()=>document.querySelectorAll('[data-name="mediavine-gdpr-cmp"] input[type=checkbox]').forEach((e=>e.checked&&e.click()))||!0,EVAL_MICROSOFT_0:()=>Array.from(document.querySelectorAll("div > button")).filter((e=>e.innerText.match("Reject|Ablehnen")))[0].click()||!0,EVAL_MICROSOFT_1:()=>Array.from(document.querySelectorAll("div > button")).filter((e=>e.innerText.match("Accept|Annehmen")))[0].click()||!0,EVAL_MICROSOFT_2:()=>!!document.cookie.match("MSCC|GHCC"),EVAL_MOOVE_0:()=>document.querySelectorAll("#moove_gdpr_cookie_modal input").forEach((e=>{e.disabled||"moove_gdpr_strict_cookies"===e.name||(e.checked=!1)}))||!0,EVAL_ONENINETWO_0:()=>document.cookie.includes("CC_ADVERTISING=NO")&&document.cookie.includes("CC_ANALYTICS=NO"),EVAL_OPERA_0:()=>document.cookie.includes("cookie_consent_essential=true")&&!document.cookie.includes("cookie_consent_marketing=true"),EVAL_PAYPAL_0:()=>!0===document.cookie.includes("cookie_prefs"),EVAL_PRIMEBOX_0:()=>!document.cookie.includes("cb-enabled=accepted"),EVAL_PUBTECH_0:()=>document.cookie.includes("euconsent-v2")&&(document.cookie.match(/.YAAAAAAAAAAA/)||document.cookie.match(/.aAAAAAAAAAAA/)||document.cookie.match(/.YAAACFgAAAAA/)),EVAL_REDDIT_0:()=>document.cookie.includes("eu_cookie={%22opted%22:true%2C%22nonessential%22:false}"),EVAL_SIBBO_0:()=>!!window.localStorage.getItem("euconsent-v2"),EVAL_SIRDATA_0:()=>document.cookie.includes("euconsent-v2"),EVAL_SNIGEL_0:()=>!!document.cookie.match("snconsent"),EVAL_STEAMPOWERED_0:()=>2===JSON.parse(decodeURIComponent(document.cookie.split(";").find((e=>e.trim().startsWith("cookieSettings"))).split("=")[1])).preference_state,EVAL_SVT_TEST:()=>document.cookie.includes('cookie-consent-1={"optedIn":true,"functionality":false,"statistics":false}'),EVAL_TAKEALOT_0:()=>document.body.classList.remove("freeze")||(document.body.style="")||!0,EVAL_TARTEAUCITRON_0:()=>tarteaucitron.userInterface.respondAll(!1)||!0,EVAL_TARTEAUCITRON_1:()=>tarteaucitron.userInterface.respondAll(!0)||!0,EVAL_TARTEAUCITRON_2:()=>document.cookie.match(/tarteaucitron=[^;]*/)[0].includes("false"),EVAL_TAUNTON_TEST:()=>document.cookie.includes("taunton_user_consent_submitted=true"),EVAL_TEALIUM_0:()=>void 0!==window.utag&&"object"==typeof utag.gdpr,EVAL_TEALIUM_1:()=>utag.gdpr.setConsentValue(!1)||!0,EVAL_TEALIUM_DONOTSELL:()=>utag.gdpr.dns?.setDnsState(!1)||!0,EVAL_TEALIUM_2:()=>utag.gdpr.setConsentValue(!0)||!0,EVAL_TEALIUM_3:()=>1!==utag.gdpr.getConsentState(),EVAL_TEALIUM_DONOTSELL_CHECK:()=>1!==utag.gdpr.dns?.getDnsState(),EVAL_TESTCMP_0:()=>"button_clicked"===window.results.results[0],EVAL_TESTCMP_COSMETIC_0:()=>"banner_hidden"===window.results.results[0],EVAL_THEFREEDICTIONARY_0:()=>cmpUi.showPurposes()||cmpUi.rejectAll()||!0,EVAL_THEFREEDICTIONARY_1:()=>cmpUi.allowAll()||!0,EVAL_THEVERGE_0:()=>document.cookie.includes("_duet_gdpr_acknowledged=1"),EVAL_UBUNTU_COM_0:()=>document.cookie.includes("_cookies_accepted=essential"),EVAL_UK_COOKIE_CONSENT_0:()=>!document.cookie.includes("catAccCookies"),EVAL_USERCENTRICS_API_0:()=>"object"==typeof UC_UI,EVAL_USERCENTRICS_API_1:()=>!!UC_UI.closeCMP(),EVAL_USERCENTRICS_API_2:()=>!!UC_UI.denyAllConsents(),EVAL_USERCENTRICS_API_3:()=>!!UC_UI.acceptAllConsents(),EVAL_USERCENTRICS_API_4:()=>!!UC_UI.closeCMP(),EVAL_USERCENTRICS_API_5:()=>!0===UC_UI.areAllConsentsAccepted(),EVAL_USERCENTRICS_API_6:()=>!1===UC_UI.areAllConsentsAccepted(),EVAL_USERCENTRICS_BUTTON_0:()=>JSON.parse(localStorage.getItem("usercentrics")).consents.every((e=>e.isEssential||!e.consentStatus)),EVAL_WAITROSE_0:()=>Array.from(document.querySelectorAll("label[id$=cookies-deny-label]")).forEach((e=>e.click()))||!0,EVAL_WAITROSE_1:()=>document.cookie.includes("wtr_cookies_advertising=0")&&document.cookie.includes("wtr_cookies_analytics=0"),EVAL_WP_COOKIE_NOTICE_0:()=>document.cookie.includes("wpl_viewed_cookie=no"),EVAL_XE_TEST:()=>document.cookie.includes("xeConsentState={%22performance%22:false%2C%22marketing%22:false%2C%22compliance%22:false}"),EVAL_XING_0:()=>document.cookie.includes("userConsent=%7B%22marketing%22%3Afalse"),EVAL_YOUTUBE_DESKTOP_0:()=>!!document.cookie.match(/SOCS=CAE/),EVAL_YOUTUBE_MOBILE_0:()=>!!document.cookie.match(/SOCS=CAE/)};var p={main:!0,frame:!1,urlPattern:""},d=class{constructor(e){this.runContext=p,this.autoconsent=e}get hasSelfTest(){throw new Error("Not Implemented")}get isIntermediate(){throw new Error("Not Implemented")}get isCosmetic(){throw new Error("Not Implemented")}mainWorldEval(e){const t=l[e];if(!t)return console.warn("Snippet not found",e),Promise.resolve(!1);const o=this.autoconsent.config.logs;if(this.autoconsent.config.isMainWorld){o.evals&&console.log("inline eval:",e,t);let c=!1;try{c=!!t.call(globalThis)}catch(t){o.evals&&console.error("error evaluating rule",e,t)}return Promise.resolve(c)}const c=`(${t.toString()})()`;return o.evals&&console.log("async eval:",e,c),function(e,t){const o=a();r.sendContentMessage({type:"eval",id:o,code:e,snippetId:t});const c=new s(o);return r.pending.set(c.id,c),c.promise}(c,e).catch((t=>(o.evals&&console.error("error evaluating rule",e,t),!1)))}checkRunContext(){const e={...p,...this.runContext},t=window.top===window;return!(t&&!e.main)&&(!(!t&&!e.frame)&&!(e.urlPattern&&!window.location.href.match(e.urlPattern)))}detectCmp(){throw new Error("Not Implemented")}async detectPopup(){return!1}optOut(){throw new Error("Not Implemented")}optIn(){throw new Error("Not Implemented")}openCmp(){throw new Error("Not Implemented")}async test(){return Promise.resolve(!0)}click(e,t=!1){return this.autoconsent.domActions.click(e,t)}elementExists(e){return this.autoconsent.domActions.elementExists(e)}elementVisible(e,t){return this.autoconsent.domActions.elementVisible(e,t)}waitForElement(e,t){return this.autoconsent.domActions.waitForElement(e,t)}waitForVisible(e,t,o){return this.autoconsent.domActions.waitForVisible(e,t,o)}waitForThenClick(e,t,o){return this.autoconsent.domActions.waitForThenClick(e,t,o)}wait(e){return this.autoconsent.domActions.wait(e)}hide(e,t){return this.autoconsent.domActions.hide(e,t)}prehide(e){return this.autoconsent.domActions.prehide(e)}undoPrehide(){return this.autoconsent.domActions.undoPrehide()}querySingleReplySelector(e,t){return this.autoconsent.domActions.querySingleReplySelector(e,t)}querySelectorChain(e){return this.autoconsent.domActions.querySelectorChain(e)}elementSelector(e){return this.autoconsent.domActions.elementSelector(e)}},u=class extends d{constructor(e,t){super(t),this.rule=e,this.name=e.name,this.runContext=e.runContext||p}get hasSelfTest(){return!!this.rule.test}get isIntermediate(){return!!this.rule.intermediate}get isCosmetic(){return!!this.rule.cosmetic}get prehideSelectors(){return this.rule.prehideSelectors}async detectCmp(){return!!this.rule.detectCmp&&this._runRulesParallel(this.rule.detectCmp)}async detectPopup(){return!!this.rule.detectPopup&&this._runRulesSequentially(this.rule.detectPopup)}async optOut(){const e=this.autoconsent.config.logs;return!!this.rule.optOut&&(e.lifecycle&&console.log("Initiated optOut()",this.rule.optOut),this._runRulesSequentially(this.rule.optOut))}async optIn(){const e=this.autoconsent.config.logs;return!!this.rule.optIn&&(e.lifecycle&&console.log("Initiated optIn()",this.rule.optIn),this._runRulesSequentially(this.rule.optIn))}async openCmp(){return!!this.rule.openCmp&&this._runRulesSequentially(this.rule.openCmp)}async test(){return this.hasSelfTest?this._runRulesSequentially(this.rule.test):super.test()}async evaluateRuleStep(e){const t=[],o=this.autoconsent.config.logs;if(e.exists&&t.push(this.elementExists(e.exists)),e.visible&&t.push(this.elementVisible(e.visible,e.check)),e.eval){const o=this.mainWorldEval(e.eval);t.push(o)}if(e.waitFor&&t.push(this.waitForElement(e.waitFor,e.timeout)),e.waitForVisible&&t.push(this.waitForVisible(e.waitForVisible,e.timeout,e.check)),e.click&&t.push(this.click(e.click,e.all)),e.waitForThenClick&&t.push(this.waitForThenClick(e.waitForThenClick,e.timeout,e.all)),e.wait&&t.push(this.wait(e.wait)),e.hide&&t.push(this.hide(e.hide,e.method)),e.if){if(!e.if.exists&&!e.if.visible)return console.error("invalid conditional rule",e.if),!1;const c=await this.evaluateRuleStep(e.if);o.rulesteps&&console.log("Condition is",c),c?t.push(this._runRulesSequentially(e.then)):e.else?t.push(this._runRulesSequentially(e.else)):t.push(!0)}if(e.any){for(const t of e.any)if(await this.evaluateRuleStep(t))return!0;return!1}if(0===t.length)return o.errors&&console.warn("Unrecognized rule",e),!1;return(await Promise.all(t)).reduce(((e,t)=>e&&t),!0)}async _runRulesParallel(e){const t=e.map((e=>this.evaluateRuleStep(e)));return(await Promise.all(t)).every((e=>!!e))}async _runRulesSequentially(e){const t=this.autoconsent.config.logs;for(const o of e){t.rulesteps&&console.log("Running rule...",o);const e=await this.evaluateRuleStep(o);if(t.rulesteps&&console.log("...rule result",e),!e&&!o.optional)return!1}return!0}},m=class{constructor(e,t){this.name=e,this.config=t,this.methods=new Map,this.runContext=p,this.isCosmetic=!1,t.methods.forEach((e=>{e.action&&this.methods.set(e.name,e.action)})),this.hasSelfTest=!1}get isIntermediate(){return!1}checkRunContext(){return!0}async detectCmp(){return this.config.detectors.map((e=>o(e.presentMatcher))).some((e=>!!e))}async detectPopup(){return this.config.detectors.map((e=>o(e.showingMatcher))).some((e=>!!e))}async executeAction(e,t){return!this.methods.has(e)||c(this.methods.get(e),t)}async optOut(){return await this.executeAction("HIDE_CMP"),await this.executeAction("OPEN_OPTIONS"),await this.executeAction("HIDE_CMP"),await this.executeAction("DO_CONSENT",[]),await this.executeAction("SAVE_CONSENT"),!0}async optIn(){return await this.executeAction("HIDE_CMP"),await this.executeAction("OPEN_OPTIONS"),await this.executeAction("HIDE_CMP"),await this.executeAction("DO_CONSENT",["D","A","B","E","F","X"]),await this.executeAction("SAVE_CONSENT"),!0}async openCmp(){return await this.executeAction("HIDE_CMP"),await this.executeAction("OPEN_OPTIONS"),!0}async test(){return!0}};function h(e="autoconsent-css-rules"){const t=`style#${e}`,o=document.querySelector(t);if(o&&o instanceof HTMLStyleElement)return o;{const t=document.head||document.getElementsByTagName("head")[0]||document.documentElement,o=document.createElement("style");return o.id=e,t.appendChild(o),o}}function k(e,t,o="display"){const c=`${t} { ${"opacity"===o?"opacity: 0":"display: none"} !important; z-index: -1 !important; pointer-events: none !important; } `;return e instanceof HTMLStyleElement&&(e.innerText+=c,t.length>0)}async function b(e,t,o){const c=await e();return!c&&t>0?new Promise((c=>{setTimeout((async()=>{c(b(e,t-1,o))}),o)})):Promise.resolve(c)}function _(e){if(!e)return!1;if(null!==e.offsetParent)return!0;{const t=window.getComputedStyle(e);if("fixed"===t.position&&"none"!==t.display)return!0}return!1}function g(e){const t={enabled:!0,autoAction:"optOut",disabledCmps:[],enablePrehide:!0,enableCosmeticRules:!0,detectRetries:20,isMainWorld:!1,prehideTimeout:2e3,logs:{lifecycle:!1,rulesteps:!1,evals:!1,errors:!0,messages:!1}},o=(c=t,globalThis.structuredClone?structuredClone(c):JSON.parse(JSON.stringify(c)));var c;for(const c of Object.keys(t))void 0!==e[c]&&(o[c]=e[c]);return o}var y="#truste-show-consent",w="#truste-consent-track",C=[class extends d{constructor(e){super(e),this.name="TrustArc-top",this.prehideSelectors=[".trustarc-banner-container",`.truste_popframe,.truste_overlay,.truste_box_overlay,${w}`],this.runContext={main:!0,frame:!1},this._shortcutButton=null,this._optInDone=!1}get hasSelfTest(){return!1}get isIntermediate(){return!this._optInDone&&!this._shortcutButton}get isCosmetic(){return!1}async detectCmp(){const e=this.elementExists(`${y},${w}`);return e&&(this._shortcutButton=document.querySelector("#truste-consent-required")),e}async detectPopup(){return this.elementVisible(`#truste-consent-content,#trustarc-banner-overlay,${w}`,"all")}openFrame(){this.click(y)}async optOut(){return this._shortcutButton?(this._shortcutButton.click(),!0):(k(h(),`.truste_popframe, .truste_overlay, .truste_box_overlay, ${w}`),this.click(y),setTimeout((()=>{h().remove()}),1e4),!0)}async optIn(){return this._optInDone=!0,this.click("#truste-consent-button")}async openCmp(){return!0}async test(){return await this.mainWorldEval("EVAL_TRUSTARC_TOP")}},class extends d{constructor(){super(...arguments),this.name="TrustArc-frame",this.runContext={main:!1,frame:!0,urlPattern:"^https://consent-pref\\.trustarc\\.com/\\?"}}get hasSelfTest(){return!1}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return!0}async detectPopup(){return this.elementVisible("#defaultpreferencemanager","any")&&this.elementVisible(".mainContent","any")}async navigateToSettings(){return await b((async()=>this.elementExists(".shp")||this.elementVisible(".advance","any")||this.elementExists(".switch span:first-child")),10,500),this.elementExists(".shp")&&this.click(".shp"),await this.waitForElement(".prefPanel",5e3),this.elementVisible(".advance","any")&&this.click(".advance"),await b((()=>this.elementVisible(".switch span:first-child","any")),5,1e3)}async optOut(){return await b((()=>"complete"===document.readyState),20,100),await this.waitForElement(".mainContent[aria-hidden=false]",5e3),!!this.click(".rejectAll")||(this.elementExists(".prefPanel")&&await this.waitForElement('.prefPanel[style="visibility: visible;"]',3e3),this.click("#catDetails0")?(this.click(".submit"),this.waitForThenClick("#gwt-debug-close_id",5e3),!0):this.click(".required")?(this.waitForThenClick("#gwt-debug-close_id",5e3),!0):(await this.navigateToSettings(),this.click(".switch span:nth-child(1):not(.active)",!0),this.click(".submit"),this.waitForThenClick("#gwt-debug-close_id",3e5),!0))}async optIn(){return this.click(".call")||(await this.navigateToSettings(),this.click(".switch span:nth-child(2)",!0),this.click(".submit"),this.waitForElement("#gwt-debug-close_id",3e5).then((()=>{this.click("#gwt-debug-close_id")}))),!0}},class extends d{constructor(){super(...arguments),this.name="Cybotcookiebot",this.prehideSelectors=["#CybotCookiebotDialog,#CybotCookiebotDialogBodyUnderlay,#dtcookie-container,#cookiebanner,#cb-cookieoverlay,.modal--cookie-banner,#cookiebanner_outer,#CookieBanner"]}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return await this.mainWorldEval("EVAL_COOKIEBOT_1")}async detectPopup(){return this.mainWorldEval("EVAL_COOKIEBOT_2")}async optOut(){await this.wait(500);let e=await this.mainWorldEval("EVAL_COOKIEBOT_3");return await this.wait(500),e=e&&await this.mainWorldEval("EVAL_COOKIEBOT_4"),e}async optIn(){return this.elementExists("#dtcookie-container")?this.click(".h-dtcookie-accept"):(this.click(".CybotCookiebotDialogBodyLevelButton:not(:checked):enabled",!0),this.click("#CybotCookiebotDialogBodyLevelButtonAccept"),this.click("#CybotCookiebotDialogBodyButtonAccept"),!0)}async test(){return await this.wait(500),await this.mainWorldEval("EVAL_COOKIEBOT_5")}},class extends d{constructor(){super(...arguments),this.name="Sourcepoint-frame",this.prehideSelectors=["div[id^='sp_message_container_'],.message-overlay","#sp_privacy_manager_container"],this.ccpaNotice=!1,this.ccpaPopup=!1,this.runContext={main:!1,frame:!0}}get hasSelfTest(){return!1}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){const e=new URL(location.href);return e.searchParams.has("message_id")&&"ccpa-notice.sp-prod.net"===e.hostname?(this.ccpaNotice=!0,!0):"ccpa-pm.sp-prod.net"===e.hostname?(this.ccpaPopup=!0,!0):("/index.html"===e.pathname||"/privacy-manager/index.html"===e.pathname||"/ccpa_pm/index.html"===e.pathname)&&(e.searchParams.has("message_id")||e.searchParams.has("requestUUID")||e.searchParams.has("consentUUID"))}async detectPopup(){return!!this.ccpaNotice||(this.ccpaPopup?await this.waitForElement(".priv-save-btn",2e3):(await this.waitForElement(".sp_choice_type_11,.sp_choice_type_12,.sp_choice_type_13,.sp_choice_type_ACCEPT_ALL,.sp_choice_type_SAVE_AND_EXIT",2e3),!this.elementExists(".sp_choice_type_9")))}async optIn(){return await this.waitForElement(".sp_choice_type_11,.sp_choice_type_ACCEPT_ALL",2e3),!!this.click(".sp_choice_type_11")||!!this.click(".sp_choice_type_ACCEPT_ALL")}isManagerOpen(){return"/privacy-manager/index.html"===location.pathname||"/ccpa_pm/index.html"===location.pathname}async optOut(){const e=this.autoconsent.config.logs;if(this.ccpaPopup){const e=document.querySelectorAll(".priv-purpose-container .sp-switch-arrow-block a.neutral.on .right");for(const t of e)t.click();const t=document.querySelectorAll(".priv-purpose-container .sp-switch-arrow-block a.switch-bg.on");for(const e of t)e.click();return this.click(".priv-save-btn")}if(!this.isManagerOpen()){if(!await this.waitForElement(".sp_choice_type_12,.sp_choice_type_13"))return!1;if(!this.elementExists(".sp_choice_type_12"))return this.click(".sp_choice_type_13");this.click(".sp_choice_type_12"),await b((()=>this.isManagerOpen()),200,100)}await this.waitForElement(".type-modal",2e4),this.waitForThenClick(".ccpa-stack .pm-switch[aria-checked=true] .slider",500,!0);try{const e=".sp_choice_type_REJECT_ALL",t=".reject-toggle",o=await Promise.race([this.waitForElement(e,2e3).then((e=>e?0:-1)),this.waitForElement(t,2e3).then((e=>e?1:-1)),this.waitForElement(".pm-features",2e3).then((e=>e?2:-1))]);if(0===o)return await this.wait(1500),this.click(e);1===o?this.click(t):2===o&&(await this.waitForElement(".pm-features",1e4),this.click(".checked > span",!0),this.click(".chevron"))}catch(t){e.errors&&console.warn(t)}return this.click(".sp_choice_type_SAVE_AND_EXIT")}},class extends d{constructor(){super(...arguments),this.name="consentmanager.net",this.prehideSelectors=["#cmpbox,#cmpbox2"],this.apiAvailable=!1}get hasSelfTest(){return this.apiAvailable}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return this.apiAvailable=await this.mainWorldEval("EVAL_CONSENTMANAGER_1"),!!this.apiAvailable||this.elementExists("#cmpbox")}async detectPopup(){return this.apiAvailable?(await this.wait(500),await this.mainWorldEval("EVAL_CONSENTMANAGER_2")):this.elementVisible("#cmpbox .cmpmore","any")}async optOut(){return await this.wait(500),this.apiAvailable?await this.mainWorldEval("EVAL_CONSENTMANAGER_3"):!!this.click(".cmpboxbtnno")||(this.elementExists(".cmpwelcomeprpsbtn")?(this.click(".cmpwelcomeprpsbtn > a[aria-checked=true]",!0),this.click(".cmpboxbtnsave"),!0):(this.click(".cmpboxbtncustom"),await this.waitForElement(".cmptblbox",2e3),this.click(".cmptdchoice > a[aria-checked=true]",!0),this.click(".cmpboxbtnyescustomchoices"),this.hide("#cmpwrapper,#cmpbox","display"),!0))}async optIn(){return this.apiAvailable?await this.mainWorldEval("EVAL_CONSENTMANAGER_4"):this.click(".cmpboxbtnyes")}async test(){if(this.apiAvailable)return await this.mainWorldEval("EVAL_CONSENTMANAGER_5")}},class extends d{constructor(){super(...arguments),this.name="Evidon"}get hasSelfTest(){return!1}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return this.elementExists("#_evidon_banner")}async detectPopup(){return this.elementVisible("#_evidon_banner","any")}async optOut(){return this.click("#_evidon-decline-button")||(k(h(),"#evidon-prefdiag-overlay,#evidon-prefdiag-background"),this.click("#_evidon-option-button"),await this.waitForElement("#evidon-prefdiag-overlay",5e3),this.click("#evidon-prefdiag-decline")),!0}async optIn(){return this.click("#_evidon-accept-button")}},class extends d{constructor(){super(...arguments),this.name="Onetrust",this.prehideSelectors=["#onetrust-banner-sdk,#onetrust-consent-sdk,.onetrust-pc-dark-filter,.js-consent-banner"],this.runContext={urlPattern:"^(?!.*https://www\\.nba\\.com/)"}}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return this.elementExists("#onetrust-banner-sdk")}async detectPopup(){return this.elementVisible("#onetrust-banner-sdk","all")}async optOut(){return this.elementVisible("#onetrust-reject-all-handler,.js-reject-cookies","any")?this.click("#onetrust-reject-all-handler,.js-reject-cookies"):(this.elementExists("#onetrust-pc-btn-handler")?this.click("#onetrust-pc-btn-handler"):this.click(".ot-sdk-show-settings,button.js-cookie-settings"),await this.waitForElement("#onetrust-consent-sdk",2e3),await this.wait(1e3),this.click("#onetrust-consent-sdk input.category-switch-handler:checked,.js-editor-toggle-state:checked",!0),await this.wait(1e3),await this.waitForElement(".save-preference-btn-handler,.js-consent-save",2e3),this.click(".save-preference-btn-handler,.js-consent-save"),await this.waitForVisible("#onetrust-banner-sdk",5e3,"none"),!0)}async optIn(){return this.click("#onetrust-accept-btn-handler,.js-accept-cookies")}async test(){return await b((()=>this.mainWorldEval("EVAL_ONETRUST_1")),10,500)}},class extends d{constructor(){super(...arguments),this.name="Klaro",this.prehideSelectors=[".klaro"],this.settingsOpen=!1}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return this.elementExists(".klaro > .cookie-modal")?(this.settingsOpen=!0,!0):this.elementExists(".klaro > .cookie-notice")}async detectPopup(){return this.elementVisible(".klaro > .cookie-notice,.klaro > .cookie-modal","any")}async optOut(){return!!this.click(".klaro .cn-decline")||(this.settingsOpen||(this.click(".klaro .cn-learn-more,.klaro .cm-button-manage"),await this.waitForElement(".klaro > .cookie-modal",2e3),this.settingsOpen=!0),!!this.click(".klaro .cn-decline")||(this.click(".cm-purpose:not(.cm-toggle-all) > input:not(.half-checked,.required,.only-required),.cm-purpose:not(.cm-toggle-all) > div > input:not(.half-checked,.required,.only-required)",!0),this.click(".cm-btn-accept,.cm-button")))}async optIn(){return!!this.click(".klaro .cm-btn-accept-all")||(this.settingsOpen?(this.click(".cm-purpose:not(.cm-toggle-all) > input.half-checked",!0),this.click(".cm-btn-accept")):this.click(".klaro .cookie-notice .cm-btn-success"))}async test(){return await this.mainWorldEval("EVAL_KLARO_1")}},class extends d{constructor(){super(...arguments),this.name="Uniconsent"}get prehideSelectors(){return[".unic",".modal:has(.unic)"]}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return this.elementExists(".unic .unic-box,.unic .unic-bar,.unic .unic-modal")}async detectPopup(){return this.elementVisible(".unic .unic-box,.unic .unic-bar,.unic .unic-modal","any")}async optOut(){if(await this.waitForElement(".unic button",1e3),document.querySelectorAll(".unic button").forEach((e=>{const t=e.textContent;(t.includes("Manage Options")||t.includes("Optionen verwalten"))&&e.click()})),await this.waitForElement(".unic input[type=checkbox]",1e3)){await this.waitForElement(".unic button",1e3),document.querySelectorAll(".unic input[type=checkbox]").forEach((e=>{e.checked&&e.click()}));for(const e of document.querySelectorAll(".unic button")){const t=e.textContent;for(const o of["Confirm Choices","Save Choices","Auswahl speichern"])if(t.includes(o))return e.click(),await this.wait(500),!0}}return!1}async optIn(){return this.waitForThenClick(".unic #unic-agree")}async test(){await this.wait(1e3);return!this.elementExists(".unic .unic-box,.unic .unic-bar")}},class extends d{constructor(){super(...arguments),this.prehideSelectors=[".cmp-root"],this.name="Conversant"}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return this.elementExists(".cmp-root .cmp-receptacle")}async detectPopup(){return this.elementVisible(".cmp-root .cmp-receptacle","any")}async optOut(){if(!await this.waitForThenClick(".cmp-main-button:not(.cmp-main-button--primary)"))return!1;if(!await this.waitForElement(".cmp-view-tab-tabs"))return!1;await this.waitForThenClick(".cmp-view-tab-tabs > :first-child"),await this.waitForThenClick(".cmp-view-tab-tabs > .cmp-view-tab--active:first-child");for(const e of Array.from(document.querySelectorAll(".cmp-accordion-item"))){e.querySelector(".cmp-accordion-item-title").click(),await b((()=>!!e.querySelector(".cmp-accordion-item-content.cmp-active")),10,50);const t=e.querySelector(".cmp-accordion-item-content.cmp-active");t.querySelectorAll(".cmp-toggle-actions .cmp-toggle-deny:not(.cmp-toggle-deny--active)").forEach((e=>e.click())),t.querySelectorAll(".cmp-toggle-actions .cmp-toggle-checkbox:not(.cmp-toggle-checkbox--active)").forEach((e=>e.click()))}return await this.click(".cmp-main-button:not(.cmp-main-button--primary)"),!0}async optIn(){return this.waitForThenClick(".cmp-main-button.cmp-main-button--primary")}async test(){return document.cookie.includes("cmp-data=0")}},class extends d{constructor(){super(...arguments),this.name="tiktok.com",this.runContext={urlPattern:"tiktok"}}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}getShadowRoot(){const e=document.querySelector("tiktok-cookie-banner");return e?e.shadowRoot:null}async detectCmp(){return this.elementExists("tiktok-cookie-banner")}async detectPopup(){return _(this.getShadowRoot().querySelector(".tiktok-cookie-banner"))}async optOut(){const e=this.autoconsent.config.logs,t=this.getShadowRoot().querySelector(".button-wrapper button:first-child");return t?(e.rulesteps&&console.log("[clicking]",t),t.click(),!0):(e.errors&&console.log("no decline button found"),!1)}async optIn(){const e=this.autoconsent.config.logs,t=this.getShadowRoot().querySelector(".button-wrapper button:last-child");return t?(e.rulesteps&&console.log("[clicking]",t),t.click(),!0):(e.errors&&console.log("no accept button found"),!1)}async test(){const e=document.cookie.match(/cookie-consent=([^;]+)/);if(!e)return!1;const t=JSON.parse(decodeURIComponent(e[1]));return Object.values(t).every((e=>"boolean"!=typeof e||!1===e))}},class extends d{constructor(){super(...arguments),this.runContext={urlPattern:"^https://(www\\.)?airbnb\\.[^/]+/"},this.prehideSelectors=["div[data-testid=main-cookies-banner-container]",'div:has(> div:first-child):has(> div:last-child):has(> section [data-testid="strictly-necessary-cookies"])']}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return this.elementExists("div[data-testid=main-cookies-banner-container]")}async detectPopup(){return this.elementVisible("div[data-testid=main-cookies-banner-container","any")}async optOut(){let e;for(await this.waitForThenClick("div[data-testid=main-cookies-banner-container] button._snbhip0");e=document.querySelector("[data-testid=modal-container] button[aria-checked=true]:not([disabled])");)e.click();return this.waitForThenClick("button[data-testid=save-btn]")}async optIn(){return this.waitForThenClick("div[data-testid=main-cookies-banner-container] button._148dgdpk")}async test(){return await b((()=>!!document.cookie.match("OptanonAlertBoxClosed")),20,200)}}],v=class{constructor(e){this.autoconsentInstance=e}click(e,t=!1){const o=this.elementSelector(e);return this.autoconsentInstance.config.logs.rulesteps&&console.log("[click]",e,t,o),o.length>0&&(t?o.forEach((e=>e.click())):o[0].click()),o.length>0}elementExists(e){return this.elementSelector(e).length>0}elementVisible(e,t){const o=this.elementSelector(e),c=new Array(o.length);return o.forEach(((e,t)=>{c[t]=_(e)})),"none"===t?c.every((e=>!e)):0!==c.length&&("any"===t?c.some((e=>e)):c.every((e=>e)))}waitForElement(e,t=1e4){const o=Math.ceil(t/200);return this.autoconsentInstance.config.logs.rulesteps&&console.log("[waitForElement]",e),b((()=>this.elementSelector(e).length>0),o,200)}waitForVisible(e,t=1e4,o="any"){return b((()=>this.elementVisible(e,o)),Math.ceil(t/200),200)}async waitForThenClick(e,t=1e4,o=!1){return await this.waitForElement(e,t),this.click(e,o)}wait(e){return new Promise((t=>{setTimeout((()=>{t(!0)}),e)}))}hide(e,t){return k(h(),e,t)}prehide(e){const t=h("autoconsent-prehide");return this.autoconsentInstance.config.logs.lifecycle&&console.log("[prehide]",t,location.href),k(t,e,"opacity")}undoPrehide(){const e=h("autoconsent-prehide");return this.autoconsentInstance.config.logs.lifecycle&&console.log("[undoprehide]",e,location.href),e&&e.remove(),!!e}querySingleReplySelector(e,t=document){if(e.startsWith("aria/"))return[];if(e.startsWith("xpath/")){const o=e.slice(6),c=document.evaluate(o,t,null,XPathResult.ANY_TYPE,null);let i=null;const n=[];for(;i=c.iterateNext();)n.push(i);return n}return e.startsWith("text/")||e.startsWith("pierce/")?[]:t.shadowRoot?Array.from(t.shadowRoot.querySelectorAll(e)):Array.from(t.querySelectorAll(e))}querySelectorChain(e){let t,o=document;for(const c of e){if(t=this.querySingleReplySelector(c,o),0===t.length)return[];o=t[0]}return t}elementSelector(e){return"string"==typeof e?this.querySingleReplySelector(e):this.querySelectorChain(e)}};var f=[{name:"192.com",detectCmp:[{exists:".ont-cookies"}],detectPopup:[{visible:".ont-cookies"}],optIn:[{click:".ont-btn-main.ont-cookies-btn.js-ont-btn-ok2"}],optOut:[{click:".ont-cookes-btn-manage"},{click:".ont-btn-main.ont-cookies-btn.js-ont-btn-choose"}],test:[{eval:"EVAL_ONENINETWO_0"}]},{name:"1password-com",cosmetic:!0,prehideSelectors:['footer #footer-root [aria-label="Cookie Consent"]'],detectCmp:[{exists:'footer #footer-root [aria-label="Cookie Consent"]'}],detectPopup:[{visible:'footer #footer-root [aria-label="Cookie Consent"]'}],optIn:[{click:'footer #footer-root [aria-label="Cookie Consent"] button'}],optOut:[{hide:'footer #footer-root [aria-label="Cookie Consent"]'}]},{name:"abconcerts.be",vendorUrl:"https://unknown",intermediate:!1,prehideSelectors:["dialog.cookie-consent"],detectCmp:[{exists:"dialog.cookie-consent form.cookie-consent__form"}],detectPopup:[{visible:"dialog.cookie-consent form.cookie-consent__form"}],optIn:[{waitForThenClick:"dialog.cookie-consent form.cookie-consent__form button[value=yes]"}],optOut:[{if:{exists:"dialog.cookie-consent form.cookie-consent__form button[value=no]"},then:[{click:"dialog.cookie-consent form.cookie-consent__form button[value=no]"}],else:[{click:"dialog.cookie-consent form.cookie-consent__form button.cookie-consent__options-toggle"},{waitForThenClick:'dialog.cookie-consent form.cookie-consent__form button[value="save_options"]'}]}]},{name:"activobank.pt",runContext:{urlPattern:"^https://(www\\.)?activobank\\.pt"},prehideSelectors:["aside#cookies,.overlay-cookies"],detectCmp:[{exists:"#cookies .cookies-btn"}],detectPopup:[{visible:"#cookies #submitCookies"}],optIn:[{waitForThenClick:"#cookies #submitCookies"}],optOut:[{waitForThenClick:"#cookies #rejectCookies"}]},{name:"Adroll",prehideSelectors:["#adroll_consent_container"],detectCmp:[{exists:"#adroll_consent_container"}],detectPopup:[{visible:"#adroll_consent_container"}],optIn:[{waitForThenClick:"#adroll_consent_accept"}],optOut:[{waitForThenClick:"#adroll_consent_reject"}],test:[{eval:"EVAL_ADROLL_0"}]},{name:"affinity.serif.com",detectCmp:[{exists:".c-cookie-banner button[data-qa='allow-all-cookies']"}],detectPopup:[{visible:".c-cookie-banner"}],optIn:[{click:'button[data-qa="allow-all-cookies"]'}],optOut:[{click:'button[data-qa="manage-cookies"]'},{waitFor:'.c-cookie-banner ~ [role="dialog"]'},{waitForThenClick:'.c-cookie-banner ~ [role="dialog"] input[type="checkbox"][value="true"]',all:!0},{click:'.c-cookie-banner ~ [role="dialog"] .c-modal__action button'}],test:[{wait:500},{eval:"EVAL_AFFINITY_SERIF_COM_0"}]},{name:"agolde.com",cosmetic:!0,prehideSelectors:["#modal-1 div[data-micromodal-close]"],detectCmp:[{exists:"#modal-1 div[aria-labelledby=modal-1-title]"}],detectPopup:[{exists:"#modal-1 div[data-micromodal-close]"}],optIn:[{click:'button[aria-label="Close modal"]'}],optOut:[{hide:"#modal-1 div[data-micromodal-close]"}]},{name:"aliexpress",vendorUrl:"https://aliexpress.com/",runContext:{urlPattern:"^https://.*\\.aliexpress\\.com/"},prehideSelectors:["#gdpr-new-container"],detectCmp:[{exists:"#gdpr-new-container"}],detectPopup:[{visible:"#gdpr-new-container"}],optIn:[{waitForThenClick:"#gdpr-new-container .btn-accept"}],optOut:[{waitForThenClick:"#gdpr-new-container .btn-more"},{waitFor:"#gdpr-new-container .gdpr-dialog-switcher"},{click:"#gdpr-new-container .switcher-on",all:!0,optional:!0},{click:"#gdpr-new-container .btn-save"}]},{name:"almacmp",prehideSelectors:["#alma-cmpv2-container"],detectCmp:[{exists:"#alma-cmpv2-container"}],detectPopup:[{visible:"#alma-cmpv2-container #almacmp-modal-layer1"}],optIn:[{waitForThenClick:"#alma-cmpv2-container #almacmp-modal-layer1 #almacmp-modalConfirmBtn"}],optOut:[{waitForThenClick:"#alma-cmpv2-container #almacmp-modal-layer1 #almacmp-modalSettingBtn"},{waitFor:"#alma-cmpv2-container #almacmp-modal-layer2"},{waitForThenClick:"#alma-cmpv2-container #almacmp-modal-layer2 #almacmp-reject-all-layer2"}],test:[{eval:"EVAL_ALMACMP_0"}]},{name:"altium.com",cosmetic:!0,prehideSelectors:[".altium-privacy-bar"],detectCmp:[{exists:".altium-privacy-bar"}],detectPopup:[{exists:".altium-privacy-bar"}],optIn:[{click:"a.altium-privacy-bar__btn"}],optOut:[{hide:".altium-privacy-bar"}]},{name:"amazon.com",prehideSelectors:['span[data-action="sp-cc"][data-sp-cc*="rejectAllAction"]'],detectCmp:[{exists:'span[data-action="sp-cc"][data-sp-cc*="rejectAllAction"]'}],detectPopup:[{visible:'span[data-action="sp-cc"][data-sp-cc*="rejectAllAction"]'}],optIn:[{waitForVisible:"#sp-cc-accept"},{wait:500},{click:"#sp-cc-accept"}],optOut:[{waitForVisible:"#sp-cc-rejectall-link"},{wait:500},{click:"#sp-cc-rejectall-link"}]},{name:"aquasana.com",cosmetic:!0,prehideSelectors:["#consent-tracking"],detectCmp:[{exists:"#consent-tracking"}],detectPopup:[{exists:"#consent-tracking"}],optIn:[{click:"#accept_consent"}],optOut:[{hide:"#consent-tracking"}]},{name:"arbeitsagentur",vendorUrl:"https://www.arbeitsagentur.de/",prehideSelectors:[".modal-open bahf-cookie-disclaimer-dpl3"],detectCmp:[{exists:"bahf-cookie-disclaimer-dpl3"}],detectPopup:[{visible:"bahf-cookie-disclaimer-dpl3"}],optIn:[{waitForThenClick:["bahf-cookie-disclaimer-dpl3","bahf-cd-modal-dpl3 .ba-btn-primary"]}],optOut:[{waitForThenClick:["bahf-cookie-disclaimer-dpl3","bahf-cd-modal-dpl3 .ba-btn-contrast"]}],test:[{eval:"EVAL_ARBEITSAGENTUR_TEST"}]},{name:"asus",vendorUrl:"https://www.asus.com/",runContext:{urlPattern:"^https://www\\.asus\\.com/"},prehideSelectors:["#cookie-policy-info,#cookie-policy-info-bg"],detectCmp:[{exists:"#cookie-policy-info"}],detectPopup:[{visible:"#cookie-policy-info"}],optIn:[{waitForThenClick:'#cookie-policy-info [data-agree="Accept Cookies"]'}],optOut:[{if:{exists:"#cookie-policy-info .btn-reject"},then:[{waitForThenClick:"#cookie-policy-info .btn-reject"}],else:[{waitForThenClick:"#cookie-policy-info .btn-setting"},{waitForThenClick:'#cookie-policy-lightbox-wrapper [data-agree="Save Settings"]'}]}]},{name:"athlinks-com",runContext:{urlPattern:"^https://(www\\.)?athlinks\\.com/"},cosmetic:!0,prehideSelectors:["#footer-container ~ div"],detectCmp:[{exists:"#footer-container ~ div"}],detectPopup:[{visible:"#footer-container > div"}],optIn:[{click:"#footer-container ~ div button"}],optOut:[{hide:"#footer-container ~ div"}]},{name:"ausopen.com",cosmetic:!0,detectCmp:[{exists:".gdpr-popup__message"}],detectPopup:[{visible:".gdpr-popup__message"}],optOut:[{hide:".gdpr-popup__message"}],optIn:[{click:".gdpr-popup__message button"}]},{name:"automattic-cmp-optout",prehideSelectors:['form[class*="cookie-banner"][method="post"]'],detectCmp:[{exists:'form[class*="cookie-banner"][method="post"]'}],detectPopup:[{visible:'form[class*="cookie-banner"][method="post"]'}],optIn:[{click:'a[class*="accept-all-button"]'}],optOut:[{click:'form[class*="cookie-banner"] div[class*="simple-options"] a[class*="customize-button"]'},{waitForThenClick:"input[type=checkbox][checked]:not([disabled])",all:!0},{click:'a[class*="accept-selection-button"]'}]},{name:"aws.amazon.com",prehideSelectors:["#awsccc-cb-content","#awsccc-cs-container","#awsccc-cs-modalOverlay","#awsccc-cs-container-inner"],detectCmp:[{exists:"#awsccc-cb-content"}],detectPopup:[{visible:"#awsccc-cb-content"}],optIn:[{click:"button[data-id=awsccc-cb-btn-accept"}],optOut:[{click:"button[data-id=awsccc-cb-btn-customize]"},{waitFor:"input[aria-checked]"},{click:"input[aria-checked=true]",all:!0,optional:!0},{click:"button[data-id=awsccc-cs-btn-save]"}]},{name:"axeptio",prehideSelectors:[".axeptio_widget"],detectCmp:[{exists:".axeptio_widget"}],detectPopup:[{visible:".axeptio_widget"}],optIn:[{waitFor:".axeptio-widget--open"},{click:"button#axeptio_btn_acceptAll"}],optOut:[{waitFor:".axeptio-widget--open"},{click:"button#axeptio_btn_dismiss"}],test:[{eval:"EVAL_AXEPTIO_0"}]},{name:"baden-wuerttemberg.de",prehideSelectors:[".cookie-alert.t-dark"],cosmetic:!0,detectCmp:[{exists:".cookie-alert.t-dark"}],detectPopup:[{visible:".cookie-alert.t-dark"}],optIn:[{click:".cookie-alert__form input:not([disabled]):not([checked])"},{click:".cookie-alert__button button"}],optOut:[{hide:".cookie-alert.t-dark"}]},{name:"bahn-de",vendorUrl:"https://www.bahn.de/",cosmetic:!1,runContext:{main:!0,frame:!1,urlPattern:"^https://(www\\.)?bahn\\.de/"},intermediate:!1,prehideSelectors:[],detectCmp:[{exists:["body > div:first-child","#consent-layer"]}],detectPopup:[{visible:["body > div:first-child","#consent-layer"]}],optIn:[{waitForThenClick:["body > div:first-child","#consent-layer .js-accept-all-cookies"]}],optOut:[{waitForThenClick:["body > div:first-child","#consent-layer .js-accept-essential-cookies"]}],test:[{eval:"EVAL_BAHN_TEST"}]},{name:"bbb.org",runContext:{urlPattern:"^https://www\\.bbb\\.org/"},cosmetic:!0,prehideSelectors:['div[aria-label="use of cookies on bbb.org"]'],detectCmp:[{exists:'div[aria-label="use of cookies on bbb.org"]'}],detectPopup:[{visible:'div[aria-label="use of cookies on bbb.org"]'}],optIn:[{click:'div[aria-label="use of cookies on bbb.org"] button.bds-button-unstyled span.visually-hidden'}],optOut:[{hide:'div[aria-label="use of cookies on bbb.org"]'}]},{name:"bing.com",prehideSelectors:["#bnp_container"],detectCmp:[{exists:"#bnp_cookie_banner"}],detectPopup:[{visible:"#bnp_cookie_banner"}],optIn:[{click:"#bnp_btn_accept"}],optOut:[{click:"#bnp_btn_preference"},{click:"#mcp_savesettings"}],test:[{eval:"EVAL_BING_0"}]},{name:"blocksy",vendorUrl:"https://creativethemes.com/blocksy/docs/extensions/cookies-consent/",cosmetic:!1,runContext:{main:!0,frame:!1},intermediate:!1,prehideSelectors:[".cookie-notification"],detectCmp:[{exists:"#blocksy-ext-cookies-consent-styles-css"}],detectPopup:[{visible:".cookie-notification"}],optIn:[{click:".cookie-notification .ct-cookies-decline-button"}],optOut:[{waitForThenClick:".cookie-notification .ct-cookies-decline-button"}],test:[{eval:"EVAL_BLOCKSY_0"}]},{name:"borlabs",detectCmp:[{exists:"._brlbs-block-content"}],detectPopup:[{visible:"._brlbs-bar-wrap,._brlbs-box-wrap"}],optIn:[{click:"a[data-cookie-accept-all]"}],optOut:[{click:"a[data-cookie-individual]"},{waitForVisible:".cookie-preference"},{click:"input[data-borlabs-cookie-checkbox]:checked",all:!0,optional:!0},{click:"#CookiePrefSave"},{wait:500}],prehideSelectors:["#BorlabsCookieBox"],test:[{eval:"EVAL_BORLABS_0"}]},{name:"bundesregierung.de",prehideSelectors:[".bpa-cookie-banner"],detectCmp:[{exists:".bpa-cookie-banner"}],detectPopup:[{visible:".bpa-cookie-banner .bpa-module-full-hero"}],optIn:[{click:".bpa-accept-all-button"}],optOut:[{wait:500,comment:"click is not immediately recognized"},{waitForThenClick:".bpa-close-button"}],test:[{eval:"EVAL_BUNDESREGIERUNG_DE_0"}]},{name:"burpee.com",cosmetic:!0,prehideSelectors:["#notice-cookie-block"],detectCmp:[{exists:"#notice-cookie-block"}],detectPopup:[{exists:"#html-body #notice-cookie-block"}],optIn:[{click:"#btn-cookie-allow"}],optOut:[{hide:"#html-body #notice-cookie-block, #notice-cookie"}]},{name:"canva.com",prehideSelectors:['div[role="dialog"] a[data-anchor-id="cookie-policy"]'],detectCmp:[{exists:'div[role="dialog"] a[data-anchor-id="cookie-policy"]'}],detectPopup:[{exists:'div[role="dialog"] a[data-anchor-id="cookie-policy"]'}],optIn:[{click:'div[role="dialog"] button:nth-child(1)'}],optOut:[{if:{exists:'div[role="dialog"] button:nth-child(3)'},then:[{click:'div[role="dialog"] button:nth-child(2)'}],else:[{click:'div[role="dialog"] button:nth-child(2)'},{waitFor:'div[role="dialog"] a[data-anchor-id="privacy-policy"]'},{click:'div[role="dialog"] button:nth-child(2)'},{click:'div[role="dialog"] div:last-child button:only-child'}]}],test:[{eval:"EVAL_CANVA_0"}]},{name:"canyon.com",runContext:{urlPattern:"^https://www\\.canyon\\.com/"},prehideSelectors:["div.modal.cookiesModal.is-open"],detectCmp:[{exists:"div.modal.cookiesModal.is-open"}],detectPopup:[{visible:"div.modal.cookiesModal.is-open"}],optIn:[{click:'div.cookiesModal__buttonWrapper > button[data-closecause="close-by-submit"]'}],optOut:[{click:'div.cookiesModal__buttonWrapper > button[data-closecause="close-by-manage-cookies"]'},{waitForThenClick:"button#js-manage-data-privacy-save-button"}]},{name:"cc-banner-springer",prehideSelectors:[".cc-banner[data-cc-banner]"],detectCmp:[{exists:".cc-banner[data-cc-banner]"}],detectPopup:[{visible:".cc-banner[data-cc-banner]"}],optIn:[{waitForThenClick:".cc-banner[data-cc-banner] button[data-cc-action=accept]"}],optOut:[{if:{exists:".cc-banner[data-cc-banner] button[data-cc-action=reject]"},then:[{click:".cc-banner[data-cc-banner] button[data-cc-action=reject]"}],else:[{waitForThenClick:".cc-banner[data-cc-banner] button[data-cc-action=preferences]"},{waitFor:".cc-preferences[data-cc-preferences]"},{click:".cc-preferences[data-cc-preferences] input[type=radio][data-cc-action=toggle-category][value=off]",all:!0},{if:{exists:".cc-preferences[data-cc-preferences] button[data-cc-action=reject]"},then:[{click:".cc-preferences[data-cc-preferences] button[data-cc-action=reject]"}],else:[{click:".cc-preferences[data-cc-preferences] button[data-cc-action=save]"}]}]}],test:[{eval:"EVAL_CC_BANNER2_0"}]},{name:"cc_banner",cosmetic:!0,prehideSelectors:[".cc_banner-wrapper"],detectCmp:[{exists:".cc_banner-wrapper"}],detectPopup:[{visible:".cc_banner"}],optIn:[{click:".cc_btn_accept_all"}],optOut:[{hide:".cc_banner-wrapper"}]},{name:"ciaopeople.it",prehideSelectors:["#cp-gdpr-choices"],detectCmp:[{exists:"#cp-gdpr-choices"}],detectPopup:[{visible:"#cp-gdpr-choices"}],optIn:[{waitForThenClick:".gdpr-btm__right > button:nth-child(2)"}],optOut:[{waitForThenClick:".gdpr-top-content > button"},{waitFor:".gdpr-top-back"},{waitForThenClick:".gdpr-btm__right > button:nth-child(1)"}],test:[{visible:"#cp-gdpr-choices",check:"none"}]},{vendorUrl:"https://www.civicuk.com/cookie-control/",name:"civic-cookie-control",prehideSelectors:["#ccc-module,#ccc-overlay"],detectCmp:[{exists:"#ccc-module"}],detectPopup:[{visible:"#ccc"},{visible:"#ccc-module"}],optOut:[{click:"#ccc-reject-settings"}],optIn:[{click:"#ccc-recommended-settings"}]},{name:"click.io",prehideSelectors:["#cl-consent"],detectCmp:[{exists:"#cl-consent"}],detectPopup:[{visible:"#cl-consent"}],optIn:[{waitForThenClick:'#cl-consent [data-role="b_agree"]'}],optOut:[{waitFor:'#cl-consent [data-role="b_options"]'},{wait:500},{click:'#cl-consent [data-role="b_options"]'},{waitFor:'.cl-consent-popup.cl-consent-visible [data-role="alloff"]'},{click:'.cl-consent-popup.cl-consent-visible [data-role="alloff"]',all:!0},{click:'[data-role="b_save"]'}],test:[{eval:"EVAL_CLICKIO_0",comment:"TODO: this only checks if we interacted at all"}]},{name:"clinch",intermediate:!1,runContext:{frame:!1,main:!0},prehideSelectors:[".consent-modal[role=dialog]"],detectCmp:[{exists:".consent-modal[role=dialog]"}],detectPopup:[{visible:".consent-modal[role=dialog]"}],optIn:[{click:"#consent_agree"}],optOut:[{click:"#manage_cookie_preferences"},{click:"#cookie_consent_preferences input:checked",all:!0,optional:!0},{click:"#consent_save"}],test:[{eval:"EVAL_CLINCH_0"}]},{name:"clustrmaps.com",runContext:{urlPattern:"^https://(www\\.)?clustrmaps\\.com/"},cosmetic:!0,prehideSelectors:["#gdpr-cookie-message"],detectCmp:[{exists:"#gdpr-cookie-message"}],detectPopup:[{visible:"#gdpr-cookie-message"}],optIn:[{click:"button#gdpr-cookie-accept"}],optOut:[{hide:"#gdpr-cookie-message"}]},{name:"coinbase",intermediate:!1,runContext:{frame:!0,main:!0,urlPattern:"^https://(www|help)\\.coinbase\\.com"},prehideSelectors:[],detectCmp:[{exists:"div[class^=CookieBannerContent__Container]"}],detectPopup:[{visible:"div[class^=CookieBannerContent__Container]"}],optIn:[{click:"div[class^=CookieBannerContent__CTA] :nth-last-child(1)"}],optOut:[{click:"button[class^=CookieBannerContent__Settings]"},{click:"div[class^=CookiePreferencesModal__CategoryContainer] input:checked",all:!0,optional:!0},{click:"div[class^=CookiePreferencesModal__ButtonContainer] > button"}],test:[{eval:"EVAL_COINBASE_0"}]},{name:"Complianz banner",prehideSelectors:["#cmplz-cookiebanner-container"],detectCmp:[{exists:"#cmplz-cookiebanner-container .cmplz-cookiebanner"}],detectPopup:[{visible:"#cmplz-cookiebanner-container .cmplz-cookiebanner",check:"any"}],optIn:[{waitForThenClick:".cmplz-cookiebanner .cmplz-accept"}],optOut:[{waitForThenClick:".cmplz-cookiebanner .cmplz-deny"}],test:[{eval:"EVAL_COMPLIANZ_BANNER_0"}]},{name:"Complianz categories",prehideSelectors:['.cc-type-categories[aria-describedby="cookieconsent:desc"]'],detectCmp:[{exists:'.cc-type-categories[aria-describedby="cookieconsent:desc"]'}],detectPopup:[{visible:'.cc-type-categories[aria-describedby="cookieconsent:desc"]'}],optIn:[{any:[{click:".cc-accept-all"},{click:".cc-allow-all"},{click:".cc-allow"},{click:".cc-dismiss"}]}],optOut:[{if:{exists:'.cc-type-categories[aria-describedby="cookieconsent:desc"] .cc-dismiss'},then:[{click:".cc-dismiss"}],else:[{click:".cc-type-categories input[type=checkbox]:not([disabled]):checked",all:!0,optional:!0},{click:".cc-save"}]}]},{name:"Complianz notice",prehideSelectors:['.cc-type-info[aria-describedby="cookieconsent:desc"]'],cosmetic:!0,detectCmp:[{exists:'.cc-type-info[aria-describedby="cookieconsent:desc"] .cc-compliance .cc-btn'}],detectPopup:[{visible:'.cc-type-info[aria-describedby="cookieconsent:desc"] .cc-compliance .cc-btn'}],optIn:[{click:".cc-accept-all",optional:!0},{click:".cc-allow",optional:!0},{click:".cc-dismiss",optional:!0}],optOut:[{if:{exists:".cc-deny"},then:[{click:".cc-deny"}],else:[{hide:'[aria-describedby="cookieconsent:desc"]'}]}]},{name:"Complianz opt-both",prehideSelectors:['[aria-describedby="cookieconsent:desc"] .cc-type-opt-both'],detectCmp:[{exists:'[aria-describedby="cookieconsent:desc"] .cc-type-opt-both'}],detectPopup:[{visible:'[aria-describedby="cookieconsent:desc"] .cc-type-opt-both'}],optIn:[{click:".cc-accept-all",optional:!0},{click:".cc-allow",optional:!0},{click:".cc-dismiss",optional:!0}],optOut:[{waitForThenClick:".cc-deny"}]},{name:"Complianz optin",prehideSelectors:['.cc-type-opt-in[aria-describedby="cookieconsent:desc"]'],detectCmp:[{exists:'.cc-type-opt-in[aria-describedby="cookieconsent:desc"]'}],detectPopup:[{visible:'.cc-type-opt-in[aria-describedby="cookieconsent:desc"]'}],optIn:[{any:[{click:".cc-accept-all"},{click:".cc-allow"},{click:".cc-dismiss"}]}],optOut:[{if:{visible:".cc-deny"},then:[{click:".cc-deny"}],else:[{if:{visible:".cc-settings"},then:[{waitForThenClick:".cc-settings"},{waitForVisible:".cc-settings-view"},{click:".cc-settings-view input[type=checkbox]:not([disabled]):checked",all:!0,optional:!0},{click:".cc-settings-view .cc-btn-accept-selected"}],else:[{click:".cc-dismiss"}]}]}]},{name:"cookie-law-info",prehideSelectors:["#cookie-law-info-bar"],detectCmp:[{exists:"#cookie-law-info-bar"},{eval:"EVAL_COOKIE_LAW_INFO_DETECT"}],detectPopup:[{visible:"#cookie-law-info-bar"}],optIn:[{click:'[data-cli_action="accept_all"]'}],optOut:[{hide:"#cookie-law-info-bar"},{eval:"EVAL_COOKIE_LAW_INFO_0"}],test:[{eval:"EVAL_COOKIE_LAW_INFO_1"}]},{name:"cookie-manager-popup",cosmetic:!1,runContext:{main:!0,frame:!1},intermediate:!1,detectCmp:[{exists:"#notice-cookie-block #allow-functional-cookies, #notice-cookie-block #btn-cookie-settings"}],detectPopup:[{visible:"#notice-cookie-block"}],optIn:[{click:"#btn-cookie-allow"}],optOut:[{if:{exists:"#allow-functional-cookies"},then:[{click:"#allow-functional-cookies"}],else:[{waitForThenClick:"#btn-cookie-settings"},{waitForVisible:".modal-body"},{click:'.modal-body input:checked, .switch[data-switch="on"]',all:!0,optional:!0},{click:'[role="dialog"] .modal-footer button'}]}],prehideSelectors:["#btn-cookie-settings"],test:[{eval:"EVAL_COOKIE_MANAGER_POPUP_0"}]},{name:"cookie-notice",prehideSelectors:["#cookie-notice"],cosmetic:!0,detectCmp:[{visible:"#cookie-notice .cookie-notice-container"}],detectPopup:[{visible:"#cookie-notice"}],optIn:[{click:"#cn-accept-cookie"}],optOut:[{hide:"#cookie-notice"}]},{name:"cookie-script",vendorUrl:"https://cookie-script.com/",prehideSelectors:["#cookiescript_injected"],detectCmp:[{exists:"#cookiescript_injected"}],detectPopup:[{visible:"#cookiescript_injected"}],optOut:[{click:"#cookiescript_reject"}],optIn:[{click:"#cookiescript_accept"}]},{name:"cookieacceptbar",vendorUrl:"https://unknown",cosmetic:!0,prehideSelectors:["#cookieAcceptBar.cookieAcceptBar"],detectCmp:[{exists:"#cookieAcceptBar.cookieAcceptBar"}],detectPopup:[{visible:"#cookieAcceptBar.cookieAcceptBar"}],optIn:[{waitForThenClick:"#cookieAcceptBarConfirm"}],optOut:[{hide:"#cookieAcceptBar.cookieAcceptBar"}]},{name:"cookiealert",intermediate:!1,prehideSelectors:[],runContext:{frame:!0,main:!0},detectCmp:[{exists:".cookie-alert-extended"}],detectPopup:[{visible:".cookie-alert-extended-modal"}],optIn:[{click:"button[data-controller='cookie-alert/extended/button/accept']"},{eval:"EVAL_COOKIEALERT_0"}],optOut:[{click:"a[data-controller='cookie-alert/extended/detail-link']"},{click:".cookie-alert-configuration-input:checked",all:!0,optional:!0},{click:"button[data-controller='cookie-alert/extended/button/configuration']"},{eval:"EVAL_COOKIEALERT_0"}],test:[{eval:"EVAL_COOKIEALERT_2"}]},{name:"cookieconsent2",vendorUrl:"https://www.github.com/orestbida/cookieconsent",comment:"supports v2.x.x of the library",prehideSelectors:["#cc--main"],detectCmp:[{exists:"#cc--main"}],detectPopup:[{visible:"#cm"},{exists:"#s-all-bn"}],optIn:[{waitForThenClick:"#s-all-bn"}],optOut:[{waitForThenClick:"#s-rall-bn"}],test:[{eval:"EVAL_COOKIECONSENT2_TEST"}]},{name:"cookieconsent3",vendorUrl:"https://www.github.com/orestbida/cookieconsent",comment:"supports v3.x.x of the library",prehideSelectors:["#cc-main"],detectCmp:[{exists:"#cc-main"}],detectPopup:[{visible:"#cc-main .cm-wrapper"}],optIn:[{waitForThenClick:".cm__btn[data-role=all]"}],optOut:[{waitForThenClick:".cm__btn[data-role=necessary]"}],test:[{eval:"EVAL_COOKIECONSENT3_TEST"}]},{name:"cookiefirst.com",prehideSelectors:["#cookiefirst-root,.cookiefirst-root,[aria-labelledby=cookie-preference-panel-title]"],detectCmp:[{exists:"#cookiefirst-root,.cookiefirst-root"}],detectPopup:[{visible:"#cookiefirst-root,.cookiefirst-root"}],optIn:[{click:"button[data-cookiefirst-action=accept]"}],optOut:[{if:{exists:"button[data-cookiefirst-action=adjust]"},then:[{click:"button[data-cookiefirst-action=adjust]"},{waitForVisible:"[data-cookiefirst-widget=modal]",timeout:1e3},{eval:"EVAL_COOKIEFIRST_1"},{wait:1e3},{click:"button[data-cookiefirst-action=save]"}],else:[{click:"button[data-cookiefirst-action=reject]"}]}],test:[{eval:"EVAL_COOKIEFIRST_0"}]},{name:"Cookie Information Banner",prehideSelectors:["#cookie-information-template-wrapper"],detectCmp:[{exists:"#cookie-information-template-wrapper"}],detectPopup:[{visible:"#cookie-information-template-wrapper"}],optIn:[{eval:"EVAL_COOKIEINFORMATION_1"}],optOut:[{hide:"#cookie-information-template-wrapper",comment:"some templates don't hide the banner automatically"},{eval:"EVAL_COOKIEINFORMATION_0"}],test:[{eval:"EVAL_COOKIEINFORMATION_2"}]},{name:"cookieyes",prehideSelectors:[".cky-overlay,.cky-consent-container"],detectCmp:[{exists:".cky-consent-container"}],detectPopup:[{visible:".cky-consent-container"}],optIn:[{waitForThenClick:".cky-consent-container [data-cky-tag=accept-button]"}],optOut:[{if:{exists:".cky-consent-container [data-cky-tag=reject-button]"},then:[{waitForThenClick:".cky-consent-container [data-cky-tag=reject-button]"}],else:[{if:{exists:".cky-consent-container [data-cky-tag=settings-button]"},then:[{click:".cky-consent-container [data-cky-tag=settings-button]"},{waitFor:".cky-modal-open input[type=checkbox]"},{click:".cky-modal-open input[type=checkbox]:checked",all:!0,optional:!0},{waitForThenClick:".cky-modal [data-cky-tag=detail-save-button]"}],else:[{hide:".cky-consent-container,.cky-overlay"}]}]}],test:[{eval:"EVAL_COOKIEYES_0"}]},{name:"corona-in-zahlen.de",prehideSelectors:[".cookiealert"],detectCmp:[{exists:".cookiealert"}],detectPopup:[{visible:".cookiealert"}],optOut:[{click:".configurecookies"},{click:".confirmcookies"}],optIn:[{click:".acceptcookies"}]},{name:"crossfit-com",cosmetic:!0,prehideSelectors:['body #modal > div > div[class^="_wrapper_"]'],detectCmp:[{exists:'body #modal > div > div[class^="_wrapper_"]'}],detectPopup:[{visible:'body #modal > div > div[class^="_wrapper_"]'}],optIn:[{click:'button[aria-label="accept cookie policy"]'}],optOut:[{hide:'body #modal > div > div[class^="_wrapper_"]'}]},{name:"csu-landtag-de",runContext:{urlPattern:"^https://(www|)?\\.csu-landtag\\.de"},prehideSelectors:["#cookie-disclaimer"],detectCmp:[{exists:"#cookie-disclaimer"}],detectPopup:[{visible:"#cookie-disclaimer"}],optIn:[{click:"#cookieall"}],optOut:[{click:"#cookiesel"}]},{name:"dailymotion-us",cosmetic:!0,prehideSelectors:['div[class*="CookiePopup__desktopContainer"]:has(div[class*="CookiePopup"])'],detectCmp:[{exists:'div[class*="CookiePopup__desktopContainer"]'}],detectPopup:[{visible:'div[class*="CookiePopup__desktopContainer"]'}],optIn:[{click:'div[class*="CookiePopup__desktopContainer"] > button > span'}],optOut:[{hide:'div[class*="CookiePopup__desktopContainer"]'}]},{name:"dailymotion.com",runContext:{urlPattern:"^https://(www\\.)?dailymotion\\.com/"},prehideSelectors:['div[class*="Overlay__container"]:has(div[class*="TCF2Popup"])'],detectCmp:[{exists:'div[class*="TCF2Popup"]'}],detectPopup:[{visible:'[class*="TCF2Popup"] a[href^="https://www.dailymotion.com/legal/cookiemanagement"]'}],optIn:[{waitForThenClick:'button[class*="TCF2Popup__button"]:not([class*="TCF2Popup__personalize"])'}],optOut:[{waitForThenClick:'button[class*="TCF2ContinueWithoutAcceptingButton"]'}],test:[{eval:"EVAL_DAILYMOTION_0"}]},{name:"deepl.com",prehideSelectors:[".dl_cookieBanner_container"],detectCmp:[{exists:".dl_cookieBanner_container"}],detectPopup:[{visible:".dl_cookieBanner_container"}],optOut:[{click:".dl_cookieBanner--buttonSelected"}],optIn:[{click:".dl_cookieBanner--buttonAll"}]},{name:"delta.com",runContext:{urlPattern:"^https://www\\.delta\\.com/"},cosmetic:!0,prehideSelectors:["ngc-cookie-banner"],detectCmp:[{exists:"div.cookie-footer-container"}],detectPopup:[{visible:"div.cookie-footer-container"}],optIn:[{click:" button.cookie-close-icon"}],optOut:[{hide:"div.cookie-footer-container"}]},{name:"dmgmedia-us",prehideSelectors:["#mol-ads-cmp-iframe, div.mol-ads-cmp > form > div"],detectCmp:[{exists:"div.mol-ads-cmp > form > div"}],detectPopup:[{waitForVisible:"div.mol-ads-cmp > form > div"}],optIn:[{waitForThenClick:"button.mol-ads-cmp--btn-primary"}],optOut:[{waitForThenClick:"div.mol-ads-ccpa--message > u > a"},{waitForVisible:".mol-ads-cmp--modal-dialog"},{waitForThenClick:"a.mol-ads-cmp-footer-privacy"},{waitForThenClick:"button.mol-ads-cmp--btn-secondary"}]},{name:"dmgmedia",prehideSelectors:['[data-project="mol-fe-cmp"]'],detectCmp:[{exists:'[data-project="mol-fe-cmp"]'}],detectPopup:[{visible:'[data-project="mol-fe-cmp"]'}],optIn:[{waitForThenClick:'[data-project="mol-fe-cmp"] button[class*=primary]'}],optOut:[{waitForThenClick:'[data-project="mol-fe-cmp"] button[class*=basic]'},{waitForVisible:'[data-project="mol-fe-cmp"] div[class*="tabContent"]'},{waitForThenClick:'[data-project="mol-fe-cmp"] div[class*="toggle"][class*="enabled"]',all:!0},{waitForThenClick:'[data-project="mol-fe-cmp"] button[class*=white]'}]},{name:"dndbeyond",vendorUrl:"https://www.dndbeyond.com/",runContext:{urlPattern:"^https://(www\\.)?dndbeyond\\.com/"},prehideSelectors:["[id^=cookie-consent-banner]"],detectCmp:[{exists:"[id^=cookie-consent-banner]"}],detectPopup:[{visible:"[id^=cookie-consent-banner]"}],optIn:[{waitForThenClick:"#cookie-consent-granted"}],optOut:[{waitForThenClick:"#cookie-consent-denied"}],test:[{eval:"EVAL_DNDBEYOND_TEST"}]},{name:"Drupal",detectCmp:[{exists:"#drupalorg-crosssite-gdpr"}],detectPopup:[{visible:"#drupalorg-crosssite-gdpr"}],optOut:[{click:".no"}],optIn:[{click:".yes"}]},{name:"WP DSGVO Tools",link:"https://wordpress.org/plugins/shapepress-dsgvo/",prehideSelectors:[".sp-dsgvo"],cosmetic:!0,detectCmp:[{exists:".sp-dsgvo.sp-dsgvo-popup-overlay"}],detectPopup:[{visible:".sp-dsgvo.sp-dsgvo-popup-overlay",check:"any"}],optIn:[{click:".sp-dsgvo-privacy-btn-accept-all",all:!0}],optOut:[{hide:".sp-dsgvo.sp-dsgvo-popup-overlay"}],test:[{eval:"EVAL_DSGVO_0"}]},{name:"dunelm.com",prehideSelectors:["div[data-testid=cookie-consent-modal-backdrop]"],detectCmp:[{exists:"div[data-testid=cookie-consent-message-contents]"}],detectPopup:[{visible:"div[data-testid=cookie-consent-message-contents]"}],optIn:[{click:'[data-testid="cookie-consent-allow-all"]'}],optOut:[{click:"button[data-testid=cookie-consent-adjust-settings]"},{click:"button[data-testid=cookie-consent-preferences-save]"}],test:[{eval:"EVAL_DUNELM_0"}]},{name:"ecosia",vendorUrl:"https://www.ecosia.org/",runContext:{urlPattern:"^https://www\\.ecosia\\.org/"},prehideSelectors:[".cookie-wrapper"],detectCmp:[{exists:".cookie-wrapper > .cookie-notice"}],detectPopup:[{visible:".cookie-wrapper > .cookie-notice"}],optIn:[{waitForThenClick:"[data-test-id=cookie-notice-accept]"}],optOut:[{waitForThenClick:"[data-test-id=cookie-notice-reject]"}]},{name:"etsy",prehideSelectors:["#gdpr-single-choice-overlay","#gdpr-privacy-settings"],detectCmp:[{exists:"#gdpr-single-choice-overlay"}],detectPopup:[{visible:"#gdpr-single-choice-overlay"}],optOut:[{click:"button[data-gdpr-open-full-settings]"},{waitForVisible:".gdpr-overlay-body input",timeout:3e3},{wait:1e3},{eval:"EVAL_ETSY_0"},{eval:"EVAL_ETSY_1"}],optIn:[{click:"button[data-gdpr-single-choice-accept]"}]},{name:"eu-cookie-compliance-banner",detectCmp:[{exists:"body.eu-cookie-compliance-popup-open"}],detectPopup:[{exists:"body.eu-cookie-compliance-popup-open"}],optIn:[{click:".agree-button"}],optOut:[{if:{visible:".decline-button,.eu-cookie-compliance-save-preferences-button"},then:[{click:".decline-button,.eu-cookie-compliance-save-preferences-button"}]},{hide:".eu-cookie-compliance-banner-info, #sliding-popup"}],test:[{eval:"EVAL_EU_COOKIE_COMPLIANCE_0"}]},{name:"EU Cookie Law",prehideSelectors:[".pea_cook_wrapper,.pea_cook_more_info_popover"],cosmetic:!0,detectCmp:[{exists:".pea_cook_wrapper"}],detectPopup:[{wait:500},{visible:".pea_cook_wrapper"}],optIn:[{click:"#pea_cook_btn"}],optOut:[{hide:".pea_cook_wrapper"}],test:[{eval:"EVAL_EU_COOKIE_LAW_0"}]},{name:"europa-eu",vendorUrl:"https://ec.europa.eu/",runContext:{urlPattern:"^https://[^/]*europa\\.eu/"},prehideSelectors:["#cookie-consent-banner"],detectCmp:[{exists:".cck-container"}],detectPopup:[{visible:".cck-container"}],optIn:[{waitForThenClick:'.cck-actions-button[href="#accept"]'}],optOut:[{waitForThenClick:'.cck-actions-button[href="#refuse"]',hide:".cck-container"}]},{name:"EZoic",prehideSelectors:["#ez-cookie-dialog-wrapper"],detectCmp:[{exists:"#ez-cookie-dialog-wrapper"}],detectPopup:[{visible:"#ez-cookie-dialog-wrapper"}],optIn:[{click:"#ez-accept-all",optional:!0},{eval:"EVAL_EZOIC_0",optional:!0}],optOut:[{wait:500},{click:"#ez-manage-settings"},{waitFor:"#ez-cookie-dialog input[type=checkbox]"},{click:"#ez-cookie-dialog input[type=checkbox]:checked",all:!0},{click:"#ez-save-settings"}],test:[{eval:"EVAL_EZOIC_1"}]},{name:"facebook",runContext:{urlPattern:"^https://([a-z0-9-]+\\.)?facebook\\.com/"},prehideSelectors:['div[data-testid="cookie-policy-manage-dialog"]'],detectCmp:[{exists:'div[data-testid="cookie-policy-manage-dialog"]'}],detectPopup:[{visible:'div[data-testid="cookie-policy-manage-dialog"]'}],optIn:[{waitForThenClick:'button[data-cookiebanner="accept_button"]'},{waitForVisible:'div[data-testid="cookie-policy-manage-dialog"]',check:"none"}],optOut:[{waitForThenClick:'button[data-cookiebanner="accept_only_essential_button"]'},{waitForVisible:'div[data-testid="cookie-policy-manage-dialog"]',check:"none"}]},{name:"fides",vendorUrl:"https://github.com/ethyca/fides",prehideSelectors:["#fides-overlay"],detectCmp:[{exists:"#fides-overlay #fides-banner"}],detectPopup:[{visible:"#fides-overlay #fides-banner"}],optIn:[{waitForThenClick:'#fides-banner [data-testid="Accept all-btn"]'}],optOut:[{waitForThenClick:'#fides-banner [data-testid="Reject all-btn"]'}]},{name:"funding-choices",prehideSelectors:[".fc-consent-root,.fc-dialog-container,.fc-dialog-overlay,.fc-dialog-content"],detectCmp:[{exists:".fc-consent-root"}],detectPopup:[{exists:".fc-dialog-container"}],optOut:[{click:".fc-cta-do-not-consent,.fc-cta-manage-options"},{click:".fc-preference-consent:checked,.fc-preference-legitimate-interest:checked",all:!0,optional:!0},{click:".fc-confirm-choices",optional:!0}],optIn:[{click:".fc-cta-consent"}]},{name:"geeks-for-geeks",runContext:{urlPattern:"^https://www\\.geeksforgeeks\\.org/"},cosmetic:!0,prehideSelectors:[".cookie-consent"],detectCmp:[{exists:".cookie-consent"}],detectPopup:[{visible:".cookie-consent"}],optIn:[{click:".cookie-consent button.consent-btn"}],optOut:[{hide:".cookie-consent"}]},{name:"generic-cosmetic",cosmetic:!0,prehideSelectors:["#js-cookie-banner,.js-cookie-banner,.cookie-banner,#cookie-banner"],detectCmp:[{exists:"#js-cookie-banner,.js-cookie-banner,.cookie-banner,#cookie-banner"}],detectPopup:[{visible:"#js-cookie-banner,.js-cookie-banner,.cookie-banner,#cookie-banner"}],optIn:[],optOut:[{hide:"#js-cookie-banner,.js-cookie-banner,.cookie-banner,#cookie-banner"}]},{name:"google-consent-standalone",prehideSelectors:[],detectCmp:[{exists:'a[href^="https://policies.google.com/technologies/cookies"'},{exists:'form[action^="https://consent.google."][action$=".com/save"]'}],detectPopup:[{visible:'a[href^="https://policies.google.com/technologies/cookies"'}],optIn:[{waitForThenClick:'form[action^="https://consent.google."][action$=".com/save"]:has(input[name=set_eom][value=false]) button'}],optOut:[{waitForThenClick:'form[action^="https://consent.google."][action$=".com/save"]:has(input[name=set_eom][value=true]) button'}]},{name:"google.com",prehideSelectors:[".HTjtHe#xe7COe"],detectCmp:[{exists:".HTjtHe#xe7COe"},{exists:'.HTjtHe#xe7COe a[href^="https://policies.google.com/technologies/cookies"]'}],detectPopup:[{visible:".HTjtHe#xe7COe button#W0wltc"}],optIn:[{waitForThenClick:".HTjtHe#xe7COe button#L2AGLb"}],optOut:[{waitForThenClick:".HTjtHe#xe7COe button#W0wltc"}],test:[{eval:"EVAL_GOOGLE_0"}]},{name:"gov.uk",detectCmp:[{exists:"#global-cookie-message"}],detectPopup:[{exists:"#global-cookie-message"}],optIn:[{click:"button[data-accept-cookies=true]"}],optOut:[{click:"button[data-reject-cookies=true],#reject-cookies"},{click:"button[data-hide-cookie-banner=true],#hide-cookie-decision"}]},{name:"hashicorp",vendorUrl:"https://hashicorp.com/",runContext:{urlPattern:"^https://[^.]*\\.hashicorp\\.com/"},prehideSelectors:["[data-testid=consent-banner]"],detectCmp:[{exists:"[data-testid=consent-banner]"}],detectPopup:[{visible:"[data-testid=consent-banner]"}],optIn:[{waitForThenClick:"[data-testid=accept]"}],optOut:[{waitForThenClick:"[data-testid=manage-preferences]"},{waitForThenClick:"[data-testid=consent-mgr-dialog] [data-ga-button=save-preferences]"}]},{name:"healthline-media",prehideSelectors:["#modal-host > div.no-hash > div.window-wrapper"],detectCmp:[{exists:"#modal-host > div.no-hash > div.window-wrapper, div[data-testid=qualtrics-container]"}],detectPopup:[{exists:"#modal-host > div.no-hash > div.window-wrapper, div[data-testid=qualtrics-container]"}],optIn:[{click:"#modal-host > div.no-hash > div.window-wrapper > div:last-child button"}],optOut:[{if:{exists:'#modal-host > div.no-hash > div.window-wrapper > div:last-child a[href="/privacy-settings"]'},then:[{click:'#modal-host > div.no-hash > div.window-wrapper > div:last-child a[href="/privacy-settings"]'}],else:[{waitForVisible:"div#__next"},{click:"#__next div:nth-child(1) > button:first-child"}]}]},{name:"hema",prehideSelectors:[".cookie-modal"],detectCmp:[{visible:".cookie-modal .cookie-accept-btn"}],detectPopup:[{visible:".cookie-modal .cookie-accept-btn"}],optIn:[{waitForThenClick:".cookie-modal .cookie-accept-btn"}],optOut:[{waitForThenClick:".cookie-modal .js-cookie-reject-btn"}],test:[{eval:"EVAL_HEMA_TEST_0"}]},{name:"hetzner.com",runContext:{urlPattern:"^https://www\\.hetzner\\.com/"},prehideSelectors:["#CookieConsent"],detectCmp:[{exists:"#CookieConsent"}],detectPopup:[{visible:"#CookieConsent"}],optIn:[{click:"#CookieConsentGiven"}],optOut:[{click:"#CookieConsentDeclined"}]},{name:"hl.co.uk",prehideSelectors:[".cookieModalContent","#cookie-banner-overlay"],detectCmp:[{exists:"#cookie-banner-overlay"}],detectPopup:[{exists:"#cookie-banner-overlay"}],optIn:[{click:"#acceptCookieButton"}],optOut:[{click:"#manageCookie"},{hide:".cookieSettingsModal"},{waitFor:"#AOCookieToggle"},{click:"#AOCookieToggle[aria-pressed=true]",optional:!0},{waitFor:"#TPCookieToggle"},{click:"#TPCookieToggle[aria-pressed=true]",optional:!0},{click:"#updateCookieButton"}]},{name:"hu-manity",vendorUrl:"https://hu-manity.co/",prehideSelectors:["#hu.hu-wrapper"],detectCmp:[{exists:"#hu.hu-visible"}],detectPopup:[{visible:"#hu.hu-visible"}],optIn:[{waitForThenClick:"[data-hu-action=cookies-notice-consent-choices-3]"},{waitForThenClick:"#hu-cookies-save"}],optOut:[{waitForThenClick:"#hu-cookies-save"}]},{name:"hubspot",detectCmp:[{exists:"#hs-eu-cookie-confirmation"}],detectPopup:[{visible:"#hs-eu-cookie-confirmation"}],optIn:[{click:"#hs-eu-confirmation-button"}],optOut:[{click:"#hs-eu-decline-button"}]},{name:"indeed.com",cosmetic:!0,prehideSelectors:["#CookiePrivacyNotice"],detectCmp:[{exists:"#CookiePrivacyNotice"}],detectPopup:[{visible:"#CookiePrivacyNotice"}],optIn:[{click:"#CookiePrivacyNotice button[data-gnav-element-name=CookiePrivacyNoticeOk]"}],optOut:[{hide:"#CookiePrivacyNotice"}]},{name:"ing.de",runContext:{urlPattern:"^https://www\\.ing\\.de/"},cosmetic:!0,prehideSelectors:['div[slot="backdrop"]'],detectCmp:[{exists:'[data-tag-name="ing-cc-dialog-frame"]'}],detectPopup:[{visible:'[data-tag-name="ing-cc-dialog-frame"]'}],optIn:[{click:['[data-tag-name="ing-cc-dialog-level0"]','[data-tag-name="ing-cc-button"][class*="accept"]']}],optOut:[{click:['[data-tag-name="ing-cc-dialog-level0"]','[data-tag-name="ing-cc-button"][class*="more"]']}]},{name:"instagram",vendorUrl:"https://instagram.com",runContext:{urlPattern:"^https://www\\.instagram\\.com/"},prehideSelectors:[".x78zum5.xdt5ytf.xg6iff7.x1n2onr6"],detectCmp:[{exists:".x1qjc9v5.x9f619.x78zum5.xdt5ytf.x1iyjqo2.xl56j7k"}],detectPopup:[{visible:".x1qjc9v5.x9f619.x78zum5.xdt5ytf.x1iyjqo2.xl56j7k"}],optIn:[{waitForThenClick:"._a9--._a9_0"}],optOut:[{waitForThenClick:"._a9--._a9_1"},{wait:2e3}]},{name:"ionos.de",prehideSelectors:[".privacy-consent--backdrop",".privacy-consent--modal"],detectCmp:[{exists:".privacy-consent--modal"}],detectPopup:[{visible:".privacy-consent--modal"}],optIn:[{click:"#selectAll"}],optOut:[{click:".footer-config-link"},{click:"#confirmSelection"}]},{name:"itopvpn.com",cosmetic:!0,prehideSelectors:[".pop-cookie"],detectCmp:[{exists:".pop-cookie"}],detectPopup:[{exists:".pop-cookie"}],optIn:[{click:"#_pcookie"}],optOut:[{hide:".pop-cookie"}]},{name:"iubenda",prehideSelectors:["#iubenda-cs-banner"],detectCmp:[{exists:"#iubenda-cs-banner"}],detectPopup:[{visible:".iubenda-cs-accept-btn"}],optIn:[{click:".iubenda-cs-accept-btn"}],optOut:[{click:".iubenda-cs-customize-btn"},{eval:"EVAL_IUBENDA_0"},{click:"#iubFooterBtn"}],test:[{eval:"EVAL_IUBENDA_1"}]},{name:"iWink",prehideSelectors:["body.cookies-request #cookie-bar"],detectCmp:[{exists:"body.cookies-request #cookie-bar"}],detectPopup:[{visible:"body.cookies-request #cookie-bar"}],optIn:[{waitForThenClick:"body.cookies-request #cookie-bar .allow-cookies"}],optOut:[{waitForThenClick:"body.cookies-request #cookie-bar .disallow-cookies"}],test:[{eval:"EVAL_IWINK_TEST"}]},{name:"jdsports",vendorUrl:"https://www.jdsports.co.uk/",runContext:{urlPattern:"^https://(www|m)\\.jdsports\\."},prehideSelectors:[".miniConsent,#PrivacyPolicyBanner"],detectCmp:[{exists:".miniConsent,#PrivacyPolicyBanner"}],detectPopup:[{visible:".miniConsent,#PrivacyPolicyBanner"}],optIn:[{waitForThenClick:".miniConsent .accept-all-cookies"}],optOut:[{if:{exists:"#PrivacyPolicyBanner"},then:[{hide:"#PrivacyPolicyBanner"}],else:[{waitForThenClick:"#cookie-settings"},{waitForThenClick:"#reject-all-cookies"}]}]},{name:"johnlewis.com",prehideSelectors:["div[class^=pecr-cookie-banner-]"],detectCmp:[{exists:"div[class^=pecr-cookie-banner-]"}],detectPopup:[{exists:"div[class^=pecr-cookie-banner-]"}],optOut:[{click:"button[data-test^=manage-cookies]"},{wait:"500"},{click:"label[data-test^=toggle][class*=checked]:not([class*=disabled])",all:!0,optional:!0},{click:"button[data-test=save-preferences]"}],optIn:[{click:"button[data-test=allow-all]"}]},{name:"jquery.cookieBar",vendorUrl:"https://github.com/kovarp/jquery.cookieBar",prehideSelectors:[".cookie-bar"],cosmetic:!0,detectCmp:[{exists:".cookie-bar .cookie-bar__message,.cookie-bar .cookie-bar__buttons"}],detectPopup:[{visible:".cookie-bar .cookie-bar__message,.cookie-bar .cookie-bar__buttons",check:"any"}],optIn:[{click:".cookie-bar .cookie-bar__btn"}],optOut:[{hide:".cookie-bar"}],test:[{visible:".cookie-bar .cookie-bar__message,.cookie-bar .cookie-bar__buttons",check:"none"},{eval:"EVAL_JQUERY_COOKIEBAR_0"}]},{name:"justwatch.com",prehideSelectors:[".consent-banner"],detectCmp:[{exists:".consent-banner .consent-banner__actions"}],detectPopup:[{visible:".consent-banner .consent-banner__actions"}],optIn:[{click:".consent-banner__actions button.basic-button.primary"}],optOut:[{click:".consent-banner__actions button.basic-button.secondary"},{waitForThenClick:".consent-modal__footer button.basic-button.secondary"},{waitForThenClick:".consent-modal ion-content > div > a:nth-child(9)"},{click:"label.consent-switch input[type=checkbox]:checked",all:!0,optional:!0},{waitForVisible:".consent-modal__footer button.basic-button.primary"},{click:".consent-modal__footer button.basic-button.primary"}]},{name:"ketch",vendorUrl:"https://www.ketch.com",runContext:{frame:!1,main:!0},intermediate:!1,prehideSelectors:["#lanyard_root div[role='dialog']"],detectCmp:[{exists:"#lanyard_root div[role='dialog']"}],detectPopup:[{visible:"#lanyard_root div[role='dialog']"}],optIn:[{if:{exists:"#lanyard_root button[class='confirmButton']"},then:[{waitForThenClick:"#lanyard_root div[class*=buttons] > :nth-child(2)"},{click:"#lanyard_root button[class='confirmButton']"}],else:[{waitForThenClick:"#lanyard_root div[class*=buttons] > :nth-child(2)"}]}],optOut:[{if:{exists:"#lanyard_root [aria-describedby=banner-description]"},then:[{waitForThenClick:"#lanyard_root div[class*=buttons] > button[class*=secondaryButton]",comment:"can be either settings or reject button"}]},{waitFor:"#lanyard_root [aria-describedby=preference-description],#lanyard_root [aria-describedby=modal-description]",timeout:1e3,optional:!0},{if:{exists:"#lanyard_root [aria-describedby=preference-description],#lanyard_root [aria-describedby=modal-description]"},then:[{waitForThenClick:"#lanyard_root button[class*=rejectButton]"},{click:"#lanyard_root button[class*=confirmButton],#lanyard_root div[class*=actions_] > button:nth-child(1)"}]}]},{name:"kleinanzeigen-de",runContext:{urlPattern:"^https?://(www\\.)?kleinanzeigen\\.de"},prehideSelectors:["#gdpr-banner-container"],detectCmp:[{any:[{exists:"#gdpr-banner-container #gdpr-banner [data-testid=gdpr-banner-cmp-button]"},{exists:"#ConsentManagementPage"}]}],detectPopup:[{any:[{visible:"#gdpr-banner-container #gdpr-banner [data-testid=gdpr-banner-cmp-button]"},{visible:"#ConsentManagementPage"}]}],optIn:[{if:{exists:"#gdpr-banner-container #gdpr-banner"},then:[{click:"#gdpr-banner-container #gdpr-banner [data-testid=gdpr-banner-accept]"}],else:[{click:"#ConsentManagementPage .Button-primary"}]}],optOut:[{if:{exists:"#gdpr-banner-container #gdpr-banner"},then:[{click:"#gdpr-banner-container #gdpr-banner [data-testid=gdpr-banner-cmp-button]"}],else:[{click:"#ConsentManagementPage .Button-secondary"}]}]},{name:"lightbox",prehideSelectors:[".darken-layer.open,.lightbox.lightbox--cookie-consent"],detectCmp:[{exists:"body.cookie-consent-is-active div.lightbox--cookie-consent > div.lightbox__content > div.cookie-consent[data-jsb]"}],detectPopup:[{visible:"body.cookie-consent-is-active div.lightbox--cookie-consent > div.lightbox__content > div.cookie-consent[data-jsb]"}],optOut:[{click:".cookie-consent__footer > button[type='submit']:not([data-button='selectAll'])"}],optIn:[{click:".cookie-consent__footer > button[type='submit'][data-button='selectAll']"}]},{name:"lineagrafica",vendorUrl:"https://addons.prestashop.com/en/legal/8734-eu-cookie-law-gdpr-banner-blocker.html",cosmetic:!0,prehideSelectors:["#lgcookieslaw_banner,#lgcookieslaw_modal,.lgcookieslaw-overlay"],detectCmp:[{exists:"#lgcookieslaw_banner,#lgcookieslaw_modal,.lgcookieslaw-overlay"}],detectPopup:[{exists:"#lgcookieslaw_banner,#lgcookieslaw_modal,.lgcookieslaw-overlay"}],optIn:[{waitForThenClick:"#lgcookieslaw_accept"}],optOut:[{hide:"#lgcookieslaw_banner,#lgcookieslaw_modal,.lgcookieslaw-overlay"}]},{name:"linkedin.com",prehideSelectors:[".artdeco-global-alert[type=COOKIE_CONSENT]"],detectCmp:[{exists:".artdeco-global-alert[type=COOKIE_CONSENT]"}],detectPopup:[{visible:".artdeco-global-alert[type=COOKIE_CONSENT]"}],optIn:[{waitForVisible:".artdeco-global-alert[type=COOKIE_CONSENT] button[action-type=ACCEPT]"},{wait:500},{waitForThenClick:".artdeco-global-alert[type=COOKIE_CONSENT] button[action-type=ACCEPT]"}],optOut:[{waitForVisible:".artdeco-global-alert[type=COOKIE_CONSENT] button[action-type=DENY]"},{wait:500},{waitForThenClick:".artdeco-global-alert[type=COOKIE_CONSENT] button[action-type=DENY]"}],test:[{waitForVisible:".artdeco-global-alert[type=COOKIE_CONSENT]",check:"none"}]},{name:"livejasmin",vendorUrl:"https://www.livejasmin.com/",runContext:{urlPattern:"^https://(m|www)\\.livejasmin\\.com/"},prehideSelectors:["#consent_modal"],detectCmp:[{exists:"#consent_modal"}],detectPopup:[{visible:"#consent_modal"}],optIn:[{waitForThenClick:"#consent_modal button[data-testid=ButtonStyledButton]:first-of-type"}],optOut:[{waitForThenClick:"#consent_modal button[data-testid=ButtonStyledButton]:nth-of-type(2)"},{waitForVisible:"[data-testid=PrivacyPreferenceCenterWithConsentCookieContent]"},{click:"[data-testid=PrivacyPreferenceCenterWithConsentCookieContent] input[data-testid=PrivacyPreferenceCenterWithConsentCookieSwitch]:checked",optional:!0,all:!0},{waitForThenClick:"[data-testid=PrivacyPreferenceCenterWithConsentCookieContent] button[data-testid=ButtonStyledButton]:last-child"}]},{name:"macpaw.com",cosmetic:!0,prehideSelectors:['div[data-banner="cookies"]'],detectCmp:[{exists:'div[data-banner="cookies"]'}],detectPopup:[{exists:'div[data-banner="cookies"]'}],optIn:[{click:'button[data-banner-close="cookies"]'}],optOut:[{hide:'div[data-banner="cookies"]'}]},{name:"marksandspencer.com",cosmetic:!0,detectCmp:[{exists:".navigation-cookiebbanner"}],detectPopup:[{visible:".navigation-cookiebbanner"}],optOut:[{hide:".navigation-cookiebbanner"}],optIn:[{click:".navigation-cookiebbanner__submit"}]},{name:"mediamarkt.de",prehideSelectors:["div[aria-labelledby=pwa-consent-layer-title]","div[class^=StyledConsentLayerWrapper-]"],detectCmp:[{exists:"div[aria-labelledby^=pwa-consent-layer-title]"}],detectPopup:[{exists:"div[aria-labelledby^=pwa-consent-layer-title]"}],optOut:[{click:"button[data-test^=pwa-consent-layer-deny-all]"}],optIn:[{click:"button[data-test^=pwa-consent-layer-accept-all"}]},{name:"Mediavine",prehideSelectors:['[data-name="mediavine-gdpr-cmp"]'],detectCmp:[{exists:'[data-name="mediavine-gdpr-cmp"]'}],detectPopup:[{wait:500},{visible:'[data-name="mediavine-gdpr-cmp"]'}],optIn:[{waitForThenClick:'[data-name="mediavine-gdpr-cmp"] [format="primary"]'}],optOut:[{waitForThenClick:'[data-name="mediavine-gdpr-cmp"] [data-view="manageSettings"]'},{waitFor:'[data-name="mediavine-gdpr-cmp"] input[type=checkbox]'},{eval:"EVAL_MEDIAVINE_0",optional:!0},{click:'[data-name="mediavine-gdpr-cmp"] [format="secondary"]'}]},{name:"microsoft.com",prehideSelectors:["#wcpConsentBannerCtrl"],detectCmp:[{exists:"#wcpConsentBannerCtrl"}],detectPopup:[{exists:"#wcpConsentBannerCtrl"}],optOut:[{eval:"EVAL_MICROSOFT_0"}],optIn:[{eval:"EVAL_MICROSOFT_1"}],test:[{eval:"EVAL_MICROSOFT_2"}]},{name:"midway-usa",runContext:{urlPattern:"^https://www\\.midwayusa\\.com/"},cosmetic:!0,prehideSelectors:["#cookie-container"],detectCmp:[{exists:['div[aria-label="Cookie Policy Banner"]']}],detectPopup:[{visible:"#cookie-container"}],optIn:[{click:"button#cookie-btn"}],optOut:[{hide:'div[aria-label="Cookie Policy Banner"]'}]},{name:"moneysavingexpert.com",detectCmp:[{exists:"dialog[data-testid=accept-our-cookies-dialog]"}],detectPopup:[{visible:"dialog[data-testid=accept-our-cookies-dialog]"}],optIn:[{click:"#banner-accept"}],optOut:[{click:"#banner-manage"},{click:"#pc-confirm"}]},{name:"monzo.com",prehideSelectors:[".cookie-alert, cookie-alert__content"],detectCmp:[{exists:'div.cookie-alert[role="dialog"]'},{exists:'a[href*="monzo"]'}],detectPopup:[{visible:".cookie-alert__content"}],optIn:[{click:".js-accept-cookie-policy"}],optOut:[{click:".js-decline-cookie-policy"}]},{name:"Moove",prehideSelectors:["#moove_gdpr_cookie_info_bar"],detectCmp:[{exists:"#moove_gdpr_cookie_info_bar"}],detectPopup:[{visible:"#moove_gdpr_cookie_info_bar"}],optIn:[{waitForThenClick:".moove-gdpr-infobar-allow-all"}],optOut:[{if:{exists:"#moove_gdpr_cookie_info_bar .change-settings-button"},then:[{click:"#moove_gdpr_cookie_info_bar .change-settings-button"},{waitForVisible:"#moove_gdpr_cookie_modal"},{eval:"EVAL_MOOVE_0"},{click:".moove-gdpr-modal-save-settings"}],else:[{hide:"#moove_gdpr_cookie_info_bar"}]}],test:[{visible:"#moove_gdpr_cookie_info_bar",check:"none"}]},{name:"national-lottery.co.uk",detectCmp:[{exists:".cuk_cookie_consent"}],detectPopup:[{visible:".cuk_cookie_consent",check:"any"}],optOut:[{click:".cuk_cookie_consent_manage_pref"},{click:".cuk_cookie_consent_save_pref"},{click:".cuk_cookie_consent_close"}],optIn:[{click:".cuk_cookie_consent_accept_all"}]},{name:"nba.com",runContext:{urlPattern:"^https://(www\\.)?nba.com/"},cosmetic:!0,prehideSelectors:["#onetrust-banner-sdk"],detectCmp:[{exists:"#onetrust-banner-sdk"}],detectPopup:[{visible:"#onetrust-banner-sdk"}],optIn:[{click:"#onetrust-accept-btn-handler"}],optOut:[{hide:"#onetrust-banner-sdk"}]},{name:"netflix.de",detectCmp:[{exists:"#cookie-disclosure"}],detectPopup:[{visible:".cookie-disclosure-message",check:"any"}],optIn:[{click:".btn-accept"}],optOut:[{hide:"#cookie-disclosure"},{click:".btn-reject"}]},{name:"nhs.uk",prehideSelectors:["#nhsuk-cookie-banner"],detectCmp:[{exists:"#nhsuk-cookie-banner"}],detectPopup:[{exists:"#nhsuk-cookie-banner"}],optOut:[{click:"#nhsuk-cookie-banner__link_accept"}],optIn:[{click:"#nhsuk-cookie-banner__link_accept_analytics"}]},{name:"notice-cookie",prehideSelectors:[".button--notice"],cosmetic:!0,detectCmp:[{exists:".notice--cookie"}],detectPopup:[{visible:".notice--cookie"}],optIn:[{click:".button--notice"}],optOut:[{hide:".notice--cookie"}]},{name:"nrk.no",cosmetic:!0,prehideSelectors:[".nrk-masthead__info-banner--cookie"],detectCmp:[{exists:".nrk-masthead__info-banner--cookie"}],detectPopup:[{exists:".nrk-masthead__info-banner--cookie"}],optIn:[{click:"div.nrk-masthead__info-banner--cookie button > span:has(+ svg.nrk-close)"}],optOut:[{hide:".nrk-masthead__info-banner--cookie"}]},{name:"obi.de",prehideSelectors:[".disc-cp--active"],detectCmp:[{exists:".disc-cp-modal__modal"}],detectPopup:[{visible:".disc-cp-modal__modal"}],optIn:[{click:".js-disc-cp-accept-all"}],optOut:[{click:".js-disc-cp-deny-all"}]},{name:"om",vendorUrl:"https://olli-machts.de/en/extension/cookie-manager",prehideSelectors:[".tx-om-cookie-consent"],detectCmp:[{exists:".tx-om-cookie-consent .active[data-omcookie-panel]"}],detectPopup:[{exists:".tx-om-cookie-consent .active[data-omcookie-panel]"}],optIn:[{waitForThenClick:"[data-omcookie-panel-save=all]"}],optOut:[{if:{exists:"[data-omcookie-panel-save=min]"},then:[{waitForThenClick:"[data-omcookie-panel-save=min]"}],else:[{click:"input[data-omcookie-panel-grp]:checked:not(:disabled)",all:!0,optional:!0},{waitForThenClick:"[data-omcookie-panel-save=save]"}]}]},{name:"onlyFans.com",prehideSelectors:["div.b-cookies-informer"],detectCmp:[{exists:"div.b-cookies-informer"}],detectPopup:[{exists:"div.b-cookies-informer"}],optIn:[{click:"div.b-cookies-informer__nav > button:nth-child(2)"}],optOut:[{click:"div.b-cookies-informer__nav > button:nth-child(1)"},{click:'div.b-cookies-informer__switchers > div:nth-child(2) > div[at-attr="checkbox"] > span.b-input-radio__container > input[type="checkbox"]'},{click:"div.b-cookies-informer__nav > button"}]},{name:"openli",vendorUrl:"https://openli.com",prehideSelectors:[".legalmonster-cleanslate"],detectCmp:[{exists:".legalmonster-cleanslate"}],detectPopup:[{visible:".legalmonster-cleanslate #lm-cookie-wall-container",check:"any"}],optIn:[{waitForThenClick:"#lm-accept-all"}],optOut:[{waitForThenClick:"#lm-accept-necessary"}]},{name:"opera.com",vendorUrl:"https://unknown",cosmetic:!1,runContext:{main:!0,frame:!1},intermediate:!1,prehideSelectors:[],detectCmp:[{exists:"#cookie-consent .manage-cookies__btn"}],detectPopup:[{visible:"#cookie-consent .cookie-basic-consent__btn"}],optIn:[{waitForThenClick:"#cookie-consent .cookie-basic-consent__btn"}],optOut:[{waitForThenClick:"#cookie-consent .manage-cookies__btn"},{waitForThenClick:"#cookie-consent .active.marketing_option_switch.cookie-consent__switch",all:!0},{waitForThenClick:"#cookie-consent .cookie-selection__btn"}],test:[{eval:"EVAL_OPERA_0"}]},{name:"osano",prehideSelectors:[".osano-cm-window,.osano-cm-dialog"],detectCmp:[{exists:".osano-cm-window"}],detectPopup:[{visible:".osano-cm-dialog"}],optIn:[{click:".osano-cm-accept-all",optional:!0}],optOut:[{waitForThenClick:".osano-cm-denyAll"}]},{name:"otto.de",prehideSelectors:[".cookieBanner--visibility"],detectCmp:[{exists:".cookieBanner--visibility"}],detectPopup:[{visible:".cookieBanner__wrapper"}],optIn:[{click:".js_cookieBannerPermissionButton"}],optOut:[{click:".js_cookieBannerProhibitionButton"}]},{name:"ourworldindata",vendorUrl:"https://ourworldindata.org/",runContext:{urlPattern:"^https://ourworldindata\\.org/"},prehideSelectors:[".cookie-manager"],detectCmp:[{exists:".cookie-manager"}],detectPopup:[{visible:".cookie-manager .cookie-notice.open"}],optIn:[{waitForThenClick:".cookie-notice [data-test=accept]"}],optOut:[{waitForThenClick:".cookie-notice [data-test=reject]"}]},{name:"pabcogypsum",vendorUrl:"https://unknown",prehideSelectors:[".js-cookie-notice:has(#cookie_settings-form)"],detectCmp:[{exists:".js-cookie-notice #cookie_settings-form"}],detectPopup:[{visible:".js-cookie-notice #cookie_settings-form"}],optIn:[{waitForThenClick:".js-cookie-notice button[value=allow]"}],optOut:[{waitForThenClick:".js-cookie-notice button[value=disable]"}]},{name:"paypal-us",prehideSelectors:["#ccpaCookieContent_wrapper, article.ppvx_modal--overpanel"],detectCmp:[{exists:"#ccpaCookieBanner, .privacy-sheet-content"}],detectPopup:[{exists:"#ccpaCookieBanner, .privacy-sheet-content"}],optIn:[{click:"#acceptAllButton"}],optOut:[{if:{exists:"a#manageCookiesLink"},then:[{click:"a#manageCookiesLink"}],else:[{waitForVisible:".privacy-sheet-content #formContent"},{click:"#formContent .cookiepref-11m2iee-checkbox_base input:checked",all:!0,optional:!0},{click:".confirmCookie #submitCookiesBtn"}]}]},{name:"paypal.com",prehideSelectors:["#gdprCookieBanner"],detectCmp:[{exists:"#gdprCookieBanner"}],detectPopup:[{visible:"#gdprCookieContent_wrapper"}],optIn:[{click:"#acceptAllButton"}],optOut:[{wait:200},{click:".gdprCookieBanner_decline-button"}],test:[{wait:500},{eval:"EVAL_PAYPAL_0"}]},{name:"pinetools.com",cosmetic:!0,prehideSelectors:["#aviso_cookies"],detectCmp:[{exists:"#aviso_cookies"}],detectPopup:[{exists:".lang_en #aviso_cookies"}],optIn:[{click:"#aviso_cookies .a_boton_cerrar"}],optOut:[{hide:"#aviso_cookies"}]},{name:"pmc",cosmetic:!0,prehideSelectors:["#pmc-pp-tou--notice"],detectCmp:[{exists:"#pmc-pp-tou--notice"}],detectPopup:[{visible:"#pmc-pp-tou--notice"}],optIn:[{click:"span.pmc-pp-tou--notice-close-btn"}],optOut:[{hide:"#pmc-pp-tou--notice"}]},{name:"pornhub.com",runContext:{urlPattern:"^https://(www\\.)?pornhub\\.com/"},cosmetic:!0,prehideSelectors:[".cookiesBanner"],detectCmp:[{exists:".cookiesBanner"}],detectPopup:[{visible:".cookiesBanner"}],optIn:[{click:".cookiesBanner .okButton"}],optOut:[{hide:".cookiesBanner"}]},{name:"pornpics.com",cosmetic:!0,prehideSelectors:["#cookie-contract"],detectCmp:[{exists:"#cookie-contract"}],detectPopup:[{visible:"#cookie-contract"}],optIn:[{click:"#cookie-contract .icon-cross"}],optOut:[{hide:"#cookie-contract"}]},{name:"PrimeBox CookieBar",prehideSelectors:["#cookie-bar"],detectCmp:[{exists:"#cookie-bar .cb-enable,#cookie-bar .cb-disable,#cookie-bar .cb-policy"}],detectPopup:[{visible:"#cookie-bar .cb-enable,#cookie-bar .cb-disable,#cookie-bar .cb-policy",check:"any"}],optIn:[{waitForThenClick:"#cookie-bar .cb-enable"}],optOut:[{click:"#cookie-bar .cb-disable",optional:!0},{hide:"#cookie-bar"}],test:[{eval:"EVAL_PRIMEBOX_0"}]},{name:"privacymanager.io",prehideSelectors:["#gdpr-consent-tool-wrapper",'iframe[src^="https://cmp-consent-tool.privacymanager.io"]'],runContext:{urlPattern:"^https://cmp-consent-tool\\.privacymanager\\.io/",main:!1,frame:!0},detectCmp:[{exists:"button#save"}],detectPopup:[{visible:"button#save"}],optIn:[{click:"button#save"}],optOut:[{if:{exists:"#denyAll"},then:[{click:"#denyAll"},{waitForThenClick:".okButton"}],else:[{waitForThenClick:"#manageSettings"},{waitFor:".purposes-overview-list"},{waitFor:"button#saveAndExit"},{click:"span[role=checkbox][aria-checked=true]",all:!0,optional:!0},{click:"button#saveAndExit"}]}]},{name:"productz.com",vendorUrl:"https://productz.com/",runContext:{urlPattern:"^https://productz\\.com/"},prehideSelectors:[],detectCmp:[{exists:".c-modal.is-active"}],detectPopup:[{visible:".c-modal.is-active"}],optIn:[{waitForThenClick:".c-modal.is-active .is-accept"}],optOut:[{waitForThenClick:".c-modal.is-active .is-dismiss"}]},{name:"pubtech",prehideSelectors:["#pubtech-cmp"],detectCmp:[{exists:"#pubtech-cmp"}],detectPopup:[{visible:"#pubtech-cmp #pt-actions"}],optIn:[{if:{exists:"#pt-accept-all"},then:[{click:"#pubtech-cmp #pt-actions #pt-accept-all"}],else:[{click:"#pubtech-cmp #pt-actions button:nth-of-type(2)"}]}],optOut:[{click:"#pubtech-cmp #pt-close"}],test:[{eval:"EVAL_PUBTECH_0"}]},{name:"quantcast",prehideSelectors:["#qc-cmp2-main,#qc-cmp2-container"],detectCmp:[{exists:"#qc-cmp2-container"}],detectPopup:[{visible:"#qc-cmp2-ui"}],optOut:[{click:'.qc-cmp2-summary-buttons > button[mode="secondary"]'},{waitFor:"#qc-cmp2-ui"},{click:'.qc-cmp2-toggle-switch > button[aria-checked="true"]',all:!0,optional:!0},{click:'.qc-cmp2-main button[aria-label="REJECT ALL"]',optional:!0},{waitForThenClick:'.qc-cmp2-main button[aria-label="SAVE & EXIT"],.qc-cmp2-buttons-desktop > button[mode="primary"]',timeout:5e3}],optIn:[{click:'.qc-cmp2-summary-buttons > button[mode="primary"]'}]},{name:"reddit.com",runContext:{urlPattern:"^https://www\\.reddit\\.com/"},prehideSelectors:["[bundlename=reddit_cookie_banner]"],detectCmp:[{exists:"reddit-cookie-banner"}],detectPopup:[{visible:"reddit-cookie-banner"}],optIn:[{waitForThenClick:["reddit-cookie-banner","#accept-all-cookies-button > button"]}],optOut:[{waitForThenClick:["reddit-cookie-banner","#reject-nonessential-cookies-button > button"]}],test:[{eval:"EVAL_REDDIT_0"}]},{name:"rog-forum.asus.com",runContext:{urlPattern:"^https://rog-forum\\.asus\\.com/"},prehideSelectors:["#cookie-policy-info"],detectCmp:[{exists:"#cookie-policy-info"}],detectPopup:[{visible:"#cookie-policy-info"}],optIn:[{click:'div.cookie-btn-box > div[aria-label="Accept"]'}],optOut:[{click:'div.cookie-btn-box > div[aria-label="Reject"]'},{waitForThenClick:'.cookie-policy-lightbox-bottom > div[aria-label="Save Settings"]'}]},{name:"roofingmegastore.co.uk",runContext:{urlPattern:"^https://(www\\.)?roofingmegastore\\.co\\.uk"},prehideSelectors:["#m-cookienotice"],detectCmp:[{exists:"#m-cookienotice"}],detectPopup:[{visible:"#m-cookienotice"}],optIn:[{click:"#accept-cookies"}],optOut:[{click:"#manage-cookies"},{waitForThenClick:"#accept-selected"}]},{name:"samsung.com",runContext:{urlPattern:"^https://www\\.samsung\\.com/"},cosmetic:!0,prehideSelectors:["div.cookie-bar"],detectCmp:[{exists:"div.cookie-bar"}],detectPopup:[{visible:"div.cookie-bar"}],optIn:[{click:"div.cookie-bar__manage > a"}],optOut:[{hide:"div.cookie-bar"}]},{name:"setapp.com",vendorUrl:"https://setapp.com/",cosmetic:!0,runContext:{urlPattern:"^https://setapp\\.com/"},prehideSelectors:[],detectCmp:[{exists:".cookie-banner.js-cookie-banner"}],detectPopup:[{visible:".cookie-banner.js-cookie-banner"}],optIn:[{waitForThenClick:".cookie-banner.js-cookie-banner button"}],optOut:[{hide:".cookie-banner.js-cookie-banner"}]},{name:"sibbo",prehideSelectors:["sibbo-cmp-layout"],detectCmp:[{exists:"sibbo-cmp-layout"}],detectPopup:[{visible:"sibbo-cmp-layout"}],optIn:[{click:"sibbo-cmp-layout [data-accept-all]"}],optOut:[{click:'.sibbo-panel__aside__buttons a[data-nav="purposes"]'},{click:'.sibbo-panel__main__header__actions a[data-focusable="reject-all"]'},{if:{exists:"[data-view=purposes] .sibbo-panel__main__footer__actions [data-save-and-exit]"},then:[],else:[{waitFor:'.sibbo-panel__main__footer__actions a[data-focusable="next"]:not(.sibbo-cmp-button--disabled)'},{click:'.sibbo-panel__main__footer__actions a[data-focusable="next"]'},{click:'.sibbo-panel__main div[data-view="purposesLegInt"] a[data-focusable="reject-all"]'}]},{waitFor:".sibbo-panel__main__footer__actions [data-save-and-exit]:not(.sibbo-cmp-button--disabled)"},{click:".sibbo-panel__main__footer__actions [data-save-and-exit]:not(.sibbo-cmp-button--disabled)"}],test:[{eval:"EVAL_SIBBO_0"}]},{name:"similarweb.com",cosmetic:!0,prehideSelectors:[".app-cookies-notification"],detectCmp:[{exists:".app-cookies-notification"}],detectPopup:[{exists:".app-layout .app-cookies-notification"}],optIn:[{click:"button.app-cookies-notification__dismiss"}],optOut:[{hide:".app-layout .app-cookies-notification"}]},{name:"Sirdata",prehideSelectors:["#sd-cmp"],detectCmp:[{exists:"#sd-cmp"}],detectPopup:[{visible:"#sd-cmp"}],optIn:[{waitForThenClick:"#sd-cmp .sd-cmp-3cRQ2"}],optOut:[{waitForThenClick:"#sd-cmp .sd-cmp-1pO44"}],test:[{eval:"EVAL_SIRDATA_0"}]},{name:"snigel",detectCmp:[{exists:".snigel-cmp-framework"}],detectPopup:[{visible:".snigel-cmp-framework"}],optOut:[{click:"#sn-b-custom"},{click:"#sn-b-save"}],test:[{eval:"EVAL_SNIGEL_0"}],optIn:[{click:".snigel-cmp-framework #accept-choices"}]},{name:"steampowered.com",detectCmp:[{exists:".cookiepreferences_popup"},{visible:".cookiepreferences_popup"}],detectPopup:[{visible:".cookiepreferences_popup"}],optOut:[{click:"#rejectAllButton"}],optIn:[{click:"#acceptAllButton"}],test:[{wait:1e3},{eval:"EVAL_STEAMPOWERED_0"}]},{name:"strato.de",prehideSelectors:["#cookie_initial_modal",".modal-backdrop"],runContext:{urlPattern:"^https://www\\.strato\\.de/"},detectCmp:[{exists:"#cookie_initial_modal"}],detectPopup:[{visible:"#cookie_initial_modal"}],optIn:[{click:"button#jss_consent_all_initial_modal"}],optOut:[{click:"button#jss_open_settings_modal"},{click:"button#jss_consent_checked"}]},{name:"svt.se",vendorUrl:"https://www.svt.se/",runContext:{urlPattern:"^https://www\\.svt\\.se/"},prehideSelectors:["[class*=CookieConsent__root___]"],detectCmp:[{exists:"[class*=CookieConsent__root___]"}],detectPopup:[{visible:"[class*=CookieConsent__modal___]"}],optIn:[{waitForThenClick:"[class*=CookieConsent__modal___] > div > button[class*=primary]"}],optOut:[{waitForThenClick:"[class*=CookieConsent__modal___] > div > button[class*=secondary]:nth-child(2)"}],test:[{eval:"EVAL_SVT_TEST"}]},{name:"takealot.com",cosmetic:!0,prehideSelectors:['div[class^="cookies-banner-module_"]'],detectCmp:[{exists:'div[class^="cookies-banner-module_cookie-banner_"]'}],detectPopup:[{exists:'div[class^="cookies-banner-module_cookie-banner_"]'}],optIn:[{click:'button[class*="cookies-banner-module_dismiss-button_"]'}],optOut:[{hide:'div[class^="cookies-banner-module_"]'},{if:{exists:'div[class^="cookies-banner-module_small-cookie-banner_"]'},then:[{eval:"EVAL_TAKEALOT_0"}],else:[]}]},{name:"tarteaucitron.js",prehideSelectors:["#tarteaucitronRoot"],detectCmp:[{exists:"#tarteaucitronRoot"}],detectPopup:[{visible:"#tarteaucitronRoot #tarteaucitronAlertSmall,#tarteaucitronRoot #tarteaucitronAlertBig",check:"any"}],optIn:[{eval:"EVAL_TARTEAUCITRON_1"}],optOut:[{eval:"EVAL_TARTEAUCITRON_0"}],test:[{eval:"EVAL_TARTEAUCITRON_2",comment:"sometimes there are required categories, so we check that at least something is false"}]},{name:"taunton",vendorUrl:"https://www.taunton.com/",prehideSelectors:["#taunton-user-consent__overlay"],detectCmp:[{exists:"#taunton-user-consent__overlay"}],detectPopup:[{exists:"#taunton-user-consent__overlay:not([aria-hidden=true])"}],optIn:[{click:"#taunton-user-consent__toolbar input[type=checkbox]:not(:checked)"},{click:"#taunton-user-consent__toolbar button[type=submit]"}],optOut:[{click:"#taunton-user-consent__toolbar input[type=checkbox]:checked",optional:!0,all:!0},{click:"#taunton-user-consent__toolbar button[type=submit]"}],test:[{eval:"EVAL_TAUNTON_TEST"}]},{name:"Tealium",prehideSelectors:["#__tealiumGDPRecModal,#__tealiumGDPRcpPrefs,#__tealiumImplicitmodal,#consent-layer"],detectCmp:[{exists:"#__tealiumGDPRecModal *,#__tealiumGDPRcpPrefs *,#__tealiumImplicitmodal *"},{eval:"EVAL_TEALIUM_0"}],detectPopup:[{visible:"#__tealiumGDPRecModal *,#__tealiumGDPRcpPrefs *,#__tealiumImplicitmodal *",check:"any"}],optOut:[{eval:"EVAL_TEALIUM_1"},{eval:"EVAL_TEALIUM_DONOTSELL"},{hide:"#__tealiumGDPRecModal,#__tealiumGDPRcpPrefs,#__tealiumImplicitmodal"},{waitForThenClick:"#cm-acceptNone,.js-accept-essential-cookies",timeout:1e3,optional:!0}],optIn:[{hide:"#__tealiumGDPRecModal,#__tealiumGDPRcpPrefs"},{eval:"EVAL_TEALIUM_2"}],test:[{eval:"EVAL_TEALIUM_3"},{eval:"EVAL_TEALIUM_DONOTSELL_CHECK"},{visible:"#__tealiumGDPRecModal,#__tealiumGDPRcpPrefs",check:"none"}]},{name:"temu",vendorUrl:"https://temu.com",runContext:{urlPattern:"^https://[^/]*temu\\.com/"},prehideSelectors:["._2d-8vq-W,._1UdBUwni"],detectCmp:[{exists:"._3YCsmIaS"}],detectPopup:[{visible:"._3YCsmIaS"}],optIn:[{waitForThenClick:"._3fKiu5wx._3zN5SumS._3tAK973O.IYOfhWEs.VGNGF1pA"}],optOut:[{waitForThenClick:"._3fKiu5wx._1_XToJBF._3tAK973O.IYOfhWEs.VGNGF1pA"}]},{name:"Termly",prehideSelectors:["#termly-code-snippet-support"],detectCmp:[{exists:"#termly-code-snippet-support"}],detectPopup:[{visible:"#termly-code-snippet-support div"}],optIn:[{waitForThenClick:'[data-tid="banner-accept"]'}],optOut:[{if:{exists:'[data-tid="banner-decline"]'},then:[{click:'[data-tid="banner-decline"]'}],else:[{click:".t-preference-button"},{wait:500},{if:{exists:".t-declineAllButton"},then:[{click:".t-declineAllButton"}],else:[{waitForThenClick:".t-preference-modal input[type=checkbox][checked]:not([disabled])",all:!0},{waitForThenClick:".t-saveButton"}]}]}]},{name:"termsfeed",vendorUrl:"https://termsfeed.com",comment:"v4.x.x",prehideSelectors:[".termsfeed-com---nb"],detectCmp:[{exists:".termsfeed-com---nb"}],detectPopup:[{visible:".termsfeed-com---nb"}],optIn:[{waitForThenClick:".cc-nb-okagree"}],optOut:[{waitForThenClick:".cc-nb-reject"}]},{name:"termsfeed3",vendorUrl:"https://termsfeed.com",comment:"v3.x.x",cosmetic:!0,prehideSelectors:[".cc_dialog.cc_css_reboot"],detectCmp:[{exists:".cc_dialog.cc_css_reboot"}],detectPopup:[{visible:".cc_dialog.cc_css_reboot"}],optIn:[{waitForThenClick:".cc_dialog.cc_css_reboot .cc_b_ok"}],optOut:[{hide:".cc_dialog.cc_css_reboot"}]},{name:"Test page cosmetic CMP",cosmetic:!0,prehideSelectors:["#privacy-test-page-cmp-test-prehide"],detectCmp:[{exists:"#privacy-test-page-cmp-test-banner"}],detectPopup:[{visible:"#privacy-test-page-cmp-test-banner"}],optIn:[{waitFor:"#accept-all"},{click:"#accept-all"}],optOut:[{hide:"#privacy-test-page-cmp-test-banner"}],test:[{wait:500},{eval:"EVAL_TESTCMP_COSMETIC_0"}]},{name:"Test page CMP",prehideSelectors:["#reject-all"],detectCmp:[{exists:"#privacy-test-page-cmp-test"}],detectPopup:[{visible:"#privacy-test-page-cmp-test"}],optIn:[{waitFor:"#accept-all"},{click:"#accept-all"}],optOut:[{waitFor:"#reject-all"},{click:"#reject-all"}],test:[{eval:"EVAL_TESTCMP_0"}]},{name:"thalia.de",prehideSelectors:[".consent-banner-box"],detectCmp:[{exists:"consent-banner[component=consent-banner]"}],detectPopup:[{visible:".consent-banner-box"}],optIn:[{click:".button-zustimmen"}],optOut:[{click:"button[data-consent=disagree]"}]},{name:"thefreedictionary.com",prehideSelectors:["#cmpBanner"],detectCmp:[{exists:"#cmpBanner"}],detectPopup:[{visible:"#cmpBanner"}],optIn:[{eval:"EVAL_THEFREEDICTIONARY_1"}],optOut:[{eval:"EVAL_THEFREEDICTIONARY_0"}]},{name:"theverge",runContext:{frame:!1,main:!0,urlPattern:"^https://(www)?\\.theverge\\.com"},intermediate:!1,prehideSelectors:[".duet--cta--cookie-banner"],detectCmp:[{exists:".duet--cta--cookie-banner"}],detectPopup:[{visible:".duet--cta--cookie-banner"}],optIn:[{click:".duet--cta--cookie-banner button.tracking-12",all:!1}],optOut:[{click:".duet--cta--cookie-banner button.tracking-12 > span"}],test:[{eval:"EVAL_THEVERGE_0"}]},{name:"tidbits-com",cosmetic:!0,prehideSelectors:["#eu_cookie_law_widget-2"],detectCmp:[{exists:"#eu_cookie_law_widget-2"}],detectPopup:[{visible:"#eu_cookie_law_widget-2"}],optIn:[{click:"#eu-cookie-law form > input.accept"}],optOut:[{hide:"#eu_cookie_law_widget-2"}]},{name:"tractor-supply",runContext:{urlPattern:"^https://www\\.tractorsupply\\.com/"},cosmetic:!0,prehideSelectors:[".tsc-cookie-banner"],detectCmp:[{exists:".tsc-cookie-banner"}],detectPopup:[{visible:".tsc-cookie-banner"}],optIn:[{click:"#cookie-banner-cancel"}],optOut:[{hide:".tsc-cookie-banner"}]},{name:"trader-joes-com",cosmetic:!0,prehideSelectors:['div.aem-page > div[class^="CookiesAlert_cookiesAlert__"]'],detectCmp:[{exists:'div.aem-page > div[class^="CookiesAlert_cookiesAlert__"]'}],detectPopup:[{visible:'div.aem-page > div[class^="CookiesAlert_cookiesAlert__"]'}],optIn:[{click:'div[class^="CookiesAlert_cookiesAlert__container__"] button'}],optOut:[{hide:'div.aem-page > div[class^="CookiesAlert_cookiesAlert__"]'}]},{name:"transcend",vendorUrl:"https://unknown",cosmetic:!0,prehideSelectors:["#transcend-consent-manager"],detectCmp:[{exists:"#transcend-consent-manager"}],detectPopup:[{visible:"#transcend-consent-manager"}],optIn:[{waitForThenClick:["#transcend-consent-manager","#consentManagerMainDialog .inner-container button"]}],optOut:[{hide:"#transcend-consent-manager"}]},{name:"transip-nl",runContext:{urlPattern:"^https://www\\.transip\\.nl/"},prehideSelectors:["#consent-modal"],detectCmp:[{any:[{exists:"#consent-modal"},{exists:"#privacy-settings-content"}]}],detectPopup:[{any:[{visible:"#consent-modal"},{visible:"#privacy-settings-content"}]}],optIn:[{click:'button[type="submit"]'}],optOut:[{if:{exists:"#privacy-settings-content"},then:[{click:'button[type="submit"]'}],else:[{click:"div.one-modal__action-footer-column--secondary > a"}]}]},{name:"tropicfeel-com",prehideSelectors:["#shopify-section-cookies-controller"],detectCmp:[{exists:"#shopify-section-cookies-controller"}],detectPopup:[{visible:"#shopify-section-cookies-controller #cookies-controller-main-pane",check:"any"}],optIn:[{waitForThenClick:"#cookies-controller-main-pane form[data-form-allow-all] button"}],optOut:[{click:"#cookies-controller-main-pane a[data-tab-target=manage-cookies]"},{waitFor:"#manage-cookies-pane.active"},{click:"#manage-cookies-pane.active input[type=checkbox][checked]:not([disabled])",all:!0},{click:"#manage-cookies-pane.active button[type=submit]"}],test:[]},{name:"true-car",runContext:{urlPattern:"^https://www\\.truecar\\.com/"},cosmetic:!0,prehideSelectors:[['div[aria-labelledby="cookie-banner-heading"]']],detectCmp:[{exists:'div[aria-labelledby="cookie-banner-heading"]'}],detectPopup:[{visible:'div[aria-labelledby="cookie-banner-heading"]'}],optIn:[{click:'div[aria-labelledby="cookie-banner-heading"] > button[aria-label="Close"]'}],optOut:[{hide:'div[aria-labelledby="cookie-banner-heading"]'}]},{name:"truyo",prehideSelectors:["#truyo-consent-module"],detectCmp:[{exists:"#truyo-cookieBarContent"}],detectPopup:[{visible:"#truyo-consent-module"}],optIn:[{click:"button#acceptAllCookieButton"}],optOut:[{click:"button#declineAllCookieButton"}]},{name:"tumblr-com",cosmetic:!0,prehideSelectors:["#cmp-app-container"],detectCmp:[{exists:"#cmp-app-container"}],detectPopup:[{visible:"#cmp-app-container"}],optIn:[{click:"#tumblr #cmp-app-container div.components-modal__frame > iframe > html body > div > div > div.cmp__dialog-footer > div > button.components-button.white-space-normal.is-primary"}],optOut:[{hide:"#cmp-app-container"}]},{name:"twitch-mobile",vendorUrl:"https://m.twitch.tv/",cosmetic:!0,runContext:{urlPattern:"^https?://m\\.twitch\\.tv"},prehideSelectors:[],detectCmp:[{exists:'.ReactModal__Overlay [href="https://www.twitch.tv/p/cookie-policy"]'}],detectPopup:[{visible:'.ReactModal__Overlay [href="https://www.twitch.tv/p/cookie-policy"]'}],optIn:[{waitForThenClick:'.ReactModal__Overlay:has([href="https://www.twitch.tv/p/cookie-policy"]) button'}],optOut:[{hide:'.ReactModal__Overlay:has([href="https://www.twitch.tv/p/cookie-policy"])'}]},{name:"twitch.tv",runContext:{urlPattern:"^https?://(www\\.)?twitch\\.tv"},prehideSelectors:["div:has(> .consent-banner .consent-banner__content--gdpr-v2),.ReactModalPortal:has([data-a-target=consent-modal-save])"],detectCmp:[{exists:".consent-banner .consent-banner__content--gdpr-v2"}],detectPopup:[{visible:".consent-banner .consent-banner__content--gdpr-v2"}],optIn:[{click:'button[data-a-target="consent-banner-accept"]'}],optOut:[{hide:"div:has(> .consent-banner .consent-banner__content--gdpr-v2)"},{click:'button[data-a-target="consent-banner-manage-preferences"]'},{waitFor:"input[type=checkbox][data-a-target=tw-checkbox]"},{click:"input[type=checkbox][data-a-target=tw-checkbox][checked]:not([disabled])",all:!0,optional:!0},{waitForThenClick:"[data-a-target=consent-modal-save]"},{waitForVisible:".ReactModalPortal:has([data-a-target=consent-modal-save])",check:"none"}]},{name:"twitter",runContext:{urlPattern:"^https://([a-z0-9-]+\\.)?twitter\\.com/"},prehideSelectors:['[data-testid="BottomBar"]'],detectCmp:[{exists:'[data-testid="BottomBar"] div'}],detectPopup:[{visible:'[data-testid="BottomBar"] div'}],optIn:[{waitForThenClick:'[data-testid="BottomBar"] > div:has(>div:first-child>div:last-child>span[role=button]) > div:last-child > div[role=button]:first-child'}],optOut:[{waitForThenClick:'[data-testid="BottomBar"] > div:has(>div:first-child>div:last-child>span[role=button]) > div:last-child > div[role=button]:last-child'}],TODOtest:[{eval:"EVAL_document.cookie.includes('d_prefs=MjoxLGNvbnNlbnRfdmVyc2lvbjoy')"}]},{name:"ubuntu.com",prehideSelectors:["dialog.cookie-policy"],detectCmp:[{any:[{exists:"dialog.cookie-policy header"},{exists:'xpath///*[@id="modal"]/div/header'}]}],detectPopup:[{any:[{visible:"dialog header"},{visible:'xpath///*[@id="modal"]/div/header'}]}],optIn:[{any:[{waitForThenClick:"#cookie-policy-button-accept"},{waitForThenClick:'xpath///*[@id="cookie-policy-button-accept"]'}]}],optOut:[{any:[{waitForThenClick:"button.js-manage"},{waitForThenClick:'xpath///*[@id="cookie-policy-content"]/p[4]/button[2]'}]},{waitForThenClick:"dialog.cookie-policy .p-switch__input:checked",optional:!0,all:!0,timeout:500},{any:[{waitForThenClick:"dialog.cookie-policy .js-save-preferences"},{waitForThenClick:'xpath///*[@id="modal"]/div/button'}]}],test:[{eval:"EVAL_UBUNTU_COM_0"}]},{name:"UK Cookie Consent",prehideSelectors:["#catapult-cookie-bar"],cosmetic:!0,detectCmp:[{exists:"#catapult-cookie-bar"}],detectPopup:[{exists:".has-cookie-bar #catapult-cookie-bar"}],optIn:[{click:"#catapultCookie"}],optOut:[{hide:"#catapult-cookie-bar"}],test:[{eval:"EVAL_UK_COOKIE_CONSENT_0"}]},{name:"urbanarmorgear-com",cosmetic:!0,prehideSelectors:['div[class^="Layout__CookieBannerContainer-"]'],detectCmp:[{exists:'div[class^="Layout__CookieBannerContainer-"]'}],detectPopup:[{visible:'div[class^="Layout__CookieBannerContainer-"]'}],optIn:[{click:'button[class^="CookieBanner__AcceptButton"]'}],optOut:[{hide:'div[class^="Layout__CookieBannerContainer-"]'}]},{name:"usercentrics-api",detectCmp:[{exists:"#usercentrics-root"}],detectPopup:[{eval:"EVAL_USERCENTRICS_API_0"},{exists:["#usercentrics-root","[data-testid=uc-container]"]},{waitForVisible:"#usercentrics-root",timeout:2e3}],optIn:[{eval:"EVAL_USERCENTRICS_API_3"},{eval:"EVAL_USERCENTRICS_API_1"},{eval:"EVAL_USERCENTRICS_API_5"}],optOut:[{eval:"EVAL_USERCENTRICS_API_1"},{eval:"EVAL_USERCENTRICS_API_2"}],test:[{eval:"EVAL_USERCENTRICS_API_6"}]},{name:"usercentrics-button",detectCmp:[{exists:"#usercentrics-button"}],detectPopup:[{visible:"#usercentrics-button #uc-btn-accept-banner"}],optIn:[{click:"#usercentrics-button #uc-btn-accept-banner"}],optOut:[{click:"#usercentrics-button #uc-btn-deny-banner"}],test:[{eval:"EVAL_USERCENTRICS_BUTTON_0"}]},{name:"uswitch.com",prehideSelectors:["#cookie-banner-wrapper"],detectCmp:[{exists:"#cookie-banner-wrapper"}],detectPopup:[{visible:"#cookie-banner-wrapper"}],optIn:[{click:"#cookie_banner_accept_mobile"}],optOut:[{click:"#cookie_banner_save"}]},{name:"vodafone.de",runContext:{urlPattern:"^https://www\\.vodafone\\.de/"},prehideSelectors:[".dip-consent,.dip-consent-container"],detectCmp:[{exists:".dip-consent-container"}],detectPopup:[{visible:".dip-consent-content"}],optOut:[{click:'.dip-consent-btn[tabindex="2"]'}],optIn:[{click:'.dip-consent-btn[tabindex="1"]'}]},{name:"waitrose.com",prehideSelectors:["div[aria-labelledby=CookieAlertModalHeading]","section[data-test=initial-waitrose-cookie-consent-banner]","section[data-test=cookie-consent-modal]"],detectCmp:[{exists:"section[data-test=initial-waitrose-cookie-consent-banner]"}],detectPopup:[{visible:"section[data-test=initial-waitrose-cookie-consent-banner]"}],optIn:[{click:"button[data-test=accept-all]"}],optOut:[{click:"button[data-test=manage-cookies]"},{wait:200},{eval:"EVAL_WAITROSE_0"},{click:"button[data-test=submit]"}],test:[{eval:"EVAL_WAITROSE_1"}]},{name:"webflow",vendorUrl:"https://webflow.com/",prehideSelectors:[".fs-cc-components"],detectCmp:[{exists:".fs-cc-components"}],detectPopup:[{visible:".fs-cc-components"},{visible:"[fs-cc=banner]"}],optIn:[{wait:500},{waitForThenClick:"[fs-cc=banner] [fs-cc=allow]"}],optOut:[{wait:500},{waitForThenClick:"[fs-cc=banner] [fs-cc=deny]"}]},{name:"wetransfer.com",detectCmp:[{exists:".welcome__cookie-notice"}],detectPopup:[{visible:".welcome__cookie-notice"}],optIn:[{click:".welcome__button--accept"}],optOut:[{click:".welcome__button--decline"}]},{name:"whitepages.com",runContext:{urlPattern:"^https://www\\.whitepages\\.com/"},cosmetic:!0,prehideSelectors:[".cookie-wrapper, .cookie-overlay"],detectCmp:[{exists:".cookie-wrapper"}],detectPopup:[{visible:".cookie-overlay"}],optIn:[{click:'button[aria-label="Got it"]'}],optOut:[{hide:".cookie-wrapper"}]},{name:"wolframalpha",vendorUrl:"https://www.wolframalpha.com",prehideSelectors:[],cosmetic:!0,runContext:{urlPattern:"^https://www\\.wolframalpha\\.com/"},detectCmp:[{exists:"section._a_yb"}],detectPopup:[{visible:"section._a_yb"}],optIn:[{waitForThenClick:"section._a_yb button"}],optOut:[{hide:"section._a_yb"}]},{name:"woo-commerce-com",prehideSelectors:[".wccom-comp-privacy-banner .wccom-privacy-banner"],detectCmp:[{exists:".wccom-comp-privacy-banner .wccom-privacy-banner"}],detectPopup:[{exists:".wccom-comp-privacy-banner .wccom-privacy-banner"}],optIn:[{click:".wccom-privacy-banner__content-buttons button.is-primary"}],optOut:[{click:".wccom-privacy-banner__content-buttons button.is-secondary"},{waitForThenClick:"input[type=checkbox][checked]:not([disabled])",all:!0},{click:"div.wccom-modal__footer > button"}]},{name:"WP Cookie Notice for GDPR",vendorUrl:"https://wordpress.org/plugins/gdpr-cookie-consent/",prehideSelectors:["#gdpr-cookie-consent-bar"],detectCmp:[{exists:"#gdpr-cookie-consent-bar"}],detectPopup:[{visible:"#gdpr-cookie-consent-bar"}],optIn:[{waitForThenClick:"#gdpr-cookie-consent-bar #cookie_action_accept"}],optOut:[{waitForThenClick:"#gdpr-cookie-consent-bar #cookie_action_reject"}],test:[{eval:"EVAL_WP_COOKIE_NOTICE_0"}]},{name:"wpcc",cosmetic:!0,prehideSelectors:[".wpcc-container"],detectCmp:[{exists:".wpcc-container"}],detectPopup:[{exists:".wpcc-container .wpcc-message"}],optIn:[{click:".wpcc-compliance .wpcc-btn"}],optOut:[{hide:".wpcc-container"}]},{name:"xe.com",vendorUrl:"https://www.xe.com/",runContext:{urlPattern:"^https://www\\.xe\\.com/"},prehideSelectors:["[class*=ConsentBanner]"],detectCmp:[{exists:"[class*=ConsentBanner]"}],detectPopup:[{visible:"[class*=ConsentBanner]"}],optIn:[{waitForThenClick:"[class*=ConsentBanner] .egnScw"}],optOut:[{wait:1e3},{waitForThenClick:"[class*=ConsentBanner] .frDWEu"},{waitForThenClick:"[class*=ConsentBanner] .hXIpFU"}],test:[{eval:"EVAL_XE_TEST"}]},{name:"xhamster-eu",prehideSelectors:[".cookies-modal"],detectCmp:[{exists:".cookies-modal"}],detectPopup:[{exists:".cookies-modal"}],optIn:[{click:"button.cmd-button-accept-all"}],optOut:[{click:"button.cmd-button-reject-all"}]},{name:"xhamster-us",runContext:{urlPattern:"^https://(www\\.)?xhamster\\d?\\.com"},cosmetic:!0,prehideSelectors:[".cookie-announce"],detectCmp:[{exists:".cookie-announce"}],detectPopup:[{visible:".cookie-announce .announce-text"}],optIn:[{click:".cookie-announce button.xh-button"}],optOut:[{hide:".cookie-announce"}]},{name:"xing.com",detectCmp:[{exists:"div[class^=cookie-consent-CookieConsent]"}],detectPopup:[{exists:"div[class^=cookie-consent-CookieConsent]"}],optIn:[{click:"#consent-accept-button"}],optOut:[{click:"#consent-settings-button"},{click:".consent-banner-button-accept-overlay"}],test:[{eval:"EVAL_XING_0"}]},{name:"xnxx-com",cosmetic:!0,prehideSelectors:["#cookies-use-alert"],detectCmp:[{exists:"#cookies-use-alert"}],detectPopup:[{visible:"#cookies-use-alert"}],optIn:[{click:"#cookies-use-alert .close"}],optOut:[{hide:"#cookies-use-alert"}]},{name:"xvideos",vendorUrl:"https://xvideos.com",runContext:{urlPattern:"^https://[^/]*xvideos\\.com/"},prehideSelectors:[],detectCmp:[{exists:".disclaimer-opened #disclaimer-cookies"}],detectPopup:[{visible:".disclaimer-opened #disclaimer-cookies"}],optIn:[{waitForThenClick:"#disclaimer-accept_cookies"}],optOut:[{waitForThenClick:"#disclaimer-reject_cookies"}]},{name:"Yahoo",runContext:{urlPattern:"^https://consent\\.yahoo\\.com/v2/"},prehideSelectors:["#reject-all"],detectCmp:[{exists:"#consent-page"}],detectPopup:[{visible:"#consent-page"}],optIn:[{waitForThenClick:"#consent-page button[value=agree]"}],optOut:[{waitForThenClick:"#consent-page button[value=reject]"}]},{name:"youporn.com",cosmetic:!0,prehideSelectors:[".euCookieModal, #js_euCookieModal"],detectCmp:[{exists:".euCookieModal"}],detectPopup:[{exists:".euCookieModal, #js_euCookieModal"}],optIn:[{click:'button[name="user_acceptCookie"]'}],optOut:[{hide:".euCookieModal"}]},{name:"youtube-desktop",prehideSelectors:["tp-yt-iron-overlay-backdrop.opened","ytd-consent-bump-v2-lightbox"],detectCmp:[{exists:"ytd-consent-bump-v2-lightbox tp-yt-paper-dialog"},{exists:'ytd-consent-bump-v2-lightbox tp-yt-paper-dialog a[href^="https://consent.youtube.com/"]'}],detectPopup:[{visible:"ytd-consent-bump-v2-lightbox tp-yt-paper-dialog"}],optIn:[{waitForThenClick:"ytd-consent-bump-v2-lightbox .eom-buttons .eom-button-row:first-child ytd-button-renderer:last-child #button,ytd-consent-bump-v2-lightbox .eom-buttons .eom-button-row:first-child ytd-button-renderer:last-child button"},{wait:500}],optOut:[{waitForThenClick:"ytd-consent-bump-v2-lightbox .eom-buttons .eom-button-row:first-child ytd-button-renderer:first-child #button,ytd-consent-bump-v2-lightbox .eom-buttons .eom-button-row:first-child ytd-button-renderer:first-child button"},{wait:500}],test:[{wait:500},{eval:"EVAL_YOUTUBE_DESKTOP_0"}]},{name:"youtube-mobile",prehideSelectors:[".consent-bump-v2-lightbox"],detectCmp:[{exists:"ytm-consent-bump-v2-renderer"}],detectPopup:[{visible:"ytm-consent-bump-v2-renderer"}],optIn:[{waitForThenClick:"ytm-consent-bump-v2-renderer .privacy-terms + .one-col-dialog-buttons c3-material-button:first-child button, ytm-consent-bump-v2-renderer .privacy-terms + .one-col-dialog-buttons ytm-button-renderer:first-child button"},{wait:500}],optOut:[{waitForThenClick:"ytm-consent-bump-v2-renderer .privacy-terms + .one-col-dialog-buttons c3-material-button:nth-child(2) button, ytm-consent-bump-v2-renderer .privacy-terms + .one-col-dialog-buttons ytm-button-renderer:nth-child(2) button"},{wait:500}],test:[{wait:500},{eval:"EVAL_YOUTUBE_MOBILE_0"}]},{name:"zdf",prehideSelectors:["#zdf-cmp-banner-sdk"],detectCmp:[{exists:"#zdf-cmp-banner-sdk"}],detectPopup:[{visible:"#zdf-cmp-main.zdf-cmp-show"}],optIn:[{waitForThenClick:"#zdf-cmp-main #zdf-cmp-accept-btn"}],optOut:[{waitForThenClick:"#zdf-cmp-main #zdf-cmp-deny-btn"}],test:[]}],A={"didomi.io":{detectors:[{presentMatcher:{target:{selector:"#didomi-host, #didomi-notice"},type:"css"},showingMatcher:{target:{selector:"body.didomi-popup-open, .didomi-notice-banner"},type:"css"}}],methods:[{action:{target:{selector:".didomi-popup-notice-buttons .didomi-button:not(.didomi-button-highlight), .didomi-notice-banner .didomi-learn-more-button"},type:"click"},name:"OPEN_OPTIONS"},{action:{actions:[{retries:50,target:{selector:"#didomi-purpose-cookies"},type:"waitcss",waitTime:50},{consents:[{description:"Share (everything) with others",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-share_whith_others]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-share_whith_others]:last-child"},type:"click"},type:"X"},{description:"Information storage and access",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-cookies]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-cookies]:last-child"},type:"click"},type:"D"},{description:"Content selection, offers and marketing",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-CL-T1Rgm7]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-CL-T1Rgm7]:last-child"},type:"click"},type:"E"},{description:"Analytics",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-analytics]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-analytics]:last-child"},type:"click"},type:"B"},{description:"Analytics",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-M9NRHJe3G]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-M9NRHJe3G]:last-child"},type:"click"},type:"B"},{description:"Ad and content selection",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-advertising_personalization]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-advertising_personalization]:last-child"},type:"click"},type:"F"},{description:"Ad and content selection",falseAction:{parent:{childFilter:{target:{selector:"#didomi-purpose-pub-ciblee"}},selector:".didomi-consent-popup-data-processing, .didomi-components-accordion-label-container"},target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-pub-ciblee]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-pub-ciblee]:last-child"},type:"click"},type:"F"},{description:"Ad and content selection - basics",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-q4zlJqdcD]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-q4zlJqdcD]:last-child"},type:"click"},type:"F"},{description:"Ad and content selection - partners and subsidiaries",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-partenaire-cAsDe8jC]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-partenaire-cAsDe8jC]:last-child"},type:"click"},type:"F"},{description:"Ad and content selection - social networks",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-p4em9a8m]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-p4em9a8m]:last-child"},type:"click"},type:"F"},{description:"Ad and content selection - others",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-autres-pub]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-autres-pub]:last-child"},type:"click"},type:"F"},{description:"Social networks",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-reseauxsociaux]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-reseauxsociaux]:last-child"},type:"click"},type:"A"},{description:"Social networks",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-social_media]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-social_media]:last-child"},type:"click"},type:"A"},{description:"Content selection",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-content_personalization]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-content_personalization]:last-child"},type:"click"},type:"E"},{description:"Ad delivery",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-ad_delivery]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-ad_delivery]:last-child"},type:"click"},type:"F"}],type:"consent"},{action:{consents:[{matcher:{childFilter:{target:{selector:":not(.didomi-components-radio__option--selected)"}},type:"css"},trueAction:{target:{selector:":nth-child(2)"},type:"click"},falseAction:{target:{selector:":first-child"},type:"click"},type:"X"}],type:"consent"},target:{selector:".didomi-components-radio"},type:"foreach"}],type:"list"},name:"DO_CONSENT"},{action:{parent:{selector:".didomi-consent-popup-footer .didomi-consent-popup-actions"},target:{selector:".didomi-components-button:first-child"},type:"click"},name:"SAVE_CONSENT"}]},oil:{detectors:[{presentMatcher:{target:{selector:".as-oil-content-overlay"},type:"css"},showingMatcher:{target:{selector:".as-oil-content-overlay"},type:"css"}}],methods:[{action:{actions:[{target:{selector:".as-js-advanced-settings"},type:"click"},{retries:"10",target:{selector:".as-oil-cpc__purpose-container"},type:"waitcss",waitTime:"250"}],type:"list"},name:"OPEN_OPTIONS"},{action:{actions:[{consents:[{matcher:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Information storage and access","Opbevaring af og adgang til oplysninger på din enhed"]},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Information storage and access","Opbevaring af og adgang til oplysninger på din enhed"]},target:{selector:".as-oil-cpc__switch"},type:"click"},type:"D"},{matcher:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Personlige annoncer","Personalisation"]},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Personlige annoncer","Personalisation"]},target:{selector:".as-oil-cpc__switch"},type:"click"},type:"E"},{matcher:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Annoncevalg, levering og rapportering","Ad selection, delivery, reporting"]},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Annoncevalg, levering og rapportering","Ad selection, delivery, reporting"]},target:{selector:".as-oil-cpc__switch"},type:"click"},type:"F"},{matcher:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Personalisering af indhold","Content selection, delivery, reporting"]},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Personalisering af indhold","Content selection, delivery, reporting"]},target:{selector:".as-oil-cpc__switch"},type:"click"},type:"E"},{matcher:{parent:{childFilter:{target:{selector:".as-oil-cpc__purpose-header",textFilter:["Måling","Measurement"]}},selector:".as-oil-cpc__purpose-container"},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{childFilter:{target:{selector:".as-oil-cpc__purpose-header",textFilter:["Måling","Measurement"]}},selector:".as-oil-cpc__purpose-container"},target:{selector:".as-oil-cpc__switch"},type:"click"},type:"B"},{matcher:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:"Google"},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:"Google"},target:{selector:".as-oil-cpc__switch"},type:"click"},type:"F"}],type:"consent"}],type:"list"},name:"DO_CONSENT"},{action:{target:{selector:".as-oil__btn-optin"},type:"click"},name:"SAVE_CONSENT"},{action:{target:{selector:"div.as-oil"},type:"hide"},name:"HIDE_CMP"}]},optanon:{detectors:[{presentMatcher:{target:{selector:"#optanon-menu, .optanon-alert-box-wrapper"},type:"css"},showingMatcher:{target:{displayFilter:!0,selector:".optanon-alert-box-wrapper"},type:"css"}}],methods:[{action:{actions:[{target:{selector:".optanon-alert-box-wrapper .optanon-toggle-display, a[onclick*='OneTrust.ToggleInfoDisplay()'], a[onclick*='Optanon.ToggleInfoDisplay()']"},type:"click"}],type:"list"},name:"OPEN_OPTIONS"},{action:{actions:[{target:{selector:".preference-menu-item #Your-privacy"},type:"click"},{target:{selector:"#optanon-vendor-consent-text"},type:"click"},{action:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"X"}],type:"consent"},target:{selector:"#optanon-vendor-consent-list .vendor-item"},type:"foreach"},{target:{selector:".vendor-consent-back-link"},type:"click"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-performance"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-performance"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"B"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-functional"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-functional"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"E"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-advertising"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-advertising"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"F"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-social"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-social"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"B"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Social Media Cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Social Media Cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"B"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Personalisation"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Personalisation"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"E"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Site monitoring cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Site monitoring cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"B"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Third party privacy-enhanced content"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Third party privacy-enhanced content"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"X"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Performance & Advertising Cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Performance & Advertising Cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"F"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Information storage and access"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Information storage and access"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"D"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Ad selection, delivery, reporting"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Ad selection, delivery, reporting"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"F"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Content selection, delivery, reporting"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Content selection, delivery, reporting"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"E"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Measurement"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Measurement"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"B"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Recommended Cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Recommended Cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"X"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Unclassified Cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Unclassified Cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"X"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Analytical Cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Analytical Cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"B"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Marketing Cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Marketing Cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"F"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Personalization"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Personalization"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"E"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Ad Selection, Delivery & Reporting"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Ad Selection, Delivery & Reporting"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"F"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Content Selection, Delivery & Reporting"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Content Selection, Delivery & Reporting"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"E"}],type:"consent"}],type:"list"},type:"ifcss"}],type:"list"},name:"DO_CONSENT"},{action:{parent:{selector:".optanon-save-settings-button"},target:{selector:".optanon-white-button-middle"},type:"click"},name:"SAVE_CONSENT"},{action:{actions:[{target:{selector:"#optanon-popup-wrapper"},type:"hide"},{target:{selector:"#optanon-popup-bg"},type:"hide"},{target:{selector:".optanon-alert-box-wrapper"},type:"hide"}],type:"list"},name:"HIDE_CMP"}]},quantcast2:{detectors:[{presentMatcher:{target:{selector:"[data-tracking-opt-in-overlay]"},type:"css"},showingMatcher:{target:{selector:"[data-tracking-opt-in-overlay] [data-tracking-opt-in-learn-more]"},type:"css"}}],methods:[{action:{target:{selector:"[data-tracking-opt-in-overlay] [data-tracking-opt-in-learn-more]"},type:"click"},name:"OPEN_OPTIONS"},{action:{actions:[{type:"wait",waitTime:500},{action:{actions:[{target:{selector:"div",textFilter:["Information storage and access"]},trueAction:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"D"}],type:"consent"},type:"ifcss"},{target:{selector:"div",textFilter:["Personalization"]},trueAction:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"F"}],type:"consent"},type:"ifcss"},{target:{selector:"div",textFilter:["Ad selection, delivery, reporting"]},trueAction:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"F"}],type:"consent"},type:"ifcss"},{target:{selector:"div",textFilter:["Content selection, delivery, reporting"]},trueAction:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"E"}],type:"consent"},type:"ifcss"},{target:{selector:"div",textFilter:["Measurement"]},trueAction:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"B"}],type:"consent"},type:"ifcss"},{target:{selector:"div",textFilter:["Other Partners"]},trueAction:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"X"}],type:"consent"},type:"ifcss"}],type:"list"},parent:{childFilter:{target:{selector:"input"}},selector:"[data-tracking-opt-in-overlay] > div > div"},target:{childFilter:{target:{selector:"input"}},selector:":scope > div"},type:"foreach"}],type:"list"},name:"DO_CONSENT"},{action:{target:{selector:"[data-tracking-opt-in-overlay] [data-tracking-opt-in-save]"},type:"click"},name:"SAVE_CONSENT"}]},springer:{detectors:[{presentMatcher:{parent:null,target:{selector:".cmp-app_gdpr"},type:"css"},showingMatcher:{parent:null,target:{displayFilter:!0,selector:".cmp-popup_popup"},type:"css"}}],methods:[{action:{actions:[{target:{selector:".cmp-intro_rejectAll"},type:"click"},{type:"wait",waitTime:250},{target:{selector:".cmp-purposes_purposeItem:not(.cmp-purposes_selectedPurpose)"},type:"click"}],type:"list"},name:"OPEN_OPTIONS"},{action:{consents:[{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Przechowywanie informacji na urządzeniu lub dostęp do nich",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Przechowywanie informacji na urządzeniu lub dostęp do nich",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"D"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Wybór podstawowych reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Wybór podstawowych reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"F"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Tworzenie profilu spersonalizowanych reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Tworzenie profilu spersonalizowanych reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"F"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Wybór spersonalizowanych reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Wybór spersonalizowanych reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"E"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Tworzenie profilu spersonalizowanych treści",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Tworzenie profilu spersonalizowanych treści",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"E"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Wybór spersonalizowanych treści",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Wybór spersonalizowanych treści",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"B"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Pomiar wydajności reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Pomiar wydajności reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"B"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Pomiar wydajności treści",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Pomiar wydajności treści",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"B"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Stosowanie badań rynkowych w celu generowania opinii odbiorców",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Stosowanie badań rynkowych w celu generowania opinii odbiorców",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"X"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Opracowywanie i ulepszanie produktów",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Opracowywanie i ulepszanie produktów",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"X"}],type:"consent"},name:"DO_CONSENT"},{action:{target:{selector:".cmp-details_save"},type:"click"},name:"SAVE_CONSENT"}]},wordpressgdpr:{detectors:[{presentMatcher:{parent:null,target:{selector:".wpgdprc-consent-bar"},type:"css"},showingMatcher:{parent:null,target:{displayFilter:!0,selector:".wpgdprc-consent-bar"},type:"css"}}],methods:[{action:{parent:null,target:{selector:".wpgdprc-consent-bar .wpgdprc-consent-bar__settings",textFilter:null},type:"click"},name:"OPEN_OPTIONS"},{action:{actions:[{target:{selector:".wpgdprc-consent-modal .wpgdprc-button",textFilter:"Eyeota"},type:"click"},{consents:[{description:"Eyeota Cookies",matcher:{parent:{selector:".wpgdprc-consent-modal__description",textFilter:"Eyeota"},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".wpgdprc-consent-modal__description",textFilter:"Eyeota"},target:{selector:"label"},type:"click"},type:"X"}],type:"consent"},{target:{selector:".wpgdprc-consent-modal .wpgdprc-button",textFilter:"Advertising"},type:"click"},{consents:[{description:"Advertising Cookies",matcher:{parent:{selector:".wpgdprc-consent-modal__description",textFilter:"Advertising"},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".wpgdprc-consent-modal__description",textFilter:"Advertising"},target:{selector:"label"},type:"click"},type:"F"}],type:"consent"}],type:"list"},name:"DO_CONSENT"},{action:{parent:null,target:{selector:".wpgdprc-button",textFilter:"Save my settings"},type:"click"},name:"SAVE_CONSENT"}]}},E={autoconsent:f,consentomatic:A},x=Object.freeze({__proto__:null,autoconsent:f,consentomatic:A,default:E});const O=new class{constructor(e,t=null,o=null){if(this.id=a(),this.rules=[],this.foundCmp=null,this.state={lifecycle:"loading",prehideOn:!1,findCmpAttempts:0,detectedCmps:[],detectedPopups:[],selfTest:null},r.sendContentMessage=e,this.sendContentMessage=e,this.rules=[],this.updateState({lifecycle:"loading"}),this.addDynamicRules(),t)this.initialize(t,o);else{o&&this.parseDeclarativeRules(o);e({type:"init",url:window.location.href}),this.updateState({lifecycle:"waitingForInitResponse"})}this.domActions=new v(this)}initialize(e,t){const o=g(e);if(o.logs.lifecycle&&console.log("autoconsent init",window.location.href),this.config=o,o.enabled){if(t&&this.parseDeclarativeRules(t),this.rules=function(e,t){return e.filter((e=>(!t.disabledCmps||!t.disabledCmps.includes(e.name))&&(t.enableCosmeticRules||!e.isCosmetic)))}(this.rules,o),e.enablePrehide)if(document.documentElement)this.prehideElements();else{const e=()=>{window.removeEventListener("DOMContentLoaded",e),this.prehideElements()};window.addEventListener("DOMContentLoaded",e)}if("loading"===document.readyState){const e=()=>{window.removeEventListener("DOMContentLoaded",e),this.start()};window.addEventListener("DOMContentLoaded",e)}else this.start();this.updateState({lifecycle:"initialized"})}else o.logs.lifecycle&&console.log("autoconsent is disabled")}addDynamicRules(){C.forEach((e=>{this.rules.push(new e(this))}))}parseDeclarativeRules(e){Object.keys(e.consentomatic).forEach((t=>{this.addConsentomaticCMP(t,e.consentomatic[t])})),e.autoconsent.forEach((e=>{this.addDeclarativeCMP(e)}))}addDeclarativeCMP(e){this.rules.push(new u(e,this))}addConsentomaticCMP(e,t){this.rules.push(new m(`com_${e}`,t))}start(){window.requestIdleCallback?window.requestIdleCallback((()=>this._start()),{timeout:500}):this._start()}async _start(){const e=this.config.logs;e.lifecycle&&console.log(`Detecting CMPs on ${window.location.href}`),this.updateState({lifecycle:"started"});const t=await this.findCmp(this.config.detectRetries);if(this.updateState({detectedCmps:t.map((e=>e.name))}),0===t.length)return e.lifecycle&&console.log("no CMP found",location.href),this.config.enablePrehide&&this.undoPrehide(),this.updateState({lifecycle:"nothingDetected"}),!1;this.updateState({lifecycle:"cmpDetected"});const o=[],c=[];for(const e of t)e.isCosmetic?c.push(e):o.push(e);let i=!1,n=await this.detectPopups(o,(async e=>{i=await this.handlePopup(e)}));if(0===n.length&&(n=await this.detectPopups(c,(async e=>{i=await this.handlePopup(e)}))),0===n.length)return e.lifecycle&&console.log("no popup found"),this.config.enablePrehide&&this.undoPrehide(),!1;if(n.length>1){const t={msg:"Found multiple CMPs, check the detection rules.",cmps:n.map((e=>e.name))};e.errors&&console.warn(t.msg,t.cmps),this.sendContentMessage({type:"autoconsentError",details:t})}return i}async findCmp(e){const t=this.config.logs;this.updateState({findCmpAttempts:this.state.findCmpAttempts+1});const o=[];for(const e of this.rules)try{if(!e.checkRunContext())continue;await e.detectCmp()&&(t.lifecycle&&console.log(`Found CMP: ${e.name} ${window.location.href}`),this.sendContentMessage({type:"cmpDetected",url:location.href,cmp:e.name}),o.push(e))}catch(o){t.errors&&console.warn(`error detecting ${e.name}`,o)}return 0===o.length&&e>0?(await this.domActions.wait(500),this.findCmp(e-1)):o}async detectPopup(e){if(await this.waitForPopup(e).catch((t=>(this.config.logs.errors&&console.warn(`error waiting for a popup for ${e.name}`,t),!1))))return this.updateState({detectedPopups:this.state.detectedPopups.concat([e.name])}),this.sendContentMessage({type:"popupFound",cmp:e.name,url:location.href}),e;throw new Error("Popup is not shown")}async detectPopups(e,t){const o=e.map((e=>this.detectPopup(e)));await Promise.any(o).then((e=>{t(e)})).catch((()=>null));const c=await Promise.allSettled(o),i=[];for(const e of c)"fulfilled"===e.status&&i.push(e.value);return i}async handlePopup(e){return this.updateState({lifecycle:"openPopupDetected"}),this.config.enablePrehide&&!this.state.prehideOn&&this.prehideElements(),this.foundCmp=e,"optOut"===this.config.autoAction?await this.doOptOut():"optIn"===this.config.autoAction?await this.doOptIn():(this.config.logs.lifecycle&&console.log("waiting for opt-out signal...",location.href),!0)}async doOptOut(){const e=this.config.logs;let t;return this.updateState({lifecycle:"runningOptOut"}),this.foundCmp?(e.lifecycle&&console.log(`CMP ${this.foundCmp.name}: opt out on ${window.location.href}`),t=await this.foundCmp.optOut(),e.lifecycle&&console.log(`${this.foundCmp.name}: opt out result ${t}`)):(e.errors&&console.log("no CMP to opt out"),t=!1),this.config.enablePrehide&&this.undoPrehide(),this.sendContentMessage({type:"optOutResult",cmp:this.foundCmp?this.foundCmp.name:"none",result:t,scheduleSelfTest:this.foundCmp&&this.foundCmp.hasSelfTest,url:location.href}),t&&!this.foundCmp.isIntermediate?(this.sendContentMessage({type:"autoconsentDone",cmp:this.foundCmp.name,isCosmetic:this.foundCmp.isCosmetic,url:location.href}),this.updateState({lifecycle:"done"})):this.updateState({lifecycle:t?"optOutSucceeded":"optOutFailed"}),t}async doOptIn(){const e=this.config.logs;let t;return this.updateState({lifecycle:"runningOptIn"}),this.foundCmp?(e.lifecycle&&console.log(`CMP ${this.foundCmp.name}: opt in on ${window.location.href}`),t=await this.foundCmp.optIn(),e.lifecycle&&console.log(`${this.foundCmp.name}: opt in result ${t}`)):(e.errors&&console.log("no CMP to opt in"),t=!1),this.config.enablePrehide&&this.undoPrehide(),this.sendContentMessage({type:"optInResult",cmp:this.foundCmp?this.foundCmp.name:"none",result:t,scheduleSelfTest:!1,url:location.href}),t&&!this.foundCmp.isIntermediate?(this.sendContentMessage({type:"autoconsentDone",cmp:this.foundCmp.name,isCosmetic:this.foundCmp.isCosmetic,url:location.href}),this.updateState({lifecycle:"done"})):this.updateState({lifecycle:t?"optInSucceeded":"optInFailed"}),t}async doSelfTest(){const e=this.config.logs;let t;return this.foundCmp?(e.lifecycle&&console.log(`CMP ${this.foundCmp.name}: self-test on ${window.location.href}`),t=await this.foundCmp.test()):(e.errors&&console.log("no CMP to self test"),t=!1),this.sendContentMessage({type:"selfTestResult",cmp:this.foundCmp?this.foundCmp.name:"none",result:t,url:location.href}),this.updateState({selfTest:t}),t}async waitForPopup(e,t=5,o=500){const c=this.config.logs;c.lifecycle&&console.log("checking if popup is open...",e.name);const i=await e.detectPopup().catch((t=>(c.errors&&console.warn(`error detecting popup for ${e.name}`,t),!1)));return!i&&t>0?(await this.domActions.wait(o),this.waitForPopup(e,t-1,o)):(c.lifecycle&&console.log(e.name,"popup is "+(i?"open":"not open")),i)}prehideElements(){const e=this.config.logs,t=this.rules.reduce(((e,t)=>t.prehideSelectors?[...e,...t.prehideSelectors]:e),["#didomi-popup,.didomi-popup-container,.didomi-popup-notice,.didomi-consent-popup-preferences,#didomi-notice,.didomi-popup-backdrop,.didomi-screen-medium"]);return this.updateState({prehideOn:!0}),setTimeout((()=>{this.config.enablePrehide&&this.state.prehideOn&&!["runningOptOut","runningOptIn"].includes(this.state.lifecycle)&&(e.lifecycle&&console.log("Process is taking too long, unhiding elements"),this.undoPrehide())}),this.config.prehideTimeout||2e3),this.domActions.prehide(t.join(","))}undoPrehide(){return this.updateState({prehideOn:!1}),this.domActions.undoPrehide()}updateState(e){Object.assign(this.state,e),this.sendContentMessage({type:"report",instanceId:this.id,url:window.location.href,mainFrame:window.top===window.self,state:this.state})}async receiveMessageCallback(e){const t=this.config?.logs;switch(t?.messages&&console.log("received from background",e,window.location.href),e.type){case"initResp":this.initialize(e.config,e.rules);break;case"optIn":await this.doOptIn();break;case"optOut":await this.doOptOut();break;case"selfTest":await this.doSelfTest();break;case"evalResp":!function(e,t){const o=r.pending.get(e);o?(r.pending.delete(e),o.timer&&window.clearTimeout(o.timer),o.resolve(t)):console.warn("no eval #",e)}(e.id,e.result)}}}((e=>{window.webkit.messageHandlers[e.type]&&window.webkit.messageHandlers[e.type].postMessage(e).then((e=>{O.receiveMessageCallback(e)}))}),null,x);window.autoconsentMessageCallback=e=>{O.receiveMessageCallback(e)}}(); +!function(){"use strict";var e=class e{static setBase(t){e.base=t}static findElement(t,o=null,c=!1){let i=null;return i=null!=o?Array.from(o.querySelectorAll(t.selector)):null!=e.base?Array.from(e.base.querySelectorAll(t.selector)):Array.from(document.querySelectorAll(t.selector)),null!=t.textFilter&&(i=i.filter((e=>{const o=e.textContent.toLowerCase();if(Array.isArray(t.textFilter)){let e=!1;for(const c of t.textFilter)if(-1!==o.indexOf(c.toLowerCase())){e=!0;break}return e}if(null!=t.textFilter)return-1!==o.indexOf(t.textFilter.toLowerCase())}))),null!=t.styleFilters&&(i=i.filter((e=>{const o=window.getComputedStyle(e);let c=!0;for(const e of t.styleFilters){const t=o[e.option];c=e.negated?c&&t!==e.value:c&&t===e.value}return c}))),null!=t.displayFilter&&(i=i.filter((e=>t.displayFilter?0!==e.offsetHeight:0===e.offsetHeight))),null!=t.iframeFilter&&(i=i.filter((()=>t.iframeFilter?window.location!==window.parent.location:window.location===window.parent.location))),null!=t.childFilter&&(i=i.filter((o=>{const c=e.base;e.setBase(o);const i=e.find(t.childFilter);return e.setBase(c),null!=i.target}))),c?i:(i.length>1&&console.warn("Multiple possible targets: ",i,t,o),i[0])}static find(t,o=!1){const c=[];if(null!=t.parent){const i=e.findElement(t.parent,null,o);if(null!=i){if(i instanceof Array)return i.forEach((i=>{const n=e.findElement(t.target,i,o);n instanceof Array?n.forEach((e=>{c.push({parent:i,target:e})})):c.push({parent:i,target:n})})),c;{const n=e.findElement(t.target,i,o);n instanceof Array?n.forEach((e=>{c.push({parent:i,target:e})})):c.push({parent:i,target:n})}}}else{const i=e.findElement(t.target,null,o);i instanceof Array?i.forEach((e=>{c.push({parent:null,target:e})})):c.push({parent:null,target:i})}return 0===c.length&&c.push({parent:null,target:null}),o?c:(1!==c.length&&console.warn("Multiple results found, even though multiple false",c),c[0])}};e.base=null;var t=e;function o(e){const o=t.find(e);return"css"===e.type?!!o.target:"checkbox"===e.type?!!o.target&&o.target.checked:void 0}async function c(e,a){switch(e.type){case"click":return async function(e){const o=t.find(e);null!=o.target&&o.target.click();return n(i)}(e);case"list":return async function(e,t){for(const o of e.actions)await c(o,t)}(e,a);case"consent":return async function(e,t){for(const i of e.consents){const e=-1!==t.indexOf(i.type);if(i.matcher&&i.toggleAction){o(i.matcher)!==e&&await c(i.toggleAction)}else e?await c(i.trueAction):await c(i.falseAction)}}(e,a);case"ifcss":return async function(e,o){const i=t.find(e);i.target?e.falseAction&&await c(e.falseAction,o):e.trueAction&&await c(e.trueAction,o)}(e,a);case"waitcss":return async function(e){await new Promise((o=>{let c=e.retries||10;const i=e.waitTime||250,n=()=>{const a=t.find(e);(e.negated&&a.target||!e.negated&&!a.target)&&c>0?(c-=1,setTimeout(n,i)):o()};n()}))}(e);case"foreach":return async function(e,o){const i=t.find(e,!0),n=t.base;for(const n of i)n.target&&(t.setBase(n.target),await c(e.action,o));t.setBase(n)}(e,a);case"hide":return async function(e){const o=t.find(e);o.target&&o.target.classList.add("Autoconsent-Hidden")}(e);case"slide":return async function(e){const o=t.find(e),c=t.find(e.dragTarget);if(o.target){const e=o.target.getBoundingClientRect(),t=c.target.getBoundingClientRect();let i=t.top-e.top,n=t.left-e.left;"y"===this.config.axis.toLowerCase()&&(n=0),"x"===this.config.axis.toLowerCase()&&(i=0);const a=window.screenX+e.left+e.width/2,s=window.screenY+e.top+e.height/2,r=e.left+e.width/2,l=e.top+e.height/2,p=document.createEvent("MouseEvents");p.initMouseEvent("mousedown",!0,!0,window,0,a,s,r,l,!1,!1,!1,!1,0,o.target);const d=document.createEvent("MouseEvents");d.initMouseEvent("mousemove",!0,!0,window,0,a+n,s+i,r+n,l+i,!1,!1,!1,!1,0,o.target);const u=document.createEvent("MouseEvents");u.initMouseEvent("mouseup",!0,!0,window,0,a+n,s+i,r+n,l+i,!1,!1,!1,!1,0,o.target),o.target.dispatchEvent(p),await this.waitTimeout(10),o.target.dispatchEvent(d),await this.waitTimeout(10),o.target.dispatchEvent(u)}}(e);case"close":return async function(){window.close()}();case"wait":return async function(e){await n(e.waitTime)}(e);case"eval":return async function(e){return console.log("eval!",e.code),new Promise((t=>{try{e.async?(window.eval(e.code),setTimeout((()=>{t(window.eval("window.__consentCheckResult"))}),e.timeout||250)):t(window.eval(e.code))}catch(o){console.warn("eval error",o,e.code),t(!1)}}))}(e);default:throw"Unknown action type: "+e.type}}var i=0;function n(e){return new Promise((t=>{setTimeout((()=>{t()}),e)}))}function a(){return crypto&&void 0!==crypto.randomUUID?crypto.randomUUID():Math.random().toString()}var s=class{constructor(e,t=1e3){this.id=e,this.promise=new Promise(((e,t)=>{this.resolve=e,this.reject=t})),this.timer=window.setTimeout((()=>{this.reject(new Error("timeout"))}),t)}},r={pending:new Map,sendContentMessage:null};var l={EVAL_0:()=>console.log(1),EVAL_CONSENTMANAGER_1:()=>window.__cmp&&"object"==typeof __cmp("getCMPData"),EVAL_CONSENTMANAGER_2:()=>!__cmp("consentStatus").userChoiceExists,EVAL_CONSENTMANAGER_3:()=>__cmp("setConsent",0),EVAL_CONSENTMANAGER_4:()=>__cmp("setConsent",1),EVAL_CONSENTMANAGER_5:()=>__cmp("consentStatus").userChoiceExists,EVAL_COOKIEBOT_1:()=>!!window.Cookiebot,EVAL_COOKIEBOT_2:()=>!window.Cookiebot.hasResponse&&!0===window.Cookiebot.dialog?.visible,EVAL_COOKIEBOT_3:()=>window.Cookiebot.withdraw()||!0,EVAL_COOKIEBOT_4:()=>window.Cookiebot.hide()||!0,EVAL_COOKIEBOT_5:()=>!0===window.Cookiebot.declined,EVAL_KLARO_1:()=>{const e=globalThis.klaroConfig||globalThis.klaro?.getManager&&globalThis.klaro.getManager().config;if(!e)return!0;const t=(e.services||e.apps).filter((e=>!e.required)).map((e=>e.name));if(klaro&&klaro.getManager){const e=klaro.getManager();return t.every((t=>!e.consents[t]))}if(klaroConfig&&"cookie"===klaroConfig.storageMethod){const e=klaroConfig.cookieName||klaroConfig.storageName,o=JSON.parse(decodeURIComponent(document.cookie.split(";").find((t=>t.trim().startsWith(e))).split("=")[1]));return Object.keys(o).filter((e=>t.includes(e))).every((e=>!1===o[e]))}},EVAL_ONETRUST_1:()=>window.OnetrustActiveGroups.split(",").filter((e=>e.length>0)).length<=1,EVAL_TRUSTARC_TOP:()=>window&&window.truste&&"0"===window.truste.eu.bindMap.prefCookie,EVAL_ADROLL_0:()=>!document.cookie.includes("__adroll_fpc"),EVAL_ALMACMP_0:()=>document.cookie.includes('"name":"Google","consent":false'),EVAL_AFFINITY_SERIF_COM_0:()=>document.cookie.includes("serif_manage_cookies_viewed")&&!document.cookie.includes("serif_allow_analytics"),EVAL_ARBEITSAGENTUR_TEST:()=>document.cookie.includes("cookie_consent=denied"),EVAL_AXEPTIO_0:()=>document.cookie.includes("axeptio_authorized_vendors=%2C%2C"),EVAL_BAHN_TEST:()=>1===utag.gdpr.getSelectedCategories().length,EVAL_BING_0:()=>document.cookie.includes("AL=0")&&document.cookie.includes("AD=0")&&document.cookie.includes("SM=0"),EVAL_BLOCKSY_0:()=>document.cookie.includes("blocksy_cookies_consent_accepted=no"),EVAL_BORLABS_0:()=>!JSON.parse(decodeURIComponent(document.cookie.split(";").find((e=>-1!==e.indexOf("borlabs-cookie"))).split("=",2)[1])).consents.statistics,EVAL_BUNDESREGIERUNG_DE_0:()=>document.cookie.match("cookie-allow-tracking=0"),EVAL_CANVA_0:()=>!document.cookie.includes("gtm_fpc_engagement_event"),EVAL_CC_BANNER2_0:()=>!!document.cookie.match(/sncc=[^;]+D%3Dtrue/),EVAL_CLICKIO_0:()=>document.cookie.includes("__lxG__consent__v2_daisybit="),EVAL_CLINCH_0:()=>document.cookie.includes("ctc_rejected=1"),EVAL_COOKIECONSENT2_TEST:()=>document.cookie.includes("cc_cookie="),EVAL_COOKIECONSENT3_TEST:()=>document.cookie.includes("cc_cookie="),EVAL_COINBASE_0:()=>JSON.parse(decodeURIComponent(document.cookie.match(/cm_(eu|default)_preferences=([0-9a-zA-Z\\{\\}\\[\\]%:]*);?/)[2])).consent.length<=1,EVAL_COMPLIANZ_BANNER_0:()=>document.cookie.includes("cmplz_banner-status=dismissed"),EVAL_COOKIE_LAW_INFO_0:()=>CLI.disableAllCookies()||CLI.reject_close()||!0,EVAL_COOKIE_LAW_INFO_1:()=>-1===document.cookie.indexOf("cookielawinfo-checkbox-non-necessary=yes"),EVAL_COOKIE_LAW_INFO_DETECT:()=>!!window.CLI,EVAL_COOKIE_MANAGER_POPUP_0:()=>!1===JSON.parse(document.cookie.split(";").find((e=>e.trim().startsWith("CookieLevel"))).split("=")[1]).social,EVAL_COOKIEALERT_0:()=>document.querySelector("body").removeAttribute("style")||!0,EVAL_COOKIEALERT_1:()=>document.querySelector("body").removeAttribute("style")||!0,EVAL_COOKIEALERT_2:()=>!0===window.CookieConsent.declined,EVAL_COOKIEFIRST_0:()=>{return!1===(e=JSON.parse(decodeURIComponent(document.cookie.split(";").find((e=>-1!==e.indexOf("cookiefirst"))).trim()).split("=")[1])).performance&&!1===e.functional&&!1===e.advertising;var e},EVAL_COOKIEFIRST_1:()=>document.querySelectorAll("button[data-cookiefirst-accent-color=true][role=checkbox]:not([disabled])").forEach((e=>"true"==e.getAttribute("aria-checked")&&e.click()))||!0,EVAL_COOKIEINFORMATION_0:()=>CookieInformation.declineAllCategories()||!0,EVAL_COOKIEINFORMATION_1:()=>CookieInformation.submitAllCategories()||!0,EVAL_COOKIEINFORMATION_2:()=>document.cookie.includes("CookieInformationConsent="),EVAL_COOKIEYES_0:()=>document.cookie.includes("advertisement:no"),EVAL_DAILYMOTION_0:()=>!!document.cookie.match("dm-euconsent-v2"),EVAL_DNDBEYOND_TEST:()=>document.cookie.includes("cookie-consent=denied"),EVAL_DSGVO_0:()=>!document.cookie.includes("sp_dsgvo_cookie_settings"),EVAL_DUNELM_0:()=>document.cookie.includes("cc_functional=0")&&document.cookie.includes("cc_targeting=0"),EVAL_ETSY_0:()=>document.querySelectorAll(".gdpr-overlay-body input").forEach((e=>{e.checked=!1}))||!0,EVAL_ETSY_1:()=>document.querySelector(".gdpr-overlay-view button[data-wt-overlay-close]").click()||!0,EVAL_EU_COOKIE_COMPLIANCE_0:()=>-1===document.cookie.indexOf("cookie-agreed=2"),EVAL_EU_COOKIE_LAW_0:()=>!document.cookie.includes("euCookie"),EVAL_EZOIC_0:()=>ezCMP.handleAcceptAllClick(),EVAL_EZOIC_1:()=>!!document.cookie.match(/ez-consent-tcf/),EVAL_GOOGLE_0:()=>!!document.cookie.match(/SOCS=CAE/),EVAL_HEMA_TEST_0:()=>document.cookie.includes("cookies_rejected=1"),EVAL_IUBENDA_0:()=>document.querySelectorAll(".purposes-item input[type=checkbox]:not([disabled])").forEach((e=>{e.checked&&e.click()}))||!0,EVAL_IUBENDA_1:()=>!!document.cookie.match(/_iub_cs-\d+=/),EVAL_IWINK_TEST:()=>document.cookie.includes("cookie_permission_granted=no"),EVAL_JQUERY_COOKIEBAR_0:()=>!document.cookie.includes("cookies-state=accepted"),EVAL_MEDIAVINE_0:()=>document.querySelectorAll('[data-name="mediavine-gdpr-cmp"] input[type=checkbox]').forEach((e=>e.checked&&e.click()))||!0,EVAL_MICROSOFT_0:()=>Array.from(document.querySelectorAll("div > button")).filter((e=>e.innerText.match("Reject|Ablehnen")))[0].click()||!0,EVAL_MICROSOFT_1:()=>Array.from(document.querySelectorAll("div > button")).filter((e=>e.innerText.match("Accept|Annehmen")))[0].click()||!0,EVAL_MICROSOFT_2:()=>!!document.cookie.match("MSCC|GHCC"),EVAL_MOOVE_0:()=>document.querySelectorAll("#moove_gdpr_cookie_modal input").forEach((e=>{e.disabled||"moove_gdpr_strict_cookies"===e.name||(e.checked=!1)}))||!0,EVAL_ONENINETWO_0:()=>document.cookie.includes("CC_ADVERTISING=NO")&&document.cookie.includes("CC_ANALYTICS=NO"),EVAL_OPERA_0:()=>document.cookie.includes("cookie_consent_essential=true")&&!document.cookie.includes("cookie_consent_marketing=true"),EVAL_PAYPAL_0:()=>!0===document.cookie.includes("cookie_prefs"),EVAL_PRIMEBOX_0:()=>!document.cookie.includes("cb-enabled=accepted"),EVAL_PUBTECH_0:()=>document.cookie.includes("euconsent-v2")&&(document.cookie.match(/.YAAAAAAAAAAA/)||document.cookie.match(/.aAAAAAAAAAAA/)||document.cookie.match(/.YAAACFgAAAAA/)),EVAL_REDDIT_0:()=>document.cookie.includes("eu_cookie={%22opted%22:true%2C%22nonessential%22:false}"),EVAL_SIBBO_0:()=>!!window.localStorage.getItem("euconsent-v2"),EVAL_SIRDATA_UNBLOCK_SCROLL:()=>(document.documentElement.classList.forEach((e=>{e.startsWith("sd-cmp-")&&document.documentElement.classList.remove(e)})),!0),EVAL_SNIGEL_0:()=>!!document.cookie.match("snconsent"),EVAL_STEAMPOWERED_0:()=>2===JSON.parse(decodeURIComponent(document.cookie.split(";").find((e=>e.trim().startsWith("cookieSettings"))).split("=")[1])).preference_state,EVAL_SVT_TEST:()=>document.cookie.includes('cookie-consent-1={"optedIn":true,"functionality":false,"statistics":false}'),EVAL_TAKEALOT_0:()=>document.body.classList.remove("freeze")||(document.body.style="")||!0,EVAL_TARTEAUCITRON_0:()=>tarteaucitron.userInterface.respondAll(!1)||!0,EVAL_TARTEAUCITRON_1:()=>tarteaucitron.userInterface.respondAll(!0)||!0,EVAL_TARTEAUCITRON_2:()=>document.cookie.match(/tarteaucitron=[^;]*/)[0].includes("false"),EVAL_TAUNTON_TEST:()=>document.cookie.includes("taunton_user_consent_submitted=true"),EVAL_TEALIUM_0:()=>void 0!==window.utag&&"object"==typeof utag.gdpr,EVAL_TEALIUM_1:()=>utag.gdpr.setConsentValue(!1)||!0,EVAL_TEALIUM_DONOTSELL:()=>utag.gdpr.dns?.setDnsState(!1)||!0,EVAL_TEALIUM_2:()=>utag.gdpr.setConsentValue(!0)||!0,EVAL_TEALIUM_3:()=>1!==utag.gdpr.getConsentState(),EVAL_TEALIUM_DONOTSELL_CHECK:()=>1!==utag.gdpr.dns?.getDnsState(),EVAL_TESTCMP_0:()=>"button_clicked"===window.results.results[0],EVAL_TESTCMP_COSMETIC_0:()=>"banner_hidden"===window.results.results[0],EVAL_THEFREEDICTIONARY_0:()=>cmpUi.showPurposes()||cmpUi.rejectAll()||!0,EVAL_THEFREEDICTIONARY_1:()=>cmpUi.allowAll()||!0,EVAL_THEVERGE_0:()=>document.cookie.includes("_duet_gdpr_acknowledged=1"),EVAL_UBUNTU_COM_0:()=>document.cookie.includes("_cookies_accepted=essential"),EVAL_UK_COOKIE_CONSENT_0:()=>!document.cookie.includes("catAccCookies"),EVAL_USERCENTRICS_API_0:()=>"object"==typeof UC_UI,EVAL_USERCENTRICS_API_1:()=>!!UC_UI.closeCMP(),EVAL_USERCENTRICS_API_2:()=>!!UC_UI.denyAllConsents(),EVAL_USERCENTRICS_API_3:()=>!!UC_UI.acceptAllConsents(),EVAL_USERCENTRICS_API_4:()=>!!UC_UI.closeCMP(),EVAL_USERCENTRICS_API_5:()=>!0===UC_UI.areAllConsentsAccepted(),EVAL_USERCENTRICS_API_6:()=>!1===UC_UI.areAllConsentsAccepted(),EVAL_USERCENTRICS_BUTTON_0:()=>JSON.parse(localStorage.getItem("usercentrics")).consents.every((e=>e.isEssential||!e.consentStatus)),EVAL_WAITROSE_0:()=>Array.from(document.querySelectorAll("label[id$=cookies-deny-label]")).forEach((e=>e.click()))||!0,EVAL_WAITROSE_1:()=>document.cookie.includes("wtr_cookies_advertising=0")&&document.cookie.includes("wtr_cookies_analytics=0"),EVAL_WP_COOKIE_NOTICE_0:()=>document.cookie.includes("wpl_viewed_cookie=no"),EVAL_XE_TEST:()=>document.cookie.includes("xeConsentState={%22performance%22:false%2C%22marketing%22:false%2C%22compliance%22:false}"),EVAL_XING_0:()=>document.cookie.includes("userConsent=%7B%22marketing%22%3Afalse"),EVAL_YOUTUBE_DESKTOP_0:()=>!!document.cookie.match(/SOCS=CAE/),EVAL_YOUTUBE_MOBILE_0:()=>!!document.cookie.match(/SOCS=CAE/)};var p={main:!0,frame:!1,urlPattern:""},d=class{constructor(e){this.runContext=p,this.autoconsent=e}get hasSelfTest(){throw new Error("Not Implemented")}get isIntermediate(){throw new Error("Not Implemented")}get isCosmetic(){throw new Error("Not Implemented")}mainWorldEval(e){const t=l[e];if(!t)return console.warn("Snippet not found",e),Promise.resolve(!1);const o=this.autoconsent.config.logs;if(this.autoconsent.config.isMainWorld){o.evals&&console.log("inline eval:",e,t);let c=!1;try{c=!!t.call(globalThis)}catch(t){o.evals&&console.error("error evaluating rule",e,t)}return Promise.resolve(c)}const c=`(${t.toString()})()`;return o.evals&&console.log("async eval:",e,c),function(e,t){const o=a();r.sendContentMessage({type:"eval",id:o,code:e,snippetId:t});const c=new s(o);return r.pending.set(c.id,c),c.promise}(c,e).catch((t=>(o.evals&&console.error("error evaluating rule",e,t),!1)))}checkRunContext(){const e={...p,...this.runContext},t=window.top===window;return!(t&&!e.main)&&(!(!t&&!e.frame)&&!(e.urlPattern&&!window.location.href.match(e.urlPattern)))}detectCmp(){throw new Error("Not Implemented")}async detectPopup(){return!1}optOut(){throw new Error("Not Implemented")}optIn(){throw new Error("Not Implemented")}openCmp(){throw new Error("Not Implemented")}async test(){return Promise.resolve(!0)}click(e,t=!1){return this.autoconsent.domActions.click(e,t)}elementExists(e){return this.autoconsent.domActions.elementExists(e)}elementVisible(e,t){return this.autoconsent.domActions.elementVisible(e,t)}waitForElement(e,t){return this.autoconsent.domActions.waitForElement(e,t)}waitForVisible(e,t,o){return this.autoconsent.domActions.waitForVisible(e,t,o)}waitForThenClick(e,t,o){return this.autoconsent.domActions.waitForThenClick(e,t,o)}wait(e){return this.autoconsent.domActions.wait(e)}hide(e,t){return this.autoconsent.domActions.hide(e,t)}prehide(e){return this.autoconsent.domActions.prehide(e)}undoPrehide(){return this.autoconsent.domActions.undoPrehide()}querySingleReplySelector(e,t){return this.autoconsent.domActions.querySingleReplySelector(e,t)}querySelectorChain(e){return this.autoconsent.domActions.querySelectorChain(e)}elementSelector(e){return this.autoconsent.domActions.elementSelector(e)}},u=class extends d{constructor(e,t){super(t),this.rule=e,this.name=e.name,this.runContext=e.runContext||p}get hasSelfTest(){return!!this.rule.test}get isIntermediate(){return!!this.rule.intermediate}get isCosmetic(){return!!this.rule.cosmetic}get prehideSelectors(){return this.rule.prehideSelectors}async detectCmp(){return!!this.rule.detectCmp&&this._runRulesParallel(this.rule.detectCmp)}async detectPopup(){return!!this.rule.detectPopup&&this._runRulesSequentially(this.rule.detectPopup)}async optOut(){const e=this.autoconsent.config.logs;return!!this.rule.optOut&&(e.lifecycle&&console.log("Initiated optOut()",this.rule.optOut),this._runRulesSequentially(this.rule.optOut))}async optIn(){const e=this.autoconsent.config.logs;return!!this.rule.optIn&&(e.lifecycle&&console.log("Initiated optIn()",this.rule.optIn),this._runRulesSequentially(this.rule.optIn))}async openCmp(){return!!this.rule.openCmp&&this._runRulesSequentially(this.rule.openCmp)}async test(){return this.hasSelfTest?this._runRulesSequentially(this.rule.test):super.test()}async evaluateRuleStep(e){const t=[],o=this.autoconsent.config.logs;if(e.exists&&t.push(this.elementExists(e.exists)),e.visible&&t.push(this.elementVisible(e.visible,e.check)),e.eval){const o=this.mainWorldEval(e.eval);t.push(o)}if(e.waitFor&&t.push(this.waitForElement(e.waitFor,e.timeout)),e.waitForVisible&&t.push(this.waitForVisible(e.waitForVisible,e.timeout,e.check)),e.click&&t.push(this.click(e.click,e.all)),e.waitForThenClick&&t.push(this.waitForThenClick(e.waitForThenClick,e.timeout,e.all)),e.wait&&t.push(this.wait(e.wait)),e.hide&&t.push(this.hide(e.hide,e.method)),e.if){if(!e.if.exists&&!e.if.visible)return console.error("invalid conditional rule",e.if),!1;const c=await this.evaluateRuleStep(e.if);o.rulesteps&&console.log("Condition is",c),c?t.push(this._runRulesSequentially(e.then)):e.else?t.push(this._runRulesSequentially(e.else)):t.push(!0)}if(e.any){for(const t of e.any)if(await this.evaluateRuleStep(t))return!0;return!1}if(0===t.length)return o.errors&&console.warn("Unrecognized rule",e),!1;return(await Promise.all(t)).reduce(((e,t)=>e&&t),!0)}async _runRulesParallel(e){const t=e.map((e=>this.evaluateRuleStep(e)));return(await Promise.all(t)).every((e=>!!e))}async _runRulesSequentially(e){const t=this.autoconsent.config.logs;for(const o of e){t.rulesteps&&console.log("Running rule...",o);const e=await this.evaluateRuleStep(o);if(t.rulesteps&&console.log("...rule result",e),!e&&!o.optional)return!1}return!0}},m=class{constructor(e,t){this.name=e,this.config=t,this.methods=new Map,this.runContext=p,this.isCosmetic=!1,t.methods.forEach((e=>{e.action&&this.methods.set(e.name,e.action)})),this.hasSelfTest=!1}get isIntermediate(){return!1}checkRunContext(){return!0}async detectCmp(){return this.config.detectors.map((e=>o(e.presentMatcher))).some((e=>!!e))}async detectPopup(){return this.config.detectors.map((e=>o(e.showingMatcher))).some((e=>!!e))}async executeAction(e,t){return!this.methods.has(e)||c(this.methods.get(e),t)}async optOut(){return await this.executeAction("HIDE_CMP"),await this.executeAction("OPEN_OPTIONS"),await this.executeAction("HIDE_CMP"),await this.executeAction("DO_CONSENT",[]),await this.executeAction("SAVE_CONSENT"),!0}async optIn(){return await this.executeAction("HIDE_CMP"),await this.executeAction("OPEN_OPTIONS"),await this.executeAction("HIDE_CMP"),await this.executeAction("DO_CONSENT",["D","A","B","E","F","X"]),await this.executeAction("SAVE_CONSENT"),!0}async openCmp(){return await this.executeAction("HIDE_CMP"),await this.executeAction("OPEN_OPTIONS"),!0}async test(){return!0}};function h(e="autoconsent-css-rules"){const t=`style#${e}`,o=document.querySelector(t);if(o&&o instanceof HTMLStyleElement)return o;{const t=document.head||document.getElementsByTagName("head")[0]||document.documentElement,o=document.createElement("style");return o.id=e,t.appendChild(o),o}}function k(e,t,o="display"){const c=`${t} { ${"opacity"===o?"opacity: 0":"display: none"} !important; z-index: -1 !important; pointer-events: none !important; } `;return e instanceof HTMLStyleElement&&(e.innerText+=c,t.length>0)}async function b(e,t,o){const c=await e();return!c&&t>0?new Promise((c=>{setTimeout((async()=>{c(b(e,t-1,o))}),o)})):Promise.resolve(c)}function _(e){if(!e)return!1;if(null!==e.offsetParent)return!0;{const t=window.getComputedStyle(e);if("fixed"===t.position&&"none"!==t.display)return!0}return!1}function g(e){const t={enabled:!0,autoAction:"optOut",disabledCmps:[],enablePrehide:!0,enableCosmeticRules:!0,detectRetries:20,isMainWorld:!1,prehideTimeout:2e3,logs:{lifecycle:!1,rulesteps:!1,evals:!1,errors:!0,messages:!1}},o=(c=t,globalThis.structuredClone?structuredClone(c):JSON.parse(JSON.stringify(c)));var c;for(const c of Object.keys(t))void 0!==e[c]&&(o[c]=e[c]);return o}var y="#truste-show-consent",w="#truste-consent-track",C=[class extends d{constructor(e){super(e),this.name="TrustArc-top",this.prehideSelectors=[".trustarc-banner-container",`.truste_popframe,.truste_overlay,.truste_box_overlay,${w}`],this.runContext={main:!0,frame:!1},this._shortcutButton=null,this._optInDone=!1}get hasSelfTest(){return!1}get isIntermediate(){return!this._optInDone&&!this._shortcutButton}get isCosmetic(){return!1}async detectCmp(){const e=this.elementExists(`${y},${w}`);return e&&(this._shortcutButton=document.querySelector("#truste-consent-required")),e}async detectPopup(){return this.elementVisible(`#truste-consent-content,#trustarc-banner-overlay,${w}`,"all")}openFrame(){this.click(y)}async optOut(){return this._shortcutButton?(this._shortcutButton.click(),!0):(k(h(),`.truste_popframe, .truste_overlay, .truste_box_overlay, ${w}`),this.click(y),setTimeout((()=>{h().remove()}),1e4),!0)}async optIn(){return this._optInDone=!0,this.click("#truste-consent-button")}async openCmp(){return!0}async test(){return await this.mainWorldEval("EVAL_TRUSTARC_TOP")}},class extends d{constructor(){super(...arguments),this.name="TrustArc-frame",this.runContext={main:!1,frame:!0,urlPattern:"^https://consent-pref\\.trustarc\\.com/\\?"}}get hasSelfTest(){return!1}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return!0}async detectPopup(){return this.elementVisible("#defaultpreferencemanager","any")&&this.elementVisible(".mainContent","any")}async navigateToSettings(){return await b((async()=>this.elementExists(".shp")||this.elementVisible(".advance","any")||this.elementExists(".switch span:first-child")),10,500),this.elementExists(".shp")&&this.click(".shp"),await this.waitForElement(".prefPanel",5e3),this.elementVisible(".advance","any")&&this.click(".advance"),await b((()=>this.elementVisible(".switch span:first-child","any")),5,1e3)}async optOut(){return await b((()=>"complete"===document.readyState),20,100),await this.waitForElement(".mainContent[aria-hidden=false]",5e3),!!this.click(".rejectAll")||(this.elementExists(".prefPanel")&&await this.waitForElement('.prefPanel[style="visibility: visible;"]',3e3),this.click("#catDetails0")?(this.click(".submit"),this.waitForThenClick("#gwt-debug-close_id",5e3),!0):this.click(".required")?(this.waitForThenClick("#gwt-debug-close_id",5e3),!0):(await this.navigateToSettings(),this.click(".switch span:nth-child(1):not(.active)",!0),this.click(".submit"),this.waitForThenClick("#gwt-debug-close_id",3e5),!0))}async optIn(){return this.click(".call")||(await this.navigateToSettings(),this.click(".switch span:nth-child(2)",!0),this.click(".submit"),this.waitForElement("#gwt-debug-close_id",3e5).then((()=>{this.click("#gwt-debug-close_id")}))),!0}},class extends d{constructor(){super(...arguments),this.name="Cybotcookiebot",this.prehideSelectors=["#CybotCookiebotDialog,#CybotCookiebotDialogBodyUnderlay,#dtcookie-container,#cookiebanner,#cb-cookieoverlay,.modal--cookie-banner,#cookiebanner_outer,#CookieBanner"]}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return await this.mainWorldEval("EVAL_COOKIEBOT_1")}async detectPopup(){return this.mainWorldEval("EVAL_COOKIEBOT_2")}async optOut(){await this.wait(500);let e=await this.mainWorldEval("EVAL_COOKIEBOT_3");return await this.wait(500),e=e&&await this.mainWorldEval("EVAL_COOKIEBOT_4"),e}async optIn(){return this.elementExists("#dtcookie-container")?this.click(".h-dtcookie-accept"):(this.click(".CybotCookiebotDialogBodyLevelButton:not(:checked):enabled",!0),this.click("#CybotCookiebotDialogBodyLevelButtonAccept"),this.click("#CybotCookiebotDialogBodyButtonAccept"),!0)}async test(){return await this.wait(500),await this.mainWorldEval("EVAL_COOKIEBOT_5")}},class extends d{constructor(){super(...arguments),this.name="Sourcepoint-frame",this.prehideSelectors=["div[id^='sp_message_container_'],.message-overlay","#sp_privacy_manager_container"],this.ccpaNotice=!1,this.ccpaPopup=!1,this.runContext={main:!1,frame:!0}}get hasSelfTest(){return!1}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){const e=new URL(location.href);return e.searchParams.has("message_id")&&"ccpa-notice.sp-prod.net"===e.hostname?(this.ccpaNotice=!0,!0):"ccpa-pm.sp-prod.net"===e.hostname?(this.ccpaPopup=!0,!0):("/index.html"===e.pathname||"/privacy-manager/index.html"===e.pathname||"/ccpa_pm/index.html"===e.pathname)&&(e.searchParams.has("message_id")||e.searchParams.has("requestUUID")||e.searchParams.has("consentUUID"))}async detectPopup(){return!!this.ccpaNotice||(this.ccpaPopup?await this.waitForElement(".priv-save-btn",2e3):(await this.waitForElement(".sp_choice_type_11,.sp_choice_type_12,.sp_choice_type_13,.sp_choice_type_ACCEPT_ALL,.sp_choice_type_SAVE_AND_EXIT",2e3),!this.elementExists(".sp_choice_type_9")))}async optIn(){return await this.waitForElement(".sp_choice_type_11,.sp_choice_type_ACCEPT_ALL",2e3),!!this.click(".sp_choice_type_11")||!!this.click(".sp_choice_type_ACCEPT_ALL")}isManagerOpen(){return"/privacy-manager/index.html"===location.pathname||"/ccpa_pm/index.html"===location.pathname}async optOut(){const e=this.autoconsent.config.logs;if(this.ccpaPopup){const e=document.querySelectorAll(".priv-purpose-container .sp-switch-arrow-block a.neutral.on .right");for(const t of e)t.click();const t=document.querySelectorAll(".priv-purpose-container .sp-switch-arrow-block a.switch-bg.on");for(const e of t)e.click();return this.click(".priv-save-btn")}if(!this.isManagerOpen()){if(!await this.waitForElement(".sp_choice_type_12,.sp_choice_type_13"))return!1;if(!this.elementExists(".sp_choice_type_12"))return this.click(".sp_choice_type_13");this.click(".sp_choice_type_12"),await b((()=>this.isManagerOpen()),200,100)}await this.waitForElement(".type-modal",2e4),this.waitForThenClick(".ccpa-stack .pm-switch[aria-checked=true] .slider",500,!0);try{const e=".sp_choice_type_REJECT_ALL",t=".reject-toggle",o=await Promise.race([this.waitForElement(e,2e3).then((e=>e?0:-1)),this.waitForElement(t,2e3).then((e=>e?1:-1)),this.waitForElement(".pm-features",2e3).then((e=>e?2:-1))]);if(0===o)return await this.wait(1500),this.click(e);1===o?this.click(t):2===o&&(await this.waitForElement(".pm-features",1e4),this.click(".checked > span",!0),this.click(".chevron"))}catch(t){e.errors&&console.warn(t)}return this.click(".sp_choice_type_SAVE_AND_EXIT")}},class extends d{constructor(){super(...arguments),this.name="consentmanager.net",this.prehideSelectors=["#cmpbox,#cmpbox2"],this.apiAvailable=!1}get hasSelfTest(){return this.apiAvailable}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return this.apiAvailable=await this.mainWorldEval("EVAL_CONSENTMANAGER_1"),!!this.apiAvailable||this.elementExists("#cmpbox")}async detectPopup(){return this.apiAvailable?(await this.wait(500),await this.mainWorldEval("EVAL_CONSENTMANAGER_2")):this.elementVisible("#cmpbox .cmpmore","any")}async optOut(){return await this.wait(500),this.apiAvailable?await this.mainWorldEval("EVAL_CONSENTMANAGER_3"):!!this.click(".cmpboxbtnno")||(this.elementExists(".cmpwelcomeprpsbtn")?(this.click(".cmpwelcomeprpsbtn > a[aria-checked=true]",!0),this.click(".cmpboxbtnsave"),!0):(this.click(".cmpboxbtncustom"),await this.waitForElement(".cmptblbox",2e3),this.click(".cmptdchoice > a[aria-checked=true]",!0),this.click(".cmpboxbtnyescustomchoices"),this.hide("#cmpwrapper,#cmpbox","display"),!0))}async optIn(){return this.apiAvailable?await this.mainWorldEval("EVAL_CONSENTMANAGER_4"):this.click(".cmpboxbtnyes")}async test(){if(this.apiAvailable)return await this.mainWorldEval("EVAL_CONSENTMANAGER_5")}},class extends d{constructor(){super(...arguments),this.name="Evidon"}get hasSelfTest(){return!1}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return this.elementExists("#_evidon_banner")}async detectPopup(){return this.elementVisible("#_evidon_banner","any")}async optOut(){return this.click("#_evidon-decline-button")||(k(h(),"#evidon-prefdiag-overlay,#evidon-prefdiag-background"),this.click("#_evidon-option-button"),await this.waitForElement("#evidon-prefdiag-overlay",5e3),this.click("#evidon-prefdiag-decline")),!0}async optIn(){return this.click("#_evidon-accept-button")}},class extends d{constructor(){super(...arguments),this.name="Onetrust",this.prehideSelectors=["#onetrust-banner-sdk,#onetrust-consent-sdk,.onetrust-pc-dark-filter,.js-consent-banner"],this.runContext={urlPattern:"^(?!.*https://www\\.nba\\.com/)"}}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return this.elementExists("#onetrust-banner-sdk,#onetrust-consent-sdk")}async detectPopup(){return this.elementVisible("#onetrust-banner-sdk,#onetrust-consent-sdk","any")}async optOut(){return this.elementVisible("#onetrust-reject-all-handler,.ot-pc-refuse-all-handler,.js-reject-cookies","any")?this.click("#onetrust-reject-all-handler,.ot-pc-refuse-all-handler,.js-reject-cookies"):(this.elementExists("#onetrust-pc-btn-handler")?this.click("#onetrust-pc-btn-handler"):this.click(".ot-sdk-show-settings,button.js-cookie-settings"),await this.waitForElement("#onetrust-consent-sdk",2e3),await this.wait(1e3),this.click("#onetrust-consent-sdk input.category-switch-handler:checked,.js-editor-toggle-state:checked",!0),await this.wait(1e3),await this.waitForElement(".save-preference-btn-handler,.js-consent-save",2e3),this.click(".save-preference-btn-handler,.js-consent-save"),await this.waitForVisible("#onetrust-banner-sdk",5e3,"none"),!0)}async optIn(){return this.click("#onetrust-accept-btn-handler,#accept-recommended-btn-handler,.js-accept-cookies")}async test(){return await b((()=>this.mainWorldEval("EVAL_ONETRUST_1")),10,500)}},class extends d{constructor(){super(...arguments),this.name="Klaro",this.prehideSelectors=[".klaro"],this.settingsOpen=!1}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return this.elementExists(".klaro > .cookie-modal")?(this.settingsOpen=!0,!0):this.elementExists(".klaro > .cookie-notice")}async detectPopup(){return this.elementVisible(".klaro > .cookie-notice,.klaro > .cookie-modal","any")}async optOut(){return!!this.click(".klaro .cn-decline")||(this.settingsOpen||(this.click(".klaro .cn-learn-more,.klaro .cm-button-manage"),await this.waitForElement(".klaro > .cookie-modal",2e3),this.settingsOpen=!0),!!this.click(".klaro .cn-decline")||(this.click(".cm-purpose:not(.cm-toggle-all) > input:not(.half-checked,.required,.only-required),.cm-purpose:not(.cm-toggle-all) > div > input:not(.half-checked,.required,.only-required)",!0),this.click(".cm-btn-accept,.cm-button")))}async optIn(){return!!this.click(".klaro .cm-btn-accept-all")||(this.settingsOpen?(this.click(".cm-purpose:not(.cm-toggle-all) > input.half-checked",!0),this.click(".cm-btn-accept")):this.click(".klaro .cookie-notice .cm-btn-success"))}async test(){return await this.mainWorldEval("EVAL_KLARO_1")}},class extends d{constructor(){super(...arguments),this.name="Uniconsent"}get prehideSelectors(){return[".unic",".modal:has(.unic)"]}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return this.elementExists(".unic .unic-box,.unic .unic-bar,.unic .unic-modal")}async detectPopup(){return this.elementVisible(".unic .unic-box,.unic .unic-bar,.unic .unic-modal","any")}async optOut(){if(await this.waitForElement(".unic button",1e3),document.querySelectorAll(".unic button").forEach((e=>{const t=e.textContent;(t.includes("Manage Options")||t.includes("Optionen verwalten"))&&e.click()})),await this.waitForElement(".unic input[type=checkbox]",1e3)){await this.waitForElement(".unic button",1e3),document.querySelectorAll(".unic input[type=checkbox]").forEach((e=>{e.checked&&e.click()}));for(const e of document.querySelectorAll(".unic button")){const t=e.textContent;for(const o of["Confirm Choices","Save Choices","Auswahl speichern"])if(t.includes(o))return e.click(),await this.wait(500),!0}}return!1}async optIn(){return this.waitForThenClick(".unic #unic-agree")}async test(){await this.wait(1e3);return!this.elementExists(".unic .unic-box,.unic .unic-bar")}},class extends d{constructor(){super(...arguments),this.prehideSelectors=[".cmp-root"],this.name="Conversant"}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return this.elementExists(".cmp-root .cmp-receptacle")}async detectPopup(){return this.elementVisible(".cmp-root .cmp-receptacle","any")}async optOut(){if(!await this.waitForThenClick(".cmp-main-button:not(.cmp-main-button--primary)"))return!1;if(!await this.waitForElement(".cmp-view-tab-tabs"))return!1;await this.waitForThenClick(".cmp-view-tab-tabs > :first-child"),await this.waitForThenClick(".cmp-view-tab-tabs > .cmp-view-tab--active:first-child");for(const e of Array.from(document.querySelectorAll(".cmp-accordion-item"))){e.querySelector(".cmp-accordion-item-title").click(),await b((()=>!!e.querySelector(".cmp-accordion-item-content.cmp-active")),10,50);const t=e.querySelector(".cmp-accordion-item-content.cmp-active");t.querySelectorAll(".cmp-toggle-actions .cmp-toggle-deny:not(.cmp-toggle-deny--active)").forEach((e=>e.click())),t.querySelectorAll(".cmp-toggle-actions .cmp-toggle-checkbox:not(.cmp-toggle-checkbox--active)").forEach((e=>e.click()))}return await this.click(".cmp-main-button:not(.cmp-main-button--primary)"),!0}async optIn(){return this.waitForThenClick(".cmp-main-button.cmp-main-button--primary")}async test(){return document.cookie.includes("cmp-data=0")}},class extends d{constructor(){super(...arguments),this.name="tiktok.com",this.runContext={urlPattern:"tiktok"}}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}getShadowRoot(){const e=document.querySelector("tiktok-cookie-banner");return e?e.shadowRoot:null}async detectCmp(){return this.elementExists("tiktok-cookie-banner")}async detectPopup(){return _(this.getShadowRoot().querySelector(".tiktok-cookie-banner"))}async optOut(){const e=this.autoconsent.config.logs,t=this.getShadowRoot().querySelector(".button-wrapper button:first-child");return t?(e.rulesteps&&console.log("[clicking]",t),t.click(),!0):(e.errors&&console.log("no decline button found"),!1)}async optIn(){const e=this.autoconsent.config.logs,t=this.getShadowRoot().querySelector(".button-wrapper button:last-child");return t?(e.rulesteps&&console.log("[clicking]",t),t.click(),!0):(e.errors&&console.log("no accept button found"),!1)}async test(){const e=document.cookie.match(/cookie-consent=([^;]+)/);if(!e)return!1;const t=JSON.parse(decodeURIComponent(e[1]));return Object.values(t).every((e=>"boolean"!=typeof e||!1===e))}},class extends d{constructor(){super(...arguments),this.runContext={urlPattern:"^https://(www\\.)?airbnb\\.[^/]+/"},this.prehideSelectors=["div[data-testid=main-cookies-banner-container]",'div:has(> div:first-child):has(> div:last-child):has(> section [data-testid="strictly-necessary-cookies"])']}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return this.elementExists("div[data-testid=main-cookies-banner-container]")}async detectPopup(){return this.elementVisible("div[data-testid=main-cookies-banner-container","any")}async optOut(){let e;for(await this.waitForThenClick("div[data-testid=main-cookies-banner-container] button._snbhip0");e=document.querySelector("[data-testid=modal-container] button[aria-checked=true]:not([disabled])");)e.click();return this.waitForThenClick("button[data-testid=save-btn]")}async optIn(){return this.waitForThenClick("div[data-testid=main-cookies-banner-container] button._148dgdpk")}async test(){return await b((()=>!!document.cookie.match("OptanonAlertBoxClosed")),20,200)}}],v=class{constructor(e){this.autoconsentInstance=e}click(e,t=!1){const o=this.elementSelector(e);return this.autoconsentInstance.config.logs.rulesteps&&console.log("[click]",e,t,o),o.length>0&&(t?o.forEach((e=>e.click())):o[0].click()),o.length>0}elementExists(e){return this.elementSelector(e).length>0}elementVisible(e,t){const o=this.elementSelector(e),c=new Array(o.length);return o.forEach(((e,t)=>{c[t]=_(e)})),"none"===t?c.every((e=>!e)):0!==c.length&&("any"===t?c.some((e=>e)):c.every((e=>e)))}waitForElement(e,t=1e4){const o=Math.ceil(t/200);return this.autoconsentInstance.config.logs.rulesteps&&console.log("[waitForElement]",e),b((()=>this.elementSelector(e).length>0),o,200)}waitForVisible(e,t=1e4,o="any"){return b((()=>this.elementVisible(e,o)),Math.ceil(t/200),200)}async waitForThenClick(e,t=1e4,o=!1){return await this.waitForElement(e,t),this.click(e,o)}wait(e){return new Promise((t=>{setTimeout((()=>{t(!0)}),e)}))}hide(e,t){return k(h(),e,t)}prehide(e){const t=h("autoconsent-prehide");return this.autoconsentInstance.config.logs.lifecycle&&console.log("[prehide]",t,location.href),k(t,e,"opacity")}undoPrehide(){const e=h("autoconsent-prehide");return this.autoconsentInstance.config.logs.lifecycle&&console.log("[undoprehide]",e,location.href),e&&e.remove(),!!e}querySingleReplySelector(e,t=document){if(e.startsWith("aria/"))return[];if(e.startsWith("xpath/")){const o=e.slice(6),c=document.evaluate(o,t,null,XPathResult.ANY_TYPE,null);let i=null;const n=[];for(;i=c.iterateNext();)n.push(i);return n}return e.startsWith("text/")||e.startsWith("pierce/")?[]:t.shadowRoot?Array.from(t.shadowRoot.querySelectorAll(e)):Array.from(t.querySelectorAll(e))}querySelectorChain(e){let t,o=document;for(const c of e){if(t=this.querySingleReplySelector(c,o),0===t.length)return[];o=t[0]}return t}elementSelector(e){return"string"==typeof e?this.querySingleReplySelector(e):this.querySelectorChain(e)}};var f=[{name:"192.com",detectCmp:[{exists:".ont-cookies"}],detectPopup:[{visible:".ont-cookies"}],optIn:[{click:".ont-btn-main.ont-cookies-btn.js-ont-btn-ok2"}],optOut:[{click:".ont-cookes-btn-manage"},{click:".ont-btn-main.ont-cookies-btn.js-ont-btn-choose"}],test:[{eval:"EVAL_ONENINETWO_0"}]},{name:"1password-com",cosmetic:!0,prehideSelectors:['footer #footer-root [aria-label="Cookie Consent"]'],detectCmp:[{exists:'footer #footer-root [aria-label="Cookie Consent"]'}],detectPopup:[{visible:'footer #footer-root [aria-label="Cookie Consent"]'}],optIn:[{click:'footer #footer-root [aria-label="Cookie Consent"] button'}],optOut:[{hide:'footer #footer-root [aria-label="Cookie Consent"]'}]},{name:"abconcerts.be",vendorUrl:"https://unknown",intermediate:!1,prehideSelectors:["dialog.cookie-consent"],detectCmp:[{exists:"dialog.cookie-consent form.cookie-consent__form"}],detectPopup:[{visible:"dialog.cookie-consent form.cookie-consent__form"}],optIn:[{waitForThenClick:"dialog.cookie-consent form.cookie-consent__form button[value=yes]"}],optOut:[{if:{exists:"dialog.cookie-consent form.cookie-consent__form button[value=no]"},then:[{click:"dialog.cookie-consent form.cookie-consent__form button[value=no]"}],else:[{click:"dialog.cookie-consent form.cookie-consent__form button.cookie-consent__options-toggle"},{waitForThenClick:'dialog.cookie-consent form.cookie-consent__form button[value="save_options"]'}]}]},{name:"activobank.pt",runContext:{urlPattern:"^https://(www\\.)?activobank\\.pt"},prehideSelectors:["aside#cookies,.overlay-cookies"],detectCmp:[{exists:"#cookies .cookies-btn"}],detectPopup:[{visible:"#cookies #submitCookies"}],optIn:[{waitForThenClick:"#cookies #submitCookies"}],optOut:[{waitForThenClick:"#cookies #rejectCookies"}]},{name:"Adroll",prehideSelectors:["#adroll_consent_container"],detectCmp:[{exists:"#adroll_consent_container"}],detectPopup:[{visible:"#adroll_consent_container"}],optIn:[{waitForThenClick:"#adroll_consent_accept"}],optOut:[{waitForThenClick:"#adroll_consent_reject"}],test:[{eval:"EVAL_ADROLL_0"}]},{name:"affinity.serif.com",detectCmp:[{exists:".c-cookie-banner button[data-qa='allow-all-cookies']"}],detectPopup:[{visible:".c-cookie-banner"}],optIn:[{click:'button[data-qa="allow-all-cookies"]'}],optOut:[{click:'button[data-qa="manage-cookies"]'},{waitFor:'.c-cookie-banner ~ [role="dialog"]'},{waitForThenClick:'.c-cookie-banner ~ [role="dialog"] input[type="checkbox"][value="true"]',all:!0},{click:'.c-cookie-banner ~ [role="dialog"] .c-modal__action button'}],test:[{wait:500},{eval:"EVAL_AFFINITY_SERIF_COM_0"}]},{name:"agolde.com",cosmetic:!0,prehideSelectors:["#modal-1 div[data-micromodal-close]"],detectCmp:[{exists:"#modal-1 div[aria-labelledby=modal-1-title]"}],detectPopup:[{exists:"#modal-1 div[data-micromodal-close]"}],optIn:[{click:'button[aria-label="Close modal"]'}],optOut:[{hide:"#modal-1 div[data-micromodal-close]"}]},{name:"aliexpress",vendorUrl:"https://aliexpress.com/",runContext:{urlPattern:"^https://.*\\.aliexpress\\.com/"},prehideSelectors:["#gdpr-new-container"],detectCmp:[{exists:"#gdpr-new-container"}],detectPopup:[{visible:"#gdpr-new-container"}],optIn:[{waitForThenClick:"#gdpr-new-container .btn-accept"}],optOut:[{waitForThenClick:"#gdpr-new-container .btn-more"},{waitFor:"#gdpr-new-container .gdpr-dialog-switcher"},{click:"#gdpr-new-container .switcher-on",all:!0,optional:!0},{click:"#gdpr-new-container .btn-save"}]},{name:"almacmp",prehideSelectors:["#alma-cmpv2-container"],detectCmp:[{exists:"#alma-cmpv2-container"}],detectPopup:[{visible:"#alma-cmpv2-container #almacmp-modal-layer1"}],optIn:[{waitForThenClick:"#alma-cmpv2-container #almacmp-modal-layer1 #almacmp-modalConfirmBtn"}],optOut:[{waitForThenClick:"#alma-cmpv2-container #almacmp-modal-layer1 #almacmp-modalSettingBtn"},{waitFor:"#alma-cmpv2-container #almacmp-modal-layer2"},{waitForThenClick:"#alma-cmpv2-container #almacmp-modal-layer2 #almacmp-reject-all-layer2"}],test:[{eval:"EVAL_ALMACMP_0"}]},{name:"altium.com",cosmetic:!0,prehideSelectors:[".altium-privacy-bar"],detectCmp:[{exists:".altium-privacy-bar"}],detectPopup:[{exists:".altium-privacy-bar"}],optIn:[{click:"a.altium-privacy-bar__btn"}],optOut:[{hide:".altium-privacy-bar"}]},{name:"amazon.com",prehideSelectors:['span[data-action="sp-cc"][data-sp-cc*="rejectAllAction"]'],detectCmp:[{exists:'span[data-action="sp-cc"][data-sp-cc*="rejectAllAction"]'}],detectPopup:[{visible:'span[data-action="sp-cc"][data-sp-cc*="rejectAllAction"]'}],optIn:[{waitForVisible:"#sp-cc-accept"},{wait:500},{click:"#sp-cc-accept"}],optOut:[{waitForVisible:"#sp-cc-rejectall-link"},{wait:500},{click:"#sp-cc-rejectall-link"}]},{name:"aquasana.com",cosmetic:!0,prehideSelectors:["#consent-tracking"],detectCmp:[{exists:"#consent-tracking"}],detectPopup:[{exists:"#consent-tracking"}],optIn:[{click:"#accept_consent"}],optOut:[{hide:"#consent-tracking"}]},{name:"arbeitsagentur",vendorUrl:"https://www.arbeitsagentur.de/",prehideSelectors:[".modal-open bahf-cookie-disclaimer-dpl3"],detectCmp:[{exists:"bahf-cookie-disclaimer-dpl3"}],detectPopup:[{visible:"bahf-cookie-disclaimer-dpl3"}],optIn:[{waitForThenClick:["bahf-cookie-disclaimer-dpl3","bahf-cd-modal-dpl3 .ba-btn-primary"]}],optOut:[{waitForThenClick:["bahf-cookie-disclaimer-dpl3","bahf-cd-modal-dpl3 .ba-btn-contrast"]}],test:[{eval:"EVAL_ARBEITSAGENTUR_TEST"}]},{name:"asus",vendorUrl:"https://www.asus.com/",runContext:{urlPattern:"^https://www\\.asus\\.com/"},prehideSelectors:["#cookie-policy-info,#cookie-policy-info-bg"],detectCmp:[{exists:"#cookie-policy-info"}],detectPopup:[{visible:"#cookie-policy-info"}],optIn:[{waitForThenClick:'#cookie-policy-info [data-agree="Accept Cookies"]'}],optOut:[{if:{exists:"#cookie-policy-info .btn-reject"},then:[{waitForThenClick:"#cookie-policy-info .btn-reject"}],else:[{waitForThenClick:"#cookie-policy-info .btn-setting"},{waitForThenClick:'#cookie-policy-lightbox-wrapper [data-agree="Save Settings"]'}]}]},{name:"athlinks-com",runContext:{urlPattern:"^https://(www\\.)?athlinks\\.com/"},cosmetic:!0,prehideSelectors:["#footer-container ~ div"],detectCmp:[{exists:"#footer-container ~ div"}],detectPopup:[{visible:"#footer-container > div"}],optIn:[{click:"#footer-container ~ div button"}],optOut:[{hide:"#footer-container ~ div"}]},{name:"ausopen.com",cosmetic:!0,detectCmp:[{exists:".gdpr-popup__message"}],detectPopup:[{visible:".gdpr-popup__message"}],optOut:[{hide:".gdpr-popup__message"}],optIn:[{click:".gdpr-popup__message button"}]},{name:"automattic-cmp-optout",prehideSelectors:['form[class*="cookie-banner"][method="post"]'],detectCmp:[{exists:'form[class*="cookie-banner"][method="post"]'}],detectPopup:[{visible:'form[class*="cookie-banner"][method="post"]'}],optIn:[{click:'a[class*="accept-all-button"]'}],optOut:[{click:'form[class*="cookie-banner"] div[class*="simple-options"] a[class*="customize-button"]'},{waitForThenClick:"input[type=checkbox][checked]:not([disabled])",all:!0},{click:'a[class*="accept-selection-button"]'}]},{name:"aws.amazon.com",prehideSelectors:["#awsccc-cb-content","#awsccc-cs-container","#awsccc-cs-modalOverlay","#awsccc-cs-container-inner"],detectCmp:[{exists:"#awsccc-cb-content"}],detectPopup:[{visible:"#awsccc-cb-content"}],optIn:[{click:"button[data-id=awsccc-cb-btn-accept"}],optOut:[{click:"button[data-id=awsccc-cb-btn-customize]"},{waitFor:"input[aria-checked]"},{click:"input[aria-checked=true]",all:!0,optional:!0},{click:"button[data-id=awsccc-cs-btn-save]"}]},{name:"axeptio",prehideSelectors:[".axeptio_widget"],detectCmp:[{exists:".axeptio_widget"}],detectPopup:[{visible:".axeptio_widget"}],optIn:[{waitFor:".axeptio-widget--open"},{click:"button#axeptio_btn_acceptAll"}],optOut:[{waitFor:".axeptio-widget--open"},{click:"button#axeptio_btn_dismiss"}],test:[{eval:"EVAL_AXEPTIO_0"}]},{name:"baden-wuerttemberg.de",prehideSelectors:[".cookie-alert.t-dark"],cosmetic:!0,detectCmp:[{exists:".cookie-alert.t-dark"}],detectPopup:[{visible:".cookie-alert.t-dark"}],optIn:[{click:".cookie-alert__form input:not([disabled]):not([checked])"},{click:".cookie-alert__button button"}],optOut:[{hide:".cookie-alert.t-dark"}]},{name:"bahn-de",vendorUrl:"https://www.bahn.de/",cosmetic:!1,runContext:{main:!0,frame:!1,urlPattern:"^https://(www\\.)?bahn\\.de/"},intermediate:!1,prehideSelectors:[],detectCmp:[{exists:["body > div:first-child","#consent-layer"]}],detectPopup:[{visible:["body > div:first-child","#consent-layer"]}],optIn:[{waitForThenClick:["body > div:first-child","#consent-layer .js-accept-all-cookies"]}],optOut:[{waitForThenClick:["body > div:first-child","#consent-layer .js-accept-essential-cookies"]}],test:[{eval:"EVAL_BAHN_TEST"}]},{name:"bbb.org",runContext:{urlPattern:"^https://www\\.bbb\\.org/"},cosmetic:!0,prehideSelectors:['div[aria-label="use of cookies on bbb.org"]'],detectCmp:[{exists:'div[aria-label="use of cookies on bbb.org"]'}],detectPopup:[{visible:'div[aria-label="use of cookies on bbb.org"]'}],optIn:[{click:'div[aria-label="use of cookies on bbb.org"] button.bds-button-unstyled span.visually-hidden'}],optOut:[{hide:'div[aria-label="use of cookies on bbb.org"]'}]},{name:"bing.com",prehideSelectors:["#bnp_container"],detectCmp:[{exists:"#bnp_cookie_banner"}],detectPopup:[{visible:"#bnp_cookie_banner"}],optIn:[{click:"#bnp_btn_accept"}],optOut:[{click:"#bnp_btn_preference"},{click:"#mcp_savesettings"}],test:[{eval:"EVAL_BING_0"}]},{name:"blocksy",vendorUrl:"https://creativethemes.com/blocksy/docs/extensions/cookies-consent/",cosmetic:!1,runContext:{main:!0,frame:!1},intermediate:!1,prehideSelectors:[".cookie-notification"],detectCmp:[{exists:"#blocksy-ext-cookies-consent-styles-css"}],detectPopup:[{visible:".cookie-notification"}],optIn:[{click:".cookie-notification .ct-cookies-decline-button"}],optOut:[{waitForThenClick:".cookie-notification .ct-cookies-decline-button"}],test:[{eval:"EVAL_BLOCKSY_0"}]},{name:"borlabs",detectCmp:[{exists:"._brlbs-block-content"}],detectPopup:[{visible:"._brlbs-bar-wrap,._brlbs-box-wrap"}],optIn:[{click:"a[data-cookie-accept-all]"}],optOut:[{click:"a[data-cookie-individual]"},{waitForVisible:".cookie-preference"},{click:"input[data-borlabs-cookie-checkbox]:checked",all:!0,optional:!0},{click:"#CookiePrefSave"},{wait:500}],prehideSelectors:["#BorlabsCookieBox"],test:[{eval:"EVAL_BORLABS_0"}]},{name:"bundesregierung.de",prehideSelectors:[".bpa-cookie-banner"],detectCmp:[{exists:".bpa-cookie-banner"}],detectPopup:[{visible:".bpa-cookie-banner .bpa-module-full-hero"}],optIn:[{click:".bpa-accept-all-button"}],optOut:[{wait:500,comment:"click is not immediately recognized"},{waitForThenClick:".bpa-close-button"}],test:[{eval:"EVAL_BUNDESREGIERUNG_DE_0"}]},{name:"burpee.com",cosmetic:!0,prehideSelectors:["#notice-cookie-block"],detectCmp:[{exists:"#notice-cookie-block"}],detectPopup:[{exists:"#html-body #notice-cookie-block"}],optIn:[{click:"#btn-cookie-allow"}],optOut:[{hide:"#html-body #notice-cookie-block, #notice-cookie"}]},{name:"canva.com",prehideSelectors:['div[role="dialog"] a[data-anchor-id="cookie-policy"]'],detectCmp:[{exists:'div[role="dialog"] a[data-anchor-id="cookie-policy"]'}],detectPopup:[{exists:'div[role="dialog"] a[data-anchor-id="cookie-policy"]'}],optIn:[{click:'div[role="dialog"] button:nth-child(1)'}],optOut:[{if:{exists:'div[role="dialog"] button:nth-child(3)'},then:[{click:'div[role="dialog"] button:nth-child(2)'}],else:[{click:'div[role="dialog"] button:nth-child(2)'},{waitFor:'div[role="dialog"] a[data-anchor-id="privacy-policy"]'},{click:'div[role="dialog"] button:nth-child(2)'},{click:'div[role="dialog"] div:last-child button:only-child'}]}],test:[{eval:"EVAL_CANVA_0"}]},{name:"canyon.com",runContext:{urlPattern:"^https://www\\.canyon\\.com/"},prehideSelectors:["div.modal.cookiesModal.is-open"],detectCmp:[{exists:"div.modal.cookiesModal.is-open"}],detectPopup:[{visible:"div.modal.cookiesModal.is-open"}],optIn:[{click:'div.cookiesModal__buttonWrapper > button[data-closecause="close-by-submit"]'}],optOut:[{click:'div.cookiesModal__buttonWrapper > button[data-closecause="close-by-manage-cookies"]'},{waitForThenClick:"button#js-manage-data-privacy-save-button"}]},{name:"cc-banner-springer",prehideSelectors:[".cc-banner[data-cc-banner]"],detectCmp:[{exists:".cc-banner[data-cc-banner]"}],detectPopup:[{visible:".cc-banner[data-cc-banner]"}],optIn:[{waitForThenClick:".cc-banner[data-cc-banner] button[data-cc-action=accept]"}],optOut:[{if:{exists:".cc-banner[data-cc-banner] button[data-cc-action=reject]"},then:[{click:".cc-banner[data-cc-banner] button[data-cc-action=reject]"}],else:[{waitForThenClick:".cc-banner[data-cc-banner] button[data-cc-action=preferences]"},{waitFor:".cc-preferences[data-cc-preferences]"},{click:".cc-preferences[data-cc-preferences] input[type=radio][data-cc-action=toggle-category][value=off]",all:!0,optional:!0},{if:{exists:".cc-preferences[data-cc-preferences] button[data-cc-action=reject]"},then:[{click:".cc-preferences[data-cc-preferences] button[data-cc-action=reject]"}],else:[{click:".cc-preferences[data-cc-preferences] button[data-cc-action=save]"}]}]}],test:[{eval:"EVAL_CC_BANNER2_0"}]},{name:"cc_banner",cosmetic:!0,prehideSelectors:[".cc_banner-wrapper"],detectCmp:[{exists:".cc_banner-wrapper"}],detectPopup:[{visible:".cc_banner"}],optIn:[{click:".cc_btn_accept_all"}],optOut:[{hide:".cc_banner-wrapper"}]},{name:"ciaopeople.it",prehideSelectors:["#cp-gdpr-choices"],detectCmp:[{exists:"#cp-gdpr-choices"}],detectPopup:[{visible:"#cp-gdpr-choices"}],optIn:[{waitForThenClick:".gdpr-btm__right > button:nth-child(2)"}],optOut:[{waitForThenClick:".gdpr-top-content > button"},{waitFor:".gdpr-top-back"},{waitForThenClick:".gdpr-btm__right > button:nth-child(1)"}],test:[{visible:"#cp-gdpr-choices",check:"none"}]},{vendorUrl:"https://www.civicuk.com/cookie-control/",name:"civic-cookie-control",prehideSelectors:["#ccc-module,#ccc-overlay"],detectCmp:[{exists:"#ccc-module"}],detectPopup:[{visible:"#ccc"},{visible:"#ccc-module"}],optOut:[{click:"#ccc-reject-settings"}],optIn:[{click:"#ccc-recommended-settings"}]},{name:"click.io",prehideSelectors:["#cl-consent"],detectCmp:[{exists:"#cl-consent"}],detectPopup:[{visible:"#cl-consent"}],optIn:[{waitForThenClick:'#cl-consent [data-role="b_agree"]'}],optOut:[{waitFor:'#cl-consent [data-role="b_options"]'},{wait:500},{click:'#cl-consent [data-role="b_options"]'},{waitFor:'.cl-consent-popup.cl-consent-visible [data-role="alloff"]'},{click:'.cl-consent-popup.cl-consent-visible [data-role="alloff"]',all:!0},{click:'[data-role="b_save"]'}],test:[{eval:"EVAL_CLICKIO_0",comment:"TODO: this only checks if we interacted at all"}]},{name:"clinch",intermediate:!1,runContext:{frame:!1,main:!0},prehideSelectors:[".consent-modal[role=dialog]"],detectCmp:[{exists:".consent-modal[role=dialog]"}],detectPopup:[{visible:".consent-modal[role=dialog]"}],optIn:[{click:"#consent_agree"}],optOut:[{if:{exists:"#consent_reject"},then:[{click:"#consent_reject"}],else:[{click:"#manage_cookie_preferences"},{click:"#cookie_consent_preferences input:checked",all:!0,optional:!0},{click:"#consent_save"}]}],test:[{eval:"EVAL_CLINCH_0"}]},{name:"clustrmaps.com",runContext:{urlPattern:"^https://(www\\.)?clustrmaps\\.com/"},cosmetic:!0,prehideSelectors:["#gdpr-cookie-message"],detectCmp:[{exists:"#gdpr-cookie-message"}],detectPopup:[{visible:"#gdpr-cookie-message"}],optIn:[{click:"button#gdpr-cookie-accept"}],optOut:[{hide:"#gdpr-cookie-message"}]},{name:"coinbase",intermediate:!1,runContext:{frame:!0,main:!0,urlPattern:"^https://(www|help)\\.coinbase\\.com"},prehideSelectors:[],detectCmp:[{exists:"div[class^=CookieBannerContent__Container]"}],detectPopup:[{visible:"div[class^=CookieBannerContent__Container]"}],optIn:[{click:"div[class^=CookieBannerContent__CTA] :nth-last-child(1)"}],optOut:[{click:"button[class^=CookieBannerContent__Settings]"},{click:"div[class^=CookiePreferencesModal__CategoryContainer] input:checked",all:!0,optional:!0},{click:"div[class^=CookiePreferencesModal__ButtonContainer] > button"}],test:[{eval:"EVAL_COINBASE_0"}]},{name:"Complianz banner",prehideSelectors:["#cmplz-cookiebanner-container"],detectCmp:[{exists:"#cmplz-cookiebanner-container .cmplz-cookiebanner"}],detectPopup:[{visible:"#cmplz-cookiebanner-container .cmplz-cookiebanner",check:"any"}],optIn:[{waitForThenClick:".cmplz-cookiebanner .cmplz-accept"}],optOut:[{waitForThenClick:".cmplz-cookiebanner .cmplz-deny"}],test:[{eval:"EVAL_COMPLIANZ_BANNER_0"}]},{name:"Complianz categories",prehideSelectors:['.cc-type-categories[aria-describedby="cookieconsent:desc"]'],detectCmp:[{exists:'.cc-type-categories[aria-describedby="cookieconsent:desc"]'}],detectPopup:[{visible:'.cc-type-categories[aria-describedby="cookieconsent:desc"]'}],optIn:[{any:[{click:".cc-accept-all"},{click:".cc-allow-all"},{click:".cc-allow"},{click:".cc-dismiss"}]}],optOut:[{if:{exists:'.cc-type-categories[aria-describedby="cookieconsent:desc"] .cc-dismiss'},then:[{click:".cc-dismiss"}],else:[{click:".cc-type-categories input[type=checkbox]:not([disabled]):checked",all:!0,optional:!0},{click:".cc-save"}]}]},{name:"Complianz notice",prehideSelectors:['.cc-type-info[aria-describedby="cookieconsent:desc"]'],cosmetic:!0,detectCmp:[{exists:'.cc-type-info[aria-describedby="cookieconsent:desc"] .cc-compliance .cc-btn'}],detectPopup:[{visible:'.cc-type-info[aria-describedby="cookieconsent:desc"] .cc-compliance .cc-btn'}],optIn:[{click:".cc-accept-all",optional:!0},{click:".cc-allow",optional:!0},{click:".cc-dismiss",optional:!0}],optOut:[{if:{exists:".cc-deny"},then:[{click:".cc-deny"}],else:[{hide:'[aria-describedby="cookieconsent:desc"]'}]}]},{name:"Complianz opt-both",prehideSelectors:['[aria-describedby="cookieconsent:desc"] .cc-type-opt-both'],detectCmp:[{exists:'[aria-describedby="cookieconsent:desc"] .cc-type-opt-both'}],detectPopup:[{visible:'[aria-describedby="cookieconsent:desc"] .cc-type-opt-both'}],optIn:[{click:".cc-accept-all",optional:!0},{click:".cc-allow",optional:!0},{click:".cc-dismiss",optional:!0}],optOut:[{waitForThenClick:".cc-deny"}]},{name:"Complianz optin",prehideSelectors:['.cc-type-opt-in[aria-describedby="cookieconsent:desc"]'],detectCmp:[{exists:'.cc-type-opt-in[aria-describedby="cookieconsent:desc"]'}],detectPopup:[{visible:'.cc-type-opt-in[aria-describedby="cookieconsent:desc"]'}],optIn:[{any:[{click:".cc-accept-all"},{click:".cc-allow"},{click:".cc-dismiss"}]}],optOut:[{if:{visible:".cc-deny"},then:[{click:".cc-deny"}],else:[{if:{visible:".cc-settings"},then:[{waitForThenClick:".cc-settings"},{waitForVisible:".cc-settings-view"},{click:".cc-settings-view input[type=checkbox]:not([disabled]):checked",all:!0,optional:!0},{click:".cc-settings-view .cc-btn-accept-selected"}],else:[{click:".cc-dismiss"}]}]}]},{name:"cookie-law-info",prehideSelectors:["#cookie-law-info-bar"],detectCmp:[{exists:"#cookie-law-info-bar"},{eval:"EVAL_COOKIE_LAW_INFO_DETECT"}],detectPopup:[{visible:"#cookie-law-info-bar"}],optIn:[{click:'[data-cli_action="accept_all"]'}],optOut:[{hide:"#cookie-law-info-bar"},{eval:"EVAL_COOKIE_LAW_INFO_0"}],test:[{eval:"EVAL_COOKIE_LAW_INFO_1"}]},{name:"cookie-manager-popup",cosmetic:!1,runContext:{main:!0,frame:!1},intermediate:!1,detectCmp:[{exists:"#notice-cookie-block #allow-functional-cookies, #notice-cookie-block #btn-cookie-settings"}],detectPopup:[{visible:"#notice-cookie-block"}],optIn:[{click:"#btn-cookie-allow"}],optOut:[{if:{exists:"#allow-functional-cookies"},then:[{click:"#allow-functional-cookies"}],else:[{waitForThenClick:"#btn-cookie-settings"},{waitForVisible:".modal-body"},{click:'.modal-body input:checked, .switch[data-switch="on"]',all:!0,optional:!0},{click:'[role="dialog"] .modal-footer button'}]}],prehideSelectors:["#btn-cookie-settings"],test:[{eval:"EVAL_COOKIE_MANAGER_POPUP_0"}]},{name:"cookie-notice",prehideSelectors:["#cookie-notice"],cosmetic:!0,detectCmp:[{visible:"#cookie-notice .cookie-notice-container"}],detectPopup:[{visible:"#cookie-notice"}],optIn:[{click:"#cn-accept-cookie"}],optOut:[{hide:"#cookie-notice"}]},{name:"cookie-script",vendorUrl:"https://cookie-script.com/",prehideSelectors:["#cookiescript_injected"],detectCmp:[{exists:"#cookiescript_injected"}],detectPopup:[{visible:"#cookiescript_injected"}],optOut:[{click:"#cookiescript_reject"}],optIn:[{click:"#cookiescript_accept"}]},{name:"cookieacceptbar",vendorUrl:"https://unknown",cosmetic:!0,prehideSelectors:["#cookieAcceptBar.cookieAcceptBar"],detectCmp:[{exists:"#cookieAcceptBar.cookieAcceptBar"}],detectPopup:[{visible:"#cookieAcceptBar.cookieAcceptBar"}],optIn:[{waitForThenClick:"#cookieAcceptBarConfirm"}],optOut:[{hide:"#cookieAcceptBar.cookieAcceptBar"}]},{name:"cookiealert",intermediate:!1,prehideSelectors:[],runContext:{frame:!0,main:!0},detectCmp:[{exists:".cookie-alert-extended"}],detectPopup:[{visible:".cookie-alert-extended-modal"}],optIn:[{click:"button[data-controller='cookie-alert/extended/button/accept']"},{eval:"EVAL_COOKIEALERT_0"}],optOut:[{click:"a[data-controller='cookie-alert/extended/detail-link']"},{click:".cookie-alert-configuration-input:checked",all:!0,optional:!0},{click:"button[data-controller='cookie-alert/extended/button/configuration']"},{eval:"EVAL_COOKIEALERT_0"}],test:[{eval:"EVAL_COOKIEALERT_2"}]},{name:"cookieconsent2",vendorUrl:"https://www.github.com/orestbida/cookieconsent",comment:"supports v2.x.x of the library",prehideSelectors:["#cc--main"],detectCmp:[{exists:"#cc--main"}],detectPopup:[{visible:"#cm"},{exists:"#s-all-bn"}],optIn:[{waitForThenClick:"#s-all-bn"}],optOut:[{waitForThenClick:"#s-rall-bn"}],test:[{eval:"EVAL_COOKIECONSENT2_TEST"}]},{name:"cookieconsent3",vendorUrl:"https://www.github.com/orestbida/cookieconsent",comment:"supports v3.x.x of the library",prehideSelectors:["#cc-main"],detectCmp:[{exists:"#cc-main"}],detectPopup:[{visible:"#cc-main .cm-wrapper"}],optIn:[{waitForThenClick:".cm__btn[data-role=all]"}],optOut:[{waitForThenClick:".cm__btn[data-role=necessary]"}],test:[{eval:"EVAL_COOKIECONSENT3_TEST"}]},{name:"cookiefirst.com",prehideSelectors:["#cookiefirst-root,.cookiefirst-root,[aria-labelledby=cookie-preference-panel-title]"],detectCmp:[{exists:"#cookiefirst-root,.cookiefirst-root"}],detectPopup:[{visible:"#cookiefirst-root,.cookiefirst-root"}],optIn:[{click:"button[data-cookiefirst-action=accept]"}],optOut:[{if:{exists:"button[data-cookiefirst-action=adjust]"},then:[{click:"button[data-cookiefirst-action=adjust]"},{waitForVisible:"[data-cookiefirst-widget=modal]",timeout:1e3},{eval:"EVAL_COOKIEFIRST_1"},{wait:1e3},{click:"button[data-cookiefirst-action=save]"}],else:[{click:"button[data-cookiefirst-action=reject]"}]}],test:[{eval:"EVAL_COOKIEFIRST_0"}]},{name:"Cookie Information Banner",prehideSelectors:["#cookie-information-template-wrapper"],detectCmp:[{exists:"#cookie-information-template-wrapper"}],detectPopup:[{visible:"#cookie-information-template-wrapper"}],optIn:[{eval:"EVAL_COOKIEINFORMATION_1"}],optOut:[{hide:"#cookie-information-template-wrapper",comment:"some templates don't hide the banner automatically"},{eval:"EVAL_COOKIEINFORMATION_0"}],test:[{eval:"EVAL_COOKIEINFORMATION_2"}]},{name:"cookieyes",prehideSelectors:[".cky-overlay,.cky-consent-container"],detectCmp:[{exists:".cky-consent-container"}],detectPopup:[{visible:".cky-consent-container"}],optIn:[{waitForThenClick:".cky-consent-container [data-cky-tag=accept-button]"}],optOut:[{if:{exists:".cky-consent-container [data-cky-tag=reject-button]"},then:[{waitForThenClick:".cky-consent-container [data-cky-tag=reject-button]"}],else:[{if:{exists:".cky-consent-container [data-cky-tag=settings-button]"},then:[{click:".cky-consent-container [data-cky-tag=settings-button]"},{waitFor:".cky-modal-open input[type=checkbox]"},{click:".cky-modal-open input[type=checkbox]:checked",all:!0,optional:!0},{waitForThenClick:".cky-modal [data-cky-tag=detail-save-button]"}],else:[{hide:".cky-consent-container,.cky-overlay"}]}]}],test:[{eval:"EVAL_COOKIEYES_0"}]},{name:"corona-in-zahlen.de",prehideSelectors:[".cookiealert"],detectCmp:[{exists:".cookiealert"}],detectPopup:[{visible:".cookiealert"}],optOut:[{click:".configurecookies"},{click:".confirmcookies"}],optIn:[{click:".acceptcookies"}]},{name:"crossfit-com",cosmetic:!0,prehideSelectors:['body #modal > div > div[class^="_wrapper_"]'],detectCmp:[{exists:'body #modal > div > div[class^="_wrapper_"]'}],detectPopup:[{visible:'body #modal > div > div[class^="_wrapper_"]'}],optIn:[{click:'button[aria-label="accept cookie policy"]'}],optOut:[{hide:'body #modal > div > div[class^="_wrapper_"]'}]},{name:"csu-landtag-de",runContext:{urlPattern:"^https://(www|)?\\.csu-landtag\\.de"},prehideSelectors:["#cookie-disclaimer"],detectCmp:[{exists:"#cookie-disclaimer"}],detectPopup:[{visible:"#cookie-disclaimer"}],optIn:[{click:"#cookieall"}],optOut:[{click:"#cookiesel"}]},{name:"dailymotion-us",cosmetic:!0,prehideSelectors:['div[class*="CookiePopup__desktopContainer"]:has(div[class*="CookiePopup"])'],detectCmp:[{exists:'div[class*="CookiePopup__desktopContainer"]'}],detectPopup:[{visible:'div[class*="CookiePopup__desktopContainer"]'}],optIn:[{click:'div[class*="CookiePopup__desktopContainer"] > button > span'}],optOut:[{hide:'div[class*="CookiePopup__desktopContainer"]'}]},{name:"dailymotion.com",runContext:{urlPattern:"^https://(www\\.)?dailymotion\\.com/"},prehideSelectors:['div[class*="Overlay__container"]:has(div[class*="TCF2Popup"])'],detectCmp:[{exists:'div[class*="TCF2Popup"]'}],detectPopup:[{visible:'[class*="TCF2Popup"] a[href^="https://www.dailymotion.com/legal/cookiemanagement"]'}],optIn:[{waitForThenClick:'button[class*="TCF2Popup__button"]:not([class*="TCF2Popup__personalize"])'}],optOut:[{waitForThenClick:'button[class*="TCF2ContinueWithoutAcceptingButton"]'}],test:[{eval:"EVAL_DAILYMOTION_0"}]},{name:"deepl.com",prehideSelectors:[".dl_cookieBanner_container"],detectCmp:[{exists:".dl_cookieBanner_container"}],detectPopup:[{visible:".dl_cookieBanner_container"}],optOut:[{click:".dl_cookieBanner--buttonSelected"}],optIn:[{click:".dl_cookieBanner--buttonAll"}]},{name:"delta.com",runContext:{urlPattern:"^https://www\\.delta\\.com/"},cosmetic:!0,prehideSelectors:["ngc-cookie-banner"],detectCmp:[{exists:"div.cookie-footer-container"}],detectPopup:[{visible:"div.cookie-footer-container"}],optIn:[{click:" button.cookie-close-icon"}],optOut:[{hide:"div.cookie-footer-container"}]},{name:"dmgmedia-us",prehideSelectors:["#mol-ads-cmp-iframe, div.mol-ads-cmp > form > div"],detectCmp:[{exists:"div.mol-ads-cmp > form > div"}],detectPopup:[{waitForVisible:"div.mol-ads-cmp > form > div"}],optIn:[{waitForThenClick:"button.mol-ads-cmp--btn-primary"}],optOut:[{waitForThenClick:"div.mol-ads-ccpa--message > u > a"},{waitForVisible:".mol-ads-cmp--modal-dialog"},{waitForThenClick:"a.mol-ads-cmp-footer-privacy"},{waitForThenClick:"button.mol-ads-cmp--btn-secondary"}]},{name:"dmgmedia",prehideSelectors:['[data-project="mol-fe-cmp"]'],detectCmp:[{exists:'[data-project="mol-fe-cmp"]'}],detectPopup:[{visible:'[data-project="mol-fe-cmp"]'}],optIn:[{waitForThenClick:'[data-project="mol-fe-cmp"] button[class*=primary]'}],optOut:[{waitForThenClick:'[data-project="mol-fe-cmp"] button[class*=basic]'},{waitForVisible:'[data-project="mol-fe-cmp"] div[class*="tabContent"]'},{waitForThenClick:'[data-project="mol-fe-cmp"] div[class*="toggle"][class*="enabled"]',all:!0},{waitForThenClick:'[data-project="mol-fe-cmp"] button[class*=white]'}]},{name:"dndbeyond",vendorUrl:"https://www.dndbeyond.com/",runContext:{urlPattern:"^https://(www\\.)?dndbeyond\\.com/"},prehideSelectors:["[id^=cookie-consent-banner]"],detectCmp:[{exists:"[id^=cookie-consent-banner]"}],detectPopup:[{visible:"[id^=cookie-consent-banner]"}],optIn:[{waitForThenClick:"#cookie-consent-granted"}],optOut:[{waitForThenClick:"#cookie-consent-denied"}],test:[{eval:"EVAL_DNDBEYOND_TEST"}]},{name:"Drupal",detectCmp:[{exists:"#drupalorg-crosssite-gdpr"}],detectPopup:[{visible:"#drupalorg-crosssite-gdpr"}],optOut:[{click:".no"}],optIn:[{click:".yes"}]},{name:"WP DSGVO Tools",link:"https://wordpress.org/plugins/shapepress-dsgvo/",prehideSelectors:[".sp-dsgvo"],cosmetic:!0,detectCmp:[{exists:".sp-dsgvo.sp-dsgvo-popup-overlay"}],detectPopup:[{visible:".sp-dsgvo.sp-dsgvo-popup-overlay",check:"any"}],optIn:[{click:".sp-dsgvo-privacy-btn-accept-all",all:!0}],optOut:[{hide:".sp-dsgvo.sp-dsgvo-popup-overlay"}],test:[{eval:"EVAL_DSGVO_0"}]},{name:"dunelm.com",prehideSelectors:["div[data-testid=cookie-consent-modal-backdrop]"],detectCmp:[{exists:"div[data-testid=cookie-consent-message-contents]"}],detectPopup:[{visible:"div[data-testid=cookie-consent-message-contents]"}],optIn:[{click:'[data-testid="cookie-consent-allow-all"]'}],optOut:[{click:"button[data-testid=cookie-consent-adjust-settings]"},{click:"button[data-testid=cookie-consent-preferences-save]"}],test:[{eval:"EVAL_DUNELM_0"}]},{name:"ecosia",vendorUrl:"https://www.ecosia.org/",runContext:{urlPattern:"^https://www\\.ecosia\\.org/"},prehideSelectors:[".cookie-wrapper"],detectCmp:[{exists:".cookie-wrapper > .cookie-notice"}],detectPopup:[{visible:".cookie-wrapper > .cookie-notice"}],optIn:[{waitForThenClick:"[data-test-id=cookie-notice-accept]"}],optOut:[{waitForThenClick:"[data-test-id=cookie-notice-reject]"}]},{name:"etsy",prehideSelectors:["#gdpr-single-choice-overlay","#gdpr-privacy-settings"],detectCmp:[{exists:"#gdpr-single-choice-overlay"}],detectPopup:[{visible:"#gdpr-single-choice-overlay"}],optOut:[{click:"button[data-gdpr-open-full-settings]"},{waitForVisible:".gdpr-overlay-body input",timeout:3e3},{wait:1e3},{eval:"EVAL_ETSY_0"},{eval:"EVAL_ETSY_1"}],optIn:[{click:"button[data-gdpr-single-choice-accept]"}]},{name:"eu-cookie-compliance-banner",detectCmp:[{exists:"body.eu-cookie-compliance-popup-open"}],detectPopup:[{exists:"body.eu-cookie-compliance-popup-open"}],optIn:[{click:".agree-button"}],optOut:[{if:{visible:".decline-button,.eu-cookie-compliance-save-preferences-button"},then:[{click:".decline-button,.eu-cookie-compliance-save-preferences-button"}]},{hide:".eu-cookie-compliance-banner-info, #sliding-popup"}],test:[{eval:"EVAL_EU_COOKIE_COMPLIANCE_0"}]},{name:"EU Cookie Law",prehideSelectors:[".pea_cook_wrapper,.pea_cook_more_info_popover"],cosmetic:!0,detectCmp:[{exists:".pea_cook_wrapper"}],detectPopup:[{wait:500},{visible:".pea_cook_wrapper"}],optIn:[{click:"#pea_cook_btn"}],optOut:[{hide:".pea_cook_wrapper"}],test:[{eval:"EVAL_EU_COOKIE_LAW_0"}]},{name:"europa-eu",vendorUrl:"https://ec.europa.eu/",runContext:{urlPattern:"^https://[^/]*europa\\.eu/"},prehideSelectors:["#cookie-consent-banner"],detectCmp:[{exists:".cck-container"}],detectPopup:[{visible:".cck-container"}],optIn:[{waitForThenClick:'.cck-actions-button[href="#accept"]'}],optOut:[{waitForThenClick:'.cck-actions-button[href="#refuse"]',hide:".cck-container"}]},{name:"EZoic",prehideSelectors:["#ez-cookie-dialog-wrapper"],detectCmp:[{exists:"#ez-cookie-dialog-wrapper"}],detectPopup:[{visible:"#ez-cookie-dialog-wrapper"}],optIn:[{click:"#ez-accept-all",optional:!0},{eval:"EVAL_EZOIC_0",optional:!0}],optOut:[{wait:500},{click:"#ez-manage-settings"},{waitFor:"#ez-cookie-dialog input[type=checkbox]"},{click:"#ez-cookie-dialog input[type=checkbox]:checked",all:!0},{click:"#ez-save-settings"}],test:[{eval:"EVAL_EZOIC_1"}]},{name:"facebook",runContext:{urlPattern:"^https://([a-z0-9-]+\\.)?facebook\\.com/"},prehideSelectors:['div[data-testid="cookie-policy-manage-dialog"]'],detectCmp:[{exists:'div[data-testid="cookie-policy-manage-dialog"]'}],detectPopup:[{visible:'div[data-testid="cookie-policy-manage-dialog"]'}],optIn:[{waitForThenClick:'button[data-cookiebanner="accept_button"]'},{waitForVisible:'div[data-testid="cookie-policy-manage-dialog"]',check:"none"}],optOut:[{waitForThenClick:'button[data-cookiebanner="accept_only_essential_button"]'},{waitForVisible:'div[data-testid="cookie-policy-manage-dialog"]',check:"none"}]},{name:"fides",vendorUrl:"https://github.com/ethyca/fides",prehideSelectors:["#fides-overlay"],detectCmp:[{exists:"#fides-overlay #fides-banner"}],detectPopup:[{visible:"#fides-overlay #fides-banner"}],optIn:[{waitForThenClick:'#fides-banner [data-testid="Accept all-btn"]'}],optOut:[{waitForThenClick:'#fides-banner [data-testid="Reject all-btn"]'}]},{name:"funding-choices",prehideSelectors:[".fc-consent-root,.fc-dialog-container,.fc-dialog-overlay,.fc-dialog-content"],detectCmp:[{exists:".fc-consent-root"}],detectPopup:[{exists:".fc-dialog-container"}],optOut:[{click:".fc-cta-do-not-consent,.fc-cta-manage-options"},{click:".fc-preference-consent:checked,.fc-preference-legitimate-interest:checked",all:!0,optional:!0},{click:".fc-confirm-choices",optional:!0}],optIn:[{click:".fc-cta-consent"}]},{name:"geeks-for-geeks",runContext:{urlPattern:"^https://www\\.geeksforgeeks\\.org/"},cosmetic:!0,prehideSelectors:[".cookie-consent"],detectCmp:[{exists:".cookie-consent"}],detectPopup:[{visible:".cookie-consent"}],optIn:[{click:".cookie-consent button.consent-btn"}],optOut:[{hide:".cookie-consent"}]},{name:"generic-cosmetic",cosmetic:!0,prehideSelectors:["#js-cookie-banner,.js-cookie-banner,.cookie-banner,#cookie-banner"],detectCmp:[{exists:"#js-cookie-banner,.js-cookie-banner,.cookie-banner,#cookie-banner"}],detectPopup:[{visible:"#js-cookie-banner,.js-cookie-banner,.cookie-banner,#cookie-banner"}],optIn:[],optOut:[{hide:"#js-cookie-banner,.js-cookie-banner,.cookie-banner,#cookie-banner"}]},{name:"google-consent-standalone",prehideSelectors:[],detectCmp:[{exists:'a[href^="https://policies.google.com/technologies/cookies"'},{exists:'form[action^="https://consent.google."][action$=".com/save"]'}],detectPopup:[{visible:'a[href^="https://policies.google.com/technologies/cookies"'}],optIn:[{waitForThenClick:'form[action^="https://consent.google."][action$=".com/save"]:has(input[name=set_eom][value=false]) button'}],optOut:[{waitForThenClick:'form[action^="https://consent.google."][action$=".com/save"]:has(input[name=set_eom][value=true]) button'}]},{name:"google.com",prehideSelectors:[".HTjtHe#xe7COe"],detectCmp:[{exists:".HTjtHe#xe7COe"},{exists:'.HTjtHe#xe7COe a[href^="https://policies.google.com/technologies/cookies"]'}],detectPopup:[{visible:".HTjtHe#xe7COe button#W0wltc"}],optIn:[{waitForThenClick:".HTjtHe#xe7COe button#L2AGLb"}],optOut:[{waitForThenClick:".HTjtHe#xe7COe button#W0wltc"}],test:[{eval:"EVAL_GOOGLE_0"}]},{name:"gov.uk",detectCmp:[{exists:"#global-cookie-message"}],detectPopup:[{exists:"#global-cookie-message"}],optIn:[{click:"button[data-accept-cookies=true]"}],optOut:[{click:"button[data-reject-cookies=true],#reject-cookies"},{click:"button[data-hide-cookie-banner=true],#hide-cookie-decision"}]},{name:"hashicorp",vendorUrl:"https://hashicorp.com/",runContext:{urlPattern:"^https://[^.]*\\.hashicorp\\.com/"},prehideSelectors:["[data-testid=consent-banner]"],detectCmp:[{exists:"[data-testid=consent-banner]"}],detectPopup:[{visible:"[data-testid=consent-banner]"}],optIn:[{waitForThenClick:"[data-testid=accept]"}],optOut:[{waitForThenClick:"[data-testid=manage-preferences]"},{waitForThenClick:"[data-testid=consent-mgr-dialog] [data-ga-button=save-preferences]"}]},{name:"healthline-media",prehideSelectors:["#modal-host > div.no-hash > div.window-wrapper"],detectCmp:[{exists:"#modal-host > div.no-hash > div.window-wrapper, div[data-testid=qualtrics-container]"}],detectPopup:[{exists:"#modal-host > div.no-hash > div.window-wrapper, div[data-testid=qualtrics-container]"}],optIn:[{click:"#modal-host > div.no-hash > div.window-wrapper > div:last-child button"}],optOut:[{if:{exists:'#modal-host > div.no-hash > div.window-wrapper > div:last-child a[href="/privacy-settings"]'},then:[{click:'#modal-host > div.no-hash > div.window-wrapper > div:last-child a[href="/privacy-settings"]'}],else:[{waitForVisible:"div#__next"},{click:"#__next div:nth-child(1) > button:first-child"}]}]},{name:"hema",prehideSelectors:[".cookie-modal"],detectCmp:[{visible:".cookie-modal .cookie-accept-btn"}],detectPopup:[{visible:".cookie-modal .cookie-accept-btn"}],optIn:[{waitForThenClick:".cookie-modal .cookie-accept-btn"}],optOut:[{waitForThenClick:".cookie-modal .js-cookie-reject-btn"}],test:[{eval:"EVAL_HEMA_TEST_0"}]},{name:"hetzner.com",runContext:{urlPattern:"^https://www\\.hetzner\\.com/"},prehideSelectors:["#CookieConsent"],detectCmp:[{exists:"#CookieConsent"}],detectPopup:[{visible:"#CookieConsent"}],optIn:[{click:"#CookieConsentGiven"}],optOut:[{click:"#CookieConsentDeclined"}]},{name:"hl.co.uk",prehideSelectors:[".cookieModalContent","#cookie-banner-overlay"],detectCmp:[{exists:"#cookie-banner-overlay"}],detectPopup:[{exists:"#cookie-banner-overlay"}],optIn:[{click:"#acceptCookieButton"}],optOut:[{click:"#manageCookie"},{hide:".cookieSettingsModal"},{waitFor:"#AOCookieToggle"},{click:"#AOCookieToggle[aria-pressed=true]",optional:!0},{waitFor:"#TPCookieToggle"},{click:"#TPCookieToggle[aria-pressed=true]",optional:!0},{click:"#updateCookieButton"}]},{name:"hu-manity",vendorUrl:"https://hu-manity.co/",prehideSelectors:["#hu.hu-wrapper"],detectCmp:[{exists:"#hu.hu-visible"}],detectPopup:[{visible:"#hu.hu-visible"}],optIn:[{waitForThenClick:"[data-hu-action=cookies-notice-consent-choices-3]"},{waitForThenClick:"#hu-cookies-save"}],optOut:[{waitForThenClick:"#hu-cookies-save"}]},{name:"hubspot",detectCmp:[{exists:"#hs-eu-cookie-confirmation"}],detectPopup:[{visible:"#hs-eu-cookie-confirmation"}],optIn:[{click:"#hs-eu-confirmation-button"}],optOut:[{click:"#hs-eu-decline-button"}]},{name:"indeed.com",cosmetic:!0,prehideSelectors:["#CookiePrivacyNotice"],detectCmp:[{exists:"#CookiePrivacyNotice"}],detectPopup:[{visible:"#CookiePrivacyNotice"}],optIn:[{click:"#CookiePrivacyNotice button[data-gnav-element-name=CookiePrivacyNoticeOk]"}],optOut:[{hide:"#CookiePrivacyNotice"}]},{name:"ing.de",runContext:{urlPattern:"^https://www\\.ing\\.de/"},cosmetic:!0,prehideSelectors:['div[slot="backdrop"]'],detectCmp:[{exists:'[data-tag-name="ing-cc-dialog-frame"]'}],detectPopup:[{visible:'[data-tag-name="ing-cc-dialog-frame"]'}],optIn:[{click:['[data-tag-name="ing-cc-dialog-level0"]','[data-tag-name="ing-cc-button"][class*="accept"]']}],optOut:[{click:['[data-tag-name="ing-cc-dialog-level0"]','[data-tag-name="ing-cc-button"][class*="more"]']}]},{name:"instagram",vendorUrl:"https://instagram.com",runContext:{urlPattern:"^https://www\\.instagram\\.com/"},prehideSelectors:[".x78zum5.xdt5ytf.xg6iff7.x1n2onr6"],detectCmp:[{exists:".x1qjc9v5.x9f619.x78zum5.xdt5ytf.x1iyjqo2.xl56j7k"}],detectPopup:[{visible:".x1qjc9v5.x9f619.x78zum5.xdt5ytf.x1iyjqo2.xl56j7k"}],optIn:[{waitForThenClick:"._a9--._a9_0"}],optOut:[{waitForThenClick:"._a9--._a9_1"},{wait:2e3}]},{name:"ionos.de",prehideSelectors:[".privacy-consent--backdrop",".privacy-consent--modal"],detectCmp:[{exists:".privacy-consent--modal"}],detectPopup:[{visible:".privacy-consent--modal"}],optIn:[{click:"#selectAll"}],optOut:[{click:".footer-config-link"},{click:"#confirmSelection"}]},{name:"itopvpn.com",cosmetic:!0,prehideSelectors:[".pop-cookie"],detectCmp:[{exists:".pop-cookie"}],detectPopup:[{exists:".pop-cookie"}],optIn:[{click:"#_pcookie"}],optOut:[{hide:".pop-cookie"}]},{name:"iubenda",prehideSelectors:["#iubenda-cs-banner"],detectCmp:[{exists:"#iubenda-cs-banner"}],detectPopup:[{visible:".iubenda-cs-accept-btn"}],optIn:[{click:".iubenda-cs-accept-btn"}],optOut:[{click:".iubenda-cs-customize-btn"},{eval:"EVAL_IUBENDA_0"},{click:"#iubFooterBtn"}],test:[{eval:"EVAL_IUBENDA_1"}]},{name:"iWink",prehideSelectors:["body.cookies-request #cookie-bar"],detectCmp:[{exists:"body.cookies-request #cookie-bar"}],detectPopup:[{visible:"body.cookies-request #cookie-bar"}],optIn:[{waitForThenClick:"body.cookies-request #cookie-bar .allow-cookies"}],optOut:[{waitForThenClick:"body.cookies-request #cookie-bar .disallow-cookies"}],test:[{eval:"EVAL_IWINK_TEST"}]},{name:"jdsports",vendorUrl:"https://www.jdsports.co.uk/",runContext:{urlPattern:"^https://(www|m)\\.jdsports\\."},prehideSelectors:[".miniConsent,#PrivacyPolicyBanner"],detectCmp:[{exists:".miniConsent,#PrivacyPolicyBanner"}],detectPopup:[{visible:".miniConsent,#PrivacyPolicyBanner"}],optIn:[{waitForThenClick:".miniConsent .accept-all-cookies"}],optOut:[{if:{exists:"#PrivacyPolicyBanner"},then:[{hide:"#PrivacyPolicyBanner"}],else:[{waitForThenClick:"#cookie-settings"},{waitForThenClick:"#reject-all-cookies"}]}]},{name:"johnlewis.com",prehideSelectors:["div[class^=pecr-cookie-banner-]"],detectCmp:[{exists:"div[class^=pecr-cookie-banner-]"}],detectPopup:[{exists:"div[class^=pecr-cookie-banner-]"}],optOut:[{click:"button[data-test^=manage-cookies]"},{wait:"500"},{click:"label[data-test^=toggle][class*=checked]:not([class*=disabled])",all:!0,optional:!0},{click:"button[data-test=save-preferences]"}],optIn:[{click:"button[data-test=allow-all]"}]},{name:"jquery.cookieBar",vendorUrl:"https://github.com/kovarp/jquery.cookieBar",prehideSelectors:[".cookie-bar"],cosmetic:!0,detectCmp:[{exists:".cookie-bar .cookie-bar__message,.cookie-bar .cookie-bar__buttons"}],detectPopup:[{visible:".cookie-bar .cookie-bar__message,.cookie-bar .cookie-bar__buttons",check:"any"}],optIn:[{click:".cookie-bar .cookie-bar__btn"}],optOut:[{hide:".cookie-bar"}],test:[{visible:".cookie-bar .cookie-bar__message,.cookie-bar .cookie-bar__buttons",check:"none"},{eval:"EVAL_JQUERY_COOKIEBAR_0"}]},{name:"justwatch.com",prehideSelectors:[".consent-banner"],detectCmp:[{exists:".consent-banner .consent-banner__actions"}],detectPopup:[{visible:".consent-banner .consent-banner__actions"}],optIn:[{click:".consent-banner__actions button.basic-button.primary"}],optOut:[{click:".consent-banner__actions button.basic-button.secondary"},{waitForThenClick:".consent-modal__footer button.basic-button.secondary"},{waitForThenClick:".consent-modal ion-content > div > a:nth-child(9)"},{click:"label.consent-switch input[type=checkbox]:checked",all:!0,optional:!0},{waitForVisible:".consent-modal__footer button.basic-button.primary"},{click:".consent-modal__footer button.basic-button.primary"}]},{name:"ketch",vendorUrl:"https://www.ketch.com",runContext:{frame:!1,main:!0},intermediate:!1,prehideSelectors:["#lanyard_root div[role='dialog']"],detectCmp:[{exists:"#lanyard_root div[role='dialog']"}],detectPopup:[{visible:"#lanyard_root div[role='dialog']"}],optIn:[{if:{exists:"#lanyard_root button[class='confirmButton']"},then:[{waitForThenClick:"#lanyard_root div[class*=buttons] > :nth-child(2)"},{click:"#lanyard_root button[class='confirmButton']"}],else:[{waitForThenClick:"#lanyard_root div[class*=buttons] > :nth-child(2)"}]}],optOut:[{if:{exists:"#lanyard_root [aria-describedby=banner-description]"},then:[{waitForThenClick:"#lanyard_root div[class*=buttons] > button[class*=secondaryButton]",comment:"can be either settings or reject button"}]},{waitFor:"#lanyard_root [aria-describedby=preference-description],#lanyard_root [aria-describedby=modal-description]",timeout:1e3,optional:!0},{if:{exists:"#lanyard_root [aria-describedby=preference-description],#lanyard_root [aria-describedby=modal-description]"},then:[{waitForThenClick:"#lanyard_root button[class*=rejectButton]"},{click:"#lanyard_root button[class*=confirmButton],#lanyard_root div[class*=actions_] > button:nth-child(1)"}]}]},{name:"kleinanzeigen-de",runContext:{urlPattern:"^https?://(www\\.)?kleinanzeigen\\.de"},prehideSelectors:["#gdpr-banner-container"],detectCmp:[{any:[{exists:"#gdpr-banner-container #gdpr-banner [data-testid=gdpr-banner-cmp-button]"},{exists:"#ConsentManagementPage"}]}],detectPopup:[{any:[{visible:"#gdpr-banner-container #gdpr-banner [data-testid=gdpr-banner-cmp-button]"},{visible:"#ConsentManagementPage"}]}],optIn:[{if:{exists:"#gdpr-banner-container #gdpr-banner"},then:[{click:"#gdpr-banner-container #gdpr-banner [data-testid=gdpr-banner-accept]"}],else:[{click:"#ConsentManagementPage .Button-primary"}]}],optOut:[{if:{exists:"#gdpr-banner-container #gdpr-banner"},then:[{click:"#gdpr-banner-container #gdpr-banner [data-testid=gdpr-banner-cmp-button]"}],else:[{click:"#ConsentManagementPage .Button-secondary"}]}]},{name:"lightbox",prehideSelectors:[".darken-layer.open,.lightbox.lightbox--cookie-consent"],detectCmp:[{exists:"body.cookie-consent-is-active div.lightbox--cookie-consent > div.lightbox__content > div.cookie-consent[data-jsb]"}],detectPopup:[{visible:"body.cookie-consent-is-active div.lightbox--cookie-consent > div.lightbox__content > div.cookie-consent[data-jsb]"}],optOut:[{click:".cookie-consent__footer > button[type='submit']:not([data-button='selectAll'])"}],optIn:[{click:".cookie-consent__footer > button[type='submit'][data-button='selectAll']"}]},{name:"lineagrafica",vendorUrl:"https://addons.prestashop.com/en/legal/8734-eu-cookie-law-gdpr-banner-blocker.html",cosmetic:!0,prehideSelectors:["#lgcookieslaw_banner,#lgcookieslaw_modal,.lgcookieslaw-overlay"],detectCmp:[{exists:"#lgcookieslaw_banner,#lgcookieslaw_modal,.lgcookieslaw-overlay"}],detectPopup:[{exists:"#lgcookieslaw_banner,#lgcookieslaw_modal,.lgcookieslaw-overlay"}],optIn:[{waitForThenClick:"#lgcookieslaw_accept"}],optOut:[{hide:"#lgcookieslaw_banner,#lgcookieslaw_modal,.lgcookieslaw-overlay"}]},{name:"linkedin.com",prehideSelectors:[".artdeco-global-alert[type=COOKIE_CONSENT]"],detectCmp:[{exists:".artdeco-global-alert[type=COOKIE_CONSENT]"}],detectPopup:[{visible:".artdeco-global-alert[type=COOKIE_CONSENT]"}],optIn:[{waitForVisible:".artdeco-global-alert[type=COOKIE_CONSENT] button[action-type=ACCEPT]"},{wait:500},{waitForThenClick:".artdeco-global-alert[type=COOKIE_CONSENT] button[action-type=ACCEPT]"}],optOut:[{waitForVisible:".artdeco-global-alert[type=COOKIE_CONSENT] button[action-type=DENY]"},{wait:500},{waitForThenClick:".artdeco-global-alert[type=COOKIE_CONSENT] button[action-type=DENY]"}],test:[{waitForVisible:".artdeco-global-alert[type=COOKIE_CONSENT]",check:"none"}]},{name:"livejasmin",vendorUrl:"https://www.livejasmin.com/",runContext:{urlPattern:"^https://(m|www)\\.livejasmin\\.com/"},prehideSelectors:["#consent_modal"],detectCmp:[{exists:"#consent_modal"}],detectPopup:[{visible:"#consent_modal"}],optIn:[{waitForThenClick:"#consent_modal button[data-testid=ButtonStyledButton]:first-of-type"}],optOut:[{waitForThenClick:"#consent_modal button[data-testid=ButtonStyledButton]:nth-of-type(2)"},{waitForVisible:"[data-testid=PrivacyPreferenceCenterWithConsentCookieContent]"},{click:"[data-testid=PrivacyPreferenceCenterWithConsentCookieContent] input[data-testid=PrivacyPreferenceCenterWithConsentCookieSwitch]:checked",optional:!0,all:!0},{waitForThenClick:"[data-testid=PrivacyPreferenceCenterWithConsentCookieContent] button[data-testid=ButtonStyledButton]:last-child"}]},{name:"macpaw.com",cosmetic:!0,prehideSelectors:['div[data-banner="cookies"]'],detectCmp:[{exists:'div[data-banner="cookies"]'}],detectPopup:[{exists:'div[data-banner="cookies"]'}],optIn:[{click:'button[data-banner-close="cookies"]'}],optOut:[{hide:'div[data-banner="cookies"]'}]},{name:"marksandspencer.com",cosmetic:!0,detectCmp:[{exists:".navigation-cookiebbanner"}],detectPopup:[{visible:".navigation-cookiebbanner"}],optOut:[{hide:".navigation-cookiebbanner"}],optIn:[{click:".navigation-cookiebbanner__submit"}]},{name:"mediamarkt.de",prehideSelectors:["div[aria-labelledby=pwa-consent-layer-title]","div[class^=StyledConsentLayerWrapper-]"],detectCmp:[{exists:"div[aria-labelledby^=pwa-consent-layer-title]"}],detectPopup:[{exists:"div[aria-labelledby^=pwa-consent-layer-title]"}],optOut:[{click:"button[data-test^=pwa-consent-layer-deny-all]"}],optIn:[{click:"button[data-test^=pwa-consent-layer-accept-all"}]},{name:"Mediavine",prehideSelectors:['[data-name="mediavine-gdpr-cmp"]'],detectCmp:[{exists:'[data-name="mediavine-gdpr-cmp"]'}],detectPopup:[{wait:500},{visible:'[data-name="mediavine-gdpr-cmp"]'}],optIn:[{waitForThenClick:'[data-name="mediavine-gdpr-cmp"] [format="primary"]'}],optOut:[{waitForThenClick:'[data-name="mediavine-gdpr-cmp"] [data-view="manageSettings"]'},{waitFor:'[data-name="mediavine-gdpr-cmp"] input[type=checkbox]'},{eval:"EVAL_MEDIAVINE_0",optional:!0},{click:'[data-name="mediavine-gdpr-cmp"] [format="secondary"]'}]},{name:"microsoft.com",prehideSelectors:["#wcpConsentBannerCtrl"],detectCmp:[{exists:"#wcpConsentBannerCtrl"}],detectPopup:[{exists:"#wcpConsentBannerCtrl"}],optOut:[{eval:"EVAL_MICROSOFT_0"}],optIn:[{eval:"EVAL_MICROSOFT_1"}],test:[{eval:"EVAL_MICROSOFT_2"}]},{name:"midway-usa",runContext:{urlPattern:"^https://www\\.midwayusa\\.com/"},cosmetic:!0,prehideSelectors:["#cookie-container"],detectCmp:[{exists:['div[aria-label="Cookie Policy Banner"]']}],detectPopup:[{visible:"#cookie-container"}],optIn:[{click:"button#cookie-btn"}],optOut:[{hide:'div[aria-label="Cookie Policy Banner"]'}]},{name:"moneysavingexpert.com",detectCmp:[{exists:"dialog[data-testid=accept-our-cookies-dialog]"}],detectPopup:[{visible:"dialog[data-testid=accept-our-cookies-dialog]"}],optIn:[{click:"#banner-accept"}],optOut:[{click:"#banner-manage"},{click:"#pc-confirm"}]},{name:"monzo.com",prehideSelectors:[".cookie-alert, cookie-alert__content"],detectCmp:[{exists:'div.cookie-alert[role="dialog"]'},{exists:'a[href*="monzo"]'}],detectPopup:[{visible:".cookie-alert__content"}],optIn:[{click:".js-accept-cookie-policy"}],optOut:[{click:".js-decline-cookie-policy"}]},{name:"Moove",prehideSelectors:["#moove_gdpr_cookie_info_bar"],detectCmp:[{exists:"#moove_gdpr_cookie_info_bar"}],detectPopup:[{visible:"#moove_gdpr_cookie_info_bar"}],optIn:[{waitForThenClick:".moove-gdpr-infobar-allow-all"}],optOut:[{if:{exists:"#moove_gdpr_cookie_info_bar .change-settings-button"},then:[{click:"#moove_gdpr_cookie_info_bar .change-settings-button"},{waitForVisible:"#moove_gdpr_cookie_modal"},{eval:"EVAL_MOOVE_0"},{click:".moove-gdpr-modal-save-settings"}],else:[{hide:"#moove_gdpr_cookie_info_bar"}]}],test:[{visible:"#moove_gdpr_cookie_info_bar",check:"none"}]},{name:"national-lottery.co.uk",detectCmp:[{exists:".cuk_cookie_consent"}],detectPopup:[{visible:".cuk_cookie_consent",check:"any"}],optOut:[{click:".cuk_cookie_consent_manage_pref"},{click:".cuk_cookie_consent_save_pref"},{click:".cuk_cookie_consent_close"}],optIn:[{click:".cuk_cookie_consent_accept_all"}]},{name:"nba.com",runContext:{urlPattern:"^https://(www\\.)?nba.com/"},cosmetic:!0,prehideSelectors:["#onetrust-banner-sdk"],detectCmp:[{exists:"#onetrust-banner-sdk"}],detectPopup:[{visible:"#onetrust-banner-sdk"}],optIn:[{click:"#onetrust-accept-btn-handler"}],optOut:[{hide:"#onetrust-banner-sdk"}]},{name:"netflix.de",detectCmp:[{exists:"#cookie-disclosure"}],detectPopup:[{visible:".cookie-disclosure-message",check:"any"}],optIn:[{click:".btn-accept"}],optOut:[{hide:"#cookie-disclosure"},{click:".btn-reject"}]},{name:"nhs.uk",prehideSelectors:["#nhsuk-cookie-banner"],detectCmp:[{exists:"#nhsuk-cookie-banner"}],detectPopup:[{exists:"#nhsuk-cookie-banner"}],optOut:[{click:"#nhsuk-cookie-banner__link_accept"}],optIn:[{click:"#nhsuk-cookie-banner__link_accept_analytics"}]},{name:"notice-cookie",prehideSelectors:[".button--notice"],cosmetic:!0,detectCmp:[{exists:".notice--cookie"}],detectPopup:[{visible:".notice--cookie"}],optIn:[{click:".button--notice"}],optOut:[{hide:".notice--cookie"}]},{name:"nrk.no",cosmetic:!0,prehideSelectors:[".nrk-masthead__info-banner--cookie"],detectCmp:[{exists:".nrk-masthead__info-banner--cookie"}],detectPopup:[{exists:".nrk-masthead__info-banner--cookie"}],optIn:[{click:"div.nrk-masthead__info-banner--cookie button > span:has(+ svg.nrk-close)"}],optOut:[{hide:".nrk-masthead__info-banner--cookie"}]},{name:"obi.de",prehideSelectors:[".disc-cp--active"],detectCmp:[{exists:".disc-cp-modal__modal"}],detectPopup:[{visible:".disc-cp-modal__modal"}],optIn:[{click:".js-disc-cp-accept-all"}],optOut:[{click:".js-disc-cp-deny-all"}]},{name:"om",vendorUrl:"https://olli-machts.de/en/extension/cookie-manager",prehideSelectors:[".tx-om-cookie-consent"],detectCmp:[{exists:".tx-om-cookie-consent .active[data-omcookie-panel]"}],detectPopup:[{exists:".tx-om-cookie-consent .active[data-omcookie-panel]"}],optIn:[{waitForThenClick:"[data-omcookie-panel-save=all]"}],optOut:[{if:{exists:"[data-omcookie-panel-save=min]"},then:[{waitForThenClick:"[data-omcookie-panel-save=min]"}],else:[{click:"input[data-omcookie-panel-grp]:checked:not(:disabled)",all:!0,optional:!0},{waitForThenClick:"[data-omcookie-panel-save=save]"}]}]},{name:"onlyFans.com",prehideSelectors:["div.b-cookies-informer"],detectCmp:[{exists:"div.b-cookies-informer"}],detectPopup:[{exists:"div.b-cookies-informer"}],optIn:[{click:"div.b-cookies-informer__nav > button:nth-child(2)"}],optOut:[{click:"div.b-cookies-informer__nav > button:nth-child(1)"},{click:'div.b-cookies-informer__switchers > div:nth-child(2) > div[at-attr="checkbox"] > span.b-input-radio__container > input[type="checkbox"]'},{click:"div.b-cookies-informer__nav > button"}]},{name:"openli",vendorUrl:"https://openli.com",prehideSelectors:[".legalmonster-cleanslate"],detectCmp:[{exists:".legalmonster-cleanslate"}],detectPopup:[{visible:".legalmonster-cleanslate #lm-cookie-wall-container",check:"any"}],optIn:[{waitForThenClick:"#lm-accept-all"}],optOut:[{waitForThenClick:"#lm-accept-necessary"}]},{name:"opera.com",vendorUrl:"https://unknown",cosmetic:!1,runContext:{main:!0,frame:!1},intermediate:!1,prehideSelectors:[],detectCmp:[{exists:"#cookie-consent .manage-cookies__btn"}],detectPopup:[{visible:"#cookie-consent .cookie-basic-consent__btn"}],optIn:[{waitForThenClick:"#cookie-consent .cookie-basic-consent__btn"}],optOut:[{waitForThenClick:"#cookie-consent .manage-cookies__btn"},{waitForThenClick:"#cookie-consent .active.marketing_option_switch.cookie-consent__switch",all:!0},{waitForThenClick:"#cookie-consent .cookie-selection__btn"}],test:[{eval:"EVAL_OPERA_0"}]},{name:"osano",prehideSelectors:[".osano-cm-window,.osano-cm-dialog"],detectCmp:[{exists:".osano-cm-window"}],detectPopup:[{visible:".osano-cm-dialog"}],optIn:[{click:".osano-cm-accept-all",optional:!0}],optOut:[{waitForThenClick:".osano-cm-denyAll"}]},{name:"otto.de",prehideSelectors:[".cookieBanner--visibility"],detectCmp:[{exists:".cookieBanner--visibility"}],detectPopup:[{visible:".cookieBanner__wrapper"}],optIn:[{click:".js_cookieBannerPermissionButton"}],optOut:[{click:".js_cookieBannerProhibitionButton"}]},{name:"ourworldindata",vendorUrl:"https://ourworldindata.org/",runContext:{urlPattern:"^https://ourworldindata\\.org/"},prehideSelectors:[".cookie-manager"],detectCmp:[{exists:".cookie-manager"}],detectPopup:[{visible:".cookie-manager .cookie-notice.open"}],optIn:[{waitForThenClick:".cookie-notice [data-test=accept]"}],optOut:[{waitForThenClick:".cookie-notice [data-test=reject]"}]},{name:"pabcogypsum",vendorUrl:"https://unknown",prehideSelectors:[".js-cookie-notice:has(#cookie_settings-form)"],detectCmp:[{exists:".js-cookie-notice #cookie_settings-form"}],detectPopup:[{visible:".js-cookie-notice #cookie_settings-form"}],optIn:[{waitForThenClick:".js-cookie-notice button[value=allow]"}],optOut:[{waitForThenClick:".js-cookie-notice button[value=disable]"}]},{name:"paypal-us",prehideSelectors:["#ccpaCookieContent_wrapper, article.ppvx_modal--overpanel"],detectCmp:[{exists:"#ccpaCookieBanner, .privacy-sheet-content"}],detectPopup:[{exists:"#ccpaCookieBanner, .privacy-sheet-content"}],optIn:[{click:"#acceptAllButton"}],optOut:[{if:{exists:"a#manageCookiesLink"},then:[{click:"a#manageCookiesLink"}],else:[{waitForVisible:".privacy-sheet-content #formContent"},{click:"#formContent .cookiepref-11m2iee-checkbox_base input:checked",all:!0,optional:!0},{click:".confirmCookie #submitCookiesBtn"}]}]},{name:"paypal.com",prehideSelectors:["#gdprCookieBanner"],detectCmp:[{exists:"#gdprCookieBanner"}],detectPopup:[{visible:"#gdprCookieContent_wrapper"}],optIn:[{click:"#acceptAllButton"}],optOut:[{wait:200},{click:".gdprCookieBanner_decline-button"}],test:[{wait:500},{eval:"EVAL_PAYPAL_0"}]},{name:"pinetools.com",cosmetic:!0,prehideSelectors:["#aviso_cookies"],detectCmp:[{exists:"#aviso_cookies"}],detectPopup:[{exists:".lang_en #aviso_cookies"}],optIn:[{click:"#aviso_cookies .a_boton_cerrar"}],optOut:[{hide:"#aviso_cookies"}]},{name:"pmc",cosmetic:!0,prehideSelectors:["#pmc-pp-tou--notice"],detectCmp:[{exists:"#pmc-pp-tou--notice"}],detectPopup:[{visible:"#pmc-pp-tou--notice"}],optIn:[{click:"span.pmc-pp-tou--notice-close-btn"}],optOut:[{hide:"#pmc-pp-tou--notice"}]},{name:"pornhub.com",runContext:{urlPattern:"^https://(www\\.)?pornhub\\.com/"},cosmetic:!0,prehideSelectors:[".cookiesBanner"],detectCmp:[{exists:".cookiesBanner"}],detectPopup:[{visible:".cookiesBanner"}],optIn:[{click:".cookiesBanner .okButton"}],optOut:[{hide:".cookiesBanner"}]},{name:"pornpics.com",cosmetic:!0,prehideSelectors:["#cookie-contract"],detectCmp:[{exists:"#cookie-contract"}],detectPopup:[{visible:"#cookie-contract"}],optIn:[{click:"#cookie-contract .icon-cross"}],optOut:[{hide:"#cookie-contract"}]},{name:"PrimeBox CookieBar",prehideSelectors:["#cookie-bar"],detectCmp:[{exists:"#cookie-bar .cb-enable,#cookie-bar .cb-disable,#cookie-bar .cb-policy"}],detectPopup:[{visible:"#cookie-bar .cb-enable,#cookie-bar .cb-disable,#cookie-bar .cb-policy",check:"any"}],optIn:[{waitForThenClick:"#cookie-bar .cb-enable"}],optOut:[{click:"#cookie-bar .cb-disable",optional:!0},{hide:"#cookie-bar"}],test:[{eval:"EVAL_PRIMEBOX_0"}]},{name:"privacymanager.io",prehideSelectors:["#gdpr-consent-tool-wrapper",'iframe[src^="https://cmp-consent-tool.privacymanager.io"]'],runContext:{urlPattern:"^https://cmp-consent-tool\\.privacymanager\\.io/",main:!1,frame:!0},detectCmp:[{exists:"button#save"}],detectPopup:[{visible:"button#save"}],optIn:[{click:"button#save"}],optOut:[{if:{exists:"#denyAll"},then:[{click:"#denyAll"},{waitForThenClick:".okButton"}],else:[{waitForThenClick:"#manageSettings"},{waitFor:".purposes-overview-list"},{waitFor:"button#saveAndExit"},{click:"span[role=checkbox][aria-checked=true]",all:!0,optional:!0},{click:"button#saveAndExit"}]}]},{name:"productz.com",vendorUrl:"https://productz.com/",runContext:{urlPattern:"^https://productz\\.com/"},prehideSelectors:[],detectCmp:[{exists:".c-modal.is-active"}],detectPopup:[{visible:".c-modal.is-active"}],optIn:[{waitForThenClick:".c-modal.is-active .is-accept"}],optOut:[{waitForThenClick:".c-modal.is-active .is-dismiss"}]},{name:"pubtech",prehideSelectors:["#pubtech-cmp"],detectCmp:[{exists:"#pubtech-cmp"}],detectPopup:[{visible:"#pubtech-cmp #pt-actions"}],optIn:[{if:{exists:"#pt-accept-all"},then:[{click:"#pubtech-cmp #pt-actions #pt-accept-all"}],else:[{click:"#pubtech-cmp #pt-actions button:nth-of-type(2)"}]}],optOut:[{click:"#pubtech-cmp #pt-close"}],test:[{eval:"EVAL_PUBTECH_0"}]},{name:"quantcast",prehideSelectors:["#qc-cmp2-main,#qc-cmp2-container"],detectCmp:[{exists:"#qc-cmp2-container"}],detectPopup:[{visible:"#qc-cmp2-ui"}],optOut:[{click:'.qc-cmp2-summary-buttons > button[mode="secondary"]'},{waitFor:"#qc-cmp2-ui"},{click:'.qc-cmp2-toggle-switch > button[aria-checked="true"]',all:!0,optional:!0},{click:'.qc-cmp2-main button[aria-label="REJECT ALL"]',optional:!0},{waitForThenClick:'.qc-cmp2-main button[aria-label="SAVE & EXIT"],.qc-cmp2-buttons-desktop > button[mode="primary"]',timeout:5e3}],optIn:[{click:'.qc-cmp2-summary-buttons > button[mode="primary"]'}]},{name:"reddit.com",runContext:{urlPattern:"^https://www\\.reddit\\.com/"},prehideSelectors:["[bundlename=reddit_cookie_banner]"],detectCmp:[{exists:"reddit-cookie-banner"}],detectPopup:[{visible:"reddit-cookie-banner"}],optIn:[{waitForThenClick:["reddit-cookie-banner","#accept-all-cookies-button > button"]}],optOut:[{waitForThenClick:["reddit-cookie-banner","#reject-nonessential-cookies-button > button"]}],test:[{eval:"EVAL_REDDIT_0"}]},{name:"rog-forum.asus.com",runContext:{urlPattern:"^https://rog-forum\\.asus\\.com/"},prehideSelectors:["#cookie-policy-info"],detectCmp:[{exists:"#cookie-policy-info"}],detectPopup:[{visible:"#cookie-policy-info"}],optIn:[{click:'div.cookie-btn-box > div[aria-label="Accept"]'}],optOut:[{click:'div.cookie-btn-box > div[aria-label="Reject"]'},{waitForThenClick:'.cookie-policy-lightbox-bottom > div[aria-label="Save Settings"]'}]},{name:"roofingmegastore.co.uk",runContext:{urlPattern:"^https://(www\\.)?roofingmegastore\\.co\\.uk"},prehideSelectors:["#m-cookienotice"],detectCmp:[{exists:"#m-cookienotice"}],detectPopup:[{visible:"#m-cookienotice"}],optIn:[{click:"#accept-cookies"}],optOut:[{click:"#manage-cookies"},{waitForThenClick:"#accept-selected"}]},{name:"samsung.com",runContext:{urlPattern:"^https://www\\.samsung\\.com/"},cosmetic:!0,prehideSelectors:["div.cookie-bar"],detectCmp:[{exists:"div.cookie-bar"}],detectPopup:[{visible:"div.cookie-bar"}],optIn:[{click:"div.cookie-bar__manage > a"}],optOut:[{hide:"div.cookie-bar"}]},{name:"setapp.com",vendorUrl:"https://setapp.com/",cosmetic:!0,runContext:{urlPattern:"^https://setapp\\.com/"},prehideSelectors:[],detectCmp:[{exists:".cookie-banner.js-cookie-banner"}],detectPopup:[{visible:".cookie-banner.js-cookie-banner"}],optIn:[{waitForThenClick:".cookie-banner.js-cookie-banner button"}],optOut:[{hide:".cookie-banner.js-cookie-banner"}]},{name:"sibbo",prehideSelectors:["sibbo-cmp-layout"],detectCmp:[{exists:"sibbo-cmp-layout"}],detectPopup:[{visible:"sibbo-cmp-layout"}],optIn:[{click:"sibbo-cmp-layout [data-accept-all]"}],optOut:[{click:'.sibbo-panel__aside__buttons a[data-nav="purposes"]'},{click:'.sibbo-panel__main__header__actions a[data-focusable="reject-all"]'},{if:{exists:"[data-view=purposes] .sibbo-panel__main__footer__actions [data-save-and-exit]"},then:[],else:[{waitFor:'.sibbo-panel__main__footer__actions a[data-focusable="next"]:not(.sibbo-cmp-button--disabled)'},{click:'.sibbo-panel__main__footer__actions a[data-focusable="next"]'},{click:'.sibbo-panel__main div[data-view="purposesLegInt"] a[data-focusable="reject-all"]'}]},{waitFor:".sibbo-panel__main__footer__actions [data-save-and-exit]:not(.sibbo-cmp-button--disabled)"},{click:".sibbo-panel__main__footer__actions [data-save-and-exit]:not(.sibbo-cmp-button--disabled)"}],test:[{eval:"EVAL_SIBBO_0"}]},{name:"similarweb.com",cosmetic:!0,prehideSelectors:[".app-cookies-notification"],detectCmp:[{exists:".app-cookies-notification"}],detectPopup:[{exists:".app-layout .app-cookies-notification"}],optIn:[{click:"button.app-cookies-notification__dismiss"}],optOut:[{hide:".app-layout .app-cookies-notification"}]},{name:"Sirdata",cosmetic:!0,prehideSelectors:["#sd-cmp"],detectCmp:[{exists:"#sd-cmp"}],detectPopup:[{visible:"#sd-cmp"}],optIn:[{waitForThenClick:"#sd-cmp .sd-cmp-3cRQ2"}],optOut:[{hide:"#sd-cmp"},{eval:"EVAL_SIRDATA_UNBLOCK_SCROLL"}]},{name:"snigel",detectCmp:[{exists:".snigel-cmp-framework"}],detectPopup:[{visible:".snigel-cmp-framework"}],optOut:[{click:"#sn-b-custom"},{click:"#sn-b-save"}],test:[{eval:"EVAL_SNIGEL_0"}],optIn:[{click:".snigel-cmp-framework #accept-choices"}]},{name:"steampowered.com",detectCmp:[{exists:".cookiepreferences_popup"},{visible:".cookiepreferences_popup"}],detectPopup:[{visible:".cookiepreferences_popup"}],optOut:[{click:"#rejectAllButton"}],optIn:[{click:"#acceptAllButton"}],test:[{wait:1e3},{eval:"EVAL_STEAMPOWERED_0"}]},{name:"strato.de",prehideSelectors:["#cookie_initial_modal",".modal-backdrop"],runContext:{urlPattern:"^https://www\\.strato\\.de/"},detectCmp:[{exists:"#cookie_initial_modal"}],detectPopup:[{visible:"#cookie_initial_modal"}],optIn:[{click:"button#jss_consent_all_initial_modal"}],optOut:[{click:"button#jss_open_settings_modal"},{click:"button#jss_consent_checked"}]},{name:"svt.se",vendorUrl:"https://www.svt.se/",runContext:{urlPattern:"^https://www\\.svt\\.se/"},prehideSelectors:["[class*=CookieConsent__root___]"],detectCmp:[{exists:"[class*=CookieConsent__root___]"}],detectPopup:[{visible:"[class*=CookieConsent__modal___]"}],optIn:[{waitForThenClick:"[class*=CookieConsent__modal___] > div > button[class*=primary]"}],optOut:[{waitForThenClick:"[class*=CookieConsent__modal___] > div > button[class*=secondary]:nth-child(2)"}],test:[{eval:"EVAL_SVT_TEST"}]},{name:"takealot.com",cosmetic:!0,prehideSelectors:['div[class^="cookies-banner-module_"]'],detectCmp:[{exists:'div[class^="cookies-banner-module_cookie-banner_"]'}],detectPopup:[{exists:'div[class^="cookies-banner-module_cookie-banner_"]'}],optIn:[{click:'button[class*="cookies-banner-module_dismiss-button_"]'}],optOut:[{hide:'div[class^="cookies-banner-module_"]'},{if:{exists:'div[class^="cookies-banner-module_small-cookie-banner_"]'},then:[{eval:"EVAL_TAKEALOT_0"}],else:[]}]},{name:"tarteaucitron.js",prehideSelectors:["#tarteaucitronRoot"],detectCmp:[{exists:"#tarteaucitronRoot"}],detectPopup:[{visible:"#tarteaucitronRoot #tarteaucitronAlertSmall,#tarteaucitronRoot #tarteaucitronAlertBig",check:"any"}],optIn:[{eval:"EVAL_TARTEAUCITRON_1"}],optOut:[{eval:"EVAL_TARTEAUCITRON_0"}],test:[{eval:"EVAL_TARTEAUCITRON_2",comment:"sometimes there are required categories, so we check that at least something is false"}]},{name:"taunton",vendorUrl:"https://www.taunton.com/",prehideSelectors:["#taunton-user-consent__overlay"],detectCmp:[{exists:"#taunton-user-consent__overlay"}],detectPopup:[{exists:"#taunton-user-consent__overlay:not([aria-hidden=true])"}],optIn:[{click:"#taunton-user-consent__toolbar input[type=checkbox]:not(:checked)"},{click:"#taunton-user-consent__toolbar button[type=submit]"}],optOut:[{click:"#taunton-user-consent__toolbar input[type=checkbox]:checked",optional:!0,all:!0},{click:"#taunton-user-consent__toolbar button[type=submit]"}],test:[{eval:"EVAL_TAUNTON_TEST"}]},{name:"Tealium",prehideSelectors:["#__tealiumGDPRecModal,#__tealiumGDPRcpPrefs,#__tealiumImplicitmodal,#consent-layer"],detectCmp:[{exists:"#__tealiumGDPRecModal *,#__tealiumGDPRcpPrefs *,#__tealiumImplicitmodal *"},{eval:"EVAL_TEALIUM_0"}],detectPopup:[{visible:"#__tealiumGDPRecModal *,#__tealiumGDPRcpPrefs *,#__tealiumImplicitmodal *",check:"any"}],optOut:[{eval:"EVAL_TEALIUM_1"},{eval:"EVAL_TEALIUM_DONOTSELL"},{hide:"#__tealiumGDPRecModal,#__tealiumGDPRcpPrefs,#__tealiumImplicitmodal"},{waitForThenClick:"#cm-acceptNone,.js-accept-essential-cookies",timeout:1e3,optional:!0}],optIn:[{hide:"#__tealiumGDPRecModal,#__tealiumGDPRcpPrefs"},{eval:"EVAL_TEALIUM_2"}],test:[{eval:"EVAL_TEALIUM_3"},{eval:"EVAL_TEALIUM_DONOTSELL_CHECK"},{visible:"#__tealiumGDPRecModal,#__tealiumGDPRcpPrefs",check:"none"}]},{name:"temu",vendorUrl:"https://temu.com",runContext:{urlPattern:"^https://[^/]*temu\\.com/"},prehideSelectors:["._2d-8vq-W,._1UdBUwni"],detectCmp:[{exists:"._3YCsmIaS"}],detectPopup:[{visible:"._3YCsmIaS"}],optIn:[{waitForThenClick:"._3fKiu5wx._3zN5SumS._3tAK973O.IYOfhWEs.VGNGF1pA"}],optOut:[{waitForThenClick:"._3fKiu5wx._1_XToJBF._3tAK973O.IYOfhWEs.VGNGF1pA"}]},{name:"Termly",prehideSelectors:["#termly-code-snippet-support"],detectCmp:[{exists:"#termly-code-snippet-support"}],detectPopup:[{visible:"#termly-code-snippet-support div"}],optIn:[{waitForThenClick:'[data-tid="banner-accept"]'}],optOut:[{if:{exists:'[data-tid="banner-decline"]'},then:[{click:'[data-tid="banner-decline"]'}],else:[{click:".t-preference-button"},{wait:500},{if:{exists:".t-declineAllButton"},then:[{click:".t-declineAllButton"}],else:[{waitForThenClick:".t-preference-modal input[type=checkbox][checked]:not([disabled])",all:!0},{waitForThenClick:".t-saveButton"}]}]}]},{name:"termsfeed",vendorUrl:"https://termsfeed.com",comment:"v4.x.x",prehideSelectors:[".termsfeed-com---nb"],detectCmp:[{exists:".termsfeed-com---nb"}],detectPopup:[{visible:".termsfeed-com---nb"}],optIn:[{waitForThenClick:".cc-nb-okagree"}],optOut:[{waitForThenClick:".cc-nb-reject"}]},{name:"termsfeed3",vendorUrl:"https://termsfeed.com",comment:"v3.x.x",cosmetic:!0,prehideSelectors:[".cc_dialog.cc_css_reboot"],detectCmp:[{exists:".cc_dialog.cc_css_reboot"}],detectPopup:[{visible:".cc_dialog.cc_css_reboot"}],optIn:[{waitForThenClick:".cc_dialog.cc_css_reboot .cc_b_ok"}],optOut:[{hide:".cc_dialog.cc_css_reboot"}]},{name:"Test page cosmetic CMP",cosmetic:!0,prehideSelectors:["#privacy-test-page-cmp-test-prehide"],detectCmp:[{exists:"#privacy-test-page-cmp-test-banner"}],detectPopup:[{visible:"#privacy-test-page-cmp-test-banner"}],optIn:[{waitFor:"#accept-all"},{click:"#accept-all"}],optOut:[{hide:"#privacy-test-page-cmp-test-banner"}],test:[{wait:500},{eval:"EVAL_TESTCMP_COSMETIC_0"}]},{name:"Test page CMP",prehideSelectors:["#reject-all"],detectCmp:[{exists:"#privacy-test-page-cmp-test"}],detectPopup:[{visible:"#privacy-test-page-cmp-test"}],optIn:[{waitFor:"#accept-all"},{click:"#accept-all"}],optOut:[{waitFor:"#reject-all"},{click:"#reject-all"}],test:[{eval:"EVAL_TESTCMP_0"}]},{name:"thalia.de",prehideSelectors:[".consent-banner-box"],detectCmp:[{exists:"consent-banner[component=consent-banner]"}],detectPopup:[{visible:".consent-banner-box"}],optIn:[{click:".button-zustimmen"}],optOut:[{click:"button[data-consent=disagree]"}]},{name:"thefreedictionary.com",prehideSelectors:["#cmpBanner"],detectCmp:[{exists:"#cmpBanner"}],detectPopup:[{visible:"#cmpBanner"}],optIn:[{eval:"EVAL_THEFREEDICTIONARY_1"}],optOut:[{eval:"EVAL_THEFREEDICTIONARY_0"}]},{name:"theverge",runContext:{frame:!1,main:!0,urlPattern:"^https://(www)?\\.theverge\\.com"},intermediate:!1,prehideSelectors:[".duet--cta--cookie-banner"],detectCmp:[{exists:".duet--cta--cookie-banner"}],detectPopup:[{visible:".duet--cta--cookie-banner"}],optIn:[{click:".duet--cta--cookie-banner button.tracking-12",all:!1}],optOut:[{click:".duet--cta--cookie-banner button.tracking-12 > span"}],test:[{eval:"EVAL_THEVERGE_0"}]},{name:"tidbits-com",cosmetic:!0,prehideSelectors:["#eu_cookie_law_widget-2"],detectCmp:[{exists:"#eu_cookie_law_widget-2"}],detectPopup:[{visible:"#eu_cookie_law_widget-2"}],optIn:[{click:"#eu-cookie-law form > input.accept"}],optOut:[{hide:"#eu_cookie_law_widget-2"}]},{name:"tractor-supply",runContext:{urlPattern:"^https://www\\.tractorsupply\\.com/"},cosmetic:!0,prehideSelectors:[".tsc-cookie-banner"],detectCmp:[{exists:".tsc-cookie-banner"}],detectPopup:[{visible:".tsc-cookie-banner"}],optIn:[{click:"#cookie-banner-cancel"}],optOut:[{hide:".tsc-cookie-banner"}]},{name:"trader-joes-com",cosmetic:!0,prehideSelectors:['div.aem-page > div[class^="CookiesAlert_cookiesAlert__"]'],detectCmp:[{exists:'div.aem-page > div[class^="CookiesAlert_cookiesAlert__"]'}],detectPopup:[{visible:'div.aem-page > div[class^="CookiesAlert_cookiesAlert__"]'}],optIn:[{click:'div[class^="CookiesAlert_cookiesAlert__container__"] button'}],optOut:[{hide:'div.aem-page > div[class^="CookiesAlert_cookiesAlert__"]'}]},{name:"transcend",vendorUrl:"https://unknown",cosmetic:!0,prehideSelectors:["#transcend-consent-manager"],detectCmp:[{exists:"#transcend-consent-manager"}],detectPopup:[{visible:"#transcend-consent-manager"}],optIn:[{waitForThenClick:["#transcend-consent-manager","#consentManagerMainDialog .inner-container button"]}],optOut:[{hide:"#transcend-consent-manager"}]},{name:"transip-nl",runContext:{urlPattern:"^https://www\\.transip\\.nl/"},prehideSelectors:["#consent-modal"],detectCmp:[{any:[{exists:"#consent-modal"},{exists:"#privacy-settings-content"}]}],detectPopup:[{any:[{visible:"#consent-modal"},{visible:"#privacy-settings-content"}]}],optIn:[{click:'button[type="submit"]'}],optOut:[{if:{exists:"#privacy-settings-content"},then:[{click:'button[type="submit"]'}],else:[{click:"div.one-modal__action-footer-column--secondary > a"}]}]},{name:"tropicfeel-com",prehideSelectors:["#shopify-section-cookies-controller"],detectCmp:[{exists:"#shopify-section-cookies-controller"}],detectPopup:[{visible:"#shopify-section-cookies-controller #cookies-controller-main-pane",check:"any"}],optIn:[{waitForThenClick:"#cookies-controller-main-pane form[data-form-allow-all] button"}],optOut:[{click:"#cookies-controller-main-pane a[data-tab-target=manage-cookies]"},{waitFor:"#manage-cookies-pane.active"},{click:"#manage-cookies-pane.active input[type=checkbox][checked]:not([disabled])",all:!0},{click:"#manage-cookies-pane.active button[type=submit]"}],test:[]},{name:"true-car",runContext:{urlPattern:"^https://www\\.truecar\\.com/"},cosmetic:!0,prehideSelectors:[['div[aria-labelledby="cookie-banner-heading"]']],detectCmp:[{exists:'div[aria-labelledby="cookie-banner-heading"]'}],detectPopup:[{visible:'div[aria-labelledby="cookie-banner-heading"]'}],optIn:[{click:'div[aria-labelledby="cookie-banner-heading"] > button[aria-label="Close"]'}],optOut:[{hide:'div[aria-labelledby="cookie-banner-heading"]'}]},{name:"truyo",prehideSelectors:["#truyo-consent-module"],detectCmp:[{exists:"#truyo-cookieBarContent"}],detectPopup:[{visible:"#truyo-consent-module"}],optIn:[{click:"button#acceptAllCookieButton"}],optOut:[{click:"button#declineAllCookieButton"}]},{name:"tumblr-com",cosmetic:!0,prehideSelectors:["#cmp-app-container"],detectCmp:[{exists:"#cmp-app-container"}],detectPopup:[{visible:"#cmp-app-container"}],optIn:[{click:"#tumblr #cmp-app-container div.components-modal__frame > iframe > html body > div > div > div.cmp__dialog-footer > div > button.components-button.white-space-normal.is-primary"}],optOut:[{hide:"#cmp-app-container"}]},{name:"twitch-mobile",vendorUrl:"https://m.twitch.tv/",cosmetic:!0,runContext:{urlPattern:"^https?://m\\.twitch\\.tv"},prehideSelectors:[],detectCmp:[{exists:'.ReactModal__Overlay [href="https://www.twitch.tv/p/cookie-policy"]'}],detectPopup:[{visible:'.ReactModal__Overlay [href="https://www.twitch.tv/p/cookie-policy"]'}],optIn:[{waitForThenClick:'.ReactModal__Overlay:has([href="https://www.twitch.tv/p/cookie-policy"]) button'}],optOut:[{hide:'.ReactModal__Overlay:has([href="https://www.twitch.tv/p/cookie-policy"])'}]},{name:"twitch.tv",runContext:{urlPattern:"^https?://(www\\.)?twitch\\.tv"},prehideSelectors:["div:has(> .consent-banner .consent-banner__content--gdpr-v2),.ReactModalPortal:has([data-a-target=consent-modal-save])"],detectCmp:[{exists:".consent-banner .consent-banner__content--gdpr-v2"}],detectPopup:[{visible:".consent-banner .consent-banner__content--gdpr-v2"}],optIn:[{click:'button[data-a-target="consent-banner-accept"]'}],optOut:[{hide:"div:has(> .consent-banner .consent-banner__content--gdpr-v2)"},{click:'button[data-a-target="consent-banner-manage-preferences"]'},{waitFor:"input[type=checkbox][data-a-target=tw-checkbox]"},{click:"input[type=checkbox][data-a-target=tw-checkbox][checked]:not([disabled])",all:!0,optional:!0},{waitForThenClick:"[data-a-target=consent-modal-save]"},{waitForVisible:".ReactModalPortal:has([data-a-target=consent-modal-save])",check:"none"}]},{name:"twitter",runContext:{urlPattern:"^https://([a-z0-9-]+\\.)?twitter\\.com/"},prehideSelectors:['[data-testid="BottomBar"]'],detectCmp:[{exists:'[data-testid="BottomBar"] div'}],detectPopup:[{visible:'[data-testid="BottomBar"] div'}],optIn:[{waitForThenClick:'[data-testid="BottomBar"] > div:has(>div:first-child>div:last-child>span[role=button]) > div:last-child > div[role=button]:first-child'}],optOut:[{waitForThenClick:'[data-testid="BottomBar"] > div:has(>div:first-child>div:last-child>span[role=button]) > div:last-child > div[role=button]:last-child'}],TODOtest:[{eval:"EVAL_document.cookie.includes('d_prefs=MjoxLGNvbnNlbnRfdmVyc2lvbjoy')"}]},{name:"ubuntu.com",prehideSelectors:["dialog.cookie-policy"],detectCmp:[{any:[{exists:"dialog.cookie-policy header"},{exists:'xpath///*[@id="modal"]/div/header'}]}],detectPopup:[{any:[{visible:"dialog header"},{visible:'xpath///*[@id="modal"]/div/header'}]}],optIn:[{any:[{waitForThenClick:"#cookie-policy-button-accept"},{waitForThenClick:'xpath///*[@id="cookie-policy-button-accept"]'}]}],optOut:[{any:[{waitForThenClick:"button.js-manage"},{waitForThenClick:'xpath///*[@id="cookie-policy-content"]/p[4]/button[2]'}]},{waitForThenClick:"dialog.cookie-policy .p-switch__input:checked",optional:!0,all:!0,timeout:500},{any:[{waitForThenClick:"dialog.cookie-policy .js-save-preferences"},{waitForThenClick:'xpath///*[@id="modal"]/div/button'}]}],test:[{eval:"EVAL_UBUNTU_COM_0"}]},{name:"UK Cookie Consent",prehideSelectors:["#catapult-cookie-bar"],cosmetic:!0,detectCmp:[{exists:"#catapult-cookie-bar"}],detectPopup:[{exists:".has-cookie-bar #catapult-cookie-bar"}],optIn:[{click:"#catapultCookie"}],optOut:[{hide:"#catapult-cookie-bar"}],test:[{eval:"EVAL_UK_COOKIE_CONSENT_0"}]},{name:"urbanarmorgear-com",cosmetic:!0,prehideSelectors:['div[class^="Layout__CookieBannerContainer-"]'],detectCmp:[{exists:'div[class^="Layout__CookieBannerContainer-"]'}],detectPopup:[{visible:'div[class^="Layout__CookieBannerContainer-"]'}],optIn:[{click:'button[class^="CookieBanner__AcceptButton"]'}],optOut:[{hide:'div[class^="Layout__CookieBannerContainer-"]'}]},{name:"usercentrics-api",detectCmp:[{exists:"#usercentrics-root"}],detectPopup:[{eval:"EVAL_USERCENTRICS_API_0"},{exists:["#usercentrics-root","[data-testid=uc-container]"]},{waitForVisible:"#usercentrics-root",timeout:2e3}],optIn:[{eval:"EVAL_USERCENTRICS_API_3"},{eval:"EVAL_USERCENTRICS_API_1"},{eval:"EVAL_USERCENTRICS_API_5"}],optOut:[{eval:"EVAL_USERCENTRICS_API_1"},{eval:"EVAL_USERCENTRICS_API_2"}],test:[{eval:"EVAL_USERCENTRICS_API_6"}]},{name:"usercentrics-button",detectCmp:[{exists:"#usercentrics-button"}],detectPopup:[{visible:"#usercentrics-button #uc-btn-accept-banner"}],optIn:[{click:"#usercentrics-button #uc-btn-accept-banner"}],optOut:[{click:"#usercentrics-button #uc-btn-deny-banner"}],test:[{eval:"EVAL_USERCENTRICS_BUTTON_0"}]},{name:"uswitch.com",prehideSelectors:["#cookie-banner-wrapper"],detectCmp:[{exists:"#cookie-banner-wrapper"}],detectPopup:[{visible:"#cookie-banner-wrapper"}],optIn:[{click:"#cookie_banner_accept_mobile"}],optOut:[{click:"#cookie_banner_save"}]},{name:"vodafone.de",runContext:{urlPattern:"^https://www\\.vodafone\\.de/"},prehideSelectors:[".dip-consent,.dip-consent-container"],detectCmp:[{exists:".dip-consent-container"}],detectPopup:[{visible:".dip-consent-content"}],optOut:[{click:'.dip-consent-btn[tabindex="2"]'}],optIn:[{click:'.dip-consent-btn[tabindex="1"]'}]},{name:"waitrose.com",prehideSelectors:["div[aria-labelledby=CookieAlertModalHeading]","section[data-test=initial-waitrose-cookie-consent-banner]","section[data-test=cookie-consent-modal]"],detectCmp:[{exists:"section[data-test=initial-waitrose-cookie-consent-banner]"}],detectPopup:[{visible:"section[data-test=initial-waitrose-cookie-consent-banner]"}],optIn:[{click:"button[data-test=accept-all]"}],optOut:[{click:"button[data-test=manage-cookies]"},{wait:200},{eval:"EVAL_WAITROSE_0"},{click:"button[data-test=submit]"}],test:[{eval:"EVAL_WAITROSE_1"}]},{name:"webflow",vendorUrl:"https://webflow.com/",prehideSelectors:[".fs-cc-components"],detectCmp:[{exists:".fs-cc-components"}],detectPopup:[{visible:".fs-cc-components"},{visible:"[fs-cc=banner]"}],optIn:[{wait:500},{waitForThenClick:"[fs-cc=banner] [fs-cc=allow]"}],optOut:[{wait:500},{waitForThenClick:"[fs-cc=banner] [fs-cc=deny]"}]},{name:"wetransfer.com",detectCmp:[{exists:".welcome__cookie-notice"}],detectPopup:[{visible:".welcome__cookie-notice"}],optIn:[{click:".welcome__button--accept"}],optOut:[{click:".welcome__button--decline"}]},{name:"whitepages.com",runContext:{urlPattern:"^https://www\\.whitepages\\.com/"},cosmetic:!0,prehideSelectors:[".cookie-wrapper, .cookie-overlay"],detectCmp:[{exists:".cookie-wrapper"}],detectPopup:[{visible:".cookie-overlay"}],optIn:[{click:'button[aria-label="Got it"]'}],optOut:[{hide:".cookie-wrapper"}]},{name:"wolframalpha",vendorUrl:"https://www.wolframalpha.com",prehideSelectors:[],cosmetic:!0,runContext:{urlPattern:"^https://www\\.wolframalpha\\.com/"},detectCmp:[{exists:"section._a_yb"}],detectPopup:[{visible:"section._a_yb"}],optIn:[{waitForThenClick:"section._a_yb button"}],optOut:[{hide:"section._a_yb"}]},{name:"woo-commerce-com",prehideSelectors:[".wccom-comp-privacy-banner .wccom-privacy-banner"],detectCmp:[{exists:".wccom-comp-privacy-banner .wccom-privacy-banner"}],detectPopup:[{exists:".wccom-comp-privacy-banner .wccom-privacy-banner"}],optIn:[{click:".wccom-privacy-banner__content-buttons button.is-primary"}],optOut:[{click:".wccom-privacy-banner__content-buttons button.is-secondary"},{waitForThenClick:"input[type=checkbox][checked]:not([disabled])",all:!0},{click:"div.wccom-modal__footer > button"}]},{name:"WP Cookie Notice for GDPR",vendorUrl:"https://wordpress.org/plugins/gdpr-cookie-consent/",prehideSelectors:["#gdpr-cookie-consent-bar"],detectCmp:[{exists:"#gdpr-cookie-consent-bar"}],detectPopup:[{visible:"#gdpr-cookie-consent-bar"}],optIn:[{waitForThenClick:"#gdpr-cookie-consent-bar #cookie_action_accept"}],optOut:[{waitForThenClick:"#gdpr-cookie-consent-bar #cookie_action_reject"}],test:[{eval:"EVAL_WP_COOKIE_NOTICE_0"}]},{name:"wpcc",cosmetic:!0,prehideSelectors:[".wpcc-container"],detectCmp:[{exists:".wpcc-container"}],detectPopup:[{exists:".wpcc-container .wpcc-message"}],optIn:[{click:".wpcc-compliance .wpcc-btn"}],optOut:[{hide:".wpcc-container"}]},{name:"xe.com",vendorUrl:"https://www.xe.com/",runContext:{urlPattern:"^https://www\\.xe\\.com/"},prehideSelectors:["[class*=ConsentBanner]"],detectCmp:[{exists:"[class*=ConsentBanner]"}],detectPopup:[{visible:"[class*=ConsentBanner]"}],optIn:[{waitForThenClick:"[class*=ConsentBanner] .egnScw"}],optOut:[{wait:1e3},{waitForThenClick:"[class*=ConsentBanner] .frDWEu"},{waitForThenClick:"[class*=ConsentBanner] .hXIpFU"}],test:[{eval:"EVAL_XE_TEST"}]},{name:"xhamster-eu",prehideSelectors:[".cookies-modal"],detectCmp:[{exists:".cookies-modal"}],detectPopup:[{exists:".cookies-modal"}],optIn:[{click:"button.cmd-button-accept-all"}],optOut:[{click:"button.cmd-button-reject-all"}]},{name:"xhamster-us",runContext:{urlPattern:"^https://(www\\.)?xhamster\\d?\\.com"},cosmetic:!0,prehideSelectors:[".cookie-announce"],detectCmp:[{exists:".cookie-announce"}],detectPopup:[{visible:".cookie-announce .announce-text"}],optIn:[{click:".cookie-announce button.xh-button"}],optOut:[{hide:".cookie-announce"}]},{name:"xing.com",detectCmp:[{exists:"div[class^=cookie-consent-CookieConsent]"}],detectPopup:[{exists:"div[class^=cookie-consent-CookieConsent]"}],optIn:[{click:"#consent-accept-button"}],optOut:[{click:"#consent-settings-button"},{click:".consent-banner-button-accept-overlay"}],test:[{eval:"EVAL_XING_0"}]},{name:"xnxx-com",cosmetic:!0,prehideSelectors:["#cookies-use-alert"],detectCmp:[{exists:"#cookies-use-alert"}],detectPopup:[{visible:"#cookies-use-alert"}],optIn:[{click:"#cookies-use-alert .close"}],optOut:[{hide:"#cookies-use-alert"}]},{name:"xvideos",vendorUrl:"https://xvideos.com",runContext:{urlPattern:"^https://[^/]*xvideos\\.com/"},prehideSelectors:[],detectCmp:[{exists:".disclaimer-opened #disclaimer-cookies"}],detectPopup:[{visible:".disclaimer-opened #disclaimer-cookies"}],optIn:[{waitForThenClick:"#disclaimer-accept_cookies"}],optOut:[{waitForThenClick:"#disclaimer-reject_cookies"}]},{name:"Yahoo",runContext:{urlPattern:"^https://consent\\.yahoo\\.com/v2/"},prehideSelectors:["#reject-all"],detectCmp:[{exists:"#consent-page"}],detectPopup:[{visible:"#consent-page"}],optIn:[{waitForThenClick:"#consent-page button[value=agree]"}],optOut:[{waitForThenClick:"#consent-page button[value=reject]"}]},{name:"youporn.com",cosmetic:!0,prehideSelectors:[".euCookieModal, #js_euCookieModal"],detectCmp:[{exists:".euCookieModal"}],detectPopup:[{exists:".euCookieModal, #js_euCookieModal"}],optIn:[{click:'button[name="user_acceptCookie"]'}],optOut:[{hide:".euCookieModal"}]},{name:"youtube-desktop",prehideSelectors:["tp-yt-iron-overlay-backdrop.opened","ytd-consent-bump-v2-lightbox"],detectCmp:[{exists:"ytd-consent-bump-v2-lightbox tp-yt-paper-dialog"},{exists:'ytd-consent-bump-v2-lightbox tp-yt-paper-dialog a[href^="https://consent.youtube.com/"]'}],detectPopup:[{visible:"ytd-consent-bump-v2-lightbox tp-yt-paper-dialog"}],optIn:[{waitForThenClick:"ytd-consent-bump-v2-lightbox .eom-buttons .eom-button-row:first-child ytd-button-renderer:last-child #button,ytd-consent-bump-v2-lightbox .eom-buttons .eom-button-row:first-child ytd-button-renderer:last-child button"},{wait:500}],optOut:[{waitForThenClick:"ytd-consent-bump-v2-lightbox .eom-buttons .eom-button-row:first-child ytd-button-renderer:first-child #button,ytd-consent-bump-v2-lightbox .eom-buttons .eom-button-row:first-child ytd-button-renderer:first-child button"},{wait:500}],test:[{wait:500},{eval:"EVAL_YOUTUBE_DESKTOP_0"}]},{name:"youtube-mobile",prehideSelectors:[".consent-bump-v2-lightbox"],detectCmp:[{exists:"ytm-consent-bump-v2-renderer"}],detectPopup:[{visible:"ytm-consent-bump-v2-renderer"}],optIn:[{waitForThenClick:"ytm-consent-bump-v2-renderer .privacy-terms + .one-col-dialog-buttons c3-material-button:first-child button, ytm-consent-bump-v2-renderer .privacy-terms + .one-col-dialog-buttons ytm-button-renderer:first-child button"},{wait:500}],optOut:[{waitForThenClick:"ytm-consent-bump-v2-renderer .privacy-terms + .one-col-dialog-buttons c3-material-button:nth-child(2) button, ytm-consent-bump-v2-renderer .privacy-terms + .one-col-dialog-buttons ytm-button-renderer:nth-child(2) button"},{wait:500}],test:[{wait:500},{eval:"EVAL_YOUTUBE_MOBILE_0"}]},{name:"zdf",prehideSelectors:["#zdf-cmp-banner-sdk"],detectCmp:[{exists:"#zdf-cmp-banner-sdk"}],detectPopup:[{visible:"#zdf-cmp-main.zdf-cmp-show"}],optIn:[{waitForThenClick:"#zdf-cmp-main #zdf-cmp-accept-btn"}],optOut:[{waitForThenClick:"#zdf-cmp-main #zdf-cmp-deny-btn"}],test:[]}],A={"didomi.io":{detectors:[{presentMatcher:{target:{selector:"#didomi-host, #didomi-notice"},type:"css"},showingMatcher:{target:{selector:"body.didomi-popup-open, .didomi-notice-banner"},type:"css"}}],methods:[{action:{target:{selector:".didomi-popup-notice-buttons .didomi-button:not(.didomi-button-highlight), .didomi-notice-banner .didomi-learn-more-button"},type:"click"},name:"OPEN_OPTIONS"},{action:{actions:[{retries:50,target:{selector:"#didomi-purpose-cookies"},type:"waitcss",waitTime:50},{consents:[{description:"Share (everything) with others",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-share_whith_others]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-share_whith_others]:last-child"},type:"click"},type:"X"},{description:"Information storage and access",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-cookies]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-cookies]:last-child"},type:"click"},type:"D"},{description:"Content selection, offers and marketing",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-CL-T1Rgm7]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-CL-T1Rgm7]:last-child"},type:"click"},type:"E"},{description:"Analytics",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-analytics]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-analytics]:last-child"},type:"click"},type:"B"},{description:"Analytics",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-M9NRHJe3G]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-M9NRHJe3G]:last-child"},type:"click"},type:"B"},{description:"Ad and content selection",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-advertising_personalization]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-advertising_personalization]:last-child"},type:"click"},type:"F"},{description:"Ad and content selection",falseAction:{parent:{childFilter:{target:{selector:"#didomi-purpose-pub-ciblee"}},selector:".didomi-consent-popup-data-processing, .didomi-components-accordion-label-container"},target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-pub-ciblee]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-pub-ciblee]:last-child"},type:"click"},type:"F"},{description:"Ad and content selection - basics",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-q4zlJqdcD]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-q4zlJqdcD]:last-child"},type:"click"},type:"F"},{description:"Ad and content selection - partners and subsidiaries",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-partenaire-cAsDe8jC]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-partenaire-cAsDe8jC]:last-child"},type:"click"},type:"F"},{description:"Ad and content selection - social networks",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-p4em9a8m]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-p4em9a8m]:last-child"},type:"click"},type:"F"},{description:"Ad and content selection - others",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-autres-pub]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-autres-pub]:last-child"},type:"click"},type:"F"},{description:"Social networks",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-reseauxsociaux]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-reseauxsociaux]:last-child"},type:"click"},type:"A"},{description:"Social networks",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-social_media]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-social_media]:last-child"},type:"click"},type:"A"},{description:"Content selection",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-content_personalization]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-content_personalization]:last-child"},type:"click"},type:"E"},{description:"Ad delivery",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-ad_delivery]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-ad_delivery]:last-child"},type:"click"},type:"F"}],type:"consent"},{action:{consents:[{matcher:{childFilter:{target:{selector:":not(.didomi-components-radio__option--selected)"}},type:"css"},trueAction:{target:{selector:":nth-child(2)"},type:"click"},falseAction:{target:{selector:":first-child"},type:"click"},type:"X"}],type:"consent"},target:{selector:".didomi-components-radio"},type:"foreach"}],type:"list"},name:"DO_CONSENT"},{action:{parent:{selector:".didomi-consent-popup-footer .didomi-consent-popup-actions"},target:{selector:".didomi-components-button:first-child"},type:"click"},name:"SAVE_CONSENT"}]},oil:{detectors:[{presentMatcher:{target:{selector:".as-oil-content-overlay"},type:"css"},showingMatcher:{target:{selector:".as-oil-content-overlay"},type:"css"}}],methods:[{action:{actions:[{target:{selector:".as-js-advanced-settings"},type:"click"},{retries:"10",target:{selector:".as-oil-cpc__purpose-container"},type:"waitcss",waitTime:"250"}],type:"list"},name:"OPEN_OPTIONS"},{action:{actions:[{consents:[{matcher:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Information storage and access","Opbevaring af og adgang til oplysninger på din enhed"]},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Information storage and access","Opbevaring af og adgang til oplysninger på din enhed"]},target:{selector:".as-oil-cpc__switch"},type:"click"},type:"D"},{matcher:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Personlige annoncer","Personalisation"]},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Personlige annoncer","Personalisation"]},target:{selector:".as-oil-cpc__switch"},type:"click"},type:"E"},{matcher:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Annoncevalg, levering og rapportering","Ad selection, delivery, reporting"]},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Annoncevalg, levering og rapportering","Ad selection, delivery, reporting"]},target:{selector:".as-oil-cpc__switch"},type:"click"},type:"F"},{matcher:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Personalisering af indhold","Content selection, delivery, reporting"]},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Personalisering af indhold","Content selection, delivery, reporting"]},target:{selector:".as-oil-cpc__switch"},type:"click"},type:"E"},{matcher:{parent:{childFilter:{target:{selector:".as-oil-cpc__purpose-header",textFilter:["Måling","Measurement"]}},selector:".as-oil-cpc__purpose-container"},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{childFilter:{target:{selector:".as-oil-cpc__purpose-header",textFilter:["Måling","Measurement"]}},selector:".as-oil-cpc__purpose-container"},target:{selector:".as-oil-cpc__switch"},type:"click"},type:"B"},{matcher:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:"Google"},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:"Google"},target:{selector:".as-oil-cpc__switch"},type:"click"},type:"F"}],type:"consent"}],type:"list"},name:"DO_CONSENT"},{action:{target:{selector:".as-oil__btn-optin"},type:"click"},name:"SAVE_CONSENT"},{action:{target:{selector:"div.as-oil"},type:"hide"},name:"HIDE_CMP"}]},optanon:{detectors:[{presentMatcher:{target:{selector:"#optanon-menu, .optanon-alert-box-wrapper"},type:"css"},showingMatcher:{target:{displayFilter:!0,selector:".optanon-alert-box-wrapper"},type:"css"}}],methods:[{action:{actions:[{target:{selector:".optanon-alert-box-wrapper .optanon-toggle-display, a[onclick*='OneTrust.ToggleInfoDisplay()'], a[onclick*='Optanon.ToggleInfoDisplay()']"},type:"click"}],type:"list"},name:"OPEN_OPTIONS"},{action:{actions:[{target:{selector:".preference-menu-item #Your-privacy"},type:"click"},{target:{selector:"#optanon-vendor-consent-text"},type:"click"},{action:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"X"}],type:"consent"},target:{selector:"#optanon-vendor-consent-list .vendor-item"},type:"foreach"},{target:{selector:".vendor-consent-back-link"},type:"click"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-performance"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-performance"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"B"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-functional"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-functional"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"E"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-advertising"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-advertising"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"F"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-social"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-social"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"B"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Social Media Cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Social Media Cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"B"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Personalisation"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Personalisation"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"E"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Site monitoring cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Site monitoring cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"B"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Third party privacy-enhanced content"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Third party privacy-enhanced content"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"X"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Performance & Advertising Cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Performance & Advertising Cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"F"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Information storage and access"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Information storage and access"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"D"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Ad selection, delivery, reporting"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Ad selection, delivery, reporting"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"F"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Content selection, delivery, reporting"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Content selection, delivery, reporting"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"E"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Measurement"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Measurement"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"B"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Recommended Cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Recommended Cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"X"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Unclassified Cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Unclassified Cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"X"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Analytical Cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Analytical Cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"B"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Marketing Cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Marketing Cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"F"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Personalization"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Personalization"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"E"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Ad Selection, Delivery & Reporting"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Ad Selection, Delivery & Reporting"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"F"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Content Selection, Delivery & Reporting"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Content Selection, Delivery & Reporting"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"E"}],type:"consent"}],type:"list"},type:"ifcss"}],type:"list"},name:"DO_CONSENT"},{action:{parent:{selector:".optanon-save-settings-button"},target:{selector:".optanon-white-button-middle"},type:"click"},name:"SAVE_CONSENT"},{action:{actions:[{target:{selector:"#optanon-popup-wrapper"},type:"hide"},{target:{selector:"#optanon-popup-bg"},type:"hide"},{target:{selector:".optanon-alert-box-wrapper"},type:"hide"}],type:"list"},name:"HIDE_CMP"}]},quantcast2:{detectors:[{presentMatcher:{target:{selector:"[data-tracking-opt-in-overlay]"},type:"css"},showingMatcher:{target:{selector:"[data-tracking-opt-in-overlay] [data-tracking-opt-in-learn-more]"},type:"css"}}],methods:[{action:{target:{selector:"[data-tracking-opt-in-overlay] [data-tracking-opt-in-learn-more]"},type:"click"},name:"OPEN_OPTIONS"},{action:{actions:[{type:"wait",waitTime:500},{action:{actions:[{target:{selector:"div",textFilter:["Information storage and access"]},trueAction:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"D"}],type:"consent"},type:"ifcss"},{target:{selector:"div",textFilter:["Personalization"]},trueAction:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"F"}],type:"consent"},type:"ifcss"},{target:{selector:"div",textFilter:["Ad selection, delivery, reporting"]},trueAction:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"F"}],type:"consent"},type:"ifcss"},{target:{selector:"div",textFilter:["Content selection, delivery, reporting"]},trueAction:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"E"}],type:"consent"},type:"ifcss"},{target:{selector:"div",textFilter:["Measurement"]},trueAction:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"B"}],type:"consent"},type:"ifcss"},{target:{selector:"div",textFilter:["Other Partners"]},trueAction:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"X"}],type:"consent"},type:"ifcss"}],type:"list"},parent:{childFilter:{target:{selector:"input"}},selector:"[data-tracking-opt-in-overlay] > div > div"},target:{childFilter:{target:{selector:"input"}},selector:":scope > div"},type:"foreach"}],type:"list"},name:"DO_CONSENT"},{action:{target:{selector:"[data-tracking-opt-in-overlay] [data-tracking-opt-in-save]"},type:"click"},name:"SAVE_CONSENT"}]},springer:{detectors:[{presentMatcher:{parent:null,target:{selector:".cmp-app_gdpr"},type:"css"},showingMatcher:{parent:null,target:{displayFilter:!0,selector:".cmp-popup_popup"},type:"css"}}],methods:[{action:{actions:[{target:{selector:".cmp-intro_rejectAll"},type:"click"},{type:"wait",waitTime:250},{target:{selector:".cmp-purposes_purposeItem:not(.cmp-purposes_selectedPurpose)"},type:"click"}],type:"list"},name:"OPEN_OPTIONS"},{action:{consents:[{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Przechowywanie informacji na urządzeniu lub dostęp do nich",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Przechowywanie informacji na urządzeniu lub dostęp do nich",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"D"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Wybór podstawowych reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Wybór podstawowych reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"F"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Tworzenie profilu spersonalizowanych reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Tworzenie profilu spersonalizowanych reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"F"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Wybór spersonalizowanych reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Wybór spersonalizowanych reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"E"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Tworzenie profilu spersonalizowanych treści",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Tworzenie profilu spersonalizowanych treści",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"E"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Wybór spersonalizowanych treści",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Wybór spersonalizowanych treści",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"B"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Pomiar wydajności reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Pomiar wydajności reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"B"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Pomiar wydajności treści",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Pomiar wydajności treści",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"B"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Stosowanie badań rynkowych w celu generowania opinii odbiorców",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Stosowanie badań rynkowych w celu generowania opinii odbiorców",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"X"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Opracowywanie i ulepszanie produktów",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Opracowywanie i ulepszanie produktów",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"X"}],type:"consent"},name:"DO_CONSENT"},{action:{target:{selector:".cmp-details_save"},type:"click"},name:"SAVE_CONSENT"}]},wordpressgdpr:{detectors:[{presentMatcher:{parent:null,target:{selector:".wpgdprc-consent-bar"},type:"css"},showingMatcher:{parent:null,target:{displayFilter:!0,selector:".wpgdprc-consent-bar"},type:"css"}}],methods:[{action:{parent:null,target:{selector:".wpgdprc-consent-bar .wpgdprc-consent-bar__settings",textFilter:null},type:"click"},name:"OPEN_OPTIONS"},{action:{actions:[{target:{selector:".wpgdprc-consent-modal .wpgdprc-button",textFilter:"Eyeota"},type:"click"},{consents:[{description:"Eyeota Cookies",matcher:{parent:{selector:".wpgdprc-consent-modal__description",textFilter:"Eyeota"},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".wpgdprc-consent-modal__description",textFilter:"Eyeota"},target:{selector:"label"},type:"click"},type:"X"}],type:"consent"},{target:{selector:".wpgdprc-consent-modal .wpgdprc-button",textFilter:"Advertising"},type:"click"},{consents:[{description:"Advertising Cookies",matcher:{parent:{selector:".wpgdprc-consent-modal__description",textFilter:"Advertising"},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".wpgdprc-consent-modal__description",textFilter:"Advertising"},target:{selector:"label"},type:"click"},type:"F"}],type:"consent"}],type:"list"},name:"DO_CONSENT"},{action:{parent:null,target:{selector:".wpgdprc-button",textFilter:"Save my settings"},type:"click"},name:"SAVE_CONSENT"}]}},E={autoconsent:f,consentomatic:A},x=Object.freeze({__proto__:null,autoconsent:f,consentomatic:A,default:E});const O=new class{constructor(e,t=null,o=null){if(this.id=a(),this.rules=[],this.foundCmp=null,this.state={lifecycle:"loading",prehideOn:!1,findCmpAttempts:0,detectedCmps:[],detectedPopups:[],selfTest:null},r.sendContentMessage=e,this.sendContentMessage=e,this.rules=[],this.updateState({lifecycle:"loading"}),this.addDynamicRules(),t)this.initialize(t,o);else{o&&this.parseDeclarativeRules(o);e({type:"init",url:window.location.href}),this.updateState({lifecycle:"waitingForInitResponse"})}this.domActions=new v(this)}initialize(e,t){const o=g(e);if(o.logs.lifecycle&&console.log("autoconsent init",window.location.href),this.config=o,o.enabled){if(t&&this.parseDeclarativeRules(t),this.rules=function(e,t){return e.filter((e=>(!t.disabledCmps||!t.disabledCmps.includes(e.name))&&(t.enableCosmeticRules||!e.isCosmetic)))}(this.rules,o),e.enablePrehide)if(document.documentElement)this.prehideElements();else{const e=()=>{window.removeEventListener("DOMContentLoaded",e),this.prehideElements()};window.addEventListener("DOMContentLoaded",e)}if("loading"===document.readyState){const e=()=>{window.removeEventListener("DOMContentLoaded",e),this.start()};window.addEventListener("DOMContentLoaded",e)}else this.start();this.updateState({lifecycle:"initialized"})}else o.logs.lifecycle&&console.log("autoconsent is disabled")}addDynamicRules(){C.forEach((e=>{this.rules.push(new e(this))}))}parseDeclarativeRules(e){Object.keys(e.consentomatic).forEach((t=>{this.addConsentomaticCMP(t,e.consentomatic[t])})),e.autoconsent.forEach((e=>{this.addDeclarativeCMP(e)}))}addDeclarativeCMP(e){this.rules.push(new u(e,this))}addConsentomaticCMP(e,t){this.rules.push(new m(`com_${e}`,t))}start(){window.requestIdleCallback?window.requestIdleCallback((()=>this._start()),{timeout:500}):this._start()}async _start(){const e=this.config.logs;e.lifecycle&&console.log(`Detecting CMPs on ${window.location.href}`),this.updateState({lifecycle:"started"});const t=await this.findCmp(this.config.detectRetries);if(this.updateState({detectedCmps:t.map((e=>e.name))}),0===t.length)return e.lifecycle&&console.log("no CMP found",location.href),this.config.enablePrehide&&this.undoPrehide(),this.updateState({lifecycle:"nothingDetected"}),!1;this.updateState({lifecycle:"cmpDetected"});const o=[],c=[];for(const e of t)e.isCosmetic?c.push(e):o.push(e);let i=!1,n=await this.detectPopups(o,(async e=>{i=await this.handlePopup(e)}));if(0===n.length&&(n=await this.detectPopups(c,(async e=>{i=await this.handlePopup(e)}))),0===n.length)return e.lifecycle&&console.log("no popup found"),this.config.enablePrehide&&this.undoPrehide(),!1;if(n.length>1){const t={msg:"Found multiple CMPs, check the detection rules.",cmps:n.map((e=>e.name))};e.errors&&console.warn(t.msg,t.cmps),this.sendContentMessage({type:"autoconsentError",details:t})}return i}async findCmp(e){const t=this.config.logs;this.updateState({findCmpAttempts:this.state.findCmpAttempts+1});const o=[];for(const e of this.rules)try{if(!e.checkRunContext())continue;await e.detectCmp()&&(t.lifecycle&&console.log(`Found CMP: ${e.name} ${window.location.href}`),this.sendContentMessage({type:"cmpDetected",url:location.href,cmp:e.name}),o.push(e))}catch(o){t.errors&&console.warn(`error detecting ${e.name}`,o)}return 0===o.length&&e>0?(await this.domActions.wait(500),this.findCmp(e-1)):o}async detectPopup(e){if(await this.waitForPopup(e).catch((t=>(this.config.logs.errors&&console.warn(`error waiting for a popup for ${e.name}`,t),!1))))return this.updateState({detectedPopups:this.state.detectedPopups.concat([e.name])}),this.sendContentMessage({type:"popupFound",cmp:e.name,url:location.href}),e;throw new Error("Popup is not shown")}async detectPopups(e,t){const o=e.map((e=>this.detectPopup(e)));await Promise.any(o).then((e=>{t(e)})).catch((()=>null));const c=await Promise.allSettled(o),i=[];for(const e of c)"fulfilled"===e.status&&i.push(e.value);return i}async handlePopup(e){return this.updateState({lifecycle:"openPopupDetected"}),this.config.enablePrehide&&!this.state.prehideOn&&this.prehideElements(),this.foundCmp=e,"optOut"===this.config.autoAction?await this.doOptOut():"optIn"===this.config.autoAction?await this.doOptIn():(this.config.logs.lifecycle&&console.log("waiting for opt-out signal...",location.href),!0)}async doOptOut(){const e=this.config.logs;let t;return this.updateState({lifecycle:"runningOptOut"}),this.foundCmp?(e.lifecycle&&console.log(`CMP ${this.foundCmp.name}: opt out on ${window.location.href}`),t=await this.foundCmp.optOut(),e.lifecycle&&console.log(`${this.foundCmp.name}: opt out result ${t}`)):(e.errors&&console.log("no CMP to opt out"),t=!1),this.config.enablePrehide&&this.undoPrehide(),this.sendContentMessage({type:"optOutResult",cmp:this.foundCmp?this.foundCmp.name:"none",result:t,scheduleSelfTest:this.foundCmp&&this.foundCmp.hasSelfTest,url:location.href}),t&&!this.foundCmp.isIntermediate?(this.sendContentMessage({type:"autoconsentDone",cmp:this.foundCmp.name,isCosmetic:this.foundCmp.isCosmetic,url:location.href}),this.updateState({lifecycle:"done"})):this.updateState({lifecycle:t?"optOutSucceeded":"optOutFailed"}),t}async doOptIn(){const e=this.config.logs;let t;return this.updateState({lifecycle:"runningOptIn"}),this.foundCmp?(e.lifecycle&&console.log(`CMP ${this.foundCmp.name}: opt in on ${window.location.href}`),t=await this.foundCmp.optIn(),e.lifecycle&&console.log(`${this.foundCmp.name}: opt in result ${t}`)):(e.errors&&console.log("no CMP to opt in"),t=!1),this.config.enablePrehide&&this.undoPrehide(),this.sendContentMessage({type:"optInResult",cmp:this.foundCmp?this.foundCmp.name:"none",result:t,scheduleSelfTest:!1,url:location.href}),t&&!this.foundCmp.isIntermediate?(this.sendContentMessage({type:"autoconsentDone",cmp:this.foundCmp.name,isCosmetic:this.foundCmp.isCosmetic,url:location.href}),this.updateState({lifecycle:"done"})):this.updateState({lifecycle:t?"optInSucceeded":"optInFailed"}),t}async doSelfTest(){const e=this.config.logs;let t;return this.foundCmp?(e.lifecycle&&console.log(`CMP ${this.foundCmp.name}: self-test on ${window.location.href}`),t=await this.foundCmp.test()):(e.errors&&console.log("no CMP to self test"),t=!1),this.sendContentMessage({type:"selfTestResult",cmp:this.foundCmp?this.foundCmp.name:"none",result:t,url:location.href}),this.updateState({selfTest:t}),t}async waitForPopup(e,t=5,o=500){const c=this.config.logs;c.lifecycle&&console.log("checking if popup is open...",e.name);const i=await e.detectPopup().catch((t=>(c.errors&&console.warn(`error detecting popup for ${e.name}`,t),!1)));return!i&&t>0?(await this.domActions.wait(o),this.waitForPopup(e,t-1,o)):(c.lifecycle&&console.log(e.name,"popup is "+(i?"open":"not open")),i)}prehideElements(){const e=this.config.logs,t=this.rules.reduce(((e,t)=>t.prehideSelectors?[...e,...t.prehideSelectors]:e),["#didomi-popup,.didomi-popup-container,.didomi-popup-notice,.didomi-consent-popup-preferences,#didomi-notice,.didomi-popup-backdrop,.didomi-screen-medium"]);return this.updateState({prehideOn:!0}),setTimeout((()=>{this.config.enablePrehide&&this.state.prehideOn&&!["runningOptOut","runningOptIn"].includes(this.state.lifecycle)&&(e.lifecycle&&console.log("Process is taking too long, unhiding elements"),this.undoPrehide())}),this.config.prehideTimeout||2e3),this.domActions.prehide(t.join(","))}undoPrehide(){return this.updateState({prehideOn:!1}),this.domActions.undoPrehide()}updateState(e){Object.assign(this.state,e),this.sendContentMessage({type:"report",instanceId:this.id,url:window.location.href,mainFrame:window.top===window.self,state:this.state})}async receiveMessageCallback(e){const t=this.config?.logs;switch(t?.messages&&console.log("received from background",e,window.location.href),e.type){case"initResp":this.initialize(e.config,e.rules);break;case"optIn":await this.doOptIn();break;case"optOut":await this.doOptOut();break;case"selfTest":await this.doSelfTest();break;case"evalResp":!function(e,t){const o=r.pending.get(e);o?(r.pending.delete(e),o.timer&&window.clearTimeout(o.timer),o.resolve(t)):console.warn("no eval #",e)}(e.id,e.result)}}}((e=>{window.webkit.messageHandlers[e.type]&&window.webkit.messageHandlers[e.type].postMessage(e).then((e=>{O.receiveMessageCallback(e)}))}),null,x);window.autoconsentMessageCallback=e=>{O.receiveMessageCallback(e)}}(); diff --git a/package-lock.json b/package-lock.json index 2947f69fc5..52bf0dfcf3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,7 @@ "name": "ios", "version": "1.0.0", "dependencies": { - "@duckduckgo/autoconsent": "^9.7.2" + "@duckduckgo/autoconsent": "^10.2.0" }, "devDependencies": { "@rollup/plugin-json": "^4.1.0", @@ -135,9 +135,9 @@ } }, "node_modules/@duckduckgo/autoconsent": { - "version": "9.7.2", - "resolved": "https://registry.npmjs.org/@duckduckgo/autoconsent/-/autoconsent-9.7.2.tgz", - "integrity": "sha512-LaJdRoZwRneDPzzVQdiHLPNjMW6B773edisKXiWYUXZOIXzGbwifKjCiYHTtDtaeaLjpuiWXlz2a9yEwMszK2A==" + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/@duckduckgo/autoconsent/-/autoconsent-10.2.0.tgz", + "integrity": "sha512-Q4sSGrvA5nWl5auJzttPQu1t25ff9N8Xj/UYglNKNqcnMAx/KxAIP5KbAFgf7JBru+q9Dq7muaEEB4FPU31fEw==" }, "node_modules/@eslint/eslintrc": { "version": "0.4.3", diff --git a/package.json b/package.json index 3224a7c33c..89d3c66b8c 100644 --- a/package.json +++ b/package.json @@ -6,17 +6,17 @@ "rebuild-autoconsent": "rollup -c" }, "devDependencies": { + "@rollup/plugin-json": "^4.1.0", + "@rollup/plugin-node-resolve": "^13.0.6", "eslint": "^7.32.0", "eslint-config-standard": "^16.0.3", "eslint-plugin-import": "^2.25.2", "eslint-plugin-node": "^11.1.0", "eslint-plugin-promise": "^5.1.0", - "@rollup/plugin-json": "^4.1.0", - "@rollup/plugin-node-resolve": "^13.0.6", "rollup": "^2.61.0", "rollup-plugin-terser": "^7.0.2" }, "dependencies": { - "@duckduckgo/autoconsent": "^9.7.2" + "@duckduckgo/autoconsent": "^10.2.0" } } From 0ca189da2047831d6cc1b11275823c9532c5ec4a Mon Sep 17 00:00:00 2001 From: Christopher Brind Date: Mon, 11 Mar 2024 14:39:27 +0000 Subject: [PATCH 14/19] Release 7.112.0-0 (#2572) --- Configuration/Version.xcconfig | 2 +- .../AppPrivacyConfigurationDataProvider.swift | 4 +- Core/AppTrackerDataSetProvider.swift | 4 +- Core/ios-config.json | 33 +- Core/trackerData.json | 1987 ++++++++++------- DuckDuckGo.xcodeproj/project.pbxproj | 56 +- DuckDuckGo/Settings.bundle/Root.plist | 2 +- 7 files changed, 1270 insertions(+), 818 deletions(-) diff --git a/Configuration/Version.xcconfig b/Configuration/Version.xcconfig index 96f77f5f65..cd6af05c70 100644 --- a/Configuration/Version.xcconfig +++ b/Configuration/Version.xcconfig @@ -1 +1 @@ -MARKETING_VERSION = 7.111.0 +MARKETING_VERSION = 7.112.0 diff --git a/Core/AppPrivacyConfigurationDataProvider.swift b/Core/AppPrivacyConfigurationDataProvider.swift index 4f17bd9b2e..beb758d321 100644 --- a/Core/AppPrivacyConfigurationDataProvider.swift +++ b/Core/AppPrivacyConfigurationDataProvider.swift @@ -23,8 +23,8 @@ import BrowserServicesKit final public class AppPrivacyConfigurationDataProvider: EmbeddedDataProvider { public struct Constants { - public static let embeddedDataETag = "\"a427a69043b2baa27604bc10edb13de1\"" - public static let embeddedDataSHA = "1d5c2e4113713fbf02bc617fc689981604ea53be172569a9fd744054b7355c39" + public static let embeddedDataETag = "\"73cfb8d6f397fd1de921f057db1bcc44\"" + public static let embeddedDataSHA = "3debb4a1e5c6cc292b3c03d9ea6ce4daa8073ab0c033131f2f8dbe1f752dfaf1" } public var embeddedDataEtag: String { diff --git a/Core/AppTrackerDataSetProvider.swift b/Core/AppTrackerDataSetProvider.swift index cf23ed6b99..21b907d9fb 100644 --- a/Core/AppTrackerDataSetProvider.swift +++ b/Core/AppTrackerDataSetProvider.swift @@ -23,8 +23,8 @@ import BrowserServicesKit final public class AppTrackerDataSetProvider: EmbeddedDataProvider { public struct Constants { - public static let embeddedDataETag = "\"0b6a7a2629abc170a505b92aebd67017\"" - public static let embeddedDataSHA = "32cd805f6be415e77affdf51929494c7add6363234cef58ea8b53ca3a08c86d4" + public static let embeddedDataETag = "\"07bd7f610e3fa234856abcc2b56ab10e\"" + public static let embeddedDataSHA = "1d7ef8f4c5a717a5d82f43383e33290021358d6255db12b6fdd0928e28d123ee" } public var embeddedDataEtag: String { diff --git a/Core/ios-config.json b/Core/ios-config.json index d1f5828cd3..3da96e361b 100644 --- a/Core/ios-config.json +++ b/Core/ios-config.json @@ -1,6 +1,6 @@ { "readme": "https://github.com/duckduckgo/privacy-configuration", - "version": 1709563256742, + "version": 1710155313369, "features": { "adClickAttribution": { "readme": "https://help.duckduckgo.com/duckduckgo-help-pages/privacy/web-tracking-protections/#3rd-party-tracker-loading-protection", @@ -261,6 +261,9 @@ { "domain": "newsmax.com" }, + { + "domain": "meneame.net" + }, { "domain": "earth.google.com" }, @@ -305,7 +308,7 @@ } } }, - "hash": "b72f756b119c762183d04867e8927892" + "hash": "7401ca90e7ef3907f1c0f8cdf163b994" }, "autofill": { "exceptions": [ @@ -3009,6 +3012,9 @@ { "domain": "newser.com", "rules": [ + { + "type": "disable-default" + }, { "selector": ".RightRailAds", "type": "hide-empty" @@ -3999,7 +4005,7 @@ ] }, "state": "enabled", - "hash": "70c18c8d5c08210844cb238822888fd7" + "hash": "065f70c0112e8f3a149bf4bc56f35203" }, "exceptionHandler": { "exceptions": [ @@ -4926,6 +4932,16 @@ } ] }, + "adition.com": { + "rules": [ + { + "rule": "ad3.adfarm1.adition.com/js", + "domains": [ + "servustv.com" + ] + } + ] + }, "adlightning.com": { "rules": [ { @@ -5567,7 +5583,8 @@ "rule": "dpm.demdex.net/id", "domains": [ "dhl.de", - "homedepot.com" + "homedepot.com", + "sbs.com.au" ] } ] @@ -5917,6 +5934,12 @@ "channel4.com" ] }, + { + "rule": "2a7e9.v.fwmrm.net/ad/l/1", + "domains": [ + "channel4.com" + ] + }, { "rule": "7cbf2.v.fwmrm.net/ad/g/1", "domains": [ @@ -7826,7 +7849,7 @@ "domain": "sundancecatalog.com" } ], - "hash": "6768849b4f63b2e635698ac9dde79aa3" + "hash": "cff67ed6fc2962e0016ac3c65b6e4633" }, "trackingCookies1p": { "settings": { diff --git a/Core/trackerData.json b/Core/trackerData.json index 2e842e28c9..647e15dbb6 100644 --- a/Core/trackerData.json +++ b/Core/trackerData.json @@ -1,6 +1,6 @@ { "_builtWith": { - "tracker-radar": "9b6a3c1e62c8a97f63db77d2aef4f185129f05aad0f770425bcb54cbf8e0db84-4013b4e91930c643394cb31c6c745356f133b04f", + "tracker-radar": "09133e827d9dcbba9465c87efdf0229ddd910d3e867f8ccd5efc31abd7073963-4013b4e91930c643394cb31c6c745356f133b04f", "tracker-surrogates": "ba0d8cefe4432723ec75b998241efd2454dff35a" }, "readme": "https://github.com/duckduckgo/tracker-blocklists", @@ -464,7 +464,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -475,7 +475,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -521,7 +521,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -2409,7 +2409,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -2420,7 +2420,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -2464,7 +2464,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -2475,7 +2475,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -2590,7 +2590,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -2724,7 +2724,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -2735,7 +2735,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -3054,7 +3054,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -3181,7 +3181,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -3305,7 +3305,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -3689,7 +3689,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -3700,7 +3700,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -3711,7 +3711,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -3722,7 +3722,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -3788,7 +3788,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -3840,7 +3840,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -3851,7 +3851,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -3988,7 +3988,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -4339,7 +4339,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -4374,7 +4374,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -4820,7 +4820,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -4831,7 +4831,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -5033,7 +5033,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -5075,7 +5075,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -5098,7 +5098,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -5133,7 +5133,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -5162,7 +5162,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -5450,7 +5450,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -5567,7 +5567,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -5578,7 +5578,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -5589,7 +5589,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -5600,7 +5600,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -5637,7 +5637,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -5692,7 +5692,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -6228,7 +6228,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -6382,7 +6382,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -6698,7 +6698,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -6709,7 +6709,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -6829,7 +6829,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -7025,7 +7025,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -7061,7 +7061,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -7072,7 +7072,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -7083,7 +7083,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -7666,7 +7666,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -7677,7 +7677,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -7688,7 +7688,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -7769,7 +7769,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -7892,7 +7892,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -7903,7 +7903,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -7981,7 +7981,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -7992,7 +7992,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -8107,7 +8107,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -8258,7 +8258,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -8269,7 +8269,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -8760,7 +8760,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -8771,7 +8771,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -8812,7 +8812,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -9228,7 +9228,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -9833,7 +9833,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -9844,7 +9844,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -9855,7 +9855,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -9895,7 +9895,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -9937,7 +9937,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -9991,7 +9991,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -10059,7 +10059,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -10070,7 +10070,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -10118,7 +10118,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -10244,7 +10244,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -10404,7 +10404,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -10415,7 +10415,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -10426,7 +10426,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -10453,7 +10453,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -10503,7 +10503,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -10550,7 +10550,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -11028,7 +11028,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -11068,7 +11068,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -11804,7 +11804,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -11893,7 +11893,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -11904,7 +11904,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -12126,7 +12126,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -12166,7 +12166,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -12177,7 +12177,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -12188,7 +12188,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -12199,7 +12199,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -12525,7 +12525,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -12536,7 +12536,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -12547,7 +12547,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -14455,7 +14455,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -15140,7 +15140,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -15215,7 +15215,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -15266,7 +15266,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -16125,7 +16125,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -16136,7 +16136,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -16165,7 +16165,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -16384,7 +16384,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -16693,7 +16693,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -16704,7 +16704,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -16889,7 +16889,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -16938,7 +16938,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -17212,7 +17212,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -17223,7 +17223,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -17786,7 +17786,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -18134,7 +18134,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -18273,7 +18273,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -18632,7 +18632,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -18843,7 +18843,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -20020,7 +20020,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -20241,7 +20241,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -20252,7 +20252,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -20263,7 +20263,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -20403,7 +20403,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -20890,7 +20890,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -20919,7 +20919,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -20930,7 +20930,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -20941,7 +20941,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -21005,7 +21005,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -21085,7 +21085,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -21133,7 +21133,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -21144,7 +21144,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -21184,7 +21184,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -21195,7 +21195,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -21230,7 +21230,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -21241,7 +21241,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -21374,7 +21374,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -21458,7 +21458,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -21929,7 +21929,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -21940,7 +21940,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -21951,7 +21951,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -22021,7 +22021,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -22032,7 +22032,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -22185,7 +22185,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -22196,7 +22196,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -22238,7 +22238,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -22249,7 +22249,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -22260,7 +22260,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -22530,7 +22530,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -22541,7 +22541,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -22602,7 +22602,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -22660,7 +22660,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -22671,7 +22671,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -22771,7 +22771,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -22794,7 +22794,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -22805,7 +22805,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -23162,7 +23162,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -23266,7 +23266,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -23402,7 +23402,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -23477,7 +23477,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -23488,7 +23488,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -23532,7 +23532,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -23543,7 +23543,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -23554,7 +23554,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -23565,7 +23565,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -23645,7 +23645,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -23678,7 +23678,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -23729,7 +23729,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -23865,7 +23865,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -23994,7 +23994,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -24005,7 +24005,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -24252,7 +24252,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -24263,7 +24263,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -24274,7 +24274,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -24555,7 +24555,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -24590,7 +24590,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -24631,7 +24631,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -24747,7 +24747,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -24758,7 +24758,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -24787,7 +24787,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -24883,7 +24883,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -24940,7 +24940,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -24951,7 +24951,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -25072,7 +25072,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -25229,7 +25229,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -25255,7 +25255,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -25266,7 +25266,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -25329,7 +25329,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -25340,7 +25340,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -25351,7 +25351,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -25582,7 +25582,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -25609,7 +25609,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -25620,7 +25620,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -25631,7 +25631,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -25661,7 +25661,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -25672,7 +25672,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -25700,7 +25700,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -25711,7 +25711,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -25784,7 +25784,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -25832,7 +25832,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -25843,7 +25843,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -25854,7 +25854,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -25865,7 +25865,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -25876,7 +25876,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -25887,7 +25887,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -25959,7 +25959,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -25970,7 +25970,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -26024,7 +26024,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -26226,7 +26226,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -26534,7 +26534,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -26545,7 +26545,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -26556,7 +26556,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -26807,7 +26807,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -26818,7 +26818,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -27236,7 +27236,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -28093,7 +28093,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -28223,7 +28223,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -28234,7 +28234,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -28367,7 +28367,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -28450,7 +28450,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -28461,7 +28461,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -28822,7 +28822,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -29129,7 +29129,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -29140,7 +29140,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -29266,7 +29266,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -29277,7 +29277,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -31019,7 +31019,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -31324,7 +31324,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32484,7 +32484,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32495,7 +32495,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32506,7 +32506,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32517,7 +32517,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32528,7 +32528,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32539,7 +32539,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32550,7 +32550,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32561,7 +32561,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32572,7 +32572,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32583,7 +32583,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32594,7 +32594,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32605,7 +32605,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32616,7 +32616,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32627,7 +32627,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32638,7 +32638,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32649,7 +32649,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32660,7 +32660,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32671,7 +32671,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32682,7 +32682,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32693,7 +32693,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32704,7 +32704,18 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, + "fingerprinting": 1, + "cookies": 0.01, + "default": "block" + }, + "ambientlagoon.com": { + "domain": "ambientlagoon.com", + "owner": { + "name": "Leven Labs, Inc. DBA Admiral", + "displayName": "Admiral" + }, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32715,7 +32726,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32726,7 +32737,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32737,7 +32748,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32748,7 +32759,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32759,7 +32770,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32770,7 +32781,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32781,7 +32792,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32792,7 +32803,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32803,7 +32814,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32814,7 +32825,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32825,7 +32836,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32836,7 +32847,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32847,7 +32858,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32858,7 +32869,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32869,7 +32880,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32880,7 +32891,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32891,7 +32902,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32902,7 +32913,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32913,7 +32924,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32924,7 +32935,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32935,7 +32946,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32946,7 +32957,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32957,7 +32968,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32968,7 +32979,18 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, + "fingerprinting": 1, + "cookies": 0.01, + "default": "block" + }, + "beancontrol.com": { + "domain": "beancontrol.com", + "owner": { + "name": "Leven Labs, Inc. DBA Admiral", + "displayName": "Admiral" + }, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32979,7 +33001,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32990,7 +33012,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33001,7 +33023,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33012,7 +33034,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33023,7 +33045,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33034,7 +33056,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33045,7 +33067,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33056,7 +33078,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33067,7 +33089,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33078,7 +33100,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33089,7 +33111,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33100,7 +33122,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33111,7 +33133,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33122,7 +33144,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33133,7 +33155,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33144,7 +33166,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33155,7 +33177,29 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, + "fingerprinting": 1, + "cookies": 0.01, + "default": "block" + }, + "brighttoe.com": { + "domain": "brighttoe.com", + "owner": { + "name": "Leven Labs, Inc. DBA Admiral", + "displayName": "Admiral" + }, + "prevalence": 0.0151, + "fingerprinting": 1, + "cookies": 0.01, + "default": "block" + }, + "briskstorm.com": { + "domain": "briskstorm.com", + "owner": { + "name": "Leven Labs, Inc. DBA Admiral", + "displayName": "Admiral" + }, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33166,7 +33210,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33177,7 +33221,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33188,7 +33232,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33199,7 +33243,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33210,7 +33254,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33221,7 +33265,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33232,7 +33276,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33243,7 +33287,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33254,7 +33298,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33265,7 +33309,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33276,7 +33320,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33287,7 +33331,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33298,7 +33342,18 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, + "fingerprinting": 1, + "cookies": 0.01, + "default": "block" + }, + "captivatingcanyon.com": { + "domain": "captivatingcanyon.com", + "owner": { + "name": "Leven Labs, Inc. DBA Admiral", + "displayName": "Admiral" + }, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33309,7 +33364,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33320,7 +33375,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33331,7 +33386,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33342,7 +33397,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33353,7 +33408,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33364,7 +33419,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33375,7 +33430,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33386,7 +33441,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33397,7 +33452,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33408,7 +33463,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33419,7 +33474,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33430,7 +33485,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33441,7 +33496,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33452,7 +33507,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33463,7 +33518,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33474,7 +33529,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33485,7 +33540,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33496,7 +33551,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33507,7 +33562,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33518,7 +33573,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33529,7 +33584,18 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, + "fingerprinting": 1, + "cookies": 0.01, + "default": "block" + }, + "cloisteredcurve.com": { + "domain": "cloisteredcurve.com", + "owner": { + "name": "Leven Labs, Inc. DBA Admiral", + "displayName": "Admiral" + }, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33540,7 +33606,18 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, + "fingerprinting": 1, + "cookies": 0.01, + "default": "block" + }, + "coatfood.com": { + "domain": "coatfood.com", + "owner": { + "name": "Leven Labs, Inc. DBA Admiral", + "displayName": "Admiral" + }, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33551,7 +33628,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33562,7 +33639,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33573,7 +33650,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33584,7 +33661,18 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, + "fingerprinting": 1, + "cookies": 0.01, + "default": "block" + }, + "combbit.com": { + "domain": "combbit.com", + "owner": { + "name": "Leven Labs, Inc. DBA Admiral", + "displayName": "Admiral" + }, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33595,7 +33683,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33606,7 +33694,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33617,7 +33705,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33628,7 +33716,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33639,7 +33727,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33650,7 +33738,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33661,7 +33749,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33672,7 +33760,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33683,7 +33771,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33694,7 +33782,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33705,7 +33793,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33716,7 +33804,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33727,7 +33815,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33738,7 +33826,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33749,7 +33837,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33760,7 +33848,18 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, + "fingerprinting": 1, + "cookies": 0.01, + "default": "block" + }, + "cozytryst.com": { + "domain": "cozytryst.com", + "owner": { + "name": "Leven Labs, Inc. DBA Admiral", + "displayName": "Admiral" + }, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33771,7 +33870,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33782,7 +33881,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33793,7 +33892,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33804,7 +33903,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33815,7 +33914,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33826,7 +33925,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33837,7 +33936,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33848,7 +33947,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33859,7 +33958,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33870,7 +33969,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33881,7 +33980,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33892,7 +33991,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33903,7 +34002,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33914,7 +34013,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33925,7 +34024,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33936,7 +34035,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33947,7 +34046,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33958,7 +34057,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33969,7 +34068,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33980,7 +34079,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33991,7 +34090,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34002,7 +34101,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34013,7 +34112,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34024,7 +34123,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34035,7 +34134,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34046,7 +34145,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34057,7 +34156,18 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, + "fingerprinting": 1, + "cookies": 0.01, + "default": "block" + }, + "dinnerquartz.com": { + "domain": "dinnerquartz.com", + "owner": { + "name": "Leven Labs, Inc. DBA Admiral", + "displayName": "Admiral" + }, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34068,7 +34178,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34079,7 +34189,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34090,7 +34200,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34101,7 +34211,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34112,7 +34222,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34123,7 +34233,18 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, + "fingerprinting": 1, + "cookies": 0.01, + "default": "block" + }, + "drollwharf.com": { + "domain": "drollwharf.com", + "owner": { + "name": "Leven Labs, Inc. DBA Admiral", + "displayName": "Admiral" + }, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34134,7 +34255,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34145,7 +34266,18 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, + "fingerprinting": 1, + "cookies": 0.01, + "default": "block" + }, + "effervescentcoral.com": { + "domain": "effervescentcoral.com", + "owner": { + "name": "Leven Labs, Inc. DBA Admiral", + "displayName": "Admiral" + }, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34156,7 +34288,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34167,7 +34299,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34178,7 +34310,18 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, + "fingerprinting": 1, + "cookies": 0.01, + "default": "block" + }, + "elusivebreeze.com": { + "domain": "elusivebreeze.com", + "owner": { + "name": "Leven Labs, Inc. DBA Admiral", + "displayName": "Admiral" + }, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34189,7 +34332,18 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, + "fingerprinting": 1, + "cookies": 0.01, + "default": "block" + }, + "enchantingdiscovery.com": { + "domain": "enchantingdiscovery.com", + "owner": { + "name": "Leven Labs, Inc. DBA Admiral", + "displayName": "Admiral" + }, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34200,7 +34354,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34211,7 +34365,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34222,7 +34376,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34233,7 +34387,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34244,7 +34398,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34255,7 +34409,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34266,7 +34420,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34277,7 +34431,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34288,7 +34442,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34299,7 +34453,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34310,7 +34464,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34321,7 +34475,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34332,7 +34486,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34343,7 +34497,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34354,7 +34508,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34365,7 +34519,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34376,7 +34530,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34387,7 +34541,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34398,7 +34552,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34409,7 +34563,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34420,7 +34574,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34431,7 +34585,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34442,7 +34596,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34453,7 +34607,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34464,7 +34618,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34475,7 +34629,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34486,7 +34640,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34497,7 +34651,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34508,7 +34662,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34519,7 +34673,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34530,7 +34684,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34541,7 +34695,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34552,7 +34706,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34563,7 +34717,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34574,7 +34728,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34585,7 +34739,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34596,7 +34750,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34607,7 +34761,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34618,7 +34772,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34629,7 +34783,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34640,7 +34794,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34651,7 +34805,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34662,7 +34816,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34673,7 +34827,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34684,7 +34838,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34695,7 +34849,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34706,7 +34860,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34717,7 +34871,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34728,7 +34882,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34739,7 +34893,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34750,7 +34904,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34761,7 +34915,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34772,7 +34926,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34783,7 +34937,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34794,7 +34948,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34805,7 +34959,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34816,7 +34970,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34827,7 +34981,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34838,7 +34992,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34849,7 +35003,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34860,7 +35014,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34871,7 +35025,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34882,7 +35036,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34893,7 +35047,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34904,7 +35058,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34915,7 +35069,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34926,7 +35080,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34937,7 +35091,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34948,7 +35102,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34959,7 +35113,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34970,7 +35124,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34981,7 +35135,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34992,7 +35146,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35003,7 +35157,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35014,7 +35168,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35025,7 +35179,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35036,7 +35190,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35047,7 +35201,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35058,7 +35212,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35069,7 +35223,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35080,7 +35234,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35091,7 +35245,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35102,7 +35256,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35113,7 +35267,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35124,7 +35278,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35135,7 +35289,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35146,7 +35300,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35157,7 +35311,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35168,7 +35322,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35179,7 +35333,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35190,7 +35344,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35201,7 +35355,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35212,7 +35366,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35223,7 +35377,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35234,7 +35388,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35245,7 +35399,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35256,7 +35410,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35267,7 +35421,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35278,7 +35432,18 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, + "fingerprinting": 1, + "cookies": 0.01, + "default": "block" + }, + "jubilantaura.com": { + "domain": "jubilantaura.com", + "owner": { + "name": "Leven Labs, Inc. DBA Admiral", + "displayName": "Admiral" + }, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35289,7 +35454,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35300,7 +35465,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35311,7 +35476,18 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, + "fingerprinting": 1, + "cookies": 0.01, + "default": "block" + }, + "jubilanttempest.com": { + "domain": "jubilanttempest.com", + "owner": { + "name": "Leven Labs, Inc. DBA Admiral", + "displayName": "Admiral" + }, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35322,7 +35498,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35333,7 +35509,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35344,7 +35520,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35355,7 +35531,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35366,7 +35542,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35377,7 +35553,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35388,7 +35564,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35399,7 +35575,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35410,7 +35586,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35421,7 +35597,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35432,7 +35608,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35443,7 +35619,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35454,7 +35630,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35465,7 +35641,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35476,7 +35652,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35487,7 +35663,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35498,7 +35674,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35509,7 +35685,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35520,7 +35696,18 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, + "fingerprinting": 1, + "cookies": 0.01, + "default": "block" + }, + "luminousboulevard.com": { + "domain": "luminousboulevard.com", + "owner": { + "name": "Leven Labs, Inc. DBA Admiral", + "displayName": "Admiral" + }, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35531,7 +35718,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35542,7 +35729,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35553,7 +35740,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35564,7 +35751,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35575,7 +35762,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35586,7 +35773,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35597,7 +35784,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35608,7 +35795,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35619,7 +35806,18 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, + "fingerprinting": 1, + "cookies": 0.01, + "default": "block" + }, + "melodiouschorus.com": { + "domain": "melodiouschorus.com", + "owner": { + "name": "Leven Labs, Inc. DBA Admiral", + "displayName": "Admiral" + }, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35630,7 +35828,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35641,7 +35839,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35652,7 +35850,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35663,7 +35861,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35674,7 +35872,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35685,7 +35883,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35696,7 +35894,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35707,7 +35905,18 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, + "fingerprinting": 1, + "cookies": 0.01, + "default": "block" + }, + "monacobeatles.com": { + "domain": "monacobeatles.com", + "owner": { + "name": "Leven Labs, Inc. DBA Admiral", + "displayName": "Admiral" + }, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35718,7 +35927,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35729,7 +35938,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35740,7 +35949,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35751,7 +35960,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35762,7 +35971,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35773,7 +35982,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35784,7 +35993,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35795,7 +36004,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35806,7 +36015,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35817,7 +36026,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35828,7 +36037,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35839,7 +36048,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35850,7 +36059,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35861,7 +36070,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35872,7 +36081,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35883,7 +36092,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35894,7 +36103,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35905,7 +36114,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35916,7 +36125,18 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, + "fingerprinting": 1, + "cookies": 0.01, + "default": "block" + }, + "nocturnalloom.com": { + "domain": "nocturnalloom.com", + "owner": { + "name": "Leven Labs, Inc. DBA Admiral", + "displayName": "Admiral" + }, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35927,7 +36147,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35938,7 +36158,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35949,7 +36169,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35960,7 +36180,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35971,7 +36191,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35982,7 +36202,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35993,7 +36213,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36004,7 +36224,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36015,7 +36235,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36026,7 +36246,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36037,7 +36257,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36048,7 +36268,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36059,7 +36279,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36070,7 +36290,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36081,7 +36301,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36092,7 +36312,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36103,7 +36323,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36114,7 +36334,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36125,7 +36345,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36136,7 +36356,18 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, + "fingerprinting": 1, + "cookies": 0.01, + "default": "block" + }, + "piquantgrove.com": { + "domain": "piquantgrove.com", + "owner": { + "name": "Leven Labs, Inc. DBA Admiral", + "displayName": "Admiral" + }, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36147,7 +36378,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36158,7 +36389,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36169,7 +36400,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36180,7 +36411,18 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, + "fingerprinting": 1, + "cookies": 0.01, + "default": "block" + }, + "playfulriver.com": { + "domain": "playfulriver.com", + "owner": { + "name": "Leven Labs, Inc. DBA Admiral", + "displayName": "Admiral" + }, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36191,7 +36433,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36202,7 +36444,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36213,7 +36455,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36224,7 +36466,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36235,7 +36477,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36246,7 +36488,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36257,7 +36499,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36268,7 +36510,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36279,7 +36521,18 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, + "fingerprinting": 1, + "cookies": 0.01, + "default": "block" + }, + "pristinegale.com": { + "domain": "pristinegale.com", + "owner": { + "name": "Leven Labs, Inc. DBA Admiral", + "displayName": "Admiral" + }, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36290,7 +36543,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36301,7 +36554,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36312,7 +36565,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36323,7 +36576,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36334,7 +36587,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36345,7 +36598,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36356,7 +36609,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36367,7 +36620,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36378,7 +36631,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36389,7 +36642,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36400,7 +36653,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36411,7 +36664,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36422,7 +36675,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36433,7 +36686,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36444,7 +36697,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36455,7 +36708,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36466,7 +36719,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36477,7 +36730,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36488,7 +36741,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36499,7 +36752,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36510,7 +36763,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36521,7 +36774,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36532,7 +36785,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36543,7 +36796,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36554,7 +36807,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36565,7 +36818,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36576,7 +36829,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36587,7 +36840,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36598,7 +36851,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36609,7 +36862,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36620,7 +36873,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36631,7 +36884,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36642,7 +36895,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36653,7 +36906,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36664,7 +36917,18 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, + "fingerprinting": 1, + "cookies": 0.01, + "default": "block" + }, + "rollconnection.com": { + "domain": "rollconnection.com", + "owner": { + "name": "Leven Labs, Inc. DBA Admiral", + "displayName": "Admiral" + }, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36675,7 +36939,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36686,7 +36950,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36697,7 +36961,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36708,7 +36972,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36719,7 +36983,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36730,7 +36994,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36741,7 +37005,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36752,7 +37016,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36763,7 +37027,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36774,7 +37038,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36785,7 +37049,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36796,7 +37060,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36807,7 +37071,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36818,7 +37082,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36829,7 +37093,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36840,7 +37104,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36851,7 +37115,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36862,7 +37126,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36873,7 +37137,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36884,7 +37148,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36895,7 +37159,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36906,7 +37170,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36917,7 +37181,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36928,7 +37192,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36939,7 +37203,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36950,7 +37214,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36961,7 +37225,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36972,7 +37236,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36983,7 +37247,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36994,7 +37258,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37005,7 +37269,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37016,7 +37280,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37027,7 +37291,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37038,7 +37302,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37049,7 +37313,18 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, + "fingerprinting": 1, + "cookies": 0.01, + "default": "block" + }, + "shinypond.com": { + "domain": "shinypond.com", + "owner": { + "name": "Leven Labs, Inc. DBA Admiral", + "displayName": "Admiral" + }, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37060,7 +37335,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37071,7 +37346,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37082,7 +37357,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37093,7 +37368,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37104,7 +37379,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37115,7 +37390,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37126,7 +37401,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37137,7 +37412,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37148,7 +37423,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37159,7 +37434,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37170,7 +37445,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37181,7 +37456,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37192,7 +37467,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37203,7 +37478,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37214,7 +37489,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37225,7 +37500,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37236,7 +37511,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37247,7 +37522,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37258,7 +37533,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37269,7 +37544,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37280,7 +37555,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37291,7 +37566,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37302,7 +37577,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37313,7 +37588,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37324,7 +37599,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37335,7 +37610,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37346,7 +37621,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37357,7 +37632,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37368,7 +37643,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37379,7 +37654,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37390,7 +37665,18 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, + "fingerprinting": 1, + "cookies": 0.01, + "default": "block" + }, + "sprysummit.com": { + "domain": "sprysummit.com", + "owner": { + "name": "Leven Labs, Inc. DBA Admiral", + "displayName": "Admiral" + }, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37401,7 +37687,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37412,7 +37698,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37423,7 +37709,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37434,7 +37720,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37445,7 +37731,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37456,7 +37742,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37467,7 +37753,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37478,7 +37764,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37489,7 +37775,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37500,7 +37786,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37511,7 +37797,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37522,7 +37808,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37533,7 +37819,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37544,7 +37830,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37555,7 +37841,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37566,7 +37852,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37577,7 +37863,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37588,7 +37874,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37599,7 +37885,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37610,7 +37896,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37621,7 +37907,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37632,7 +37918,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37643,7 +37929,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37654,7 +37940,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37665,7 +37951,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37676,7 +37962,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37687,7 +37973,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37698,7 +37984,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37709,7 +37995,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37720,7 +38006,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37731,7 +38017,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37742,7 +38028,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37753,7 +38039,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37764,7 +38050,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37775,7 +38061,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37786,7 +38072,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37797,7 +38083,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37808,7 +38094,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37819,7 +38105,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37830,7 +38116,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37841,7 +38127,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37852,7 +38138,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37863,7 +38149,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37874,7 +38160,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37885,7 +38171,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37896,7 +38182,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37907,7 +38193,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37918,7 +38204,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37929,7 +38215,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37940,7 +38226,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37951,7 +38237,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37962,7 +38248,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37973,7 +38259,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37984,7 +38270,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -37995,7 +38281,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38006,7 +38292,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38017,7 +38303,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38028,7 +38314,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38039,7 +38325,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38050,7 +38336,29 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, + "fingerprinting": 1, + "cookies": 0.01, + "default": "block" + }, + "tranquilveil.com": { + "domain": "tranquilveil.com", + "owner": { + "name": "Leven Labs, Inc. DBA Admiral", + "displayName": "Admiral" + }, + "prevalence": 0.0151, + "fingerprinting": 1, + "cookies": 0.01, + "default": "block" + }, + "tranquilveranda.com": { + "domain": "tranquilveranda.com", + "owner": { + "name": "Leven Labs, Inc. DBA Admiral", + "displayName": "Admiral" + }, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38061,7 +38369,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38072,7 +38380,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38083,7 +38391,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38094,7 +38402,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38105,7 +38413,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38116,7 +38424,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38127,7 +38435,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38138,7 +38446,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38149,7 +38457,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38160,7 +38468,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38171,7 +38479,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38182,7 +38490,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38193,7 +38501,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38204,7 +38512,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38215,7 +38523,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38226,7 +38534,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38237,7 +38545,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38248,7 +38556,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38259,7 +38567,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38270,7 +38578,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38281,7 +38589,18 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, + "fingerprinting": 1, + "cookies": 0.01, + "default": "block" + }, + "uppitytime.com": { + "domain": "uppitytime.com", + "owner": { + "name": "Leven Labs, Inc. DBA Admiral", + "displayName": "Admiral" + }, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38292,7 +38611,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38303,7 +38622,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38314,7 +38633,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38325,7 +38644,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38336,7 +38655,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38347,7 +38666,18 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, + "fingerprinting": 1, + "cookies": 0.01, + "default": "block" + }, + "verdantlabyrinth.com": { + "domain": "verdantlabyrinth.com", + "owner": { + "name": "Leven Labs, Inc. DBA Admiral", + "displayName": "Admiral" + }, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38358,7 +38688,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38369,7 +38699,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38380,7 +38710,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38391,7 +38721,18 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, + "fingerprinting": 1, + "cookies": 0.01, + "default": "block" + }, + "vibrantpact.com": { + "domain": "vibrantpact.com", + "owner": { + "name": "Leven Labs, Inc. DBA Admiral", + "displayName": "Admiral" + }, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38402,7 +38743,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38413,7 +38754,18 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, + "fingerprinting": 1, + "cookies": 0.01, + "default": "block" + }, + "vividcanopy.com": { + "domain": "vividcanopy.com", + "owner": { + "name": "Leven Labs, Inc. DBA Admiral", + "displayName": "Admiral" + }, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38424,7 +38776,18 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, + "fingerprinting": 1, + "cookies": 0.01, + "default": "block" + }, + "vividplume.com": { + "domain": "vividplume.com", + "owner": { + "name": "Leven Labs, Inc. DBA Admiral", + "displayName": "Admiral" + }, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38435,7 +38798,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38446,7 +38809,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38457,7 +38820,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38468,7 +38831,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38479,7 +38842,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38490,7 +38853,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38501,7 +38864,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38512,7 +38875,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38523,7 +38886,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38534,7 +38897,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38545,7 +38908,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38556,7 +38919,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38567,7 +38930,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38578,7 +38941,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38589,7 +38952,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38600,7 +38963,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38611,7 +38974,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -38622,7 +38985,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0146, + "prevalence": 0.0151, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -48649,6 +49012,7 @@ "alluringbucket.com", "aloofvest.com", "ambientdusk.com", + "ambientlagoon.com", "ambiguousafternoon.com", "ambiguousdinosaurs.com", "ambrosialsummit.com", @@ -48684,6 +49048,7 @@ "basketballbelieve.com", "bawdybalance.com", "beamvolcano.com", + "beancontrol.com", "bedsberry.com", "beginnerpancake.com", "begintrain.com", @@ -48706,6 +49071,8 @@ "bravecalculator.com", "breadbalance.com", "breakfastboat.com", + "brighttoe.com", + "briskstorm.com", "broadborder.com", "brotherslocket.com", "buildingknife.com", @@ -48723,6 +49090,7 @@ "calmcactus.com", "capablecup.com", "capriciouscorn.com", + "captivatingcanyon.com", "carefuldolls.com", "caringcast.com", "carpentercomparison.com", @@ -48754,12 +49122,15 @@ "circlelevel.com", "cleanhaircut.com", "cloisteredcord.com", + "cloisteredcurve.com", "closedcows.com", + "coatfood.com", "coldbalance.com", "colossalclouds.com", "colossalcoat.com", "colossalcry.com", "combativecar.com", + "combbit.com", "combcattle.com", "combcompetition.com", "comfortablecheese.com", @@ -48777,6 +49148,7 @@ "courageousbaby.com", "coverapparatus.com", "cozyhillside.com", + "cozytryst.com", "crabbychin.com", "cratecamera.com", "creatorcherry.com", @@ -48817,6 +49189,7 @@ "devilishdinner.com", "dewdroplagoon.com", "digestiondrawer.com", + "dinnerquartz.com", "diplomahawaii.com", "disagreeabledrop.com", "discreetfield.com", @@ -48828,12 +49201,16 @@ "drainpaste.com", "dramaticdirection.com", "dreamycanyon.com", + "drollwharf.com", "dustydime.com", "dustyhammer.com", + "effervescentcoral.com", "effervescentvista.com", "elasticchange.com", "elderlybean.com", + "elusivebreeze.com", "eminentbubble.com", + "enchantingdiscovery.com", "enchantingmystique.com", "encouragingthread.com", "endurablebulb.com", @@ -48967,9 +49344,11 @@ "internalcondition.com", "internalsink.com", "j93557g.com", + "jubilantaura.com", "jubilantcanyon.com", "jubilantcascade.com", "jubilantglimmer.com", + "jubilanttempest.com", "jubilantwhisper.com", "kaputquill.com", "knitstamp.com", @@ -48994,6 +49373,7 @@ "losslace.com", "lovelydrum.com", "ludicrousarch.com", + "luminousboulevard.com", "luminouscatalyst.com", "lumpylumber.com", "lunchroomlock.com", @@ -49009,6 +49389,7 @@ "measlymiddle.com", "meatydime.com", "meddleplant.com", + "melodiouschorus.com", "meltmilk.com", "memopilot.com", "memorizematch.com", @@ -49019,6 +49400,7 @@ "minorcattle.com", "mixedreading.com", "modularmental.com", + "monacobeatles.com", "moorshoes.com", "motionflowers.com", "motionlessbag.com", @@ -49041,6 +49423,7 @@ "nervoussummer.com", "niftyhospital.com", "nightwound.com", + "nocturnalloom.com", "nondescriptcrowd.com", "nondescriptnote.com", "nostalgicneed.com", @@ -49069,11 +49452,13 @@ "passivepolo.com", "peacefullimit.com", "petiteumbrella.com", + "piquantgrove.com", "piquantvortex.com", "placidactivity.com", "placidperson.com", "planebasin.com", "plantdigestion.com", + "playfulriver.com", "pleasantpump.com", "plotrabbit.com", "pluckypocket.com", @@ -49094,6 +49479,7 @@ "priceypies.com", "pricklydebt.com", "pricklypollution.com", + "pristinegale.com", "processplantation.com", "profusesupport.com", "protestcopy.com", @@ -49149,6 +49535,7 @@ "righteouscrayon.com", "rightfulfall.com", "rigidrobin.com", + "rollconnection.com", "roofrelation.com", "roseincome.com", "ruralrobin.com", @@ -49202,6 +49589,7 @@ "shapecomb.com", "sheargovernor.com", "shesubscriptions.com", + "shinypond.com", "shirtsidewalk.com", "shiveringspot.com", "shiverscissors.com", @@ -49249,6 +49637,7 @@ "spookysleet.com", "spotlessstamp.com", "spottednoise.com", + "sprysummit.com", "spuriousair.com", "spysubstance.com", "squalidscrew.com", @@ -49334,6 +49723,8 @@ "tranquilcan.com", "tranquilcanyon.com", "tranquilplume.com", + "tranquilveil.com", + "tranquilveranda.com", "tremendousearthquake.com", "tremendousplastic.com", "tritebadge.com", @@ -49361,6 +49752,7 @@ "unwieldyhealth.com", "unwieldyimpulse.com", "unwieldyplastic.com", + "uppitytime.com", "uselesslumber.com", "vanishmemory.com", "velvetquasar.com", @@ -49368,13 +49760,17 @@ "venomousvessel.com", "venusgloria.com", "verdantanswer.com", + "verdantlabyrinth.com", "verdantloom.com", "verseballs.com", "vibrantgale.com", "vibranthaven.com", + "vibrantpact.com", "vibranttalisman.com", "virtualvincent.com", + "vividcanopy.com", "vividmeadow.com", + "vividplume.com", "volatileprofit.com", "volatilevessel.com", "voraciousgrip.com", @@ -49399,7 +49795,7 @@ "zipperxray.com", "zlp6s.pw" ], - "prevalence": 0.0146, + "prevalence": 0.0151, "displayName": "Admiral" } }, @@ -50146,6 +50542,7 @@ "alluringbucket.com": "Leven Labs, Inc. DBA Admiral", "aloofvest.com": "Leven Labs, Inc. DBA Admiral", "ambientdusk.com": "Leven Labs, Inc. DBA Admiral", + "ambientlagoon.com": "Leven Labs, Inc. DBA Admiral", "ambiguousafternoon.com": "Leven Labs, Inc. DBA Admiral", "ambiguousdinosaurs.com": "Leven Labs, Inc. DBA Admiral", "ambrosialsummit.com": "Leven Labs, Inc. DBA Admiral", @@ -50181,6 +50578,7 @@ "basketballbelieve.com": "Leven Labs, Inc. DBA Admiral", "bawdybalance.com": "Leven Labs, Inc. DBA Admiral", "beamvolcano.com": "Leven Labs, Inc. DBA Admiral", + "beancontrol.com": "Leven Labs, Inc. DBA Admiral", "bedsberry.com": "Leven Labs, Inc. DBA Admiral", "beginnerpancake.com": "Leven Labs, Inc. DBA Admiral", "begintrain.com": "Leven Labs, Inc. DBA Admiral", @@ -50203,6 +50601,8 @@ "bravecalculator.com": "Leven Labs, Inc. DBA Admiral", "breadbalance.com": "Leven Labs, Inc. DBA Admiral", "breakfastboat.com": "Leven Labs, Inc. DBA Admiral", + "brighttoe.com": "Leven Labs, Inc. DBA Admiral", + "briskstorm.com": "Leven Labs, Inc. DBA Admiral", "broadborder.com": "Leven Labs, Inc. DBA Admiral", "brotherslocket.com": "Leven Labs, Inc. DBA Admiral", "buildingknife.com": "Leven Labs, Inc. DBA Admiral", @@ -50220,6 +50620,7 @@ "calmcactus.com": "Leven Labs, Inc. DBA Admiral", "capablecup.com": "Leven Labs, Inc. DBA Admiral", "capriciouscorn.com": "Leven Labs, Inc. DBA Admiral", + "captivatingcanyon.com": "Leven Labs, Inc. DBA Admiral", "carefuldolls.com": "Leven Labs, Inc. DBA Admiral", "caringcast.com": "Leven Labs, Inc. DBA Admiral", "carpentercomparison.com": "Leven Labs, Inc. DBA Admiral", @@ -50251,12 +50652,15 @@ "circlelevel.com": "Leven Labs, Inc. DBA Admiral", "cleanhaircut.com": "Leven Labs, Inc. DBA Admiral", "cloisteredcord.com": "Leven Labs, Inc. DBA Admiral", + "cloisteredcurve.com": "Leven Labs, Inc. DBA Admiral", "closedcows.com": "Leven Labs, Inc. DBA Admiral", + "coatfood.com": "Leven Labs, Inc. DBA Admiral", "coldbalance.com": "Leven Labs, Inc. DBA Admiral", "colossalclouds.com": "Leven Labs, Inc. DBA Admiral", "colossalcoat.com": "Leven Labs, Inc. DBA Admiral", "colossalcry.com": "Leven Labs, Inc. DBA Admiral", "combativecar.com": "Leven Labs, Inc. DBA Admiral", + "combbit.com": "Leven Labs, Inc. DBA Admiral", "combcattle.com": "Leven Labs, Inc. DBA Admiral", "combcompetition.com": "Leven Labs, Inc. DBA Admiral", "comfortablecheese.com": "Leven Labs, Inc. DBA Admiral", @@ -50274,6 +50678,7 @@ "courageousbaby.com": "Leven Labs, Inc. DBA Admiral", "coverapparatus.com": "Leven Labs, Inc. DBA Admiral", "cozyhillside.com": "Leven Labs, Inc. DBA Admiral", + "cozytryst.com": "Leven Labs, Inc. DBA Admiral", "crabbychin.com": "Leven Labs, Inc. DBA Admiral", "cratecamera.com": "Leven Labs, Inc. DBA Admiral", "creatorcherry.com": "Leven Labs, Inc. DBA Admiral", @@ -50314,6 +50719,7 @@ "devilishdinner.com": "Leven Labs, Inc. DBA Admiral", "dewdroplagoon.com": "Leven Labs, Inc. DBA Admiral", "digestiondrawer.com": "Leven Labs, Inc. DBA Admiral", + "dinnerquartz.com": "Leven Labs, Inc. DBA Admiral", "diplomahawaii.com": "Leven Labs, Inc. DBA Admiral", "disagreeabledrop.com": "Leven Labs, Inc. DBA Admiral", "discreetfield.com": "Leven Labs, Inc. DBA Admiral", @@ -50325,12 +50731,16 @@ "drainpaste.com": "Leven Labs, Inc. DBA Admiral", "dramaticdirection.com": "Leven Labs, Inc. DBA Admiral", "dreamycanyon.com": "Leven Labs, Inc. DBA Admiral", + "drollwharf.com": "Leven Labs, Inc. DBA Admiral", "dustydime.com": "Leven Labs, Inc. DBA Admiral", "dustyhammer.com": "Leven Labs, Inc. DBA Admiral", + "effervescentcoral.com": "Leven Labs, Inc. DBA Admiral", "effervescentvista.com": "Leven Labs, Inc. DBA Admiral", "elasticchange.com": "Leven Labs, Inc. DBA Admiral", "elderlybean.com": "Leven Labs, Inc. DBA Admiral", + "elusivebreeze.com": "Leven Labs, Inc. DBA Admiral", "eminentbubble.com": "Leven Labs, Inc. DBA Admiral", + "enchantingdiscovery.com": "Leven Labs, Inc. DBA Admiral", "enchantingmystique.com": "Leven Labs, Inc. DBA Admiral", "encouragingthread.com": "Leven Labs, Inc. DBA Admiral", "endurablebulb.com": "Leven Labs, Inc. DBA Admiral", @@ -50464,9 +50874,11 @@ "internalcondition.com": "Leven Labs, Inc. DBA Admiral", "internalsink.com": "Leven Labs, Inc. DBA Admiral", "j93557g.com": "Leven Labs, Inc. DBA Admiral", + "jubilantaura.com": "Leven Labs, Inc. DBA Admiral", "jubilantcanyon.com": "Leven Labs, Inc. DBA Admiral", "jubilantcascade.com": "Leven Labs, Inc. DBA Admiral", "jubilantglimmer.com": "Leven Labs, Inc. DBA Admiral", + "jubilanttempest.com": "Leven Labs, Inc. DBA Admiral", "jubilantwhisper.com": "Leven Labs, Inc. DBA Admiral", "kaputquill.com": "Leven Labs, Inc. DBA Admiral", "knitstamp.com": "Leven Labs, Inc. DBA Admiral", @@ -50491,6 +50903,7 @@ "losslace.com": "Leven Labs, Inc. DBA Admiral", "lovelydrum.com": "Leven Labs, Inc. DBA Admiral", "ludicrousarch.com": "Leven Labs, Inc. DBA Admiral", + "luminousboulevard.com": "Leven Labs, Inc. DBA Admiral", "luminouscatalyst.com": "Leven Labs, Inc. DBA Admiral", "lumpylumber.com": "Leven Labs, Inc. DBA Admiral", "lunchroomlock.com": "Leven Labs, Inc. DBA Admiral", @@ -50506,6 +50919,7 @@ "measlymiddle.com": "Leven Labs, Inc. DBA Admiral", "meatydime.com": "Leven Labs, Inc. DBA Admiral", "meddleplant.com": "Leven Labs, Inc. DBA Admiral", + "melodiouschorus.com": "Leven Labs, Inc. DBA Admiral", "meltmilk.com": "Leven Labs, Inc. DBA Admiral", "memopilot.com": "Leven Labs, Inc. DBA Admiral", "memorizematch.com": "Leven Labs, Inc. DBA Admiral", @@ -50516,6 +50930,7 @@ "minorcattle.com": "Leven Labs, Inc. DBA Admiral", "mixedreading.com": "Leven Labs, Inc. DBA Admiral", "modularmental.com": "Leven Labs, Inc. DBA Admiral", + "monacobeatles.com": "Leven Labs, Inc. DBA Admiral", "moorshoes.com": "Leven Labs, Inc. DBA Admiral", "motionflowers.com": "Leven Labs, Inc. DBA Admiral", "motionlessbag.com": "Leven Labs, Inc. DBA Admiral", @@ -50538,6 +50953,7 @@ "nervoussummer.com": "Leven Labs, Inc. DBA Admiral", "niftyhospital.com": "Leven Labs, Inc. DBA Admiral", "nightwound.com": "Leven Labs, Inc. DBA Admiral", + "nocturnalloom.com": "Leven Labs, Inc. DBA Admiral", "nondescriptcrowd.com": "Leven Labs, Inc. DBA Admiral", "nondescriptnote.com": "Leven Labs, Inc. DBA Admiral", "nostalgicneed.com": "Leven Labs, Inc. DBA Admiral", @@ -50566,11 +50982,13 @@ "passivepolo.com": "Leven Labs, Inc. DBA Admiral", "peacefullimit.com": "Leven Labs, Inc. DBA Admiral", "petiteumbrella.com": "Leven Labs, Inc. DBA Admiral", + "piquantgrove.com": "Leven Labs, Inc. DBA Admiral", "piquantvortex.com": "Leven Labs, Inc. DBA Admiral", "placidactivity.com": "Leven Labs, Inc. DBA Admiral", "placidperson.com": "Leven Labs, Inc. DBA Admiral", "planebasin.com": "Leven Labs, Inc. DBA Admiral", "plantdigestion.com": "Leven Labs, Inc. DBA Admiral", + "playfulriver.com": "Leven Labs, Inc. DBA Admiral", "pleasantpump.com": "Leven Labs, Inc. DBA Admiral", "plotrabbit.com": "Leven Labs, Inc. DBA Admiral", "pluckypocket.com": "Leven Labs, Inc. DBA Admiral", @@ -50591,6 +51009,7 @@ "priceypies.com": "Leven Labs, Inc. DBA Admiral", "pricklydebt.com": "Leven Labs, Inc. DBA Admiral", "pricklypollution.com": "Leven Labs, Inc. DBA Admiral", + "pristinegale.com": "Leven Labs, Inc. DBA Admiral", "processplantation.com": "Leven Labs, Inc. DBA Admiral", "profusesupport.com": "Leven Labs, Inc. DBA Admiral", "protestcopy.com": "Leven Labs, Inc. DBA Admiral", @@ -50646,6 +51065,7 @@ "righteouscrayon.com": "Leven Labs, Inc. DBA Admiral", "rightfulfall.com": "Leven Labs, Inc. DBA Admiral", "rigidrobin.com": "Leven Labs, Inc. DBA Admiral", + "rollconnection.com": "Leven Labs, Inc. DBA Admiral", "roofrelation.com": "Leven Labs, Inc. DBA Admiral", "roseincome.com": "Leven Labs, Inc. DBA Admiral", "ruralrobin.com": "Leven Labs, Inc. DBA Admiral", @@ -50699,6 +51119,7 @@ "shapecomb.com": "Leven Labs, Inc. DBA Admiral", "sheargovernor.com": "Leven Labs, Inc. DBA Admiral", "shesubscriptions.com": "Leven Labs, Inc. DBA Admiral", + "shinypond.com": "Leven Labs, Inc. DBA Admiral", "shirtsidewalk.com": "Leven Labs, Inc. DBA Admiral", "shiveringspot.com": "Leven Labs, Inc. DBA Admiral", "shiverscissors.com": "Leven Labs, Inc. DBA Admiral", @@ -50746,6 +51167,7 @@ "spookysleet.com": "Leven Labs, Inc. DBA Admiral", "spotlessstamp.com": "Leven Labs, Inc. DBA Admiral", "spottednoise.com": "Leven Labs, Inc. DBA Admiral", + "sprysummit.com": "Leven Labs, Inc. DBA Admiral", "spuriousair.com": "Leven Labs, Inc. DBA Admiral", "spysubstance.com": "Leven Labs, Inc. DBA Admiral", "squalidscrew.com": "Leven Labs, Inc. DBA Admiral", @@ -50831,6 +51253,8 @@ "tranquilcan.com": "Leven Labs, Inc. DBA Admiral", "tranquilcanyon.com": "Leven Labs, Inc. DBA Admiral", "tranquilplume.com": "Leven Labs, Inc. DBA Admiral", + "tranquilveil.com": "Leven Labs, Inc. DBA Admiral", + "tranquilveranda.com": "Leven Labs, Inc. DBA Admiral", "tremendousearthquake.com": "Leven Labs, Inc. DBA Admiral", "tremendousplastic.com": "Leven Labs, Inc. DBA Admiral", "tritebadge.com": "Leven Labs, Inc. DBA Admiral", @@ -50858,6 +51282,7 @@ "unwieldyhealth.com": "Leven Labs, Inc. DBA Admiral", "unwieldyimpulse.com": "Leven Labs, Inc. DBA Admiral", "unwieldyplastic.com": "Leven Labs, Inc. DBA Admiral", + "uppitytime.com": "Leven Labs, Inc. DBA Admiral", "uselesslumber.com": "Leven Labs, Inc. DBA Admiral", "vanishmemory.com": "Leven Labs, Inc. DBA Admiral", "velvetquasar.com": "Leven Labs, Inc. DBA Admiral", @@ -50865,13 +51290,17 @@ "venomousvessel.com": "Leven Labs, Inc. DBA Admiral", "venusgloria.com": "Leven Labs, Inc. DBA Admiral", "verdantanswer.com": "Leven Labs, Inc. DBA Admiral", + "verdantlabyrinth.com": "Leven Labs, Inc. DBA Admiral", "verdantloom.com": "Leven Labs, Inc. DBA Admiral", "verseballs.com": "Leven Labs, Inc. DBA Admiral", "vibrantgale.com": "Leven Labs, Inc. DBA Admiral", "vibranthaven.com": "Leven Labs, Inc. DBA Admiral", + "vibrantpact.com": "Leven Labs, Inc. DBA Admiral", "vibranttalisman.com": "Leven Labs, Inc. DBA Admiral", "virtualvincent.com": "Leven Labs, Inc. DBA Admiral", + "vividcanopy.com": "Leven Labs, Inc. DBA Admiral", "vividmeadow.com": "Leven Labs, Inc. DBA Admiral", + "vividplume.com": "Leven Labs, Inc. DBA Admiral", "volatileprofit.com": "Leven Labs, Inc. DBA Admiral", "volatilevessel.com": "Leven Labs, Inc. DBA Admiral", "voraciousgrip.com": "Leven Labs, Inc. DBA Admiral", diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index ac4bdd1f2e..d64d8b8e83 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -8248,7 +8248,7 @@ CODE_SIGN_ENTITLEMENTS = PacketTunnelProvider/PacketTunnelProvider.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 0; DEVELOPMENT_TEAM = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; @@ -8285,7 +8285,7 @@ CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 0; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -8377,7 +8377,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 0; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = ShareExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -8405,7 +8405,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 0; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -8555,7 +8555,7 @@ CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGo.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 0; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_ASSET_PATHS = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -8581,7 +8581,7 @@ CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGo.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 0; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; INFOPLIST_FILE = DuckDuckGo/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -8646,7 +8646,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 0; DEAD_CODE_STRIPPING = NO; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = Widgets/Info.plist; @@ -8681,7 +8681,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 0; DEAD_CODE_STRIPPING = NO; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -8715,7 +8715,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 0; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = OpenAction/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -8746,7 +8746,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 0; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -9033,7 +9033,7 @@ CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGoAlpha.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 0; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_ASSET_PATHS = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -9064,7 +9064,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 0; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = ShareExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -9093,7 +9093,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 0; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = OpenAction/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -9127,7 +9127,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 0; DEAD_CODE_STRIPPING = NO; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = Widgets/Info.plist; @@ -9158,7 +9158,7 @@ CODE_SIGN_ENTITLEMENTS = PacketTunnelProvider/PacketTunnelProviderAlpha.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 0; DEVELOPMENT_TEAM = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; @@ -9191,11 +9191,11 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 0; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 2; + DYLIB_CURRENT_VERSION = 0; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Core/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -9429,7 +9429,7 @@ CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGoAlpha.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 0; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_ASSET_PATHS = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -9456,7 +9456,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 0; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -9489,7 +9489,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 0; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -9527,7 +9527,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 0; DEAD_CODE_STRIPPING = NO; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -9563,7 +9563,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 0; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -9598,11 +9598,11 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 0; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 2; + DYLIB_CURRENT_VERSION = 0; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Core/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -9776,11 +9776,11 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 0; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 2; + DYLIB_CURRENT_VERSION = 0; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Core/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -9809,10 +9809,10 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 0; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 2; + DYLIB_CURRENT_VERSION = 0; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Core/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; diff --git a/DuckDuckGo/Settings.bundle/Root.plist b/DuckDuckGo/Settings.bundle/Root.plist index d0dff36535..beab9dd941 100644 --- a/DuckDuckGo/Settings.bundle/Root.plist +++ b/DuckDuckGo/Settings.bundle/Root.plist @@ -6,7 +6,7 @@ DefaultValue - 7.111.0 + 7.112.0 Key version Title From 072142220cc187f8a90dabbe9180a7149afe4989 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jacek=20=C5=81yp?= Date: Mon, 11 Mar 2024 17:47:34 +0100 Subject: [PATCH 15/19] Report on toggle protections off (#2536) Task/Issue URL: https://app.asana.com/0/72649045549333/1205734628204384/f Description: Send a simplified breakage report through Pixel when user disables protections on iOS and macOS as such volume information from the toggle can help us identify breakage beyond what we can learn from regular breakage reports. We aim to: - Explicitly request permission for such report without directing the user toward a specific response. - Be transparent by openly listing the exact information we intend to send. Steps to test this PR: See: https://app.asana.com/0/0/1206747477156794/f --- Core/ContentBlocking.swift | 12 ++ Core/PixelEvent.swift | 11 ++ DuckDuckGo.xcodeproj/project.pbxproj | 52 ++++-- .../xcshareddata/swiftpm/Package.resolved | 8 +- DuckDuckGo/AppDelegate.swift | 1 + DuckDuckGo/AppDependencyProvider.swift | 2 + DuckDuckGo/MainViewController+Segues.swift | 5 +- DuckDuckGo/MainViewController.swift | 6 +- .../PrivacyDashboardViewController.swift | 176 ++++++++++++------ DuckDuckGo/TabDelegate.swift | 4 +- DuckDuckGo/TabViewController.swift | 3 +- ...bViewControllerBrowsingMenuExtension.swift | 28 ++- DuckDuckGo/Theme+DesignSystem.swift | 4 +- DuckDuckGo/UserText.swift | 3 +- DuckDuckGo/en.lproj/Localizable.strings | 3 + .../AppPrivacyConfigurationTests.swift | 5 +- DuckDuckGoTests/AppURLsTests.swift | 3 +- .../BrokenSiteReportingTests.swift | 46 ++--- .../ContentBlockerProtectionStoreTests.swift | 3 +- DuckDuckGoTests/MockDependencyProvider.swift | 2 + .../MockPrivacyConfiguration.swift | 2 + .../PrivacyConfigurationManagerMock.swift | 2 + DuckDuckGoTests/WebViewTestHelper.swift | 3 +- .../AutoconsentBackgroundTests.swift | 4 +- 24 files changed, 271 insertions(+), 117 deletions(-) diff --git a/Core/ContentBlocking.swift b/Core/ContentBlocking.swift index 43b5348b42..1135f0526f 100644 --- a/Core/ContentBlocking.swift +++ b/Core/ContentBlocking.swift @@ -23,6 +23,7 @@ import Combine import Common public final class ContentBlocking { + public static let shared = ContentBlocking() public let privacyConfigurationManager: PrivacyConfigurationManaging @@ -49,6 +50,7 @@ public final class ContentBlocking { embeddedDataProvider: AppPrivacyConfigurationDataProvider(), localProtection: DomainsProtectionUserDefaultsStore(), errorReporting: Self.debugEvents, + toggleProtectionsCounterEventReporting: toggleProtectionsEvents, internalUserDecider: internalUserDecider, installDate: statisticsStore.installDate) self.privacyConfigurationManager = privacyConfigurationManager @@ -195,6 +197,16 @@ public final class ContentBlocking { Pixel.fire(pixel: domainEvent, includedParameters: []) } + private let toggleProtectionsEvents = EventMapping { event, _, parameters, _ in + let domainEvent: Pixel.Event + switch event { + case .toggleProtectionsCounterDaily: + domainEvent = .toggleProtectionsDailyCount + } + + Pixel.fire(pixel: domainEvent, withAdditionalParameters: parameters ?? [:]) + } + } public class DomainsProtectionUserDefaultsStore: DomainsProtectionStore { diff --git a/Core/PixelEvent.swift b/Core/PixelEvent.swift index 796409c66b..3e2aad0624 100644 --- a/Core/PixelEvent.swift +++ b/Core/PixelEvent.swift @@ -542,6 +542,11 @@ extension Pixel { case appRatingPromptFetchError + case protectionToggledOffBreakageReport + case toggleProtectionsDailyCount + case toggleReportDoNotSend + case toggleReportDismiss + case userBehaviorReloadTwice case userBehaviorReloadAndRestart case userBehaviorReloadAndFireButton @@ -1090,6 +1095,12 @@ extension Pixel.Event { // MARK: - Return user measurement case .debugReturnUserAddATB: return "m_debug_return_user_add_atb" case .debugReturnUserUpdateATB: return "m_debug_return_user_update_atb" + + // MARK: - Toggle reports + case .protectionToggledOffBreakageReport: return "m_protection-toggled-off-breakage-report" + case .toggleProtectionsDailyCount: return "m_toggle-protections-daily-count" + case .toggleReportDoNotSend: return "m_toggle-report-do-not-send" + case .toggleReportDismiss: return "m_toggle-report-dismiss" case .appRatingPromptFetchError: return "m_d_app_rating_prompt_fetch_error" diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index d64d8b8e83..e69e2ef144 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -132,7 +132,6 @@ 1E4FAA6627D8DFC800ADC5B3 /* CompleteDownloadRowViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E4FAA6527D8DFC800ADC5B3 /* CompleteDownloadRowViewModel.swift */; }; 1E60989B290009C700A508F9 /* Common in Frameworks */ = {isa = PBXBuildFile; productRef = 1E7060BD28F88EE200E4CCDB /* Common */; }; 1E60989D290011E600A508F9 /* ContentBlocking in Frameworks */ = {isa = PBXBuildFile; productRef = 1E60989C290011E600A508F9 /* ContentBlocking */; }; - 1E60989F290011E600A508F9 /* PrivacyDashboard in Frameworks */ = {isa = PBXBuildFile; productRef = 1E60989E290011E600A508F9 /* PrivacyDashboard */; }; 1E6098A1290011E600A508F9 /* UserScript in Frameworks */ = {isa = PBXBuildFile; productRef = 1E6098A0290011E600A508F9 /* UserScript */; }; 1E61BC2A27074BED00B2854D /* TextSizeUserScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E61BC2927074BED00B2854D /* TextSizeUserScript.swift */; }; 1E6A4D692984208800A371D3 /* LocaleExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E6A4D682984208800A371D3 /* LocaleExtension.swift */; }; @@ -300,6 +299,10 @@ 4BCD146B2B05C4B5000B1E4C /* VPNWaitlistTermsAndConditionsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BCD146A2B05C4B5000B1E4C /* VPNWaitlistTermsAndConditionsViewController.swift */; }; 4BCD146D2B05DB09000B1E4C /* NetworkProtectionAccessControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BCD146C2B05DB09000B1E4C /* NetworkProtectionAccessControllerTests.swift */; }; 4BE2756827304F57006B20B0 /* URLRequestExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BE27566272F878F006B20B0 /* URLRequestExtension.swift */; }; + 4BE67B012B96B741007335F7 /* Common in Frameworks */ = {isa = PBXBuildFile; productRef = 4BE67B002B96B741007335F7 /* Common */; }; + 4BE67B032B96B864007335F7 /* ContentBlocking in Frameworks */ = {isa = PBXBuildFile; productRef = 4BE67B022B96B864007335F7 /* ContentBlocking */; }; + 4BE67B052B96B9AB007335F7 /* ContentBlocking in Frameworks */ = {isa = PBXBuildFile; productRef = 4BE67B042B96B9AB007335F7 /* ContentBlocking */; }; + 4BE67B072B96B9B0007335F7 /* Common in Frameworks */ = {isa = PBXBuildFile; productRef = 4BE67B062B96B9B0007335F7 /* Common */; }; 4BEF65692989C2FC00B650CB /* AdapterSocketEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 021D307A2989C0C400918636 /* AdapterSocketEvent.swift */; }; 4BEF656A2989C2FC00B650CB /* ProxyServerEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 021D307C2989C0C600918636 /* ProxyServerEvent.swift */; }; 4BEF656B2989C2FC00B650CB /* RuleMatchEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 021D307D2989C0C700918636 /* RuleMatchEvent.swift */; }; @@ -772,6 +775,7 @@ CB5516D1286500290079B175 /* ContentBlockingRulesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02CA904C24FD2DB000D41DDF /* ContentBlockingRulesTests.swift */; }; CB5516D2286500290079B175 /* AtbServerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85F21DBD21121147002631A6 /* AtbServerTests.swift */; }; CB84C7BD29A3EF530088A5B8 /* AppConfigurationURLProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB24F70E29A3EB15006DCC58 /* AppConfigurationURLProvider.swift */; }; + CB941A6E2B96AB08000F9E7A /* PrivacyDashboard in Frameworks */ = {isa = PBXBuildFile; productRef = CB941A6D2B96AB08000F9E7A /* PrivacyDashboard */; }; CB9B8739278C8E72001F4906 /* WidgetEducationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB9B8738278C8E72001F4906 /* WidgetEducationViewController.swift */; }; CB9B873C278C8FEA001F4906 /* WidgetEducationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB9B873B278C8FEA001F4906 /* WidgetEducationView.swift */; }; CB9B873E278C93C2001F4906 /* HomeMessage.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CB9B873D278C93C2001F4906 /* HomeMessage.xcassets */; }; @@ -2730,6 +2734,7 @@ F143C2EB1E4A4CD400CFDE3A /* Core.framework in Frameworks */, 4B2754EC29E8C7DF00394032 /* Lottie in Frameworks */, 31E69A63280F4CB600478327 /* DuckUI in Frameworks */, + CB941A6E2B96AB08000F9E7A /* PrivacyDashboard in Frameworks */, F42D541D29DCA40B004C4FF1 /* DesignResourcesKit in Frameworks */, 85875B6129912A9900115F05 /* SyncUI in Frameworks */, F4D7F634298C00C3006C3AE9 /* FindInPageIOSJSSupport in Frameworks */, @@ -2745,8 +2750,10 @@ B6A26C0A2B9835A800DF9EAD /* Macros in Frameworks */, F486D3362506A037002D07D7 /* OHHTTPStubs in Frameworks */, F486D3382506A225002D07D7 /* OHHTTPStubsSwift in Frameworks */, + 4BE67B052B96B9AB007335F7 /* ContentBlocking in Frameworks */, F115ED9C2B4EFC8E001A0453 /* TestUtils in Frameworks */, EEFAB4672A73C230008A38E4 /* NetworkProtectionTestUtils in Frameworks */, + 4BE67B072B96B9B0007335F7 /* Common in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2776,6 +2783,8 @@ B6A26C102B9835B400DF9EAD /* Macros in Frameworks */, 1E1D8B632995143200C96994 /* OHHTTPStubs in Frameworks */, 1E1D8B652995143200C96994 /* OHHTTPStubsSwift in Frameworks */, + 4BE67B012B96B741007335F7 /* Common in Frameworks */, + 4BE67B032B96B864007335F7 /* ContentBlocking in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2813,7 +2822,6 @@ D61CDA182B7CF78300A0FBB9 /* ZIPFoundation in Frameworks */, 98A16C2D28A11D6200A6C003 /* BrowserServicesKit in Frameworks */, 8599690F29D2F1C100DBF9FA /* DDGSync in Frameworks */, - 1E60989F290011E600A508F9 /* PrivacyDashboard in Frameworks */, 851481882A600EFC00ABC65F /* RemoteMessaging in Frameworks */, 37DF000C29F9CA80002B7D3E /* SyncDataProviders in Frameworks */, 1E6098A1290011E600A508F9 /* UserScript in Frameworks */, @@ -5111,8 +5119,6 @@ isa = PBXGroup; children = ( BDA583852B98B69C00732FDC /* Subscription */, - 858479C72B8792C900D156C1 /* History */, - EE7A92852AC6DE2500832A36 /* NetworkProtection */, 4B470ED4299C484B0086EBDC /* AppTrackingProtection */, F1CE42A71ECA0A520074A8DF /* Bookmarks */, 837774491F8E1ECE00E17A29 /* ContentBlocker */, @@ -5767,6 +5773,7 @@ F42D541C29DCA40B004C4FF1 /* DesignResourcesKit */, 0238E44E29C0FAA100615E30 /* FindInPageIOSJSSupport */, 4B2754EB29E8C7DF00394032 /* Lottie */, + CB941A6D2B96AB08000F9E7A /* PrivacyDashboard */, B6A26C032B98358B00DF9EAD /* Macros */, F1D43AF92B99C1D300BAB743 /* BareBonesBrowserKit */, ); @@ -5794,6 +5801,8 @@ F486D3372506A225002D07D7 /* OHHTTPStubsSwift */, EEFAB4662A73C230008A38E4 /* NetworkProtectionTestUtils */, F115ED9B2B4EFC8E001A0453 /* TestUtils */, + 4BE67B042B96B9AB007335F7 /* ContentBlocking */, + 4BE67B062B96B9B0007335F7 /* Common */, B6A26C092B9835A800DF9EAD /* Macros */, ); productName = DuckDuckGoTests; @@ -5858,6 +5867,8 @@ packageProductDependencies = ( 1E1D8B622995143200C96994 /* OHHTTPStubs */, 1E1D8B642995143200C96994 /* OHHTTPStubsSwift */, + 4BE67B002B96B741007335F7 /* Common */, + 4BE67B022B96B864007335F7 /* ContentBlocking */, B6A26C0F2B9835B400DF9EAD /* Macros */, ); productName = IntegrationTests; @@ -5945,7 +5956,6 @@ 98A16C2C28A11D6200A6C003 /* BrowserServicesKit */, 1E7060BD28F88EE200E4CCDB /* Common */, 1E60989C290011E600A508F9 /* ContentBlocking */, - 1E60989E290011E600A508F9 /* PrivacyDashboard */, 1E6098A0290011E600A508F9 /* UserScript */, 98A50961294B48A400D10880 /* Bookmarks */, CBC83E3329B631780008E19C /* Configuration */, @@ -10019,7 +10029,7 @@ repositoryURL = "https://github.com/DuckDuckGo/BrowserServicesKit"; requirement = { kind = exactVersion; - version = 120.0.0; + version = 121.0.0; }; }; B6F997C22B8F374300476735 /* XCRemoteSwiftPackageReference "apple-toolbox" */ = { @@ -10114,11 +10124,6 @@ package = 98A16C2928A11BDE00A6C003 /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; productName = ContentBlocking; }; - 1E60989E290011E600A508F9 /* PrivacyDashboard */ = { - isa = XCSwiftPackageProductDependency; - package = 98A16C2928A11BDE00A6C003 /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; - productName = PrivacyDashboard; - }; 1E6098A0290011E600A508F9 /* UserScript */ = { isa = XCSwiftPackageProductDependency; package = 98A16C2928A11BDE00A6C003 /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; @@ -10157,6 +10162,26 @@ package = F42D541B29DCA40B004C4FF1 /* XCRemoteSwiftPackageReference "DesignResourcesKit" */; productName = DesignResourcesKit; }; + 4BE67B002B96B741007335F7 /* Common */ = { + isa = XCSwiftPackageProductDependency; + package = 98A16C2928A11BDE00A6C003 /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; + productName = Common; + }; + 4BE67B022B96B864007335F7 /* ContentBlocking */ = { + isa = XCSwiftPackageProductDependency; + package = 98A16C2928A11BDE00A6C003 /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; + productName = ContentBlocking; + }; + 4BE67B042B96B9AB007335F7 /* ContentBlocking */ = { + isa = XCSwiftPackageProductDependency; + package = 98A16C2928A11BDE00A6C003 /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; + productName = ContentBlocking; + }; + 4BE67B062B96B9B0007335F7 /* Common */ = { + isa = XCSwiftPackageProductDependency; + package = 98A16C2928A11BDE00A6C003 /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; + productName = Common; + }; 851481872A600EFC00ABC65F /* RemoteMessaging */ = { isa = XCSwiftPackageProductDependency; package = 98A16C2928A11BDE00A6C003 /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; @@ -10286,6 +10311,11 @@ package = C14882EB27F211A000D59F0C /* XCRemoteSwiftPackageReference "SwiftSoup" */; productName = SwiftSoup; }; + CB941A6D2B96AB08000F9E7A /* PrivacyDashboard */ = { + isa = XCSwiftPackageProductDependency; + package = 98A16C2928A11BDE00A6C003 /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; + productName = PrivacyDashboard; + }; CBC83E3329B631780008E19C /* Configuration */ = { isa = XCSwiftPackageProductDependency; package = 98A16C2928A11BDE00A6C003 /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 5a5344ce43..1632d16407 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" : "cea7c43e5ab1d7484ab29abeda429350ed8a7dc1", - "version" : "120.0.0" + "revision" : "4555c3dbf265f1dca0304c69e7013b9d46a758b3", + "version" : "121.0.0" } }, { @@ -122,8 +122,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/duckduckgo/privacy-dashboard", "state" : { - "revision" : "c67d268bf234760f49034a0fe7a6137a1b216b05", - "version" : "3.2.0" + "revision" : "43a6e1c1864846679a254e60c91332c3fbd922ee", + "version" : "3.3.0" } }, { diff --git a/DuckDuckGo/AppDelegate.swift b/DuckDuckGo/AppDelegate.swift index 197b75ccbe..159549363b 100644 --- a/DuckDuckGo/AppDelegate.swift +++ b/DuckDuckGo/AppDelegate.swift @@ -330,6 +330,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { clearDebugWaitlistState() + AppDependencyProvider.shared.toggleProtectionsCounter.sendEventsIfNeeded() AppDependencyProvider.shared.userBehaviorMonitor.handleAction(.reopenApp) return true diff --git a/DuckDuckGo/AppDependencyProvider.swift b/DuckDuckGo/AppDependencyProvider.swift index 55ae87a87b..c928b90ed5 100644 --- a/DuckDuckGo/AppDependencyProvider.swift +++ b/DuckDuckGo/AppDependencyProvider.swift @@ -37,6 +37,7 @@ protocol DependencyProvider { var autofillLoginSession: AutofillLoginSession { get } var autofillNeverPromptWebsitesManager: AutofillNeverPromptWebsitesManager { get } var configurationManager: ConfigurationManager { get } + var toggleProtectionsCounter: ToggleProtectionsCounter { get } var userBehaviorMonitor: UserBehaviorMonitor { get } } @@ -65,6 +66,7 @@ class AppDependencyProvider: DependencyProvider { let configurationManager = ConfigurationManager() + let toggleProtectionsCounter: ToggleProtectionsCounter = ContentBlocking.shared.privacyConfigurationManager.toggleProtectionsCounter let userBehaviorMonitor = UserBehaviorMonitor() } diff --git a/DuckDuckGo/MainViewController+Segues.swift b/DuckDuckGo/MainViewController+Segues.swift index c0e6351e14..0ea72c8bc3 100644 --- a/DuckDuckGo/MainViewController+Segues.swift +++ b/DuckDuckGo/MainViewController+Segues.swift @@ -23,6 +23,7 @@ import Core import Bookmarks import BrowserServicesKit import SwiftUI +import PrivacyDashboard #if SUBSCRIPTION import Subscription @@ -123,7 +124,7 @@ extension MainViewController { present(controller, animated: true) } - func segueToReportBrokenSite() { + func segueToReportBrokenSite(mode: PrivacyDashboardMode = .report) { os_log(#function, log: .generalLog, type: .debug) hideAllHighlightsIfNeeded() @@ -137,9 +138,9 @@ extension MainViewController { let controller = storyboard.instantiateInitialViewController { coder in PrivacyDashboardViewController(coder: coder, privacyInfo: privacyInfo, + dashboardMode: mode, privacyConfigurationManager: ContentBlocking.shared.privacyConfigurationManager, contentBlockingManager: ContentBlocking.shared.contentBlockingManager, - initMode: .reportBrokenSite, breakageAdditionalInfo: self.currentTab?.makeBreakageAdditionalInfo()) } diff --git a/DuckDuckGo/MainViewController.swift b/DuckDuckGo/MainViewController.swift index 9496a1bfaa..b3e04842cf 100644 --- a/DuckDuckGo/MainViewController.swift +++ b/DuckDuckGo/MainViewController.swift @@ -1967,7 +1967,11 @@ extension MainViewController: TabDelegate { func tabDidRequestReportBrokenSite(tab: TabViewController) { segueToReportBrokenSite() } - + + func tab(_ tab: TabViewController, didRequestToggleReportWithCompletionHandler completionHandler: @escaping (Bool) -> Void) { + segueToReportBrokenSite(mode: .toggleReport(completionHandler: completionHandler)) + } + func tabDidRequestBookmarks(tab: TabViewController) { Pixel.fire(pixel: .bookmarksButtonPressed, withAdditionalParameters: [PixelParameters.originatedFromMenu: "1"]) diff --git a/DuckDuckGo/PrivacyDashboard/PrivacyDashboardViewController.swift b/DuckDuckGo/PrivacyDashboard/PrivacyDashboardViewController.swift index 98283d584a..ec3eca1efa 100644 --- a/DuckDuckGo/PrivacyDashboard/PrivacyDashboardViewController.swift +++ b/DuckDuckGo/PrivacyDashboard/PrivacyDashboardViewController.swift @@ -25,51 +25,64 @@ import BrowserServicesKit import PrivacyDashboard import Common -/// View controller used for `Privacy Dasboard` or `Report broken site`, the web content is chosen at init time setting the correct `initMode` -class PrivacyDashboardViewController: UIViewController { - - /// Type of web page displayed - enum Mode { - case privacyDashboard - case reportBrokenSite - } +final class PrivacyDashboardViewController: UIViewController { @IBOutlet private(set) weak var webView: WKWebView! - private let initMode: Mode private let privacyDashboardController: PrivacyDashboardController private let privacyConfigurationManager: PrivacyConfigurationManaging private let contentBlockingManager: ContentBlockerRulesManager public var breakageAdditionalInfo: BreakageAdditionalInfo? - - var source: WebsiteBreakage.Source { - initMode == .reportBrokenSite ? .appMenu : .dashboard - } + private var privacyDashboardDidTriggerDismiss: Bool = false - private let websiteBreakageReporter: WebsiteBreakageReporter = { - WebsiteBreakageReporter(pixelHandler: { parameters in + private let brokenSiteReporter: BrokenSiteReporter = { + BrokenSiteReporter(pixelHandler: { parameters in Pixel.fire(pixel: .brokenSiteReport, withAdditionalParameters: parameters, - allowedQueryReservedCharacters: WebsiteBreakage.allowedQueryReservedCharacters) + allowedQueryReservedCharacters: BrokenSiteReport.allowedQueryReservedCharacters) }, keyValueStoring: UserDefaults.standard) }() - + + private let toggleProtectionsOffReporter: BrokenSiteReporter = { + BrokenSiteReporter(pixelHandler: { parameters in + Pixel.fire(pixel: .protectionToggledOffBreakageReport, + withAdditionalParameters: parameters, + allowedQueryReservedCharacters: BrokenSiteReport.allowedQueryReservedCharacters) + }, keyValueStoring: UserDefaults.standard) + }() + + private let toggleReportEvents = EventMapping { event, _, parameters, _ in + let domainEvent: Pixel.Event + switch event { + case .toggleReportDismiss: domainEvent = .toggleReportDismiss + case .toggleReportDoNotSend: domainEvent = .toggleReportDoNotSend + } + if let parameters { + Pixel.fire(pixel: domainEvent, withAdditionalParameters: parameters) + } else { + Pixel.fire(pixel: domainEvent) + } + } + init?(coder: NSCoder, privacyInfo: PrivacyInfo?, + dashboardMode: PrivacyDashboardMode, privacyConfigurationManager: PrivacyConfigurationManaging, contentBlockingManager: ContentBlockerRulesManager, - initMode: Mode, breakageAdditionalInfo: BreakageAdditionalInfo?) { - self.privacyDashboardController = PrivacyDashboardController(privacyInfo: privacyInfo) + self.privacyDashboardController = PrivacyDashboardController(privacyInfo: privacyInfo, + dashboardMode: dashboardMode, + privacyConfigurationManager: privacyConfigurationManager, + eventMapping: toggleReportEvents) self.privacyConfigurationManager = privacyConfigurationManager self.contentBlockingManager = contentBlockingManager - self.initMode = initMode self.breakageAdditionalInfo = breakageAdditionalInfo super.init(coder: coder) self.privacyDashboardController.privacyDashboardDelegate = self self.privacyDashboardController.privacyDashboardNavigationDelegate = self self.privacyDashboardController.privacyDashboardReportBrokenSiteDelegate = self + self.privacyDashboardController.privacyDashboardToggleReportDelegate = self } required init?(coder: NSCoder) { @@ -78,13 +91,16 @@ class PrivacyDashboardViewController: UIViewController { public override func viewDidLoad() { super.viewDidLoad() - privacyDashboardController.setup(for: webView, reportBrokenSiteOnly: initMode == Mode.reportBrokenSite ? true : false) + privacyDashboardController.setup(for: webView) privacyDashboardController.preferredLocale = Bundle.main.preferredLocalizations.first applyTheme(ThemeManager.shared.currentTheme) } - + public override func viewDidDisappear(_ animated: Bool) { super.viewDidDisappear(animated) + if !privacyDashboardDidTriggerDismiss { + privacyDashboardController.handleViewWillDisappear() + } privacyDashboardController.cleanUp() } @@ -93,12 +109,11 @@ class PrivacyDashboardViewController: UIViewController { privacyDashboardController.updatePrivacyInfo(privacyInfo) } - private func privacyDashboardProtectionSwitchChangeHandler(state: ProtectionState) { - - dismiss(animated: true) - + private func privacyDashboardProtectionSwitchChangeHandler(state: ProtectionState, didSendReport: Bool = false) { + privacyDashboardDidTriggerDismiss = true guard let domain = privacyDashboardController.privacyInfo?.url.host else { return } + let source: BrokenSiteReport.Source = privacyDashboardController.initDashboardMode == .report ? .appMenu : .dashboard let privacyConfiguration = privacyConfigurationManager.privacyConfig let pixelParam = ["trigger_origin": state.eventOrigin.screen.rawValue, "source": source.rawValue] @@ -108,7 +123,11 @@ class PrivacyDashboardViewController: UIViewController { Pixel.fire(pixel: .dashboardProtectionAllowlistRemove, withAdditionalParameters: pixelParam) } else { privacyConfiguration.userDisabledProtection(forDomain: domain) - ActionMessageView.present(message: UserText.messageProtectionDisabled.format(arguments: domain)) + if didSendReport { + ActionMessageView.present(message: UserText.messageProtectionDisabledAndToggleReportSent.format(arguments: domain)) + } else { + ActionMessageView.present(message: UserText.messageProtectionDisabled.format(arguments: domain)) + } Pixel.fire(pixel: .dashboardProtectionAllowlistAdd, withAdditionalParameters: pixelParam) } @@ -117,8 +136,10 @@ class PrivacyDashboardViewController: UIViewController { } private func privacyDashboardCloseHandler() { + privacyDashboardDidTriggerDismiss = true dismiss(animated: true) } + } extension PrivacyDashboardViewController: Themable { @@ -135,19 +156,21 @@ extension PrivacyDashboardViewController: Themable { default: return .light } } + } // MARK: - PrivacyDashboardControllerDelegate extension PrivacyDashboardViewController: PrivacyDashboardControllerDelegate { - func privacyDashboardController(_ privacyDashboardController: PrivacyDashboardController, didChangeProtectionSwitch protectionState: ProtectionState) { - privacyDashboardProtectionSwitchChangeHandler(state: protectionState) + func privacyDashboardController(_ privacyDashboardController: PrivacyDashboardController, + didChangeProtectionSwitch protectionState: ProtectionState, + didSendReport: Bool) { + privacyDashboardProtectionSwitchChangeHandler(state: protectionState, didSendReport: didSendReport) } func privacyDashboardController(_ privacyDashboardController: PrivacyDashboardController, didRequestOpenUrlInNewTab url: URL) { guard let mainViewController = presentingViewController as? MainViewController else { return } - dismiss(animated: true) { mainViewController.loadUrlInNewTab(url, inheritedAttribution: nil) } @@ -170,6 +193,7 @@ extension PrivacyDashboardViewController: PrivacyDashboardControllerDelegate { } } } + } // MARK: - PrivacyDashboardNavigationDelegate @@ -184,6 +208,7 @@ extension PrivacyDashboardViewController: PrivacyDashboardNavigationDelegate { func privacyDashboardControllerDidTapClose(_ privacyDashboardController: PrivacyDashboardController) { privacyDashboardCloseHandler() } + } // MARK: - PrivacyDashboardReportBrokenSiteDelegate @@ -196,18 +221,42 @@ extension PrivacyDashboardViewController: PrivacyDashboardReportBrokenSiteDelega } func privacyDashboardController(_ privacyDashboardController: PrivacyDashboard.PrivacyDashboardController, - didRequestSubmitBrokenSiteReportWithCategory category: String, description: String) { - + didRequestSubmitBrokenSiteReportWithCategory category: String, + description: String) { + let source: BrokenSiteReport.Source = privacyDashboardController.initDashboardMode == .report ? .appMenu : .dashboard do { - let breakageReport = try makeWebsiteBreakage(category: category, description: description) - try websiteBreakageReporter.report(breakage: breakageReport) + let report = try makeBrokenSiteReport(category: category, description: description, source: source) + try brokenSiteReporter.report(report, reportMode: .regular) } catch { - os_log("Failed to generate or send the website breakage report: %@", type: .error, error.localizedDescription) + os_log("Failed to generate or send the broken site report: %@", type: .error, error.localizedDescription) } ActionMessageView.present(message: UserText.feedbackSumbittedConfirmation) privacyDashboardCloseHandler() } + +} + +// MARK: - PrivacyDashboardToggleReportDelegate + +extension PrivacyDashboardViewController: PrivacyDashboardToggleReportDelegate { + + func privacyDashboardController(_ privacyDashboardController: PrivacyDashboardController, + didRequestSubmitToggleReportWithSource source: BrokenSiteReport.Source, + didOpenReportInfo: Bool, + toggleReportCounter: Int?) { + do { + let report = try makeBrokenSiteReport(source: source, + didOpenReportInfo: didOpenReportInfo, + toggleReportCounter: toggleReportCounter) + try toggleProtectionsOffReporter.report(report, reportMode: .toggle) + } catch { + os_log("Failed to generate or send the broken site report: %@", type: .error, error.localizedDescription) + } + + privacyDashboardCloseHandler() + } + } extension PrivacyDashboardViewController: UIPopoverPresentationControllerDelegate {} @@ -224,20 +273,24 @@ extension PrivacyDashboardViewController { let httpStatusCode: Int? } - enum WebsiteBreakageError: Error { + enum BrokenSiteReportError: Error { case failedToFetchTheCurrentWebsiteInfo } - private func makeWebsiteBreakage(category: String, description: String) throws -> WebsiteBreakage { - + private func makeBrokenSiteReport(category: String = "", + description: String = "", + source: BrokenSiteReport.Source, + didOpenReportInfo: Bool = false, + toggleReportCounter: Int? = nil) throws -> BrokenSiteReport { + guard let privacyInfo = privacyDashboardController.privacyInfo, let breakageAdditionalInfo = breakageAdditionalInfo else { - throw WebsiteBreakageError.failedToFetchTheCurrentWebsiteInfo + throw BrokenSiteReportError.failedToFetchTheCurrentWebsiteInfo } let blockedTrackerDomains = privacyInfo.trackerInfo.trackersBlocked.compactMap { $0.domain } - let configuration = ContentBlocking.shared.privacyConfigurationManager.privacyConfig - let protectionsState = configuration.isFeature(.contentBlocking, enabledForDomain: breakageAdditionalInfo.currentURL.host) + let protectionsState = privacyConfigurationManager.privacyConfig.isFeature(.contentBlocking, + enabledForDomain: breakageAdditionalInfo.currentURL.host) var errors: [Error]? var statusCodes: [Int]? @@ -248,24 +301,27 @@ extension PrivacyDashboardViewController { statusCodes = [httpStatusCode] } - return WebsiteBreakage(siteUrl: breakageAdditionalInfo.currentURL, - category: category, - description: description, - osVersion: "\(ProcessInfo().operatingSystemVersion.majorVersion)", - manufacturer: "Apple", - upgradedHttps: breakageAdditionalInfo.httpsForced, - tdsETag: ContentBlocking.shared.contentBlockingManager.currentMainRules?.etag ?? "", - blockedTrackerDomains: blockedTrackerDomains, - installedSurrogates: privacyInfo.trackerInfo.installedSurrogates.map { $0 }, - isGPCEnabled: AppDependencyProvider.shared.appSettings.sendDoNotSell, - ampURL: breakageAdditionalInfo.ampURLString, - urlParametersRemoved: breakageAdditionalInfo.urlParametersRemoved, - protectionsState: protectionsState, - reportFlow: source, - siteType: breakageAdditionalInfo.isDesktop ? .desktop : .mobile, - atb: StatisticsUserDefaults().atb ?? "", - model: UIDevice.current.model, - errors: errors, - httpStatusCodes: statusCodes) + return BrokenSiteReport(siteUrl: breakageAdditionalInfo.currentURL, + category: category, + description: description, + osVersion: "\(ProcessInfo().operatingSystemVersion.majorVersion)", + manufacturer: "Apple", + upgradedHttps: breakageAdditionalInfo.httpsForced, + tdsETag: ContentBlocking.shared.contentBlockingManager.currentMainRules?.etag ?? "", + blockedTrackerDomains: blockedTrackerDomains, + installedSurrogates: privacyInfo.trackerInfo.installedSurrogates.map { $0 }, + isGPCEnabled: AppDependencyProvider.shared.appSettings.sendDoNotSell, + ampURL: breakageAdditionalInfo.ampURLString, + urlParametersRemoved: breakageAdditionalInfo.urlParametersRemoved, + protectionsState: protectionsState, + reportFlow: source, + siteType: breakageAdditionalInfo.isDesktop ? .desktop : .mobile, + atb: StatisticsUserDefaults().atb ?? "", + model: UIDevice.current.model, + errors: errors, + httpStatusCodes: statusCodes, + didOpenReportInfo: didOpenReportInfo, + toggleReportCounter: toggleReportCounter) } + } diff --git a/DuckDuckGo/TabDelegate.swift b/DuckDuckGo/TabDelegate.swift index d5c58da091..d1a9a0b9ce 100644 --- a/DuckDuckGo/TabDelegate.swift +++ b/DuckDuckGo/TabDelegate.swift @@ -50,7 +50,9 @@ protocol TabDelegate: AnyObject { func tab(_ tab: TabViewController, didChangePrivacyInfo privacyInfo: PrivacyInfo?) func tabDidRequestReportBrokenSite(tab: TabViewController) - + + func tab(_ tab: TabViewController, didRequestToggleReportWithCompletionHandler completionHandler: @escaping (Bool) -> Void) + func tabDidRequestBookmarks(tab: TabViewController) func tabDidRequestEditBookmark(tab: TabViewController) diff --git a/DuckDuckGo/TabViewController.swift b/DuckDuckGo/TabViewController.swift index f58029637e..5e0e482895 100644 --- a/DuckDuckGo/TabViewController.swift +++ b/DuckDuckGo/TabViewController.swift @@ -106,6 +106,7 @@ class TabViewController: UIViewController { private static let tld = AppDependencyProvider.shared.storageCache.tld private let adClickAttributionDetection = ContentBlocking.shared.makeAdClickAttributionDetection(tld: tld) let adClickAttributionLogic = ContentBlocking.shared.makeAdClickAttributionLogic(tld: tld) + private var httpsForced: Bool = false private var lastUpgradedURL: URL? @@ -743,9 +744,9 @@ class TabViewController: UIViewController { private func makePrivacyDashboardViewController(coder: NSCoder) -> PrivacyDashboardViewController? { PrivacyDashboardViewController(coder: coder, privacyInfo: privacyInfo, + dashboardMode: .dashboard, privacyConfigurationManager: ContentBlocking.shared.privacyConfigurationManager, contentBlockingManager: ContentBlocking.shared.contentBlockingManager, - initMode: .privacyDashboard, breakageAdditionalInfo: makeBreakageAdditionalInfo()) } diff --git a/DuckDuckGo/TabViewControllerBrowsingMenuExtension.swift b/DuckDuckGo/TabViewControllerBrowsingMenuExtension.swift index 2d4f1397c3..ad041cf5c3 100644 --- a/DuckDuckGo/TabViewControllerBrowsingMenuExtension.swift +++ b/DuckDuckGo/TabViewControllerBrowsingMenuExtension.swift @@ -24,6 +24,7 @@ import Bookmarks import simd import WidgetKit import Common +import PrivacyDashboard // swiftlint:disable file_length extension TabViewController { @@ -406,7 +407,7 @@ extension TabViewController { AppDependencyProvider.shared.userBehaviorMonitor.handleAction(.openSettings) delegate?.tabDidRequestSettings(tab: self) } - + private func buildToggleProtectionEntry(forDomain domain: String) -> BrowsingMenuEntry { let config = ContentBlocking.shared.privacyConfigurationManager.privacyConfig let isProtected = !config.isUserUnprotected(domain: domain) @@ -414,12 +415,23 @@ extension TabViewController { let image = isProtected ? UIImage(named: "Protections-Blocked-16")! : UIImage(named: "Protections-16")! return BrowsingMenuEntry.regular(name: title, image: image, action: { [weak self] in - Pixel.fire(pixel: isProtected ? .browsingMenuDisableProtection : .browsingMenuEnableProtection) - self?.togglePrivacyProtection(domain: domain) + self?.onToggleProtectionAction(forDomain: domain, isProtected: isProtected) }) } - - private func togglePrivacyProtection(domain: String) { + + private func onToggleProtectionAction(forDomain domain: String, isProtected: Bool) { + let config = ContentBlocking.shared.privacyConfigurationManager.privacyConfig + if isProtected && ToggleReportsFeature(privacyConfiguration: config).isEnabled { + delegate?.tab(self, didRequestToggleReportWithCompletionHandler: { [weak self] didSendReport in + self?.togglePrivacyProtection(domain: domain, didSendReport: didSendReport) + }) + } else { + togglePrivacyProtection(domain: domain) + } + Pixel.fire(pixel: isProtected ? .browsingMenuDisableProtection : .browsingMenuEnableProtection) + } + + private func togglePrivacyProtection(domain: String, didSendReport: Bool = false) { let config = ContentBlocking.shared.privacyConfigurationManager.privacyConfig let isProtected = !config.isUserUnprotected(domain: domain) if isProtected { @@ -430,7 +442,11 @@ extension TabViewController { let message: String if isProtected { - message = UserText.messageProtectionDisabled.format(arguments: domain) + if didSendReport { + message = UserText.messageProtectionDisabledAndToggleReportSent.format(arguments: domain) + } else { + message = UserText.messageProtectionDisabled.format(arguments: domain) + } } else { message = UserText.messageProtectionEnabled.format(arguments: domain) } diff --git a/DuckDuckGo/Theme+DesignSystem.swift b/DuckDuckGo/Theme+DesignSystem.swift index d496616332..4d5ccd89bc 100644 --- a/DuckDuckGo/Theme+DesignSystem.swift +++ b/DuckDuckGo/Theme+DesignSystem.swift @@ -43,7 +43,7 @@ extension Theme { var ddgTextTintColor: UIColor { UIColor(designSystemColor: .textPrimary) } - var privacyDashboardWebviewBackgroundColor: UIColor { UIColor(designSystemColor: .surface) } + var privacyDashboardWebviewBackgroundColor: UIColor { UIColor(designSystemColor: .background) } var iconCellBorderColor: UIColor { UIColor(designSystemColor: .icons) } @@ -77,7 +77,7 @@ extension Theme { var searchBarTextColor: UIColor { UIColor(designSystemColor: .textPrimary) } var navigationBarTitleColor: UIColor { UIColor(designSystemColor: .textPrimary) } - var tableHeaderTextColor: UIColor {UIColor(designSystemColor: .textSecondary) } + var tableHeaderTextColor: UIColor { UIColor(designSystemColor: .textSecondary) } var faviconBackgroundColor: UIColor { UIColor(designSystemColor: .surface) } diff --git a/DuckDuckGo/UserText.swift b/DuckDuckGo/UserText.swift index 134db22317..3aeeba2b15 100644 --- a/DuckDuckGo/UserText.swift +++ b/DuckDuckGo/UserText.swift @@ -81,7 +81,8 @@ public struct UserText { public static let alertDisableProtectionPlaceholder = NSLocalizedString("alert.title.disable.protection.placeholder", value: "www.example.com", comment: "Disable potection alert placeholder - leave as it is") public static let messageProtectionDisabled = NSLocalizedString("toast.protection.disabled", value: "Privacy Protection disabled for %@", comment: "Confirmation of an action - populated with a domain name") public static let messageProtectionEnabled = NSLocalizedString("toast.protection.enabled", value: "Privacy Protection enabled for %@", comment: "Confirmation of an action - populated with a domain name") - + public static let messageProtectionDisabledAndToggleReportSent = NSLocalizedString("toast.protection.disabled.and.toggle.report.sent", value: "Privacy Protections disabled for %@ and report sent.", comment: "Confirmation of an action - populated with a domain name") + public static let authAlertTitle = NSLocalizedString("auth.alert.title", value: "Authentication Required", comment: "Authentication Alert Title") public static let authAlertEncryptedConnectionMessage = NSLocalizedString("auth.alert.message.encrypted", value: "Sign in to %@. Your login information will be sent securely.", comment: "Authentication Alert - populated with a domain name") public static let authAlertPlainConnectionMessage = NSLocalizedString("auth.alert.message.plain", value: "Log in to %@. Your password will be sent insecurely because the connection is unencrypted.", comment: "Authentication Alert - populated with a domain name") diff --git a/DuckDuckGo/en.lproj/Localizable.strings b/DuckDuckGo/en.lproj/Localizable.strings index 5654129084..cf0aa4ca37 100644 --- a/DuckDuckGo/en.lproj/Localizable.strings +++ b/DuckDuckGo/en.lproj/Localizable.strings @@ -2256,6 +2256,9 @@ But if you *do* want a peek under the hood, you can find more information about /* Confirmation of an action - populated with a domain name */ "toast.protection.disabled" = "Privacy Protection disabled for %@"; +/* Confirmation of an action - populated with a domain name */ +"toast.protection.disabled.and.toggle.report.sent" = "Privacy Protections disabled for %@ and report sent."; + /* Confirmation of an action - populated with a domain name */ "toast.protection.enabled" = "Privacy Protection enabled for %@"; diff --git a/DuckDuckGoTests/AppPrivacyConfigurationTests.swift b/DuckDuckGoTests/AppPrivacyConfigurationTests.swift index 7e3cd649ff..402fe493a0 100644 --- a/DuckDuckGoTests/AppPrivacyConfigurationTests.swift +++ b/DuckDuckGoTests/AppPrivacyConfigurationTests.swift @@ -42,8 +42,9 @@ class AppPrivacyConfigurationTests: XCTestCase { let config = AppPrivacyConfiguration(data: configData, identifier: "", localProtection: MockDomainsProtectionStore(), - internalUserDecider: DefaultInternalUserDecider(store: InternalUserStore())) - + internalUserDecider: DefaultInternalUserDecider(store: InternalUserStore()), + toggleProtectionsCounter: ToggleProtectionsCounter(eventReporting: nil)) + XCTAssert(config.isEnabled(featureKey: .contentBlocking)) } diff --git a/DuckDuckGoTests/AppURLsTests.swift b/DuckDuckGoTests/AppURLsTests.swift index 830fa92b30..c3221f0096 100644 --- a/DuckDuckGoTests/AppURLsTests.swift +++ b/DuckDuckGoTests/AppURLsTests.swift @@ -48,7 +48,8 @@ final class AppURLsTests: XCTestCase { appConfig = AppPrivacyConfiguration(data: privacyData, identifier: "", localProtection: localProtection, - internalUserDecider: DefaultInternalUserDecider()) + internalUserDecider: DefaultInternalUserDecider(), + toggleProtectionsCounter: ToggleProtectionsCounter(eventReporting: nil)) } func testWhenRemoveInternalSearchParametersFromSearchUrlThenUrlIsChanged() throws { diff --git a/DuckDuckGoTests/BrokenSiteReportingTests.swift b/DuckDuckGoTests/BrokenSiteReportingTests.swift index e449d5fe4b..7045dd5b80 100644 --- a/DuckDuckGoTests/BrokenSiteReportingTests.swift +++ b/DuckDuckGoTests/BrokenSiteReportingTests.swift @@ -95,27 +95,29 @@ final class BrokenSiteReportingTests: XCTestCase { errors = errs.map { MockError($0) } } - let websiteBreakage = WebsiteBreakage(siteUrl: URL(string: test.siteURL)!, - category: test.category, - description: test.providedDescription, - osVersion: test.os ?? "", - manufacturer: test.manufacturer ?? "", - upgradedHttps: test.wasUpgraded, - tdsETag: test.blocklistVersion, - blockedTrackerDomains: test.blockedTrackers, - installedSurrogates: test.surrogates, - isGPCEnabled: test.gpcEnabled ?? false, - ampURL: "", - urlParametersRemoved: false, - protectionsState: test.protectionsEnabled, - reportFlow: .dashboard, - siteType: .mobile, - atb: "", - model: test.model ?? "", - errors: errors, - httpStatusCodes: test.httpErrorCodes ?? []) - - let reporter = WebsiteBreakageReporter(pixelHandler: { params in + let report = BrokenSiteReport(siteUrl: URL(string: test.siteURL)!, + category: test.category, + description: test.providedDescription, + osVersion: test.os ?? "", + manufacturer: test.manufacturer ?? "", + upgradedHttps: test.wasUpgraded, + tdsETag: test.blocklistVersion, + blockedTrackerDomains: test.blockedTrackers, + installedSurrogates: test.surrogates, + isGPCEnabled: test.gpcEnabled ?? false, + ampURL: "", + urlParametersRemoved: false, + protectionsState: test.protectionsEnabled, + reportFlow: .dashboard, + siteType: .mobile, + atb: "", + model: test.model ?? "", + errors: errors, + httpStatusCodes: test.httpErrorCodes ?? [], + didOpenReportInfo: false, + toggleReportCounter: nil) + + let reporter = BrokenSiteReporter(pixelHandler: { params in for expectedParam in test.expectReportURLParams { @@ -136,7 +138,7 @@ final class BrokenSiteReportingTests: XCTestCase { onTestExecuted.fulfill() try? self.runReferenceTests(onTestExecuted: onTestExecuted) }, keyValueStoring: MockKeyValueStore()) - try reporter.report(breakage: websiteBreakage) + try reporter.report(report, reportMode: .regular) } } diff --git a/DuckDuckGoTests/ContentBlockerProtectionStoreTests.swift b/DuckDuckGoTests/ContentBlockerProtectionStoreTests.swift index 7fb9efde56..71e33ec66e 100644 --- a/DuckDuckGoTests/ContentBlockerProtectionStoreTests.swift +++ b/DuckDuckGoTests/ContentBlockerProtectionStoreTests.swift @@ -51,7 +51,8 @@ class ContentBlockerProtectionStoreTests: XCTestCase { let config = AppPrivacyConfiguration(data: newConfig.data, identifier: "", localProtection: DomainsProtectionUserDefaultsStore(), - internalUserDecider: DefaultInternalUserDecider()) + internalUserDecider: DefaultInternalUserDecider(), + toggleProtectionsCounter: ToggleProtectionsCounter(eventReporting: nil)) XCTAssertFalse(config.isTempUnprotected(domain: "main1.com")) XCTAssertFalse(config.isTempUnprotected(domain: "notdomain1.com")) diff --git a/DuckDuckGoTests/MockDependencyProvider.swift b/DuckDuckGoTests/MockDependencyProvider.swift index 0b53f456a4..8666c34788 100644 --- a/DuckDuckGoTests/MockDependencyProvider.swift +++ b/DuckDuckGoTests/MockDependencyProvider.swift @@ -37,6 +37,7 @@ class MockDependencyProvider: DependencyProvider { var autofillNeverPromptWebsitesManager: AutofillNeverPromptWebsitesManager var configurationManager: ConfigurationManager var userBehaviorMonitor: UserBehaviorMonitor + var toggleProtectionsCounter: ToggleProtectionsCounter init() { let defaultProvider = AppDependencyProvider() @@ -53,5 +54,6 @@ class MockDependencyProvider: DependencyProvider { autofillNeverPromptWebsitesManager = defaultProvider.autofillNeverPromptWebsitesManager configurationManager = defaultProvider.configurationManager userBehaviorMonitor = defaultProvider.userBehaviorMonitor + toggleProtectionsCounter = defaultProvider.toggleProtectionsCounter } } diff --git a/DuckDuckGoTests/MockPrivacyConfiguration.swift b/DuckDuckGoTests/MockPrivacyConfiguration.swift index 6e84efd206..e14a517217 100644 --- a/DuckDuckGoTests/MockPrivacyConfiguration.swift +++ b/DuckDuckGoTests/MockPrivacyConfiguration.swift @@ -87,4 +87,6 @@ class MockPrivacyConfigurationManager: NSObject, PrivacyConfigurationManaging { var updatesPublisher: AnyPublisher = Just(()).eraseToAnyPublisher() var privacyConfig: PrivacyConfiguration = MockPrivacyConfiguration() var internalUserDecider: InternalUserDecider = DefaultInternalUserDecider() + var toggleProtectionsCounter: ToggleProtectionsCounter = ToggleProtectionsCounter(eventReporting: nil) + } diff --git a/DuckDuckGoTests/PrivacyConfigurationManagerMock.swift b/DuckDuckGoTests/PrivacyConfigurationManagerMock.swift index d6fa3c4921..dec46c64d8 100644 --- a/DuckDuckGoTests/PrivacyConfigurationManagerMock.swift +++ b/DuckDuckGoTests/PrivacyConfigurationManagerMock.swift @@ -101,6 +101,7 @@ class PrivacyConfigurationMock: PrivacyConfiguration { } class PrivacyConfigurationManagerMock: PrivacyConfigurationManaging { + var embeddedConfigData: BrowserServicesKit.PrivacyConfigurationManager.ConfigurationData { fatalError("not implemented") } @@ -120,6 +121,7 @@ class PrivacyConfigurationManagerMock: PrivacyConfigurationManaging { var privacyConfig: PrivacyConfiguration = PrivacyConfigurationMock() var internalUserDecider: InternalUserDecider = DefaultInternalUserDecider() + var toggleProtectionsCounter: ToggleProtectionsCounter = ToggleProtectionsCounter(eventReporting: nil) var reloadFired = [(etag: String?, data: Data?)]() var reloadResult: PrivacyConfigurationManager.ReloadResult = .embedded diff --git a/DuckDuckGoTests/WebViewTestHelper.swift b/DuckDuckGoTests/WebViewTestHelper.swift index 3f3d5ecee5..5f346bf66c 100644 --- a/DuckDuckGoTests/WebViewTestHelper.swift +++ b/DuckDuckGoTests/WebViewTestHelper.swift @@ -108,7 +108,8 @@ class WebKitTestHelper { return AppPrivacyConfiguration(data: privacyData, identifier: "", localProtection: localProtection, - internalUserDecider: DefaultInternalUserDecider()) + internalUserDecider: DefaultInternalUserDecider(), + toggleProtectionsCounter: ToggleProtectionsCounter(eventReporting: nil)) } static func prepareContentBlockingRules(trackerData: TrackerData, diff --git a/IntegrationTests/AutoconsentBackgroundTests.swift b/IntegrationTests/AutoconsentBackgroundTests.swift index 5b7cc496d4..121b7afe18 100644 --- a/IntegrationTests/AutoconsentBackgroundTests.swift +++ b/IntegrationTests/AutoconsentBackgroundTests.swift @@ -22,6 +22,7 @@ import XCTest @testable import Core @testable import BrowserServicesKit import WebKit +import Common final class AutoconsentBackgroundTests: XCTestCase { @@ -46,11 +47,12 @@ final class AutoconsentBackgroundTests: XCTestCase { """.data(using: .utf8)! let mockEmbeddedData = MockEmbeddedDataProvider(data: embeddedConfig, etag: "embedded") - + let eventMapping = EventMapping { _, _, _, _ in } let manager = PrivacyConfigurationManager(fetchedETag: nil, fetchedData: nil, embeddedDataProvider: mockEmbeddedData, localProtection: MockDomainsProtectionStore(), + toggleProtectionsCounterEventReporting: eventMapping, internalUserDecider: DefaultInternalUserDecider()) return AutoconsentUserScript(config: manager.privacyConfig, preferences: MockAutoconsentPreferences(), From c8f15013548338413419cf2ac3795234ccbb100d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jacek=20=C5=81yp?= Date: Mon, 11 Mar 2024 17:57:50 +0100 Subject: [PATCH 16/19] Release 7.112.0-1 (#2573) --- DuckDuckGo.xcodeproj/project.pbxproj | 56 ++++++++++++++-------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index e69e2ef144..873ec456d0 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -8258,7 +8258,7 @@ CODE_SIGN_ENTITLEMENTS = PacketTunnelProvider/PacketTunnelProvider.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; @@ -8295,7 +8295,7 @@ CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -8387,7 +8387,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = ShareExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -8415,7 +8415,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -8565,7 +8565,7 @@ CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGo.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_ASSET_PATHS = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -8591,7 +8591,7 @@ CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGo.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; INFOPLIST_FILE = DuckDuckGo/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -8656,7 +8656,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEAD_CODE_STRIPPING = NO; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = Widgets/Info.plist; @@ -8691,7 +8691,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEAD_CODE_STRIPPING = NO; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -8725,7 +8725,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = OpenAction/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -8756,7 +8756,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -9043,7 +9043,7 @@ CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGoAlpha.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_ASSET_PATHS = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -9074,7 +9074,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = ShareExtension/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -9103,7 +9103,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = OpenAction/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -9137,7 +9137,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEAD_CODE_STRIPPING = NO; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = Widgets/Info.plist; @@ -9168,7 +9168,7 @@ CODE_SIGN_ENTITLEMENTS = PacketTunnelProvider/PacketTunnelProviderAlpha.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; @@ -9201,11 +9201,11 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 0; + DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Core/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -9439,7 +9439,7 @@ CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGoAlpha.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_ASSET_PATHS = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -9466,7 +9466,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -9499,7 +9499,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -9537,7 +9537,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEAD_CODE_STRIPPING = NO; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -9573,7 +9573,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -9608,11 +9608,11 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 0; + DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Core/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -9786,11 +9786,11 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 0; + DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Core/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -9819,10 +9819,10 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 0; + DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Core/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; From 576c98f192ff5b011db0827987ea6b3820604d09 Mon Sep 17 00:00:00 2001 From: Christopher Brind Date: Mon, 11 Mar 2024 17:48:36 +0000 Subject: [PATCH 17/19] Use History on iOS (#2539) Task/Issue URL: https://app.asana.com/0/414235014887631/1206524433066955/f Tech Design URL: CC: Description: Adds history collection to iOS Steps to test this PR: See add history to ios BrowserServicesKit#693 In the manager's computed var for the coordinator where the store is loaded, change code to set an error message and ensure that the preemptive crash alert is shown. --- Core/HistoryCapture.swift | 69 +++++++ Core/HistoryManager.swift | 185 +++++++++++++++++- Core/PixelEvent.swift | 20 ++ DuckDuckGo.xcodeproj/project.pbxproj | 22 ++- .../xcshareddata/swiftpm/Package.resolved | 4 +- DuckDuckGo/AppDelegate.swift | 25 +++ DuckDuckGo/Debug.storyboard | 54 +++-- DuckDuckGo/HistoryDebugViewController.swift | 96 +++++++++ DuckDuckGo/MainViewController.swift | 8 + DuckDuckGo/TabManager.swift | 14 +- DuckDuckGo/TabViewController.swift | 23 ++- ...ViewControllerLongPressMenuExtension.swift | 8 +- DuckDuckGoTests/HistoryCaptureTests.swift | 105 ++++++++++ DuckDuckGoTests/HistoryManagerTests.swift | 41 +++- 14 files changed, 640 insertions(+), 34 deletions(-) create mode 100644 Core/HistoryCapture.swift create mode 100644 DuckDuckGo/HistoryDebugViewController.swift create mode 100644 DuckDuckGoTests/HistoryCaptureTests.swift diff --git a/Core/HistoryCapture.swift b/Core/HistoryCapture.swift new file mode 100644 index 0000000000..d63c7fd917 --- /dev/null +++ b/Core/HistoryCapture.swift @@ -0,0 +1,69 @@ +// +// HistoryCapture.swift +// DuckDuckGo +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation +import History + +public class HistoryCapture { + + enum VisitState { + case added + case expected + } + + let historyManager: HistoryManaging + var coordinator: HistoryCoordinating { + historyManager.historyCoordinator + } + + var url: URL? + + public init(historyManager: HistoryManaging) { + self.historyManager = historyManager + } + + public func webViewDidCommit(url: URL) { + self.url = url + coordinator.addVisit(of: url.urlOrDuckDuckGoCleanQuery) + } + + public func titleDidChange(_ title: String?, forURL url: URL?) { + guard self.url == url else { + return + } + + guard let url = url?.urlOrDuckDuckGoCleanQuery, let title, !title.isEmpty else { + return + } + coordinator.updateTitleIfNeeded(title: title, url: url) + coordinator.commitChanges(url: url) + } + +} + +extension URL { + + var urlOrDuckDuckGoCleanQuery: URL { + guard isDuckDuckGoSearch, + let searchQuery, + let url = URL.makeSearchURL(query: searchQuery)?.removingInternalSearchParameters() else { return self } + return url + } + +} diff --git a/Core/HistoryManager.swift b/Core/HistoryManager.swift index 49b2049f78..b0b833224e 100644 --- a/Core/HistoryManager.swift +++ b/Core/HistoryManager.swift @@ -17,21 +17,196 @@ // limitations under the License. // +import CoreData import Foundation import BrowserServicesKit +import History +import Common +import Persistence -class HistoryManager { +public protocol HistoryManaging { - let privacyConfig: PrivacyConfiguration + var historyCoordinator: HistoryCoordinating { get } + func loadStore() + +} + +public class HistoryManager: HistoryManaging { + + let privacyConfigManager: PrivacyConfigurationManaging let variantManager: VariantManager + let database: CoreDataDatabase + let onStoreLoadFailed: (Error) -> Void + + private var currentHistoryCoordinator: HistoryCoordinating? + + public var historyCoordinator: HistoryCoordinating { + guard isHistoryFeatureEnabled() else { + currentHistoryCoordinator = nil + return NullHistoryCoordinator() + } + + if let currentHistoryCoordinator { + return currentHistoryCoordinator + } + + var loadError: Error? + database.loadStore { _, error in + loadError = error + } + + if let loadError { + onStoreLoadFailed(loadError) + return NullHistoryCoordinator() + } - init(privacyConfig: PrivacyConfiguration, variantManager: VariantManager) { - self.privacyConfig = privacyConfig + let context = database.makeContext(concurrencyType: .privateQueueConcurrencyType) + let historyCoordinator = HistoryCoordinator(historyStoring: HistoryStore(context: context, eventMapper: HistoryStoreEventMapper())) + currentHistoryCoordinator = historyCoordinator + return historyCoordinator + } + + public init(privacyConfigManager: PrivacyConfigurationManaging, variantManager: VariantManager, database: CoreDataDatabase, onStoreLoadFailed: @escaping (Error) -> Void) { + self.privacyConfigManager = privacyConfigManager self.variantManager = variantManager + self.database = database + self.onStoreLoadFailed = onStoreLoadFailed } func isHistoryFeatureEnabled() -> Bool { - return privacyConfig.isEnabled(featureKey: .history) && variantManager.isSupported(feature: .history) + return privacyConfigManager.privacyConfig.isEnabled(featureKey: .history) && variantManager.isSupported(feature: .history) + } + + public func removeAllHistory() async { + await withCheckedContinuation { continuation in + historyCoordinator.burnAll { + continuation.resume() + } + } + } + + public func loadStore() { + historyCoordinator.loadHistory { + // Do migrations here if needed + } + } + +} + +class NullHistoryCoordinator: HistoryCoordinating { + + func loadHistory(onCleanFinished: @escaping () -> Void) { + } + + var history: History.BrowsingHistory? + + var allHistoryVisits: [History.Visit]? + + @Published private(set) public var historyDictionary: [URL: HistoryEntry]? + var historyDictionaryPublisher: Published<[URL: History.HistoryEntry]?>.Publisher { + $historyDictionary + } + + func addVisit(of url: URL) -> History.Visit? { + return nil + } + + func addBlockedTracker(entityName: String, on url: URL) { + } + + func trackerFound(on: URL) { + } + + func updateTitleIfNeeded(title: String, url: URL) { } + func markFailedToLoadUrl(_ url: URL) { + } + + func commitChanges(url: URL) { + } + + func title(for url: URL) -> String? { + return nil + } + + func burnAll(completion: @escaping () -> Void) { + completion() + } + + func burnDomains(_ baseDomains: Set, tld: Common.TLD, completion: @escaping () -> Void) { + completion() + } + + func burnVisits(_ visits: [History.Visit], completion: @escaping () -> Void) { + completion() + } + +} + +public class HistoryDatabase { + + private init() { } + + public static var defaultDBLocation: URL = { + guard let url = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first else { + os_log("HistoryDatabase.make - OUT, failed to get application support directory") + fatalError("Failed to get location") + } + return url + }() + + public static var defaultDBFileURL: URL = { + return defaultDBLocation.appendingPathComponent("History.sqlite", conformingTo: .database) + }() + + public static func make(location: URL = defaultDBLocation, readOnly: Bool = false) -> CoreDataDatabase { + os_log("HistoryDatabase.make - IN - %s", location.absoluteString) + let bundle = History.bundle + guard let model = CoreDataDatabase.loadModel(from: bundle, named: "BrowsingHistory") else { + os_log("HistoryDatabase.make - OUT, failed to loadModel") + fatalError("Failed to load model") + } + + let db = CoreDataDatabase(name: "History", + containerLocation: location, + model: model, + readOnly: readOnly) + os_log("HistoryDatabase.make - OUT") + return db + } +} + +class HistoryStoreEventMapper: EventMapping { + public init() { + super.init { event, error, _, _ in + switch event { + case .removeFailed: + Pixel.fire(pixel: .historyRemoveFailed, error: error) + + case .reloadFailed: + Pixel.fire(pixel: .historyReloadFailed, error: error) + + case .cleanEntriesFailed: + Pixel.fire(pixel: .historyCleanEntriesFailed, error: error) + + case .cleanVisitsFailed: + Pixel.fire(pixel: .historyCleanVisitsFailed, error: error) + + case .saveFailed: + Pixel.fire(pixel: .historySaveFailed, error: error) + + case .insertVisitFailed: + Pixel.fire(pixel: .historyInsertVisitFailed, error: error) + + case .removeVisitsFailed: + Pixel.fire(pixel: .historyRemoveVisitsFailed, error: error) + } + + } + } + + override init(mapping: @escaping EventMapping.Mapping) { + fatalError("Use init()") + } } diff --git a/Core/PixelEvent.swift b/Core/PixelEvent.swift index 3e2aad0624..78521372c7 100644 --- a/Core/PixelEvent.swift +++ b/Core/PixelEvent.swift @@ -555,6 +555,16 @@ extension Pixel { case userBehaviorFireButtonAndRestart case userBehaviorFireButtonAndTogglePrivacyControls + // MARK: History + case historyStoreLoadFailed + case historyRemoveFailed + case historyReloadFailed + case historyCleanEntriesFailed + case historyCleanVisitsFailed + case historySaveFailed + case historyInsertVisitFailed + case historyRemoveVisitsFailed + // MARK: Privacy pro case privacyProSubscriptionActive case privacyProOfferScreenImpression @@ -1113,6 +1123,16 @@ extension Pixel.Event { case .userBehaviorFireButtonAndRestart: return "m_fire-button-and-restart" case .userBehaviorFireButtonAndTogglePrivacyControls: return "m_fire-button-and-toggle-privacy-controls" + // MARK: - History debug + case .historyStoreLoadFailed: return "m_debug_history-store-load-failed" + case .historyRemoveFailed: return "m_debug_history-remove-failed" + case .historyReloadFailed: return "m_debug_history-reload-failed" + case .historyCleanEntriesFailed: return "m_debug_history-clean-entries-failed" + case .historyCleanVisitsFailed: return "m_debug_history-clean-visits-failed" + case .historySaveFailed: return "m_debug_history-save-failed" + case .historyInsertVisitFailed: return "m_debug_history-insert-visit-failed" + case .historyRemoveVisitsFailed: return "m_debug_history-remove-visits-failed" + // MARK: Privacy pro case .privacyProSubscriptionActive: return "m_privacy-pro_app_subscription_active" case .privacyProOfferScreenImpression: return "m_privacy-pro_offer_screen_impression" diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 873ec456d0..891f03a26f 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -363,6 +363,9 @@ 8512EA5D24ED30D30073EE19 /* WidgetsExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 8512EA4D24ED30D20073EE19 /* WidgetsExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 8512EA9D24EEA6820073EE19 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F143C2B11E49D78C00CFDE3A /* Assets.xcassets */; }; 851481882A600EFC00ABC65F /* RemoteMessaging in Frameworks */ = {isa = PBXBuildFile; productRef = 851481872A600EFC00ABC65F /* RemoteMessaging */; }; + 851624C22B95F8BD002D5CD7 /* HistoryCapture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 851624C12B95F8BD002D5CD7 /* HistoryCapture.swift */; }; + 851624C52B9602A4002D5CD7 /* HistoryCaptureTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 851624C32B96029B002D5CD7 /* HistoryCaptureTests.swift */; }; + 851624C72B96389D002D5CD7 /* HistoryDebugViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 851624C62B96389D002D5CD7 /* HistoryDebugViewController.swift */; }; 8517D98B221783A0006A8DD0 /* FindInPage.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8517D98A221783A0006A8DD0 /* FindInPage.xcassets */; }; 851B1283221FE65E004781BC /* ImproveOnboardingExperiment1Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 851B1281221FE64E004781BC /* ImproveOnboardingExperiment1Tests.swift */; }; 851B128822200575004781BC /* Onboarding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 851B128722200575004781BC /* Onboarding.swift */; }; @@ -441,6 +444,7 @@ 85875B6129912A9900115F05 /* SyncUI in Frameworks */ = {isa = PBXBuildFile; productRef = 85875B6029912A9900115F05 /* SyncUI */; }; 8588026624E420BD00C24AB6 /* LargeOmniBarStateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8588026424E4209900C24AB6 /* LargeOmniBarStateTests.swift */; }; 8588026A24E424EE00C24AB6 /* AppWidthObserverTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8588026824E424AF00C24AB6 /* AppWidthObserverTests.swift */; }; + 858D009D2B9799FC004E5B4C /* History in Frameworks */ = {isa = PBXBuildFile; productRef = 858D009C2B9799FC004E5B4C /* History */; }; 8590CB612684D0600089F6BF /* CookieDebugViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8590CB602684D0600089F6BF /* CookieDebugViewController.swift */; }; 8590CB632684F10F0089F6BF /* ContentBlockerProtectionStoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8590CB622684F10F0089F6BF /* ContentBlockerProtectionStoreTests.swift */; }; 8590CB67268A2E520089F6BF /* RootDebugViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8590CB66268A2E520089F6BF /* RootDebugViewController.swift */; }; @@ -1479,6 +1483,9 @@ 8512EA5324ED30D20073EE19 /* Widgets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Widgets.swift; sourceTree = ""; }; 8512EA5624ED30D30073EE19 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 8512EA5824ED30D30073EE19 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 851624C12B95F8BD002D5CD7 /* HistoryCapture.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryCapture.swift; sourceTree = ""; }; + 851624C32B96029B002D5CD7 /* HistoryCaptureTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryCaptureTests.swift; sourceTree = ""; }; + 851624C62B96389D002D5CD7 /* HistoryDebugViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryDebugViewController.swift; sourceTree = ""; }; 8517D98A221783A0006A8DD0 /* FindInPage.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = FindInPage.xcassets; sourceTree = ""; }; 851B1281221FE64E004781BC /* ImproveOnboardingExperiment1Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImproveOnboardingExperiment1Tests.swift; sourceTree = ""; }; 851B128722200575004781BC /* Onboarding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Onboarding.swift; sourceTree = ""; }; @@ -2826,6 +2833,7 @@ 37DF000C29F9CA80002B7D3E /* SyncDataProviders in Frameworks */, 1E6098A1290011E600A508F9 /* UserScript in Frameworks */, D61CDA162B7CF77300A0FBB9 /* Subscription in Frameworks */, + 858D009D2B9799FC004E5B4C /* History in Frameworks */, C14882ED27F211A000D59F0C /* SwiftSoup in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -3928,6 +3936,7 @@ isa = PBXGroup; children = ( 858479C82B8792D800D156C1 /* HistoryManager.swift */, + 851624C12B95F8BD002D5CD7 /* HistoryCapture.swift */, ); name = History; sourceTree = ""; @@ -3936,6 +3945,7 @@ isa = PBXGroup; children = ( 858479CB2B8795C900D156C1 /* HistoryManagerTests.swift */, + 851624C32B96029B002D5CD7 /* HistoryCaptureTests.swift */, ); name = History; sourceTree = ""; @@ -3958,6 +3968,7 @@ F46FEC5627987A5F0061D9DF /* KeychainItemsDebugViewController.swift */, EE72CA842A862D000043B5B3 /* NetworkProtectionDebugViewController.swift */, CBFCB30D2B2CD47800253E9E /* ConfigurationURLDebugViewController.swift */, + 851624C62B96389D002D5CD7 /* HistoryDebugViewController.swift */, ); name = Debug; sourceTree = ""; @@ -5966,6 +5977,7 @@ EE8E56892A56BCE400F11DCA /* NetworkProtection */, D61CDA152B7CF77300A0FBB9 /* Subscription */, D61CDA172B7CF78300A0FBB9 /* ZIPFoundation */, + 858D009C2B9799FC004E5B4C /* History */, B6A26C072B9835A000DF9EAD /* Macros */, ); productName = Core; @@ -6909,6 +6921,7 @@ C13F3F6A2B7F883A0083BE40 /* AuthConfirmationPromptViewController.swift in Sources */, 02C57C4B2514FEFB009E5129 /* DoNotSellSettingsViewController.swift in Sources */, 02A54A9C2A097C95000C8FED /* AppTPHomeViewSectionRenderer.swift in Sources */, + 851624C72B96389D002D5CD7 /* HistoryDebugViewController.swift in Sources */, 8540BBA22440857A00017FE4 /* PreserveLoginsWorker.swift in Sources */, 85DFEDF924CF3D0E00973FE7 /* TabsBarCell.swift in Sources */, F17922DB1E717C8D006E3D97 /* Suggestion.swift in Sources */, @@ -7096,6 +7109,7 @@ 981FED7422046017008488D7 /* AutoClearTests.swift in Sources */, 98DDF9F322C4029D00DE38DB /* InitHelpers.swift in Sources */, B6AD9E3628D4510A0019CDE9 /* ContentBlockerRulesManagerMock.swift in Sources */, + 851624C52B9602A4002D5CD7 /* HistoryCaptureTests.swift in Sources */, F1E092C11E92A72E00732CCC /* UIColorExtensionTests.swift in Sources */, 85010504292FFB080033978F /* FireproofFaviconUpdaterTests.swift in Sources */, F1D477C91F2139410031ED49 /* SmallOmniBarStateTests.swift in Sources */, @@ -7229,6 +7243,7 @@ F1134EAB1F3E2C6A00B73467 /* StatisticsUserDefaults.swift in Sources */, CB258D1E29A52AF900DEBA24 /* FileStore.swift in Sources */, F1075C921E9EF827006BE8A8 /* UserDefaultsExtension.swift in Sources */, + 851624C22B95F8BD002D5CD7 /* HistoryCapture.swift in Sources */, 85CA53AC24BBD39300A6288C /* FaviconRequestModifier.swift in Sources */, CB258D1D29A52AF900DEBA24 /* EtagStorage.swift in Sources */, C1B7B52D2894469D0098FD6A /* DefaultVariantManager.swift in Sources */, @@ -10029,7 +10044,7 @@ repositoryURL = "https://github.com/DuckDuckGo/BrowserServicesKit"; requirement = { kind = exactVersion; - version = 121.0.0; + version = 122.0.0; }; }; B6F997C22B8F374300476735 /* XCRemoteSwiftPackageReference "apple-toolbox" */ = { @@ -10191,6 +10206,11 @@ isa = XCSwiftPackageProductDependency; productName = SyncUI; }; + 858D009C2B9799FC004E5B4C /* History */ = { + isa = XCSwiftPackageProductDependency; + package = 98A16C2928A11BDE00A6C003 /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; + productName = History; + }; 8599690E29D2F1C100DBF9FA /* DDGSync */ = { isa = XCSwiftPackageProductDependency; package = 98A16C2928A11BDE00A6C003 /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 1632d16407..faaf2e1be7 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" : "4555c3dbf265f1dca0304c69e7013b9d46a758b3", - "version" : "121.0.0" + "revision" : "83bcbbf0dace717db6e518e4d867d617c846a3b5", + "version" : "122.0.0" } }, { diff --git a/DuckDuckGo/AppDelegate.swift b/DuckDuckGo/AppDelegate.swift index 159549363b..f6eaa9a624 100644 --- a/DuckDuckGo/AppDelegate.swift +++ b/DuckDuckGo/AppDelegate.swift @@ -265,16 +265,20 @@ class AppDelegate: UIResponder, UIApplicationDelegate { }) } + let historyManager = makeHistoryManager() + #if APP_TRACKING_PROTECTION let main = MainViewController(bookmarksDatabase: bookmarksDatabase, bookmarksDatabaseCleaner: syncDataProviders.bookmarksAdapter.databaseCleaner, appTrackingProtectionDatabase: appTrackingProtectionDatabase, + historyManager: historyManager, syncService: syncService, syncDataProviders: syncDataProviders, appSettings: AppDependencyProvider.shared.appSettings) #else let main = MainViewController(bookmarksDatabase: bookmarksDatabase, bookmarksDatabaseCleaner: syncDataProviders.bookmarksAdapter.databaseCleaner, + historyManager: historyManager, syncService: syncService, syncDataProviders: syncDataProviders, appSettings: AppDependencyProvider.shared.appSettings) @@ -336,6 +340,27 @@ class AppDelegate: UIResponder, UIApplicationDelegate { return true } + private func makeHistoryManager() -> HistoryManager { + let historyManager = HistoryManager(privacyConfigManager: ContentBlocking.shared.privacyConfigurationManager, + variantManager: DefaultVariantManager(), + database: HistoryDatabase.make()) { error in + Pixel.fire(pixel: .historyStoreLoadFailed, error: error) + if error.isDiskFull { + self.presentInsufficientDiskSpaceAlert() + } else { + self.presentPreemptiveCrashAlert() + } + } + + // This is a compromise to support hot reloading via privacy config. + // * If the history is disabled this will do nothing. If it is subsequently enabled then it won't start collecting history + // until the app cold launches at least once. + // * If the history is enabled this loads the store sets up the history manager + // correctly. If the history manager is subsequently disabled it will stop working immediately. + historyManager.loadStore() + return historyManager + } + private func presentPreemptiveCrashAlert() { Task { @MainActor in let alertController = CriticalAlerts.makePreemptiveCrashAlert() diff --git a/DuckDuckGo/Debug.storyboard b/DuckDuckGo/Debug.storyboard index d3940730a7..3fba4ea1a2 100644 --- a/DuckDuckGo/Debug.storyboard +++ b/DuckDuckGo/Debug.storyboard @@ -1,9 +1,9 @@ - + - + @@ -239,9 +239,21 @@ - + + + + + + + + + + + + + @@ -249,7 +261,7 @@ - + @@ -258,7 +270,7 @@ - + @@ -267,7 +279,7 @@ - + @@ -843,17 +855,17 @@ - + - + - + - + @@ -911,6 +923,22 @@ + + + + + + + + + + + + + + + + diff --git a/DuckDuckGo/HistoryDebugViewController.swift b/DuckDuckGo/HistoryDebugViewController.swift new file mode 100644 index 0000000000..3d516818c3 --- /dev/null +++ b/DuckDuckGo/HistoryDebugViewController.swift @@ -0,0 +1,96 @@ +// +// HistoryDebugViewController.swift +// DuckDuckGo +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit +import SwiftUI +import History +import Core +import Combine +import Persistence + +class HistoryDebugViewController: UIHostingController { + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder, rootView: HistoryDebugRootView()) + } + +} + +struct HistoryDebugRootView: View { + + @ObservedObject var model = HistoryDebugViewModel() + + var body: some View { + List(model.history, id: \.id) { entry in + VStack(alignment: .leading) { + Text(entry.title ?? "") + .font(.system(size: 14)) + Text(entry.url?.absoluteString ?? "") + .font(.system(size: 12)) + Text(entry.lastVisit?.description ?? "") + .font(.system(size: 10)) + } + } + .navigationTitle("\(model.history.count) History Items") + .toolbar { + if #available(iOS 15, *) { + Button("Delete All", role: .destructive) { + model.deleteAll() + } + } + } + } + +} + +class HistoryDebugViewModel: ObservableObject { + + @Published var history: [BrowsingHistoryEntryManagedObject] = [] + + let database: CoreDataDatabase + + init() { + database = HistoryDatabase.make() + database.loadStore() + fetch() + } + + func deleteAll() { + let context = database.makeContext(concurrencyType: .mainQueueConcurrencyType) + let fetchRequest = BrowsingHistoryEntryManagedObject.fetchRequest() + let items = try? context.fetch(fetchRequest) + items?.forEach { obj in + context.delete(obj) + } + do { + try context.save() + } catch { + assertionFailure("Failed to save after delete all") + } + fetch() + } + + func fetch() { + let context = database.makeContext(concurrencyType: .mainQueueConcurrencyType) + let fetchRequest = BrowsingHistoryEntryManagedObject.fetchRequest() + fetchRequest.returnsObjectsAsFaults = false + history = (try? context.fetch(fetchRequest)) ?? [] + } + +} diff --git a/DuckDuckGo/MainViewController.swift b/DuckDuckGo/MainViewController.swift index b3e04842cf..704e2098ea 100644 --- a/DuckDuckGo/MainViewController.swift +++ b/DuckDuckGo/MainViewController.swift @@ -161,6 +161,7 @@ class MainViewController: UIViewController { fatalError("Use init?(code:") } + var historyManager: HistoryManager var viewCoordinator: MainViewCoordinator! #if APP_TRACKING_PROTECTION @@ -168,6 +169,7 @@ class MainViewController: UIViewController { bookmarksDatabase: CoreDataDatabase, bookmarksDatabaseCleaner: BookmarkDatabaseCleaner, appTrackingProtectionDatabase: CoreDataDatabase, + historyManager: HistoryManager, syncService: DDGSyncing, syncDataProviders: SyncDataProviders, appSettings: AppSettings = AppUserDefaults() @@ -175,6 +177,7 @@ class MainViewController: UIViewController { self.appTrackingProtectionDatabase = appTrackingProtectionDatabase self.bookmarksDatabase = bookmarksDatabase self.bookmarksDatabaseCleaner = bookmarksDatabaseCleaner + self.historyManager = historyManager self.syncService = syncService self.syncDataProviders = syncDataProviders self.favoritesViewModel = FavoritesListViewModel(bookmarksDatabase: bookmarksDatabase, favoritesDisplayMode: appSettings.favoritesDisplayMode) @@ -190,12 +193,14 @@ class MainViewController: UIViewController { init( bookmarksDatabase: CoreDataDatabase, bookmarksDatabaseCleaner: BookmarkDatabaseCleaner, + historyManager: HistoryManager, syncService: DDGSyncing, syncDataProviders: SyncDataProviders, appSettings: AppSettings ) { self.bookmarksDatabase = bookmarksDatabase self.bookmarksDatabaseCleaner = bookmarksDatabaseCleaner + self.historyManager = historyManager self.syncService = syncService self.syncDataProviders = syncDataProviders self.favoritesViewModel = FavoritesListViewModel(bookmarksDatabase: bookmarksDatabase, favoritesDisplayMode: appSettings.favoritesDisplayMode) @@ -706,6 +711,7 @@ class MainViewController: UIViewController { tabManager = TabManager(model: tabsModel, previewsSource: previewsSource, bookmarksDatabase: bookmarksDatabase, + historyManager: historyManager, syncService: syncService, delegate: self) } @@ -2258,6 +2264,8 @@ extension MainViewController: AutoClearWorker { self.bookmarksDatabaseCleaner?.cleanUpDatabaseNow() } + await historyManager.removeAllHistory() + self.clearInProgress = false self.postClear?() diff --git a/DuckDuckGo/TabManager.swift b/DuckDuckGo/TabManager.swift index 68a8fa473a..a6bf166cf2 100644 --- a/DuckDuckGo/TabManager.swift +++ b/DuckDuckGo/TabManager.swift @@ -23,6 +23,7 @@ import DDGSync import WebKit import BrowserServicesKit import Persistence +import History class TabManager { @@ -31,6 +32,7 @@ class TabManager { private var tabControllerCache = [TabViewController]() private let bookmarksDatabase: CoreDataDatabase + private let historyManager: HistoryManager private let syncService: DDGSyncing private var previewsSource: TabPreviewsSource weak var delegate: TabDelegate? @@ -42,11 +44,13 @@ class TabManager { init(model: TabsModel, previewsSource: TabPreviewsSource, bookmarksDatabase: CoreDataDatabase, + historyManager: HistoryManager, syncService: DDGSyncing, delegate: TabDelegate) { self.model = model self.previewsSource = previewsSource self.bookmarksDatabase = bookmarksDatabase + self.historyManager = historyManager self.syncService = syncService self.delegate = delegate let index = model.currentIndex @@ -68,7 +72,10 @@ class TabManager { @MainActor private func buildController(forTab tab: Tab, url: URL?, inheritedAttribution: AdClickAttributionLogic.State?) -> TabViewController { let configuration = WKWebViewConfiguration.persistent() - let controller = TabViewController.loadFromStoryboard(model: tab, bookmarksDatabase: bookmarksDatabase, syncService: syncService) + let controller = TabViewController.loadFromStoryboard(model: tab, + bookmarksDatabase: bookmarksDatabase, + historyManager: historyManager, + syncService: syncService) controller.applyInheritedAttribution(inheritedAttribution) controller.attachWebView(configuration: configuration, andLoadRequest: url == nil ? nil : URLRequest.userInitiated(url!), @@ -137,7 +144,10 @@ class TabManager { model.insert(tab: tab, at: model.currentIndex + 1) model.select(tabAt: model.currentIndex + 1) - let controller = TabViewController.loadFromStoryboard(model: tab, bookmarksDatabase: bookmarksDatabase, syncService: syncService) + let controller = TabViewController.loadFromStoryboard(model: tab, + bookmarksDatabase: bookmarksDatabase, + historyManager: historyManager, + syncService: syncService) controller.attachWebView(configuration: configCopy, andLoadRequest: request, consumeCookies: !model.hasActiveTabs, diff --git a/DuckDuckGo/TabViewController.swift b/DuckDuckGo/TabViewController.swift index 5e0e482895..7ecc8667ea 100644 --- a/DuckDuckGo/TabViewController.swift +++ b/DuckDuckGo/TabViewController.swift @@ -18,8 +18,8 @@ // import WebKit -import Combine import Core +import Combine import StoreKit import LocalAuthentication import BrowserServicesKit @@ -34,6 +34,7 @@ import ContentBlocking import TrackerRadarKit import Networking import SecureStorage +import History #if NETWORK_PROTECTION import NetworkProtection @@ -175,6 +176,7 @@ class TabViewController: UIViewController { didSet { updateTabModel() delegate?.tabLoadingStateDidChange(tab: self) + historyCapture.titleDidChange(title, forURL: url) } } @@ -272,6 +274,7 @@ class TabViewController: UIViewController { static func loadFromStoryboard(model: Tab, appSettings: AppSettings = AppDependencyProvider.shared.appSettings, bookmarksDatabase: CoreDataDatabase, + historyManager: HistoryManager, syncService: DDGSyncing) -> TabViewController { let storyboard = UIStoryboard(name: "Tab", bundle: nil) let controller = storyboard.instantiateViewController(identifier: "TabViewController", creator: { coder in @@ -279,6 +282,7 @@ class TabViewController: UIViewController { tabModel: model, appSettings: appSettings, bookmarksDatabase: bookmarksDatabase, + historyManager: historyManager, syncService: syncService) }) return controller @@ -287,15 +291,21 @@ class TabViewController: UIViewController { private var userContentController: UserContentController { (webView.configuration.userContentController as? UserContentController)! } - + + let historyManager: HistoryManager + let historyCapture: HistoryCapture + required init?(coder aDecoder: NSCoder, tabModel: Tab, appSettings: AppSettings, bookmarksDatabase: CoreDataDatabase, + historyManager: HistoryManager, syncService: DDGSyncing) { self.tabModel = tabModel self.appSettings = appSettings self.bookmarksDatabase = bookmarksDatabase + self.historyManager = historyManager + self.historyCapture = HistoryCapture(historyManager: historyManager) self.syncService = syncService super.init(coder: aDecoder) } @@ -303,7 +313,7 @@ class TabViewController: UIViewController { required init?(coder aDecoder: NSCoder) { fatalError("Not implemented") } - + override func viewDidLoad() { super.viewDidLoad() @@ -380,7 +390,8 @@ class TabViewController: UIViewController { func updateTabModel() { if let url = url { - tabModel.link = Link(title: title, url: url) + let link = Link(title: title, url: url) + tabModel.link = link } else { tabModel.link = nil } @@ -1021,7 +1032,9 @@ extension TabViewController: WKNavigationDelegate { } func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) { + if let url = webView.url { + historyCapture.webViewDidCommit(url: url) instrumentation.willLoad(url: url) } @@ -1058,6 +1071,7 @@ extension TabViewController: WKNavigationDelegate { func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) { + let mimeType = MIMEType(from: navigationResponse.response.mimeType) let httpResponse = navigationResponse.response as? HTTPURLResponse @@ -1130,6 +1144,7 @@ extension TabViewController: WKNavigationDelegate { } func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { + adClickAttributionDetection.onDidFinishNavigation(url: webView.url) adClickAttributionLogic.onDidFinishNavigation(host: webView.url?.host) hideProgressIndicator() diff --git a/DuckDuckGo/TabViewControllerLongPressMenuExtension.swift b/DuckDuckGo/TabViewControllerLongPressMenuExtension.swift index a4ef959ea7..d760a85b0a 100644 --- a/DuckDuckGo/TabViewControllerLongPressMenuExtension.swift +++ b/DuckDuckGo/TabViewControllerLongPressMenuExtension.swift @@ -21,6 +21,9 @@ import UIKit import Core import SafariServices import WebKit +import History +import Common +import Combine extension TabViewController { @@ -99,7 +102,10 @@ extension TabViewController { fileprivate func buildOpenLinkPreview(for url: URL) -> UIViewController? { let tab = Tab(link: Link(title: nil, url: url)) - let tabController = TabViewController.loadFromStoryboard(model: tab, bookmarksDatabase: bookmarksDatabase, syncService: syncService) + let tabController = TabViewController.loadFromStoryboard(model: tab, + bookmarksDatabase: bookmarksDatabase, + historyManager: historyManager, + syncService: syncService) tabController.isLinkPreview = true tabController.decorate(with: ThemeManager.shared.currentTheme) let configuration = WKWebViewConfiguration.nonPersistent() diff --git a/DuckDuckGoTests/HistoryCaptureTests.swift b/DuckDuckGoTests/HistoryCaptureTests.swift new file mode 100644 index 0000000000..78b65f955a --- /dev/null +++ b/DuckDuckGoTests/HistoryCaptureTests.swift @@ -0,0 +1,105 @@ +// +// HistoryCaptureTests.swift +// DuckDuckGo +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation +import XCTest +import BrowserServicesKit +import Persistence +import History +@testable import Core + +final class HistoryCaptureTests: XCTestCase { + + let mockHistoryCoordinator = MockHistoryCoordinator() + + func test_whenURLIsCommitted_ThenVisitIsStored() { + let capture = makeCapture() + capture.webViewDidCommit(url: URL.example) + XCTAssertEqual(1, mockHistoryCoordinator.addVisitCalls.count) + XCTAssertEqual([URL.example], mockHistoryCoordinator.addVisitCalls) + } + + func test_whenURLIsDDGQuery_ThenOnlyQueryIsStored() { + let capture = makeCapture() + capture.webViewDidCommit(url: URL.makeSearchURL(query: "test")!) + XCTAssertEqual(1, mockHistoryCoordinator.addVisitCalls.count) + XCTAssertEqual("https://duckduckgo.com?q=test", mockHistoryCoordinator.addVisitCalls[0].absoluteString) + } + + func test_whenURLIsDDGQueryWithExtraParams_ThenOnlyQueryIsStored() { + let capture = makeCapture() + capture.webViewDidCommit(url: URL.makeSearchURL(query: "test")!.appendingParameter(name: "ia", value: "web")) + XCTAssertEqual(1, mockHistoryCoordinator.addVisitCalls.count) + XCTAssertEqual("https://duckduckgo.com?q=test", mockHistoryCoordinator.addVisitCalls[0].absoluteString) + } + + func test_whenTitleIsUpdatedForMatchingURL_ThenTitleIsSaved() { + let capture = makeCapture() + capture.webViewDidCommit(url: URL.example) + capture.titleDidChange("test", forURL: URL.example) + XCTAssertEqual(1, mockHistoryCoordinator.updateTitleIfNeededCalls.count) + XCTAssertEqual(mockHistoryCoordinator.updateTitleIfNeededCalls[0].title, "test") + XCTAssertEqual(mockHistoryCoordinator.updateTitleIfNeededCalls[0].url, URL.example) + } + + func test_whenTitleIsUpdatedForDifferentURL_ThenTitleIsIgnored() { + let capture = makeCapture() + capture.webViewDidCommit(url: URL.example) + capture.titleDidChange("test", forURL: URL.example.appendingPathComponent("path")) + XCTAssertEqual(0, mockHistoryCoordinator.updateTitleIfNeededCalls.count) + } + + func makeCapture() -> HistoryCapture { + return HistoryCapture(historyManager: MockHistoryManager(historyCoordinator: mockHistoryCoordinator)) + } + +} + +class MockHistoryCoordinator: NullHistoryCoordinator { + + var addVisitCalls = [URL]() + var updateTitleIfNeededCalls = [(title: String, url: URL)]() + + override func addVisit(of url: URL) -> Visit? { + addVisitCalls.append(url) + return nil + } + + override func updateTitleIfNeeded(title: String, url: URL) { + updateTitleIfNeededCalls.append((title: title, url: url)) + } + +} + +private extension URL { + static let example = URL(string: "https://example.com")! +} + +class MockHistoryManager: HistoryManaging { + + let historyCoordinator: HistoryCoordinating + + init(historyCoordinator: HistoryCoordinating) { + self.historyCoordinator = historyCoordinator + } + + func loadStore() { + } + +} diff --git a/DuckDuckGoTests/HistoryManagerTests.swift b/DuckDuckGoTests/HistoryManagerTests.swift index c969773b29..50828b8eda 100644 --- a/DuckDuckGoTests/HistoryManagerTests.swift +++ b/DuckDuckGoTests/HistoryManagerTests.swift @@ -20,17 +20,15 @@ import Foundation import XCTest import BrowserServicesKit +import Persistence +import History @testable import Core final class HistoryManagerTests: XCTestCase { - let privacyConfig = MockPrivacyConfiguration() + let privacyConfigManager = MockPrivacyConfigurationManager() var variantManager = MockVariantManager() - lazy var historyManager: HistoryManager = { - HistoryManager(privacyConfig: privacyConfig, variantManager: variantManager) - }() - func test() { struct Condition { @@ -48,6 +46,7 @@ final class HistoryManagerTests: XCTestCase { ] let privacyConfig = MockPrivacyConfiguration() + let privacyConfigManager = MockPrivacyConfigurationManager() var variantManager = MockVariantManager() for condition in conditions { @@ -56,11 +55,41 @@ final class HistoryManagerTests: XCTestCase { return condition.privacy } + privacyConfigManager.privacyConfig = privacyConfig variantManager.isSupportedReturns = condition.variant - let historyManager = HistoryManager(privacyConfig: privacyConfig, variantManager: variantManager) + let model = CoreDataDatabase.loadModel(from: History.bundle, named: "BrowsingHistory")! + let db = CoreDataDatabase(name: "Test", containerLocation: tempDBDir(), model: model) + + let historyManager = HistoryManager(privacyConfigManager: privacyConfigManager, variantManager: variantManager, database: db) { + XCTFail("DB Error \($0)") + } XCTAssertEqual(condition.expected, historyManager.isHistoryFeatureEnabled(), String(describing: condition)) } } + + func test_WhenManagerFailsToLoadStore_ThenThrowsError() { + let privacyConfig = MockPrivacyConfiguration() + let privacyConfigManager = MockPrivacyConfigurationManager() + var variantManager = MockVariantManager() + + privacyConfig.isFeatureKeyEnabled = { feature, _ in + XCTAssertEqual(feature, .history) + return true + } + + privacyConfigManager.privacyConfig = privacyConfig + variantManager.isSupportedReturns = true + + let model = CoreDataDatabase.loadModel(from: History.bundle, named: "BrowsingHistory")! + let db = CoreDataDatabase(name: "Test", containerLocation: URL.aboutLink, model: model) + + var error: Error? + let historyManager = HistoryManager(privacyConfigManager: privacyConfigManager, variantManager: variantManager, database: db) { + error = $0 + } + _ = historyManager.historyCoordinator + XCTAssertNotNil(error) + } } From bbacdd73a2ef35e4010ead1f49081a07d29e4796 Mon Sep 17 00:00:00 2001 From: Daniel Bernal Date: Mon, 11 Mar 2024 23:42:32 +0100 Subject: [PATCH 18/19] Subscriptions: 19. Error handling and minor updates (#2567) --- DuckDuckGo/SettingsSubscriptionView.swift | 14 ++-- DuckDuckGo/SettingsViewModel.swift | 18 ++--- .../AsyncHeadlessWebView.swift | 3 + .../AsyncHeadlessWebViewModel.swift | 1 + .../HeadlessWebView.swift | 2 + .../HeadlessWebViewCoordinator.swift | 11 ++- ...IdentityTheftRestorationPagesFeature.swift | 9 ++- .../SubscriptionEmailViewModel.swift | 12 ++++ .../ViewModel/SubscriptionFlowViewModel.swift | 23 +++++- .../ViewModel/SubscriptionITPViewModel.swift | 15 +++- .../SubscriptionRestoreViewModel.swift | 2 +- .../Views/SubscriptionEmailView.swift | 9 +++ .../Views/SubscriptionFlowView.swift | 70 +++++++++++++++---- .../Views/SubscriptionITPView.swift | 11 +++ .../Views/SubscriptionRestoreView.swift | 12 +++- DuckDuckGo/UserText.swift | 4 ++ DuckDuckGo/en.lproj/Localizable.strings | 9 +++ submodules/privacy-reference-tests | 2 +- 18 files changed, 187 insertions(+), 40 deletions(-) diff --git a/DuckDuckGo/SettingsSubscriptionView.swift b/DuckDuckGo/SettingsSubscriptionView.swift index 4b2c71b8e5..a727b87dc0 100644 --- a/DuckDuckGo/SettingsSubscriptionView.swift +++ b/DuckDuckGo/SettingsSubscriptionView.swift @@ -138,18 +138,14 @@ struct SettingsSubscriptionView: View { SettingsCellView(label: UserText.settingsPProDBPTitle, subtitle: UserText.settingsPProDBPSubTitle, action: { isShowingDBP.toggle() }, isButton: true) - .sheet(isPresented: $isShowingDBP) { - SubscriptionPIRView() - } + } if viewModel.shouldShowITP { SettingsCellView(label: UserText.settingsPProITRTitle, subtitle: UserText.settingsPProITRSubTitle, action: { isShowingITP.toggle() }, isButton: true) - .sheet(isPresented: $isShowingITP) { - SubscriptionITPView() - } + } NavigationLink(destination: SubscriptionSettingsView()) { @@ -157,6 +153,12 @@ struct SettingsSubscriptionView: View { } } + .sheet(isPresented: $isShowingDBP) { + SubscriptionPIRView() + } + .sheet(isPresented: $isShowingITP) { + SubscriptionITPView() + } } var body: some View { diff --git a/DuckDuckGo/SettingsViewModel.swift b/DuckDuckGo/SettingsViewModel.swift index fd7ea5e1de..7aaba476b6 100644 --- a/DuckDuckGo/SettingsViewModel.swift +++ b/DuckDuckGo/SettingsViewModel.swift @@ -25,6 +25,7 @@ import Common import Combine import SyncUI + #if SUBSCRIPTION import Subscription #endif @@ -47,12 +48,20 @@ final class SettingsViewModel: ObservableObject { private var legacyViewProvider: SettingsLegacyViewProvider private lazy var versionProvider: AppVersion = AppVersion.shared private let voiceSearchHelper: VoiceSearchHelperProtocol + #if SUBSCRIPTION private var accountManager: AccountManager private var signOutObserver: Any? + + // Sheet Presentation & Navigation @Published var isRestoringSubscription: Bool = false @Published var shouldDisplayRestoreSubscriptionError: Bool = false + @Published var shouldShowNetP = false + @Published var shouldShowDBP = false + @Published var shouldShowITP = false #endif + @UserDefaultsWrapper(key: .subscriptionIsActive, defaultValue: false) + static private var cachedHasActiveSubscription: Bool #if NETWORK_PROTECTION @@ -63,10 +72,6 @@ final class SettingsViewModel: ObservableObject { private lazy var isPad = UIDevice.current.userInterfaceIdiom == .pad private var cancellables = Set() - // Defaults - @UserDefaultsWrapper(key: .subscriptionIsActive, defaultValue: false) - static private var cachedHasActiveSubscription: Bool - // Closures to interact with legacy view controllers through the container var onRequestPushLegacyView: ((UIViewController) -> Void)? var onRequestPresentLegacyView: ((UIViewController, _ modal: Bool) -> Void)? @@ -78,10 +83,6 @@ final class SettingsViewModel: ObservableObject { @Published var shouldNavigateToDBP = false @Published var shouldNavigateToITP = false @Published var shouldNavigateToSubscriptionFlow = false - - @Published var shouldShowNetP = false - @Published var shouldShowDBP = false - @Published var shouldShowITP = false // Our View State @Published private(set) var state: SettingsState @@ -372,7 +373,6 @@ extension SettingsViewModel { } } } - default: // Account is active but there's not a valid subscription / entitlements signOutUser() diff --git a/DuckDuckGo/Subscription/AsyncHeadlessWebview/AsyncHeadlessWebView.swift b/DuckDuckGo/Subscription/AsyncHeadlessWebview/AsyncHeadlessWebView.swift index aa6a8fdbce..306a636443 100644 --- a/DuckDuckGo/Subscription/AsyncHeadlessWebview/AsyncHeadlessWebView.swift +++ b/DuckDuckGo/Subscription/AsyncHeadlessWebview/AsyncHeadlessWebView.swift @@ -65,6 +65,9 @@ struct AsyncHeadlessWebView: View { onContentType: { value in viewModel.contentType = value }, + onNavigationError: { value in + viewModel.navigationError = value + }, navigationCoordinator: viewModel.navigationCoordinator ) .frame(width: geometry.size.width, height: geometry.size.height) diff --git a/DuckDuckGo/Subscription/AsyncHeadlessWebview/AsyncHeadlessWebViewModel.swift b/DuckDuckGo/Subscription/AsyncHeadlessWebview/AsyncHeadlessWebViewModel.swift index 190352d246..688f0ed213 100644 --- a/DuckDuckGo/Subscription/AsyncHeadlessWebview/AsyncHeadlessWebViewModel.swift +++ b/DuckDuckGo/Subscription/AsyncHeadlessWebview/AsyncHeadlessWebViewModel.swift @@ -37,6 +37,7 @@ final class AsyncHeadlessWebViewViewModel: ObservableObject { @Published var canGoBack: Bool = false @Published var canGoForward: Bool = false @Published var contentType: String = "" + @Published var navigationError: Error? @Published var allowedDomains: [String]? var navigationCoordinator = HeadlessWebViewNavCoordinator(webView: nil) diff --git a/DuckDuckGo/Subscription/AsyncHeadlessWebview/HeadlessWebView.swift b/DuckDuckGo/Subscription/AsyncHeadlessWebview/HeadlessWebView.swift index 7467489ac2..3452302d90 100644 --- a/DuckDuckGo/Subscription/AsyncHeadlessWebview/HeadlessWebView.swift +++ b/DuckDuckGo/Subscription/AsyncHeadlessWebview/HeadlessWebView.swift @@ -32,6 +32,7 @@ struct HeadlessWebView: UIViewRepresentable { var onCanGoBack: ((Bool) -> Void)? var onCanGoForward: ((Bool) -> Void)? var onContentType: ((String) -> Void)? + var onNavigationError: ((Error?) -> Void)? var navigationCoordinator: HeadlessWebViewNavCoordinator func makeUIView(context: Context) -> WKWebView { @@ -73,6 +74,7 @@ struct HeadlessWebView: UIViewRepresentable { onCanGoBack: onCanGoBack, onCanGoForward: onCanGoForward, onContentType: onContentType, + onNavigationError: onNavigationError, settings: settings ) } diff --git a/DuckDuckGo/Subscription/AsyncHeadlessWebview/HeadlessWebViewCoordinator.swift b/DuckDuckGo/Subscription/AsyncHeadlessWebview/HeadlessWebViewCoordinator.swift index 92c8f1a7a4..d8fc66a461 100644 --- a/DuckDuckGo/Subscription/AsyncHeadlessWebview/HeadlessWebViewCoordinator.swift +++ b/DuckDuckGo/Subscription/AsyncHeadlessWebview/HeadlessWebViewCoordinator.swift @@ -30,6 +30,7 @@ final class HeadlessWebViewCoordinator: NSObject { var onCanGoBack: ((Bool) -> Void)? var onCanGoForward: ((Bool) -> Void)? var onContentType: ((String) -> Void)? + var onNavigationError: ((Error?) -> Void)? var settings: AsyncHeadlessWebViewSettings var size: CGSize = .zero @@ -52,6 +53,7 @@ final class HeadlessWebViewCoordinator: NSObject { onCanGoBack: ((Bool) -> Void)?, onCanGoForward: ((Bool) -> Void)?, onContentType: ((String) -> Void)?, + onNavigationError: ((Error?) -> Void)?, allowedDomains: [String]? = nil, settings: AsyncHeadlessWebViewSettings = AsyncHeadlessWebViewSettings()) { self.parent = parent @@ -60,6 +62,7 @@ final class HeadlessWebViewCoordinator: NSObject { self.onURLChange = onURLChange self.onCanGoBack = onCanGoBack self.onCanGoForward = onCanGoForward + self.onNavigationError = onNavigationError self.onContentType = onContentType self.settings = settings } @@ -106,6 +109,7 @@ final class HeadlessWebViewCoordinator: NSObject { onCanGoBack = nil onCanGoForward = nil onContentType = nil + onNavigationError = nil } } @@ -128,6 +132,7 @@ extension HeadlessWebViewCoordinator: WKNavigationDelegate { } func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) { + onNavigationError?(nil) if let url = webView.url, url != lastURL { onURLChange?(url) lastURL = url @@ -182,8 +187,12 @@ extension HeadlessWebViewCoordinator: WKNavigationDelegate { decisionHandler(.allow) } + func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: any Error) { + onNavigationError?(error) + } + func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) { - // NOOP + onNavigationError?(error) } // Javascript Confirm dialogs Delegate diff --git a/DuckDuckGo/Subscription/UserScripts/IdentityTheftRestorationPagesFeature.swift b/DuckDuckGo/Subscription/UserScripts/IdentityTheftRestorationPagesFeature.swift index 276ef14926..a577a61613 100644 --- a/DuckDuckGo/Subscription/UserScripts/IdentityTheftRestorationPagesFeature.swift +++ b/DuckDuckGo/Subscription/UserScripts/IdentityTheftRestorationPagesFeature.swift @@ -66,10 +66,13 @@ final class IdentityTheftRestorationPagesFeature: Subfeature, ObservableObject { return nil } } - + func getAccessToken(params: Any, original: WKScriptMessage) async throws -> Encodable? { - let authToken = AccountManager().authToken ?? "" - return [Constants.token: authToken] + if let accessToken = AccountManager().accessToken { + return [Constants.token: accessToken] + } else { + return [String: String]() + } } deinit { diff --git a/DuckDuckGo/Subscription/ViewModel/SubscriptionEmailViewModel.swift b/DuckDuckGo/Subscription/ViewModel/SubscriptionEmailViewModel.swift index 6cb2ea102f..8a5874fff9 100644 --- a/DuckDuckGo/Subscription/ViewModel/SubscriptionEmailViewModel.swift +++ b/DuckDuckGo/Subscription/ViewModel/SubscriptionEmailViewModel.swift @@ -38,6 +38,7 @@ final class SubscriptionEmailViewModel: ObservableObject { @Published var activateSubscription = false @Published var managingSubscriptionEmail = false @Published var transactionError: SubscriptionRestoreError? + @Published var navigationError: Bool = false @Published var shouldDisplayInactiveError: Bool = false var webViewModel: AsyncHeadlessWebViewViewModel @@ -101,6 +102,17 @@ final class SubscriptionEmailViewModel: ObservableObject { } } .store(in: &cancellables) + + webViewModel.$navigationError + .receive(on: DispatchQueue.main) + .sink { [weak self] error in + guard let strongSelf = self else { return } + DispatchQueue.main.async { + strongSelf.navigationError = error != nil ? true : false + } + + } + .store(in: &cancellables) } private func handleTransactionError(error: SubscriptionPagesUseSubscriptionFeature.UseSubscriptionError) { diff --git a/DuckDuckGo/Subscription/ViewModel/SubscriptionFlowViewModel.swift b/DuckDuckGo/Subscription/ViewModel/SubscriptionFlowViewModel.swift index 6d0cb332d7..295b22e45b 100644 --- a/DuckDuckGo/Subscription/ViewModel/SubscriptionFlowViewModel.swift +++ b/DuckDuckGo/Subscription/ViewModel/SubscriptionFlowViewModel.swift @@ -224,10 +224,24 @@ final class SubscriptionFlowViewModel: ObservableObject { } .store(in: &cancellables) + webViewModel.$navigationError + .receive(on: DispatchQueue.main) + .sink { [weak self] error in + guard let strongSelf = self else { return } + DispatchQueue.main.async { + strongSelf.transactionError = error != nil ? .generalError : nil + } + + } + .store(in: &cancellables) + canGoBackCancellable = webViewModel.$canGoBack .receive(on: DispatchQueue.main) .sink { [weak self] value in - self?.canNavigateBack = value + guard let strongSelf = self else { return } + + let shouldNavigateBack = value && (strongSelf.webViewModel.url?.lastPathComponent != URL.subscriptionBaseURL.lastPathComponent) + strongSelf.canNavigateBack = shouldNavigateBack } } @@ -242,6 +256,12 @@ final class SubscriptionFlowViewModel: ObservableObject { canNavigateBack = false } + private func urlRemovingQueryParams(_ url: URL) -> URL? { + var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false) + urlComponents?.query = nil // Remove the query string + return urlComponents?.url + } + func initializeViewData() async { await self.setupTransactionObserver() await self .setupWebViewObservers() @@ -257,6 +277,7 @@ final class SubscriptionFlowViewModel: ObservableObject { userTappedRestoreButton = false shouldShowNavigationBar = false selectedFeature = nil + transactionError = nil canNavigateBack = false shouldDismissView = true subFeature.cleanup() diff --git a/DuckDuckGo/Subscription/ViewModel/SubscriptionITPViewModel.swift b/DuckDuckGo/Subscription/ViewModel/SubscriptionITPViewModel.swift index 6e613491a8..99694a5468 100644 --- a/DuckDuckGo/Subscription/ViewModel/SubscriptionITPViewModel.swift +++ b/DuckDuckGo/Subscription/ViewModel/SubscriptionITPViewModel.swift @@ -46,6 +46,7 @@ final class SubscriptionITPViewModel: ObservableObject { @Published var isDownloadableContent: Bool = false @Published var activityItems: [Any] = [] @Published var attachmentURL: URL? + @Published var navigationError: Bool = false var webViewModel: AsyncHeadlessWebViewViewModel @Published var shouldNavigateToExternalURL: URL? @@ -81,9 +82,20 @@ final class SubscriptionITPViewModel: ObservableObject { settings: webViewSettings) } - // Observe transaction status + // swiftlint:disable function_body_length private func setupSubscribers() async { + webViewModel.$navigationError + .receive(on: DispatchQueue.main) + .sink { [weak self] error in + guard let strongSelf = self else { return } + DispatchQueue.main.async { + strongSelf.navigationError = error != nil ? true : false + } + + } + .store(in: &cancellables) + webViewModel.$scrollPosition .receive(on: DispatchQueue.main) .throttle(for: .milliseconds(100), scheduler: DispatchQueue.main, latest: true) @@ -134,6 +146,7 @@ final class SubscriptionITPViewModel: ObservableObject { self?.canNavigateBack = value } } + // swiftlint:enable function_body_length func initializeView() { webViewModel.navigationCoordinator.navigateTo(url: manageITPURL ) diff --git a/DuckDuckGo/Subscription/ViewModel/SubscriptionRestoreViewModel.swift b/DuckDuckGo/Subscription/ViewModel/SubscriptionRestoreViewModel.swift index 3da604761f..cbeee2fb26 100644 --- a/DuckDuckGo/Subscription/ViewModel/SubscriptionRestoreViewModel.swift +++ b/DuckDuckGo/Subscription/ViewModel/SubscriptionRestoreViewModel.swift @@ -81,7 +81,7 @@ final class SubscriptionRestoreViewModel: ObservableObject { private func handleRestoreError(error: SubscriptionPagesUseSubscriptionFeature.UseSubscriptionError) { switch error { case .failedToRestorePastPurchase: - activationResult = .notFound + activationResult = .error case .subscriptionExpired: activationResult = .expired case .subscriptionNotFound: diff --git a/DuckDuckGo/Subscription/Views/SubscriptionEmailView.swift b/DuckDuckGo/Subscription/Views/SubscriptionEmailView.swift index 8712560f36..a8b3d72c83 100644 --- a/DuckDuckGo/Subscription/Views/SubscriptionEmailView.swift +++ b/DuckDuckGo/Subscription/Views/SubscriptionEmailView.swift @@ -50,6 +50,15 @@ struct SubscriptionEmailView: View { ) } + .alert(isPresented: $viewModel.navigationError) { + Alert( + title: Text(UserText.subscriptionBackendErrorTitle), + message: Text(UserText.subscriptionBackendErrorMessage), + dismissButton: .cancel(Text(UserText.subscriptionBackendErrorButton)) { + dismiss() + }) + } + .onAppear { viewModel.loadURL() } diff --git a/DuckDuckGo/Subscription/Views/SubscriptionFlowView.swift b/DuckDuckGo/Subscription/Views/SubscriptionFlowView.swift index 85e820fd3a..f611c5ead8 100644 --- a/DuckDuckGo/Subscription/Views/SubscriptionFlowView.swift +++ b/DuckDuckGo/Subscription/Views/SubscriptionFlowView.swift @@ -31,7 +31,8 @@ struct SubscriptionFlowView: View { @State private var shouldShowNavigationBar = false @State private var isActive = false @State private var transactionError: SubscriptionFlowViewModel.SubscriptionPurchaseError? - @State private var shouldPresentError = false + @State private var errorMessage: SubscriptionErrorMessage = .general + @State private var shouldPresentError: Bool = false @State private var isFirstOnAppear = true enum Constants { @@ -42,6 +43,13 @@ struct SubscriptionFlowView: View { static let backButtonImage = "chevron.left" } + enum SubscriptionErrorMessage { + case activeSubscription + case appStore + case backend + case general + } + var body: some View { NavigationView { baseView @@ -134,10 +142,28 @@ struct SubscriptionFlowView: View { } .onChange(of: viewModel.transactionError) { value in - if value != nil { - shouldPresentError = true + + if !shouldPresentError { + let displayError: Bool = { + switch value { + case .hasActiveSubscription: + errorMessage = .activeSubscription + return true + case .failedToRestorePastPurchase, .purchaseFailed: + errorMessage = .appStore + return true + case .failedToGetSubscriptionOptions, .generalError: + errorMessage = .backend + return true + default: + return false + } + }() + + if displayError { + shouldPresentError = true + } } - transactionError = value } .onAppear(perform: { @@ -165,7 +191,8 @@ struct SubscriptionFlowView: View { }) .alert(isPresented: $shouldPresentError) { - getAlert() + getAlert(error: self.errorMessage) + } // The trailing close button should be hidden when a transaction is in progress @@ -173,12 +200,12 @@ struct SubscriptionFlowView: View { ? Button(UserText.subscriptionCloseButton) { viewModel.finalizeSubscriptionFlow() } : nil) } - - private func getAlert() -> Alert { - switch transactionError { - case .hasActiveSubscription: - Alert( + private func getAlert(error: SubscriptionErrorMessage) -> Alert { + + switch error { + case .activeSubscription: + return Alert( title: Text(UserText.subscriptionFoundTitle), message: Text(UserText.subscriptionFoundText), primaryButton: .cancel(Text(UserText.subscriptionFoundCancel)) { @@ -188,14 +215,23 @@ struct SubscriptionFlowView: View { viewModel.restoreAppstoreTransaction() } ) - default: - Alert( + case .appStore: + return Alert( title: Text(UserText.subscriptionAppStoreErrorTitle), message: Text(UserText.subscriptionAppStoreErrorMessage), dismissButton: .cancel(Text(UserText.actionOK)) { Task { await viewModel.initializeViewData() } } ) + case .backend, .general: + return Alert( + title: Text(UserText.subscriptionBackendErrorTitle), + message: Text(UserText.subscriptionBackendErrorMessage), + dismissButton: .cancel(Text(UserText.subscriptionBackendErrorButton)) { + viewModel.finalizeSubscriptionFlow() + dismiss() + } + ) } } @@ -203,8 +239,14 @@ struct SubscriptionFlowView: View { private var webView: some View { ZStack(alignment: .top) { - // Restore View Hidden Link - NavigationLink(destination: SubscriptionRestoreView(), isActive: $isActive) { + + // Restore View Hidden Link + let restoreView = SubscriptionRestoreView( + onDismissStack: { + viewModel.finalizeSubscriptionFlow() + dismiss() + }) + NavigationLink(destination: restoreView, isActive: $isActive) { EmptyView() }.isDetailLink(false) diff --git a/DuckDuckGo/Subscription/Views/SubscriptionITPView.swift b/DuckDuckGo/Subscription/Views/SubscriptionITPView.swift index 32f0f60d0e..96e2653cf6 100644 --- a/DuckDuckGo/Subscription/Views/SubscriptionITPView.swift +++ b/DuckDuckGo/Subscription/Views/SubscriptionITPView.swift @@ -85,6 +85,17 @@ struct SubscriptionITPView: View { } .tint(Color(designSystemColor: .textPrimary)) + + .alert(isPresented: $viewModel.navigationError) { + Alert( + title: Text(UserText.subscriptionBackendErrorTitle), + message: Text(UserText.subscriptionBackendErrorMessage), + dismissButton: .cancel(Text(UserText.subscriptionBackendErrorButton)) { + dismiss() + }) + } + + .sheet(isPresented: Binding( get: { viewModel.shouldShowExternalURLSheet }, set: { if !$0 { viewModel.shouldNavigateToExternalURL = nil } } diff --git a/DuckDuckGo/Subscription/Views/SubscriptionRestoreView.swift b/DuckDuckGo/Subscription/Views/SubscriptionRestoreView.swift index 7185163b3f..d6b1d7cf6a 100644 --- a/DuckDuckGo/Subscription/Views/SubscriptionRestoreView.swift +++ b/DuckDuckGo/Subscription/Views/SubscriptionRestoreView.swift @@ -32,6 +32,7 @@ struct SubscriptionRestoreView: View { @State private var expandedItemId: Int = 0 @State private var isAlertVisible = false @State private var isActive: Bool = false + var onDismissStack: (() -> Void)? private enum Constants { static let heroImage = "ManageSubscriptionHero" @@ -273,10 +274,15 @@ struct SubscriptionRestoreView: View { dismiss() }), secondaryButton: .cancel()) - case .error: - return Alert(title: Text(UserText.subscriptionAppStoreErrorTitle), message: Text(UserText.subscriptionAppStoreErrorMessage)) default: - return Alert(title: Text(UserText.subscriptionAppStoreErrorTitle), message: Text(UserText.subscriptionAppStoreErrorMessage)) + return Alert( + title: Text(UserText.subscriptionBackendErrorTitle), + message: Text(UserText.subscriptionBackendErrorMessage), + dismissButton: .cancel(Text(UserText.subscriptionBackendErrorButton)) { + onDismissStack?() + dismiss() + } + ) } } diff --git a/DuckDuckGo/UserText.swift b/DuckDuckGo/UserText.swift index 3aeeba2b15..c7cf2b241d 100644 --- a/DuckDuckGo/UserText.swift +++ b/DuckDuckGo/UserText.swift @@ -1135,6 +1135,10 @@ But if you *do* want a peek under the hood, you can find more information about public static let subscriptionAppStoreErrorTitle = NSLocalizedString("subscription.restore.general.error.title", value: "Something Went Wrong", comment: "Alert for general error title") public static let subscriptionAppStoreErrorMessage = NSLocalizedString("subscription.restore.general.error.message", value: "The App Store was unable to process your purchase. Please try again later.", comment: "Alert for general error message") + public static let subscriptionBackendErrorTitle = NSLocalizedString("subscription.restore.backend.error.title", value: "Something Went Wrong", comment: "Alert for general error title") + public static let subscriptionBackendErrorMessage = NSLocalizedString("subscription.restore.backend.error.message", value: "We’re having trouble connecting. Please try again later.", comment: "Alert for general error message") + public static let subscriptionBackendErrorButton = NSLocalizedString("subscription.restore.backend.error.button", value: "Back to Settings", comment: "Button text for general error message") + // PIR: public static let subscriptionPIRHeroText = NSLocalizedString("subscription.pir.hero", value: "Activate Privacy Pro on desktop to set up Personal Information Removal", comment: "Hero Text for Personal information removal") public static let subscriptionPIRHeroDetail = NSLocalizedString("subscription.pir.heroText", value: "In the DuckDuckGo browser for desktop, go to %@ and click %@ to get started.", comment: "Description on how to use Personal information removal in desktop. The first placeholder references a location in the Desktop application. Privacy Pro>, and the second, the menu entry. i.e. ") diff --git a/DuckDuckGo/en.lproj/Localizable.strings b/DuckDuckGo/en.lproj/Localizable.strings index cf0aa4ca37..400655f967 100644 --- a/DuckDuckGo/en.lproj/Localizable.strings +++ b/DuckDuckGo/en.lproj/Localizable.strings @@ -2151,6 +2151,15 @@ But if you *do* want a peek under the hood, you can find more information about /* text for renewal string */ "subscription.renews" = "renews"; +/* Button text for general error message */ +"subscription.restore.backend.error.button" = "Back to Settings"; + +/* Alert for general error message */ +"subscription.restore.backend.error.message" = "We’re having trouble connecting. Please try again later."; + +/* Alert for general error title */ +"subscription.restore.backend.error.title" = "Something Went Wrong"; + /* Alert for general error message */ "subscription.restore.general.error.message" = "The App Store was unable to process your purchase. Please try again later."; diff --git a/submodules/privacy-reference-tests b/submodules/privacy-reference-tests index 40ce86837d..6b7ad1e7f1 160000 --- a/submodules/privacy-reference-tests +++ b/submodules/privacy-reference-tests @@ -1 +1 @@ -Subproject commit 40ce86837def0adbf558f00ed0531ab4df5839a8 +Subproject commit 6b7ad1e7f15270f9dfeb58a272199f4d57c3eb22 From 454fb216dadbcb3f3380e28db226dbd906314e03 Mon Sep 17 00:00:00 2001 From: Daniel Bernal Date: Mon, 11 Mar 2024 23:56:15 +0100 Subject: [PATCH 19/19] Subscriptions: 20 - Subscription Caching (#2569) Task/Issue URL: https://app.asana.com/0/0/1206800657723184/f BSK PR: duckduckgo/BrowserServicesKit#710 Description: Bumps BSK to use subscription caching: Push Purchase Cancel update to Webview on user cancellation Minor presentation updates for sheets and subscription-related Settings cleanup Co-authored-by: Michal Smaga --- DuckDuckGo.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/swiftpm/Package.resolved | 4 +- DuckDuckGo/SettingsSubscriptionView.swift | 32 ++++++-------- DuckDuckGo/SettingsViewModel.swift | 44 ++++++++----------- ...scriptionPagesUseSubscriptionFeature.swift | 2 + .../SubscriptionSettingsViewModel.swift | 32 ++++++-------- submodules/privacy-reference-tests | 2 +- 7 files changed, 51 insertions(+), 67 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 891f03a26f..e1800adb4f 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -10044,7 +10044,7 @@ repositoryURL = "https://github.com/DuckDuckGo/BrowserServicesKit"; requirement = { kind = exactVersion; - version = 122.0.0; + version = 122.1.0; }; }; B6F997C22B8F374300476735 /* XCRemoteSwiftPackageReference "apple-toolbox" */ = { diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index faaf2e1be7..fde59987a6 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" : "83bcbbf0dace717db6e518e4d867d617c846a3b5", - "version" : "122.0.0" + "revision" : "4042a8e04396584566df24fb90c7b529bbe1c661", + "version" : "122.1.0" } }, { diff --git a/DuckDuckGo/SettingsSubscriptionView.swift b/DuckDuckGo/SettingsSubscriptionView.swift index a727b87dc0..846e20893a 100644 --- a/DuckDuckGo/SettingsSubscriptionView.swift +++ b/DuckDuckGo/SettingsSubscriptionView.swift @@ -94,6 +94,11 @@ struct SettingsSubscriptionView: View { action: { isShowingsubScriptionFlow = true }, isButton: true ) + // Subscription Restore + .sheet(isPresented: $isShowingsubScriptionFlow, + onDismiss: { Task { viewModel.onAppear() } }, + content: { SubscriptionFlowView(viewModel: subscriptionFlowViewModel).interactiveDismissDisabled() }) + SettingsCustomCell(content: { iHaveASubscriptionView }, action: { isShowingsubScriptionFlow = true @@ -139,6 +144,10 @@ struct SettingsSubscriptionView: View { subtitle: UserText.settingsPProDBPSubTitle, action: { isShowingDBP.toggle() }, isButton: true) + .sheet(isPresented: $isShowingDBP) { + SubscriptionPIRView() + } + } if viewModel.shouldShowITP { @@ -146,6 +155,10 @@ struct SettingsSubscriptionView: View { subtitle: UserText.settingsPProITRSubTitle, action: { isShowingITP.toggle() }, isButton: true) + .sheet(isPresented: $isShowingITP) { + SubscriptionITPView() + } + } NavigationLink(destination: SubscriptionSettingsView()) { @@ -153,12 +166,7 @@ struct SettingsSubscriptionView: View { } } - .sheet(isPresented: $isShowingDBP) { - SubscriptionPIRView() - } - .sheet(isPresented: $isShowingITP) { - SubscriptionITPView() - } + } var body: some View { @@ -181,18 +189,6 @@ struct SettingsSubscriptionView: View { } } - // Subscription Restore - .sheet(isPresented: $isShowingsubScriptionFlow) { - SubscriptionFlowView(viewModel: subscriptionFlowViewModel).interactiveDismissDisabled() - } - - - // Refresh subscription when dismissing the Subscription Flow - .onChange(of: isShowingsubScriptionFlow, perform: { value in - if !value { - Task { viewModel.onAppear() } - } - }) .onChange(of: viewModel.shouldNavigateToDBP, perform: { value in if value { diff --git a/DuckDuckGo/SettingsViewModel.swift b/DuckDuckGo/SettingsViewModel.swift index 7aaba476b6..f67e383194 100644 --- a/DuckDuckGo/SettingsViewModel.swift +++ b/DuckDuckGo/SettingsViewModel.swift @@ -248,8 +248,9 @@ extension SettingsViewModel { // This manual (re)initialization will go away once appSettings and // other dependencies are observable (Such as AppIcon and netP) // and we can use subscribers (Currently called from the view onAppear) - private func initState() { - self.state = SettingsState( + @MainActor + private func initState() async { + self.state = await SettingsState( appTheme: appSettings.currentThemeName, appIcon: AppIconManager.shared.appIcon, fireButtonAnimation: appSettings.currentFireButtonAnimation, @@ -275,15 +276,6 @@ extension SettingsViewModel { setupSubscribers() - #if SUBSCRIPTION - if #available(iOS 15, *) { - Task { - if state.subscription.enabled { - await setupSubscriptionEnvironment() - } - } - } - #endif } private func getNetworkProtectionState() -> SettingsState.NetworkProtection { @@ -297,14 +289,24 @@ extension SettingsViewModel { return SettingsState.NetworkProtection(enabled: enabled, status: "") } - private func getSubscriptionState() -> SettingsState.Subscription { + private func getSubscriptionState() async -> SettingsState.Subscription { var enabled = false var canPurchase = false - let hasActiveSubscription = Self.cachedHasActiveSubscription - #if SUBSCRIPTION + var hasActiveSubscription = false + +#if SUBSCRIPTION + if #available(iOS 15, *) { enabled = featureFlagger.isFeatureOn(.subscription) canPurchase = SubscriptionPurchaseEnvironment.canPurchase - #endif + await setupSubscriptionEnvironment() + if let token = AccountManager().accessToken { + let subscriptionResult = await SubscriptionService.getSubscription(accessToken: token) + if case .success(let subscription) = subscriptionResult { + hasActiveSubscription = subscription.isActive + } + } + } +#endif return SettingsState.Subscription(enabled: enabled, canPurchase: canPurchase, hasActiveSubscription: hasActiveSubscription) @@ -354,9 +356,6 @@ extension SettingsViewModel { case .success(let subscription) where subscription.isActive: - // Cache Subscription state - cacheSubscriptionState(active: true) - // Check entitlements and update UI accordingly let entitlements: [Entitlement.ProductName] = [.identityTheftRestoration, .dataBrokerProtection, .networkProtection] for entitlement in entitlements { @@ -382,18 +381,11 @@ extension SettingsViewModel { @available(iOS 15.0, *) private func signOutUser() { AccountManager().signOut() - cacheSubscriptionState(active: false) setupSubscriptionPurchaseOptions() } - private func cacheSubscriptionState(active: Bool) { - self.state.subscription.hasActiveSubscription = active - Self.cachedHasActiveSubscription = active - } - @available(iOS 15.0, *) private func setupSubscriptionPurchaseOptions() { - cacheSubscriptionState(active: false) PurchaseManager.shared.$availableProducts .receive(on: RunLoop.main) .sink { [weak self] products in @@ -470,7 +462,7 @@ extension SettingsViewModel { extension SettingsViewModel { func onAppear() { - initState() + Task { await initState() } Task { await MainActor.run { navigateOnAppear() } } } diff --git a/DuckDuckGo/Subscription/UserScripts/SubscriptionPagesUseSubscriptionFeature.swift b/DuckDuckGo/Subscription/UserScripts/SubscriptionPagesUseSubscriptionFeature.swift index 8ad92a96ca..f2d5a01c27 100644 --- a/DuckDuckGo/Subscription/UserScripts/SubscriptionPagesUseSubscriptionFeature.swift +++ b/DuckDuckGo/Subscription/UserScripts/SubscriptionPagesUseSubscriptionFeature.swift @@ -225,6 +225,8 @@ final class SubscriptionPagesUseSubscriptionFeature: Subfeature, ObservableObjec switch error { case .cancelledByUser: setTransactionError(.cancelledByUser) + await pushPurchaseUpdate(originalMessage: message, purchaseUpdate: PurchaseUpdate(type: "canceled")) + return nil case .accountCreationFailed: setTransactionError(.accountCreationFailed) case .activeSubscriptionAlreadyPresent: diff --git a/DuckDuckGo/Subscription/ViewModel/SubscriptionSettingsViewModel.swift b/DuckDuckGo/Subscription/ViewModel/SubscriptionSettingsViewModel.swift index 5c65f88e98..102a365523 100644 --- a/DuckDuckGo/Subscription/ViewModel/SubscriptionSettingsViewModel.swift +++ b/DuckDuckGo/Subscription/ViewModel/SubscriptionSettingsViewModel.swift @@ -56,24 +56,16 @@ final class SubscriptionSettingsViewModel: ObservableObject { }() @MainActor - func fetchAndUpdateSubscriptionDetails() { + func fetchAndUpdateSubscriptionDetails(cachePolicy: SubscriptionService.CachePolicy = .returnCacheDataElseLoad) { Task { - guard let token = accountManager.accessToken else { return } - - if let cachedDate = SubscriptionService.cachedGetSubscriptionResponse?.expiresOrRenewsAt, - let cachedStatus = SubscriptionService.cachedGetSubscriptionResponse?.status, - let productID = SubscriptionService.cachedGetSubscriptionResponse?.productId { - updateSubscriptionDetails(status: cachedStatus, date: cachedDate, product: productID) - } - - if case .success(let subscription) = await SubscriptionService.getSubscription(accessToken: token) { - if !subscription.isActive { - AccountManager().signOut() - shouldDismissView = true - return - } else { - updateSubscriptionDetails(status: subscription.status, date: subscription.expiresOrRenewsAt, product: subscription.productId) - } + guard let token = self.accountManager.accessToken else { return } + let subscriptionResult = await SubscriptionService.getSubscription(accessToken: token, cachePolicy: cachePolicy) + switch subscriptionResult { + case .success(let subscription): + updateSubscriptionDetails(status: subscription.status, date: subscription.expiresOrRenewsAt, product: subscription.productId) + case .failure(let error): + AccountManager().signOut() + shouldDismissView = true } } } @@ -86,11 +78,13 @@ final class SubscriptionSettingsViewModel: ObservableObject { } } + // Re-fetch subscription from server ignoring cache + // This ensure that if the user changed something on the Apple view, state will be updated private func setupSubscriptionUpdater() { subscriptionUpdateTimer = Timer.scheduledTimer(withTimeInterval: 10.0, repeats: true) { [weak self] _ in guard let strongSelf = self else { return } Task { - await strongSelf.fetchAndUpdateSubscriptionDetails() + await strongSelf.fetchAndUpdateSubscriptionDetails(cachePolicy: .reloadIgnoringLocalCacheData) } } } @@ -104,7 +98,7 @@ final class SubscriptionSettingsViewModel: ObservableObject { func removeSubscription() { AccountManager().signOut() - let messageView = ActionMessageView() + _ = ActionMessageView() ActionMessageView.present(message: UserText.subscriptionRemovalConfirmation, presentationLocation: .withoutBottomBar) } diff --git a/submodules/privacy-reference-tests b/submodules/privacy-reference-tests index 6b7ad1e7f1..a603ff9af2 160000 --- a/submodules/privacy-reference-tests +++ b/submodules/privacy-reference-tests @@ -1 +1 @@ -Subproject commit 6b7ad1e7f15270f9dfeb58a272199f4d57c3eb22 +Subproject commit a603ff9af22ca3ff7ce2e7ffbfe18c447d9f23e8