From 6a19b3cb6d10adc3407d44fd1014b5ad701c7944 Mon Sep 17 00:00:00 2001 From: Dominik Kapusta Date: Tue, 8 Aug 2023 00:51:55 +0200 Subject: [PATCH 01/17] Skip /Applications symlink script for Debug builds (#1443) Task/Issue URL: https://app.asana.com/0/1203301625297703/1205226515221343/f Description: Run the /Applications symlink script only on Debug builds because it has unexpected and severe consequences when run in Release configuration (such as deleting the production release app bundle). --- DuckDuckGo.xcodeproj/project.pbxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 0587e1c353..75a6c080e8 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -7429,7 +7429,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "# if Xcode created real Applications directory inside BUILT_PRODUCTS_DIR \nif [[ ! -L ${BUILT_PRODUCTS_DIR}/Applications ]]; then\n # we only get here on clean build, remove an existing app in /Applications/DEBUG on clean build\n echo \"rm -rf /${INSTALL_PATH}/${PRODUCT_NAME}.app\"\n rm -rf \"/${INSTALL_PATH}/${PRODUCT_NAME}.app\"\n\n # create /Applications/DEBUG dir\n echo \"mkdir -p /${INSTALL_PATH}\"\n mkdir -p \"/${INSTALL_PATH}\"\n\n # move the app bundle to /Applications/DEBUG\n echo \"mv ${DSTROOT}/${INSTALL_PATH}/${PRODUCT_NAME}.app /${INSTALL_PATH}/${PRODUCT_NAME}.app\"\n mv \"${DSTROOT}/${INSTALL_PATH}/${PRODUCT_NAME}.app\" \"/${INSTALL_PATH}/${PRODUCT_NAME}.app\"\n\n # rm ${BUILT_PRODUCTS_DIR}/Applications directory created by Xcode\n echo \"rm -rf ${BUILT_PRODUCTS_DIR}/Applications\" \n rm -rf \"${BUILT_PRODUCTS_DIR}/Applications\"\n # create ${BUILT_PRODUCTS_DIR}/Applications symlink to /Applications\n echo \"ln -s /Applications ${BUILT_PRODUCTS_DIR}/Applications\"\n ln -s /Applications \"${BUILT_PRODUCTS_DIR}/Applications\"\nfi\n"; + shellScript = "if [[ \"${CONFIGURATION}\" != \"Debug\" ]]; then\n # run only for Debug builds\n exit 0\nfi\n\n# if Xcode created real Applications directory inside BUILT_PRODUCTS_DIR \nif [[ ! -L ${BUILT_PRODUCTS_DIR}/Applications ]]; then\n # we only get here on clean build, remove an existing app in /Applications/DEBUG on clean build\n echo \"rm -rf /${INSTALL_PATH}/${PRODUCT_NAME}.app\"\n rm -rf \"/${INSTALL_PATH}/${PRODUCT_NAME}.app\"\n\n # create /Applications/DEBUG dir\n echo \"mkdir -p /${INSTALL_PATH}\"\n mkdir -p \"/${INSTALL_PATH}\"\n\n # move the app bundle to /Applications/DEBUG\n echo \"mv ${DSTROOT}/${INSTALL_PATH}/${PRODUCT_NAME}.app /${INSTALL_PATH}/${PRODUCT_NAME}.app\"\n mv \"${DSTROOT}/${INSTALL_PATH}/${PRODUCT_NAME}.app\" \"/${INSTALL_PATH}/${PRODUCT_NAME}.app\"\n\n # rm ${BUILT_PRODUCTS_DIR}/Applications directory created by Xcode\n echo \"rm -rf ${BUILT_PRODUCTS_DIR}/Applications\" \n rm -rf \"${BUILT_PRODUCTS_DIR}/Applications\"\n # create ${BUILT_PRODUCTS_DIR}/Applications symlink to /Applications\n echo \"ln -s /Applications ${BUILT_PRODUCTS_DIR}/Applications\"\n ln -s /Applications \"${BUILT_PRODUCTS_DIR}/Applications\"\nfi\n"; }; CBCCF59E299667B700C02DFE /* Assert Xcode version */ = { isa = PBXShellScriptBuildPhase; From 0f8769c454b4a0da0d580a4c2ef6d30376a175fd Mon Sep 17 00:00:00 2001 From: Graeme Arthur <2030310+graeme@users.noreply.github.com> Date: Wed, 9 Aug 2023 11:33:15 +0200 Subject: [PATCH 02/17] Encapsulate NetP subjects within observers (#1436) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task/Issue URL: https://app.asana.com/0/0/1205210093476964/f BSK PR: https://github.com/duckduckgo/BrowserServicesKit/pull/447 CC: **Description**: For [Update Status View to match designs](https://app.asana.com/0/0/1205084446087078), I planned to rely on the shared Connection*Observer types to receive events from the PacketTunnnelProvider and other NetP types. However, on doing this, I noticed that we’re exposing the Subject type in order to synchronously access the most recent state. Exposing these types as it means these events can be sent from anywhere and not just the source of the events. Rather than letting dependency on this propagate into iOS too, I decided to do a quick refactor to clean it up. Additionally, I’ve created the associated mock types which I will also need throughout the [iOS: Network Protection MVP TestFlight build for internal testing](https://app.asana.com/0/0/1204630829332227) project. I decided now was the right time to create them as these types have been altered. Creating the MockTunnelController is included in this BSK release though it seems somewhat independent. This is so that it can be included in the same BSK release as it will also be needed for the parent task. **Steps to test this PR**: **Test StatusConnectionObserver** 1. Connect to NetP 2. Disconnect from NetP 3. Ensure connection state updates correctly **Test ConnectionServerInfoObserver** 1. Connect to NetP 2. Change the server location through the debug menu 3. Check his updates correctly in the UI **Test ConnectionErrorObserver** 1. Connect to NetP 2. Simulate a tunnel error through the debug menu (Debug -> Network Protection -> Simulate Failure) 3. Check the status view to see if the error is shown **Test ConnectivityIssueObserver** _Preparation_ - Start Network Protection, make sure you're asked to allow notifications. - Allow notifications - Save this file to your desktop. It's a script to temporarily block UDP traffic. - [ruleset.txt](https://github.com/duckduckgo/macos-browser/files/12018072/ruleset.txt) _Disconnection_ - Using a terminal window type sudo pfctl -ef ~/Desktop/ruleset.txt - Wait until NetP reports a problem and make sure a notification is shown for it. - Wait a bit more until NetP reports through a notification that the connection was stopped. - Once you're done restore UDP traffic by executing: sudo pfctl -d - Test 2: Reconnected notification _Reconnection_ - Run the steps from the previous test up to, and including step 5. - Once the notification for the connection issues is shown, run this command in a terminal window to restore UDP traffic: sudo pfctl -d - After a while make sure NetP reports it successfully restored the connection. **Test ControllerErrorMesssageObserver** 1. Connect to NetP 2. Simulate a Controller error through the debug menu (Debug -> Network Protection -> Simulate Failure) 3. Check the status view to see if the error is shown --- ###### Internal references: [Pull Request Review Checklist](https://app.asana.com/0/1202500774821704/1203764234894239/f) [Software Engineering Expectations](https://app.asana.com/0/59792373528535/199064865822552) [Technical Design Template](https://app.asana.com/0/59792373528535/184709971311943) [Pull Request Documentation](https://app.asana.com/0/1202500774821704/1204012835277482/f) --- DuckDuckGo.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/swiftpm/Package.resolved | 6 +-- .../View/NavigationBarPopovers.swift | 4 +- .../NetworkProtectionNavBarButtonModel.swift | 10 +++-- .../NetworkProtectionUI/Package.swift | 7 ++- .../Menu/NetworkProtectionStatusBarMenu.swift | 5 ++- .../NetworkProtectionIconPublisher.swift | 8 ++-- .../NetworkProtectionStatusView.swift | 43 ------------------- .../NetworkProtectionStatusViewModel.swift | 22 +++++----- .../NetworkProtectionStatusBarMenuTests.swift | 17 ++------ ...etworkProtectionStatusViewModelTests.swift | 35 ++++++++++----- 11 files changed, 65 insertions(+), 94 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 2b8186082b..f8d438ff51 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -10203,7 +10203,7 @@ repositoryURL = "https://github.com/duckduckgo/BrowserServicesKit"; requirement = { kind = exactVersion; - version = 72.1.0; + version = 73.0.0; }; }; AA06B6B52672AF8100F541C5 /* XCRemoteSwiftPackageReference "Sparkle" */ = { diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index cb577a080d..7cd547027b 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -14,8 +14,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/duckduckgo/BrowserServicesKit", "state" : { - "revision" : "dee5c815afe37bd82c48408c41646ad5af5b24e7", - "version" : "72.1.0" + "revision" : "b01e0010c7c0618f7329184c11c85a3c64a4307f", + "version" : "73.0.0" } }, { @@ -129,7 +129,7 @@ { "identity" : "trackerradarkit", "kind" : "remoteSourceControl", - "location" : "https://github.com/duckduckgo/TrackerRadarKit.git", + "location" : "https://github.com/duckduckgo/TrackerRadarKit", "state" : { "revision" : "4684440d03304e7638a2c8086895367e90987463", "version" : "1.2.1" diff --git a/DuckDuckGo/NavigationBar/View/NavigationBarPopovers.swift b/DuckDuckGo/NavigationBar/View/NavigationBarPopovers.swift index 083f88edb9..d9386e9261 100644 --- a/DuckDuckGo/NavigationBar/View/NavigationBarPopovers.swift +++ b/DuckDuckGo/NavigationBar/View/NavigationBarPopovers.swift @@ -277,7 +277,9 @@ final class NavigationBarPopovers { let statusReporter = DefaultNetworkProtectionStatusReporter( statusObserver: statusObserver, serverInfoObserver: statusInfoObserver, - connectionErrorObserver: connectionErrorObserver + connectionErrorObserver: connectionErrorObserver, + connectivityIssuesObserver: ConnectivityIssueObserverThroughDistributedNotifications(), + controllerErrorMessageObserver: ControllerErrorMesssageObserverThroughDistributedNotifications() ) let menuItems = [ diff --git a/DuckDuckGo/NetworkProtection/AppTargets/BothAppTargets/NetworkProtectionNavBarButtonModel.swift b/DuckDuckGo/NetworkProtection/AppTargets/BothAppTargets/NetworkProtectionNavBarButtonModel.swift index ce4dc215b2..583942a889 100644 --- a/DuckDuckGo/NetworkProtection/AppTargets/BothAppTargets/NetworkProtectionNavBarButtonModel.swift +++ b/DuckDuckGo/NetworkProtection/AppTargets/BothAppTargets/NetworkProtectionNavBarButtonModel.swift @@ -80,14 +80,16 @@ final class NetworkProtectionNavBarButtonModel: NSObject, ObservableObject { self.networkProtectionStatusReporter = statusReporter ?? DefaultNetworkProtectionStatusReporter( statusObserver: statusObserver, serverInfoObserver: statusInfoObserver, - connectionErrorObserver: connectionErrorObserver + connectionErrorObserver: connectionErrorObserver, + connectivityIssuesObserver: ConnectivityIssueObserverThroughDistributedNotifications(), + controllerErrorMessageObserver: ControllerErrorMesssageObserverThroughDistributedNotifications() ) self.iconPublisher = NetworkProtectionIconPublisher(statusReporter: networkProtectionStatusReporter, iconProvider: iconProvider) self.popovers = popovers self.pinningManager = pinningManager isPinned = pinningManager.isPinned(.networkProtection) - isHavingConnectivityIssues = networkProtectionStatusReporter.connectivityIssuesPublisher.value + isHavingConnectivityIssues = networkProtectionStatusReporter.connectivityIssuesObserver.recentValue buttonImage = .image(for: iconPublisher.icon) super.init() @@ -110,7 +112,7 @@ final class NetworkProtectionNavBarButtonModel: NSObject, ObservableObject { } private func setupStatusSubscription() { - statusChangeCancellable = networkProtectionStatusReporter.statusPublisher.sink { [weak self] status in + statusChangeCancellable = networkProtectionStatusReporter.statusObserver.publisher.sink { [weak self] status in guard let self = self else { return } @@ -123,7 +125,7 @@ final class NetworkProtectionNavBarButtonModel: NSObject, ObservableObject { } private func setupInterruptionSubscription() { - interruptionCancellable = networkProtectionStatusReporter.connectivityIssuesPublisher.sink { [weak self] isHavingConnectivityIssues in + interruptionCancellable = networkProtectionStatusReporter.connectivityIssuesObserver.publisher.sink { [weak self] isHavingConnectivityIssues in guard let self = self else { return } diff --git a/LocalPackages/NetworkProtectionUI/Package.swift b/LocalPackages/NetworkProtectionUI/Package.swift index 6291837fd3..ce732f5f1b 100644 --- a/LocalPackages/NetworkProtectionUI/Package.swift +++ b/LocalPackages/NetworkProtectionUI/Package.swift @@ -15,7 +15,7 @@ let package = Package( targets: ["NetworkProtectionUI"]) ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "72.1.0"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "73.0.0"), .package(path: "../SwiftUIExtensions") ], targets: [ @@ -30,6 +30,9 @@ let package = Package( ]), .testTarget( name: "NetworkProtectionUITests", - dependencies: ["NetworkProtectionUI"]) + dependencies: [ + "NetworkProtectionUI", + .product(name: "NetworkProtectionTestUtils", package: "BrowserServicesKit") + ]) ] ) diff --git a/LocalPackages/NetworkProtectionUI/Sources/NetworkProtectionUI/Menu/NetworkProtectionStatusBarMenu.swift b/LocalPackages/NetworkProtectionUI/Sources/NetworkProtectionUI/Menu/NetworkProtectionStatusBarMenu.swift index 93fc02e8fd..86ae1eede9 100644 --- a/LocalPackages/NetworkProtectionUI/Sources/NetworkProtectionUI/Menu/NetworkProtectionStatusBarMenu.swift +++ b/LocalPackages/NetworkProtectionUI/Sources/NetworkProtectionUI/Menu/NetworkProtectionStatusBarMenu.swift @@ -50,7 +50,10 @@ public final class StatusBarMenu { let statusReporter = statusReporter ?? DefaultNetworkProtectionStatusReporter( statusObserver: ConnectionStatusObserverThroughDistributedNotifications(), serverInfoObserver: ConnectionServerInfoObserverThroughDistributedNotifications(), - connectionErrorObserver: ConnectionErrorObserverThroughDistributedNotifications()) + connectionErrorObserver: ConnectionErrorObserverThroughDistributedNotifications(), + connectivityIssuesObserver: ConnectivityIssueObserverThroughDistributedNotifications(), + controllerErrorMessageObserver: ControllerErrorMesssageObserverThroughDistributedNotifications() + ) self.statusItem = statusItem ?? NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength) self.iconPublisher = NetworkProtectionIconPublisher(statusReporter: statusReporter, iconProvider: iconProvider) diff --git a/LocalPackages/NetworkProtectionUI/Sources/NetworkProtectionUI/NetworkProtectionIconPublisher.swift b/LocalPackages/NetworkProtectionUI/Sources/NetworkProtectionUI/NetworkProtectionIconPublisher.swift index 0f365ba65d..c6c4527539 100644 --- a/LocalPackages/NetworkProtectionUI/Sources/NetworkProtectionUI/NetworkProtectionIconPublisher.swift +++ b/LocalPackages/NetworkProtectionUI/Sources/NetworkProtectionUI/NetworkProtectionIconPublisher.swift @@ -60,13 +60,13 @@ public final class NetworkProtectionIconPublisher { // MARK: - Subscribing to NetP updates private func subscribeToConnectionStatusChanges() { - statusChangeCancellable = statusReporter.statusPublisher.sink { [weak self] _ in + statusChangeCancellable = statusReporter.statusObserver.publisher.sink { [weak self] _ in self?.updateMenuIcon() } } private func subscribeToConnectionIssues() { - connectivityIssuesCancellable = statusReporter.connectivityIssuesPublisher.sink { [weak self] _ in + connectivityIssuesCancellable = statusReporter.connectivityIssuesObserver.publisher.sink { [weak self] _ in self?.updateMenuIcon() } } @@ -76,11 +76,11 @@ public final class NetworkProtectionIconPublisher { /// Resolves the correct icon to show, based on the current NetP status. /// private func menuIcon() -> NetworkProtectionAsset { - guard !statusReporter.connectivityIssuesPublisher.value else { + guard !statusReporter.connectivityIssuesObserver.recentValue else { return iconProvider.issueIcon } - switch statusReporter.statusPublisher.value { + switch statusReporter.statusObserver.recentValue { case .connected: return iconProvider.onIcon default: diff --git a/LocalPackages/NetworkProtectionUI/Sources/NetworkProtectionUI/NetworkProtectionStatusView.swift b/LocalPackages/NetworkProtectionUI/Sources/NetworkProtectionUI/NetworkProtectionStatusView.swift index 0f4b447db5..dfe9e160d3 100644 --- a/LocalPackages/NetworkProtectionUI/Sources/NetworkProtectionUI/NetworkProtectionStatusView.swift +++ b/LocalPackages/NetworkProtectionUI/Sources/NetworkProtectionUI/NetworkProtectionStatusView.swift @@ -307,46 +307,3 @@ public struct NetworkProtectionStatusView: View { .padding(EdgeInsets(top: 6, leading: 10, bottom: 6, trailing: 9)) } } - -struct NetworkProtectionStatusView_Previews: PreviewProvider { - - private class PreviewController: NetworkProtection.TunnelController { - func isConnected() async -> Bool { - false - } - - func start() async { - print("Preview controller started") - } - - func stop() async { - print("Preview controller stopped") - } - } - - /// Convenience reporter for SwiftUI preview - /// - private final class PreviewNetworkProtectionStatusReporter: NetworkProtectionStatusReporter { - let statusPublisher = CurrentValueSubject(.connected(connectedDate: Date())) - let connectivityIssuesPublisher = CurrentValueSubject(false) - let serverInfoPublisher = CurrentValueSubject(NetworkProtectionStatusServerInfo(serverLocation: "Los Angeles, USA", serverAddress: "127.0.0.1")) - let connectionErrorPublisher = CurrentValueSubject(nil) - let controllerErrorMessagePublisher = CurrentValueSubject(nil) - - func forceRefresh() { - // No-op - } - } - - static var previews: some View { - let statusReporter = PreviewNetworkProtectionStatusReporter() - let menuItems = [ - NetworkProtectionStatusView.Model.MenuItem(name: "Share Feedback...", action: {}) - ] - let model = NetworkProtectionStatusView.Model(controller: PreviewController(), - statusReporter: statusReporter, - menuItems: menuItems) - - NetworkProtectionStatusView(model: model) - } -} diff --git a/LocalPackages/NetworkProtectionUI/Sources/NetworkProtectionUI/NetworkProtectionStatusViewModel.swift b/LocalPackages/NetworkProtectionUI/Sources/NetworkProtectionUI/NetworkProtectionStatusViewModel.swift index 5992649653..22d6109008 100644 --- a/LocalPackages/NetworkProtectionUI/Sources/NetworkProtectionUI/NetworkProtectionStatusViewModel.swift +++ b/LocalPackages/NetworkProtectionUI/Sources/NetworkProtectionUI/NetworkProtectionStatusViewModel.swift @@ -102,12 +102,12 @@ extension NetworkProtectionStatusView { self.menuItems = menuItems self.runLoopMode = runLoopMode - connectionStatus = statusReporter.statusPublisher.value - isHavingConnectivityIssues = statusReporter.connectivityIssuesPublisher.value - internalServerAddress = statusReporter.serverInfoPublisher.value.serverAddress - internalServerLocation = statusReporter.serverInfoPublisher.value.serverLocation - lastTunnelErrorMessage = statusReporter.connectionErrorPublisher.value - lastControllerErrorMessage = statusReporter.controllerErrorMessagePublisher.value + connectionStatus = statusReporter.statusObserver.recentValue + isHavingConnectivityIssues = statusReporter.connectivityIssuesObserver.recentValue + internalServerAddress = statusReporter.serverInfoObserver.recentValue.serverAddress + internalServerLocation = statusReporter.serverInfoObserver.recentValue.serverLocation + lastTunnelErrorMessage = statusReporter.connectionErrorObserver.recentValue + lastControllerErrorMessage = statusReporter.controllerErrorMessageObserver.recentValue // Particularly useful when unit testing with an initial status of our choosing. refreshInternalIsRunning() @@ -127,7 +127,7 @@ extension NetworkProtectionStatusView { // MARK: - Subscriptions private func subscribeToStatusChanges() { - statusChangeCancellable = statusReporter.statusPublisher + statusChangeCancellable = statusReporter.statusObserver.publisher .subscribe(on: Self.statusDispatchQueue) .sink { [weak self] status in @@ -142,7 +142,7 @@ extension NetworkProtectionStatusView { } private func subscribeToConnectivityIssues() { - connectivityIssuesCancellable = statusReporter.connectivityIssuesPublisher + connectivityIssuesCancellable = statusReporter.connectivityIssuesObserver.publisher .subscribe(on: Self.connectivityIssuesDispatchQueue) .sink { [weak self] isHavingConnectivityIssues in @@ -157,7 +157,7 @@ extension NetworkProtectionStatusView { } private func subscribeToTunnelErrorMessages() { - tunnelErrorMessageCancellable = statusReporter.connectionErrorPublisher + tunnelErrorMessageCancellable = statusReporter.connectionErrorObserver.publisher .subscribe(on: Self.tunnelErrorDispatchQueue) .sink { [weak self] errorMessage in @@ -172,7 +172,7 @@ extension NetworkProtectionStatusView { } private func subscribeToControllerErrorMessages() { - controllerErrorMessageCancellable = statusReporter.controllerErrorMessagePublisher + controllerErrorMessageCancellable = statusReporter.controllerErrorMessageObserver.publisher .subscribe(on: Self.controllerErrorDispatchQueue) .sink { [weak self] errorMessage in @@ -187,7 +187,7 @@ extension NetworkProtectionStatusView { } private func subscribeToServerInfoChanges() { - serverInfoCancellable = statusReporter.serverInfoPublisher + serverInfoCancellable = statusReporter.serverInfoObserver.publisher .subscribe(on: Self.serverInfoDispatchQueue) .sink { [weak self] serverInfo in diff --git a/LocalPackages/NetworkProtectionUI/Tests/NetworkProtectionUITests/NetworkProtectionStatusBarMenuTests.swift b/LocalPackages/NetworkProtectionUI/Tests/NetworkProtectionUITests/NetworkProtectionStatusBarMenuTests.swift index 3c9c9c2b30..56b20659a3 100644 --- a/LocalPackages/NetworkProtectionUI/Tests/NetworkProtectionUITests/NetworkProtectionStatusBarMenuTests.swift +++ b/LocalPackages/NetworkProtectionUI/Tests/NetworkProtectionUITests/NetworkProtectionStatusBarMenuTests.swift @@ -22,6 +22,7 @@ import SwiftUI import NetworkProtection import XCTest @testable import NetworkProtectionUI +import NetworkProtectionTestUtils final class StatusBarMenuTests: XCTestCase { @@ -31,23 +32,11 @@ final class StatusBarMenuTests: XCTestCase { } } - private final class TestStatusReporter: NetworkProtectionStatusReporter { - var statusPublisher = CurrentValueSubject(.disconnected) - var connectivityIssuesPublisher = CurrentValueSubject(false) - var serverInfoPublisher = CurrentValueSubject(.unknown) - var connectionErrorPublisher = CurrentValueSubject(nil) - var controllerErrorMessagePublisher = CurrentValueSubject(nil) - - func forceRefresh() { - // no-op - } - } - func testShowStatusBarMenu() { let item = NSStatusItem() let menu = StatusBarMenu( statusItem: item, - statusReporter: TestStatusReporter(), + statusReporter: MockNetworkProtectionStatusReporter(), appLauncher: TestAppLauncher(), iconProvider: MenuIconProvider()) @@ -60,7 +49,7 @@ final class StatusBarMenuTests: XCTestCase { let item = NSStatusItem() let menu = StatusBarMenu( statusItem: item, - statusReporter: TestStatusReporter(), + statusReporter: MockNetworkProtectionStatusReporter(), appLauncher: TestAppLauncher(), iconProvider: MenuIconProvider()) diff --git a/LocalPackages/NetworkProtectionUI/Tests/NetworkProtectionUITests/NetworkProtectionStatusViewModelTests.swift b/LocalPackages/NetworkProtectionUI/Tests/NetworkProtectionUITests/NetworkProtectionStatusViewModelTests.swift index 4401b05c45..75bcece6df 100644 --- a/LocalPackages/NetworkProtectionUI/Tests/NetworkProtectionUITests/NetworkProtectionStatusViewModelTests.swift +++ b/LocalPackages/NetworkProtectionUI/Tests/NetworkProtectionUITests/NetworkProtectionStatusViewModelTests.swift @@ -22,6 +22,7 @@ import SwiftUI import XCTest import NetworkProtection @testable import NetworkProtectionUI +import NetworkProtectionTestUtils final class NetworkProtectionStatusViewModelTests: XCTestCase { @@ -30,11 +31,11 @@ final class NetworkProtectionStatusViewModelTests: XCTestCase { serverLocation: "New York, USA", serverAddress: "127.0.0.1") - var statusPublisher: CurrentValueSubject - var connectivityIssuesPublisher: CurrentValueSubject - var serverInfoPublisher: CurrentValueSubject - var connectionErrorPublisher: CurrentValueSubject - var controllerErrorMessagePublisher: CurrentValueSubject + let statusObserver: ConnectionStatusObserver + let serverInfoObserver: ConnectionServerInfoObserver + let connectionErrorObserver: ConnectionErrorObserver + let connectivityIssuesObserver: ConnectivityIssueObserver + let controllerErrorMessageObserver: ControllerErrorMesssageObserver init(status: ConnectionStatus, isHavingConnectivityIssues: Bool = false, @@ -42,11 +43,25 @@ final class NetworkProtectionStatusViewModelTests: XCTestCase { tunnelErrorMessage: String? = nil, controllerErrorMessage: String? = nil) { - statusPublisher = CurrentValueSubject(status) - connectivityIssuesPublisher = CurrentValueSubject(isHavingConnectivityIssues) - serverInfoPublisher = CurrentValueSubject(serverInfo) - connectionErrorPublisher = CurrentValueSubject(tunnelErrorMessage) - controllerErrorMessagePublisher = CurrentValueSubject(controllerErrorMessage) + let mockStatusObserver = MockConnectionStatusObserver() + mockStatusObserver.subject.send(status) + statusObserver = mockStatusObserver + + let mockServerInfoObserver = MockConnectionServerInfoObserver() + mockServerInfoObserver.subject.send(serverInfo) + serverInfoObserver = mockServerInfoObserver + + let mockConnectivityIssueObserver = MockConnectivityIssueObserver() + mockConnectivityIssueObserver.subject.send(isHavingConnectivityIssues) + connectivityIssuesObserver = mockConnectivityIssueObserver + + let mockConnectionErrorObserver = MockConnectionErrorObserver() + mockConnectionErrorObserver.subject.send(tunnelErrorMessage) + connectionErrorObserver = mockConnectionErrorObserver + + let mockControllerErrorMessageObserver = MockControllerErrorMesssageObserver() + mockControllerErrorMessageObserver.subject.send(controllerErrorMessage) + controllerErrorMessageObserver = mockControllerErrorMessageObserver } func forceRefresh() { From d016fa6ae6519233249c984b058402567fc287a8 Mon Sep 17 00:00:00 2001 From: Tom Strba <57389842+tomasstrba@users.noreply.github.com> Date: Wed, 9 Aug 2023 22:19:29 +0200 Subject: [PATCH 03/17] Add pixel to determine Bitwarden use on Mac (#1412) Task/Issue URL: https://app.asana.com/0/1201048563534612/1205153296064896/f **Description**: Pixels fired when a password is saved or autofilled when Bitwarden is enabled --- .../ContentOverlayViewController.swift | 7 +++++++ .../PasswordManagerCoordinator.swift | 19 +++++++++++++++++++ .../View/SaveCredentialsViewController.swift | 4 ++++ DuckDuckGo/Statistics/PixelEvent.swift | 9 +++++++++ DuckDuckGo/Statistics/PixelParameters.swift | 2 ++ .../TabExtensions/AutofillTabExtension.swift | 6 ++++++ .../PasswordManagerCoordinatingMock.swift | 4 ++++ 7 files changed, 51 insertions(+) diff --git a/DuckDuckGo/Autofill/ContentOverlayViewController.swift b/DuckDuckGo/Autofill/ContentOverlayViewController.swift index 40742725c9..8b51a7066b 100644 --- a/DuckDuckGo/Autofill/ContentOverlayViewController.swift +++ b/DuckDuckGo/Autofill/ContentOverlayViewController.swift @@ -51,6 +51,8 @@ public final class ContentOverlayViewController: NSViewController, EmailManagerR return model }() + lazy var passwordManagerCoordinator: PasswordManagerCoordinating = PasswordManagerCoordinator.shared + public override func viewDidLoad() { initWebView() addTrackingArea() @@ -250,6 +252,11 @@ extension ContentOverlayViewController: SecureVaultManagerDelegate { public func secureVaultManager(_: SecureVaultManager, didAutofill type: AutofillType, withObjectId objectId: String) { Pixel.fire(.formAutofilled(kind: type.formAutofillKind)) + + if type.formAutofillKind == .password && + passwordManagerCoordinator.isEnabled { + passwordManagerCoordinator.reportPasswordAutofill() + } } public func secureVaultManager(_: SecureVaultManager, didRequestAuthenticationWithCompletionHandler handler: @escaping (Bool) -> Void) { diff --git a/DuckDuckGo/PasswordManager/PasswordManagerCoordinator.swift b/DuckDuckGo/PasswordManager/PasswordManagerCoordinator.swift index c01e26772a..ef523ea714 100644 --- a/DuckDuckGo/PasswordManager/PasswordManagerCoordinator.swift +++ b/DuckDuckGo/PasswordManager/PasswordManagerCoordinator.swift @@ -25,6 +25,9 @@ protocol PasswordManagerCoordinating: BrowserServicesKit.PasswordManager { var displayName: String { get } + func reportPasswordAutofill() + func reportPasswordSave() + } // Encapsulation of third party password managers @@ -239,6 +242,22 @@ final class PasswordManagerCoordinator: PasswordManagerCoordinating { #endif + func reportPasswordAutofill() { + guard isEnabled else { + return + } + + Pixel.fire(.bitwardenPasswordAutofilled) + } + + func reportPasswordSave() { + guard isEnabled else { + return + } + + Pixel.fire(.bitwardenPasswordSaved) + } + // MARK: - Cache private func cache(credentials: [BWCredential], for url: URL) { diff --git a/DuckDuckGo/SecureVault/View/SaveCredentialsViewController.swift b/DuckDuckGo/SecureVault/View/SaveCredentialsViewController.swift index 434253ef95..39d0e4fabe 100644 --- a/DuckDuckGo/SecureVault/View/SaveCredentialsViewController.swift +++ b/DuckDuckGo/SecureVault/View/SaveCredentialsViewController.swift @@ -179,6 +179,10 @@ final class SaveCredentialsViewController: NSViewController { } Pixel.fire(.autofillItemSaved(kind: .password)) + if passwordManagerCoordinator.isEnabled { + passwordManagerCoordinator.reportPasswordSave() + } + if self.fireproofCheck.state == .on { FireproofDomains.shared.add(domain: account.domain) } else { diff --git a/DuckDuckGo/Statistics/PixelEvent.swift b/DuckDuckGo/Statistics/PixelEvent.swift index bd556191a7..590cc28df0 100644 --- a/DuckDuckGo/Statistics/PixelEvent.swift +++ b/DuckDuckGo/Statistics/PixelEvent.swift @@ -94,6 +94,9 @@ extension Pixel { case formAutofilled(kind: FormAutofillKind) case autofillItemSaved(kind: FormAutofillKind) + case bitwardenPasswordAutofilled + case bitwardenPasswordSaved + case autoconsentOptOutFailed case autoconsentSelfTestFailed @@ -301,6 +304,12 @@ extension Pixel.Event { case .autofillItemSaved(kind: let kind): return "m_mac_save_\(kind)" + case .bitwardenPasswordAutofilled: + return "m_mac_bitwarden_autofill_password" + + case .bitwardenPasswordSaved: + return "m_mac_bitwarden_save_password" + case .debug(event: let event, error: _): return "m_mac_debug_\(event.name)" diff --git a/DuckDuckGo/Statistics/PixelParameters.swift b/DuckDuckGo/Statistics/PixelParameters.swift index 2153b44029..5e6ee57ca5 100644 --- a/DuckDuckGo/Statistics/PixelParameters.swift +++ b/DuckDuckGo/Statistics/PixelParameters.swift @@ -107,6 +107,8 @@ extension Pixel.Event { .faviconImportFailed, .formAutofilled, .autofillItemSaved, + .bitwardenPasswordAutofilled, + .bitwardenPasswordSaved, .autoconsentOptOutFailed, .autoconsentSelfTestFailed, .ampBlockingRulesCompilationFailed, diff --git a/DuckDuckGo/Tab/TabExtensions/AutofillTabExtension.swift b/DuckDuckGo/Tab/TabExtensions/AutofillTabExtension.swift index 99bd734ea8..47cd9b5aa5 100644 --- a/DuckDuckGo/Tab/TabExtensions/AutofillTabExtension.swift +++ b/DuckDuckGo/Tab/TabExtensions/AutofillTabExtension.swift @@ -53,6 +53,7 @@ final class AutofillTabExtension: TabExtension { } private var emailManager: AutofillEmailDelegate? private var vaultManager: AutofillSecureVaultDelegate? + private var passwordManagerCoordinator: PasswordManagerCoordinating = PasswordManagerCoordinator.shared private let isBurner: Bool @Published var autofillDataToSave: AutofillData? @@ -106,6 +107,11 @@ extension AutofillTabExtension: SecureVaultManagerDelegate { func secureVaultManager(_: SecureVaultManager, didAutofill type: AutofillType, withObjectId objectId: String) { Pixel.fire(.formAutofilled(kind: type.formAutofillKind)) + + if type.formAutofillKind == .password && + passwordManagerCoordinator.isEnabled { + passwordManagerCoordinator.reportPasswordAutofill() + } } func secureVaultManager(_: SecureVaultManager, didRequestAuthenticationWithCompletionHandler handler: @escaping (Bool) -> Void) { diff --git a/UnitTests/PasswordManagers/PasswordManagerCoordinatingMock.swift b/UnitTests/PasswordManagers/PasswordManagerCoordinatingMock.swift index a501b9e37c..93a1f29bdb 100644 --- a/UnitTests/PasswordManagers/PasswordManagerCoordinatingMock.swift +++ b/UnitTests/PasswordManagers/PasswordManagerCoordinatingMock.swift @@ -45,4 +45,8 @@ final class PasswordManagerCoordinatingMock: PasswordManagerCoordinating { func askToUnlock(completionHandler: @escaping () -> Void) {} + func reportPasswordAutofill() {} + + func reportPasswordSave() {} + } From 613fccdd528ad1a8727ddf4bdf522f626810654d Mon Sep 17 00:00:00 2001 From: Dominik Kapusta Date: Thu, 10 Aug 2023 10:10:33 +0200 Subject: [PATCH 04/17] Fix FutureExtensionTests (#1450) Task/Issue URL: https://app.asana.com/0/0/1205205535714739/f Description: Avoid waiting for some expectations twice in a single unit test. --- .../Extensions/FutureExtensionTests.swift | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/UnitTests/Common/Extensions/FutureExtensionTests.swift b/UnitTests/Common/Extensions/FutureExtensionTests.swift index 40f0fd1c6c..855d103569 100644 --- a/UnitTests/Common/Extensions/FutureExtensionTests.swift +++ b/UnitTests/Common/Extensions/FutureExtensionTests.swift @@ -157,11 +157,11 @@ final class FutureExtensionTests: XCTestCase { func testFulfilledPromiseOnGlobalQueueHasValue() { let e = expectation(description: "background job done") + let eFulfilled = expectation(description: "fulfilled") DispatchQueue.global().async { let promise = Future.promise() var value: String? - let eFulfilled = self.expectation(description: "fulfilled") let c = promise.future.sink { XCTAssertEqual($0, "test") value = $0 @@ -175,16 +175,16 @@ final class FutureExtensionTests: XCTestCase { e.fulfill() } - waitForExpectations(timeout: 1) + wait(for: [e], timeout: 1) } func testPromiseOnGlobalQueueFulfilledAsyncHasValue() { let e = expectation(description: "background job done") + let eFulfilled = expectation(description: "fulfilled") DispatchQueue.global().async { let promise = Future.promise() var value: String? - let eFulfilled = self.expectation(description: "fulfilled") let c = promise.future.sink { XCTAssertEqual($0, "test") value = $0 @@ -200,16 +200,16 @@ final class FutureExtensionTests: XCTestCase { e.fulfill() } - waitForExpectations(timeout: 1) + wait(for: [e], timeout: 1) } func testPromiseOnGlobalQueueFulfilledInBackgroundHasValue() { let e = expectation(description: "background job done") + let eFulfilled = expectation(description: "fulfilled") DispatchQueue.global().async { let promise = Future.promise() var value: String? - let eFulfilled = self.expectation(description: "fulfilled") let c = promise.future.sink { XCTAssertEqual($0, "test") value = $0 @@ -225,7 +225,7 @@ final class FutureExtensionTests: XCTestCase { e.fulfill() } - waitForExpectations(timeout: 1) + wait(for: [e], timeout: 1) } // - main queue async - @@ -269,11 +269,11 @@ final class FutureExtensionTests: XCTestCase { func testFulfilledPromiseAsyncHasValue() { let e = expectation(description: "async job done") + let eFulfilled = expectation(description: "fulfilled") DispatchQueue.main.async { let promise = Future.promise() var value: String? - let eFulfilled = self.expectation(description: "fulfilled") let c = promise.future.sink { XCTAssertEqual($0, "test") value = $0 @@ -287,16 +287,16 @@ final class FutureExtensionTests: XCTestCase { e.fulfill() } - waitForExpectations(timeout: 1) + wait(for: [e], timeout: 1) } func testPromiseAsyncFulfilledAsyncHasValue() { let e = expectation(description: "async job done") + let eFulfilled = expectation(description: "fulfilled") RunLoop.main.perform { let promise = Future.promise() var value: String? - let eFulfilled = self.expectation(description: "fulfilled") let c = promise.future.sink { XCTAssertEqual($0, "test") value = $0 @@ -312,16 +312,16 @@ final class FutureExtensionTests: XCTestCase { e.fulfill() } - waitForExpectations(timeout: 1) + wait(for: [e], timeout: 1) } func testPromiseAsyncFulfilledInBackgroundHasValue() { let e = expectation(description: "async job done") + let eFulfilled = expectation(description: "fulfilled") DispatchQueue.main.async { let promise = Future.promise() var value: String? - let eFulfilled = self.expectation(description: "fulfilled") let c = promise.future.sink { XCTAssertEqual($0, "test") value = $0 @@ -337,7 +337,7 @@ final class FutureExtensionTests: XCTestCase { e.fulfill() } - waitForExpectations(timeout: 1) + wait(for: [e], timeout: 1) } // MARK: - Publishers.First.get() From a3a1a96b886d3c2c96f97412da0e63ac8189db0e Mon Sep 17 00:00:00 2001 From: Sabrina Tardio <44158575+SabrinaTardio@users.noreply.github.com> Date: Thu, 10 Aug 2023 12:09:32 +0200 Subject: [PATCH 05/17] =?UTF-8?q?fix=20bug=20where=20wrong=20address=20bar?= =?UTF-8?q?=20text=20is=20shown=20when=20opening=20DuckPlayer=E2=80=A6=20(?= =?UTF-8?q?#1434)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task/Issue URL: https://app.asana.com/0/1177771139624306/1204301052974318/f **Description**: fix bug where wrong address bar text is shown when opening DuckPlayer from target=blank links. Moves the check for DuckPlayer URLs in the Tab view model before when in case the url is nil the tab content url is associated to the parent tab content url. --- DuckDuckGo/Tab/ViewModel/TabViewModel.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DuckDuckGo/Tab/ViewModel/TabViewModel.swift b/DuckDuckGo/Tab/ViewModel/TabViewModel.swift index 49b1446ce1..de2596597a 100644 --- a/DuckDuckGo/Tab/ViewModel/TabViewModel.swift +++ b/DuckDuckGo/Tab/ViewModel/TabViewModel.swift @@ -187,7 +187,7 @@ final class TabViewModel { } private var tabURL: URL? { - return tab.content.url ?? tab.parentTab?.content.url + return tab.content.url } private var tabHostURL: URL? { From d1067c5375c6bc68fe2e544403246d2c0cba4cda Mon Sep 17 00:00:00 2001 From: Maxim Tsoy Date: Thu, 10 Aug 2023 13:34:38 +0200 Subject: [PATCH 06/17] Fix an autoconsent exception on old Safari (#1460) Task/Issue URL: https://app.asana.com/0/1177771139624306/1205239910258201/f Tech Design URL: CC: @kdzwinel @sammacbeth @shakyShane **Description**: Due to the updated build pipeline, previous autoconsent version caused exceptions in older Webkit versions (versions of Safari older than 16.4) **Steps to test this PR**: 1. run the app on the oldest supported macOS/WebView version 2. enable CPM 3. confirm that CPM works (e.g. on https://doodle.com) 4. confirm no CPM exceptions on that and other pages --- ###### Internal references: [Pull Request Review Checklist](https://app.asana.com/0/1202500774821704/1203764234894239/f) [Software Engineering Expectations](https://app.asana.com/0/59792373528535/199064865822552) [Technical Design Template](https://app.asana.com/0/59792373528535/184709971311943) [Pull Request Documentation](https://app.asana.com/0/1202500774821704/1204012835277482/f) --- DuckDuckGo/Autoconsent/autoconsent-bundle.js | 2 +- package-lock.json | 8 ++++---- package.json | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/DuckDuckGo/Autoconsent/autoconsent-bundle.js b/DuckDuckGo/Autoconsent/autoconsent-bundle.js index 0080eccb43..b39376f7e3 100644 --- a/DuckDuckGo/Autoconsent/autoconsent-bundle.js +++ b/DuckDuckGo/Autoconsent/autoconsent-bundle.js @@ -1 +1 @@ -!function(){"use strict";function e(){return crypto&&void 0!==crypto.randomUUID?crypto.randomUUID():Math.random().toString()}var t={pending:new Map,sendContentMessage:null};function o(o){const c=e();t.sendContentMessage({type:"eval",id:c,code:o});const i=new 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)}}(c);return t.pending.set(i.id,i),i.promise}function c(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 i(e,t,o="display"){const c="opacity"===o?"opacity: 0":"display: none",i=`${t.join(",")} { ${c} !important; z-index: -1 !important; pointer-events: none !important; } `;return e instanceof HTMLStyleElement&&(e.innerText+=i,t.length>0)}async function n(e,t,o){const c=await e();return!c&&t>0?new Promise((c=>{setTimeout((async()=>{c(n(e,t-1,o))}),o)})):Promise.resolve(c)}function a(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 s(e){return o(e).catch((e=>!1))}function r(e,t=!1){const o=k(e);return o.length>0&&(t?o.forEach((e=>e.click())):o[0].click()),o.length>0}function p(e){return k(e).length>0}function l(e,t){const o=k(e),c=new Array(o.length);return o.forEach(((e,t)=>{c[t]=a(e)})),"none"===t?c.every((e=>!e)):0!==c.length&&("any"===t?c.some((e=>e)):c.every((e=>e)))}function d(e,t=1e4){return n((()=>k(e).length>0),Math.ceil(t/200),200)}async function u(e,t=1e4,o=!1){return await d(e,t),r(e,o)}function m(e){return new Promise((t=>{setTimeout((()=>{t(!0)}),e)}))}function h(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))}function k(e){return"string"==typeof e?h(e):function(e){let t,o=document;for(const c of e){if(t=h(c,o),0===t.length)return[];o=t[0]}return t}(e)}var b={main:!0,frame:!1,urlPattern:""},g=class{constructor(e){this.runContext=b,this.name=e}get hasSelfTest(){throw new Error("Not Implemented")}get isIntermediate(){throw new Error("Not Implemented")}get isCosmetic(){throw new Error("Not Implemented")}checkRunContext(){const e={...b,...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)}};async function y(e){const t=[];if(e.exists&&t.push(p(e.exists)),e.visible&&t.push(l(e.visible,e.check)),e.eval){const o=s(e.eval);t.push(o)}var o,a;if(e.waitFor&&t.push(d(e.waitFor,e.timeout)),e.waitForVisible&&t.push(function(e,t=1e4,o="any"){return n((()=>l(e,o)),Math.ceil(t/200),200)}(e.waitForVisible,e.timeout,e.check)),e.click&&t.push(r(e.click,e.all)),e.waitForThenClick&&t.push(u(e.waitForThenClick,e.timeout,e.all)),e.wait&&t.push(m(e.wait)),e.hide&&t.push((o=e.hide,a=e.method,i(c(),o,a))),e.if){if(!e.if.exists&&!e.if.visible)return console.error("invalid conditional rule",e.if),!1;await y(e.if)?t.push(w(e.then)):e.else&&t.push(w(e.else))}if(e.any){for(const t of e.any)if(await y(t))return!0;return!1}if(0===t.length)return!1;return(await Promise.all(t)).reduce(((e,t)=>e&&t),!0)}async function w(e){for(const t of e){if(!await y(t)&&!t.optional)return!1}return!0}var _=class extends g{constructor(e){super(e.name),this.config=e,this.runContext=e.runContext||b}get hasSelfTest(){return!!this.config.test}get isIntermediate(){return!!this.config.intermediate}get isCosmetic(){return!!this.config.cosmetic}get prehideSelectors(){return this.config.prehideSelectors}async detectCmp(){return!!this.config.detectCmp&&async function(e){const t=e.map((e=>y(e)));return(await Promise.all(t)).every((e=>!!e))}(this.config.detectCmp)}async detectPopup(){return!!this.config.detectPopup&&w(this.config.detectPopup)}async optOut(){return!!this.config.optOut&&w(this.config.optOut)}async optIn(){return!!this.config.optIn&&w(this.config.optIn)}async openCmp(){return!!this.config.openCmp&&w(this.config.openCmp)}async test(){return this.hasSelfTest?w(this.config.test):super.test()}},v="#truste-show-consent",f="#truste-consent-track";var C=[new class extends g{constructor(){super("TrustArc-top"),this.prehideSelectors=[".trustarc-banner-container",`.truste_popframe,.truste_overlay,.truste_box_overlay,${f}`],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=p(`${v},${f}`);return e&&(this._shortcutButton=document.querySelector("#truste-consent-required")),e}async detectPopup(){return l(`#truste-consent-content,#trustarc-banner-overlay,${f}`,"all")}openFrame(){r(v)}async optOut(){return this._shortcutButton?(this._shortcutButton.click(),!0):(i(c(),[".truste_popframe",".truste_overlay",".truste_box_overlay",f]),r(v),setTimeout((()=>{c().remove()}),1e4),!0)}async optIn(){return this._optInDone=!0,r("#truste-consent-button")}async openCmp(){return!0}async test(){return await s("window && window.truste && window.truste.eu.bindMap.prefCookie === '0'")}},new class extends g{constructor(){super("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 l("#defaultpreferencemanager","any")&&l(".mainContent","any")}async navigateToSettings(){return await n((async()=>p(".shp")||l(".advance","any")||p(".switch span:first-child")),10,500),p(".shp")&&r(".shp"),await d(".prefPanel",5e3),l(".advance","any")&&r(".advance"),await n((()=>l(".switch span:first-child","any")),5,1e3)}async optOut(){return await n((()=>"complete"===document.readyState),20,100),await d(".mainContent[aria-hidden=false]",5e3),!!r(".rejectAll")||(p(".prefPanel")&&await d('.prefPanel[style="visibility: visible;"]',3e3),r("#catDetails0")?(r(".submit"),!0):(r(".required")||(await this.navigateToSettings(),r(".switch span:nth-child(1):not(.active)",!0),r(".submit"),d("#gwt-debug-close_id",3e5).then((()=>{r("#gwt-debug-close_id")}))),!0))}async optIn(){return r(".call")||(await this.navigateToSettings(),r(".switch span:nth-child(2)",!0),r(".submit"),d("#gwt-debug-close_id",3e5).then((()=>{r("#gwt-debug-close_id")}))),!0}},new class extends g{constructor(){super("Cybotcookiebot"),this.prehideSelectors=["#CybotCookiebotDialog,#dtcookie-container,#cookiebanner,#cb-cookieoverlay"]}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return p("#CybotCookiebotDialogBodyLevelButtonPreferences")}async detectPopup(){return p("#CybotCookiebotDialog,#dtcookie-container,#cookiebanner,#cb-cookiebanner")}async optOut(){return r(".cookie-alert-extended-detail-link")?(await d(".cookie-alert-configuration",2e3),r(".cookie-alert-configuration-input:checked",!0),r(".cookie-alert-extended-button-secondary"),!0):p("#dtcookie-container")?r(".h-dtcookie-decline"):(r(".cookiebot__button--settings")||r("#CybotCookiebotDialogBodyButtonDecline")||(r(".cookiebanner__link--details"),r('.CybotCookiebotDialogBodyLevelButton:checked:enabled,input[id*="CybotCookiebotDialogBodyLevelButton"]:checked:enabled',!0),r("#CybotCookiebotDialogBodyButtonDecline"),r("input[id^=CybotCookiebotDialogBodyLevelButton]:checked",!0),p("#CybotCookiebotDialogBodyButtonAcceptSelected")?r("#CybotCookiebotDialogBodyButtonAcceptSelected"):r("#CybotCookiebotDialogBodyLevelButtonAccept,#CybotCookiebotDialogBodyButtonAccept,#CybotCookiebotDialogBodyLevelButtonLevelOptinAllowallSelection",!0),await s("window.CookieConsent.hasResponse !== true")&&(await s("window.Cookiebot.dialog.submitConsent()"),await m(500)),p("#cb-confirmedSettings")&&await s("endCookieProcess()")),!0)}async optIn(){return p("#dtcookie-container")?r(".h-dtcookie-accept"):(r(".CybotCookiebotDialogBodyLevelButton:not(:checked):enabled",!0),r("#CybotCookiebotDialogBodyLevelButtonAccept"),r("#CybotCookiebotDialogBodyButtonAccept"),!0)}async test(){return s("window.CookieConsent.declined === true")}},new class extends g{constructor(){super("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)&&(e.searchParams.has("message_id")||e.searchParams.has("requestUUID")||e.searchParams.has("consentUUID"))}async detectPopup(){return!!this.ccpaNotice||(this.ccpaPopup?await d(".priv-save-btn",2e3):(await d(".sp_choice_type_11,.sp_choice_type_12,.sp_choice_type_13,.sp_choice_type_ACCEPT_ALL",2e3),!p(".sp_choice_type_9")))}async optIn(){return await d(".sp_choice_type_11,.sp_choice_type_ACCEPT_ALL",2e3),!!r(".sp_choice_type_11")||!!r(".sp_choice_type_ACCEPT_ALL")}isManagerOpen(){return"/privacy-manager/index.html"===location.pathname}async optOut(){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 r(".priv-save-btn")}if(!this.isManagerOpen()){if(!await d(".sp_choice_type_12,.sp_choice_type_13"))return!1;if(!p(".sp_choice_type_12"))return r(".sp_choice_type_13");r(".sp_choice_type_12"),await n((()=>this.isManagerOpen()),200,100)}await d(".type-modal",2e4);try{const e=".sp_choice_type_REJECT_ALL",t=".reject-toggle",o=await Promise.race([d(e,2e3).then((e=>e?0:-1)),d(t,2e3).then((e=>e?1:-1)),d(".pm-features",2e3).then((e=>e?2:-1))]);if(0===o)return await m(1e3),r(e);1===o?r(t):2===o&&(await d(".pm-features",1e4),r(".checked > span",!0),r(".chevron"))}catch(e){}return r(".sp_choice_type_SAVE_AND_EXIT")}},new class extends g{constructor(){super("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 s('window.__cmp && typeof __cmp("getCMPData") === "object"'),!!this.apiAvailable||p("#cmpbox")}async detectPopup(){return this.apiAvailable?(await m(500),await s("!__cmp('consentStatus').userChoiceExists")):l("#cmpbox .cmpmore","any")}async optOut(){return await m(500),this.apiAvailable?await s("__cmp('setConsent', 0)"):!!r(".cmpboxbtnno")||(p(".cmpwelcomeprpsbtn")?(r(".cmpwelcomeprpsbtn > a[aria-checked=true]",!0),r(".cmpboxbtnsave"),!0):(r(".cmpboxbtncustom"),await d(".cmptblbox",2e3),r(".cmptdchoice > a[aria-checked=true]",!0),r(".cmpboxbtnyescustomchoices"),!0))}async optIn(){return this.apiAvailable?await s("__cmp('setConsent', 1)"):r(".cmpboxbtnyes")}async test(){if(this.apiAvailable)return await s("__cmp('consentStatus').userChoiceExists")}},new class extends g{constructor(){super("Evidon")}get hasSelfTest(){return!1}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return p("#_evidon_banner")}async detectPopup(){return l("#_evidon_banner","any")}async optOut(){return r("#_evidon-decline-button")||(i(c(),["#evidon-prefdiag-overlay","#evidon-prefdiag-background"]),r("#_evidon-option-button"),await d("#evidon-prefdiag-overlay",5e3),r("#evidon-prefdiag-decline")),!0}async optIn(){return r("#_evidon-accept-button")}},new class extends g{constructor(){super("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 p("#onetrust-banner-sdk")}async detectPopup(){return l("#onetrust-banner-sdk","all")}async optOut(){return p("#onetrust-pc-btn-handler")?r("#onetrust-pc-btn-handler"):r(".ot-sdk-show-settings,button.js-cookie-settings"),await d("#onetrust-consent-sdk",2e3),await m(1e3),r("#onetrust-consent-sdk input.category-switch-handler:checked,.js-editor-toggle-state:checked",!0),await m(1e3),await d(".save-preference-btn-handler,.js-consent-save",2e3),r(".save-preference-btn-handler,.js-consent-save"),await n((()=>l("#onetrust-banner-sdk","none")),10,500),!0}async optIn(){return r("#onetrust-accept-btn-handler,.js-accept-cookies")}async test(){return await s("window.OnetrustActiveGroups.split(',').filter(s => s.length > 0).length <= 1")}},new class extends g{constructor(){super("Klaro"),this.prehideSelectors=[".klaro"],this.settingsOpen=!1}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return p(".klaro > .cookie-modal")?(this.settingsOpen=!0,!0):p(".klaro > .cookie-notice")}async detectPopup(){return l(".klaro > .cookie-notice,.klaro > .cookie-modal","any")}async optOut(){return!!r(".klaro .cn-decline")||(this.settingsOpen||(r(".klaro .cn-learn-more"),await d(".klaro > .cookie-modal",2e3),this.settingsOpen=!0),!!r(".klaro .cn-decline")||(r(".cm-purpose:not(.cm-toggle-all) > input:not(.half-checked)",!0),r(".cm-btn-accept")))}async optIn(){return!!r(".klaro .cm-btn-accept-all")||(this.settingsOpen?(r(".cm-purpose:not(.cm-toggle-all) > input.half-checked",!0),r(".cm-btn-accept")):r(".klaro .cookie-notice .cm-btn-success"))}async test(){return await s("klaro.getManager().config.services.every(c => c.required || !klaro.getManager().consents[c.name])")}},new class extends g{constructor(){super("Uniconsent")}get prehideSelectors(){return[".unic",".modal:has(.unic)"]}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return p(".unic .unic-box,.unic .unic-bar")}async detectPopup(){return l(".unic .unic-box,.unic .unic-bar","any")}async optOut(){if(await d(".unic button",1e3),document.querySelectorAll(".unic button").forEach((e=>{const t=e.textContent;(t.includes("Manage Options")||t.includes("Optionen verwalten"))&&e.click()})),await d(".unic input[type=checkbox]",1e3)){await d(".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 m(500),!0}}return!1}async optIn(){return u(".unic #unic-agree")}async test(){await m(1e3);return!p(".unic .unic-box,.unic .unic-bar")}},new class extends g{constructor(){super("Conversant"),this.prehideSelectors=[".cmp-root"]}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return p(".cmp-root .cmp-receptacle")}async detectPopup(){return l(".cmp-root .cmp-receptacle","any")}async optOut(){if(!await u(".cmp-main-button:not(.cmp-main-button--primary)"))return!1;if(!await d(".cmp-view-tab-tabs"))return!1;await u(".cmp-view-tab-tabs > :first-child"),await u(".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 n((()=>!!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 r(".cmp-main-button:not(.cmp-main-button--primary)"),!0}async optIn(){return u(".cmp-main-button.cmp-main-button--primary")}async test(){return document.cookie.includes("cmp-data=0")}},new class extends g{constructor(){super("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 p("tiktok-cookie-banner")}async detectPopup(){return a(this.getShadowRoot().querySelector(".tiktok-cookie-banner"))}async optOut(){const e=this.getShadowRoot().querySelector(".button-wrapper button:first-child");return!!e&&(e.click(),!0)}async optIn(){const e=this.getShadowRoot().querySelector(".button-wrapper button:last-child");return!!e&&(e.click(),!0)}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))}},new class extends g{constructor(){super("airbnb"),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 p("div[data-testid=main-cookies-banner-container]")}async detectPopup(){return l("div[data-testid=main-cookies-banner-container","any")}async optOut(){let e;for(await u("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 u("button[data-testid=save-btn]")}async optIn(){return u("div[data-testid=main-cookies-banner-container] button._148dgdpk")}async test(){return await n((()=>!!document.cookie.match("OptanonAlertBoxClosed")),20,200)}}],x=class e{static{this.base=null}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])}};function S(e){const t=x.find(e);return"css"===e.type?!!t.target:"checkbox"===e.type?!!t.target&&t.target.checked:void 0}async function A(e,t){switch(e.type){case"click":return async function(e){const t=x.find(e);null!=t.target&&t.target.click();return P(0)}(e);case"list":return async function(e,t){for(const o of e.actions)await A(o,t)}(e,t);case"consent":return async function(e,t){for(const o of e.consents){const e=-1!==t.indexOf(o.type);if(o.matcher&&o.toggleAction){S(o.matcher)!==e&&await A(o.toggleAction)}else e?await A(o.trueAction):await A(o.falseAction)}}(e,t);case"ifcss":return async function(e,t){const o=x.find(e);o.target?e.falseAction&&await A(e.falseAction,t):e.trueAction&&await A(e.trueAction,t)}(e,t);case"waitcss":return async function(e){await new Promise((t=>{let o=e.retries||10;const c=e.waitTime||250,i=()=>{const n=x.find(e);(e.negated&&n.target||!e.negated&&!n.target)&&o>0?(o-=1,setTimeout(i,c)):t()};i()}))}(e);case"foreach":return async function(e,t){const o=x.find(e,!0),c=x.base;for(const c of o)c.target&&(x.setBase(c.target),await A(e.action,t));x.setBase(c)}(e,t);case"hide":return async function(e){const t=x.find(e);t.target&&t.target.classList.add("Autoconsent-Hidden")}(e);case"slide":return async function(e){const t=x.find(e),o=x.find(e.dragTarget);if(t.target){const e=t.target.getBoundingClientRect(),c=o.target.getBoundingClientRect();let i=c.top-e.top,n=c.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,p=e.top+e.height/2,l=document.createEvent("MouseEvents");l.initMouseEvent("mousedown",!0,!0,window,0,a,s,r,p,!1,!1,!1,!1,0,t.target);const d=document.createEvent("MouseEvents");d.initMouseEvent("mousemove",!0,!0,window,0,a+n,s+i,r+n,p+i,!1,!1,!1,!1,0,t.target);const u=document.createEvent("MouseEvents");u.initMouseEvent("mouseup",!0,!0,window,0,a+n,s+i,r+n,p+i,!1,!1,!1,!1,0,t.target),t.target.dispatchEvent(l),await this.waitTimeout(10),t.target.dispatchEvent(d),await this.waitTimeout(10),t.target.dispatchEvent(u)}}(e);case"close":return async function(){window.close()}();case"wait":return async function(e){await P(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}}function P(e){return new Promise((t=>{setTimeout((()=>{t()}),e)}))}var O=[{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:"document.cookie.includes('CC_ADVERTISING=NO') && document.cookie.includes('CC_ANALYTICS=NO')"}]},{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:"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:"!document.cookie.includes('__adroll_fpc')"}]},{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:"document.cookie.includes('serif_manage_cookies_viewed') && !document.cookie.includes('serif_allow_analytics')"}]},{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:"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:"arzt-auskunft.de",prehideSelectors:["#cookiescript_injected"],detectCmp:[{exists:"#cookiescript_injected"}],detectPopup:[{visible:"#cookiescript_injected"}],optOut:[{click:"#cookiescript_reject"}],optIn:[{click:"#cookiescript_accept"}]},{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:"document.cookie.includes('axeptio_authorized_vendors=%2C%2C')"}]},{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:"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:"document.cookie.includes('AL=0') && document.cookie.includes('AD=0') && document.cookie.includes('SM=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:"!JSON.parse(decodeURIComponent(document.cookie.split(';').find(c => c.indexOf('borlabs-cookie') !== -1).split('=', 2)[1])).consents.statistics"}]},{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:"document.cookie.match('cookie-allow-tracking=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:"!document.cookie.includes('gtm_fpc_engagement_event')"}]},{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"]}]},{comment:"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:"document.cookie.includes('__lxG__consent__v2_daisybit=')",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:"document.cookie.includes('ctc_rejected=1')"}]},{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:"JSON.parse(decodeURIComponent(document.cookie.match(/cm_(eu|default)_preferences=([0-9a-zA-Z\\{\\}\\[\\]%:]*);?/)[2])).consent.length <= 1"}]},{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:"document.cookie.includes('cmplz_banner-status=dismissed')"}]},{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:[{click:".cc-accept-all",optional:!0},{click:".cc-allow",optional:!0},{click:".cc-dismiss",optional:!0}],optOut:[{click:".cc-dismiss"}],test:[{eval:"!!document.cookie.match(/cmplz_[^=]+=deny/)"}]},{name:"Complianz notice",prehideSelectors:['.cc-type-info[aria-describedby="cookieconsent:desc"]'],cosmetic:!0,detectCmp:[{exists:'.cc-type-info[aria-describedby="cookieconsent:desc"]'}],detectPopup:[{visible:'.cc-type-info[aria-describedby="cookieconsent:desc"]'}],optIn:[{click:".cc-accept-all",optional:!0},{click:".cc-allow",optional:!0},{click:".cc-dismiss",optional:!0}],optOut:[{hide:['[aria-describedby="cookieconsent:desc"]']}]},{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:[{click:".cc-accept-all",optional:!0},{click:".cc-allow",optional:!0},{click:".cc-dismiss",optional:!0}],optOut:[{click:".cc-settings"},{waitForVisible:'[aria-label="cookies preferences popup"]'},{click:'[aria-label="cookies preferences popup"] input[type=checkbox]:not([disabled]):checked',all:!0,optional:!0},{click:'[aria-label="cookies preferences popup"] [aria-label="Accept Selected"], [aria-label="cookies preferences popup"] [aria-label="Save my choice"], .cc-btn-accept-selected, .cc-deny',optional:!0}],test:[{eval:"!!document.cookie.match(/cookieconsent_preferences_disabled=[^;]+/)"}]},{name:"cookie-law-info",prehideSelectors:["#cookie-law-info-bar"],detectCmp:[{exists:"#cookie-law-info-bar"}],detectPopup:[{visible:"#cookie-law-info-bar"}],optIn:[{click:'[data-cli_action="accept_all"]'}],optOut:[{hide:["#cookie-law-info-bar"]},{eval:"CLI.disableAllCookies() || CLI.reject_close() || true"}],test:[{eval:"document.cookie.indexOf('cookielawinfo-checkbox-non-necessary=yes') === -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:"JSON.parse(document.cookie.split(';').find(c => c.trim().startsWith('CookieLevel')).split('=')[1]).social === false"}]},{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:"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:"document.querySelector('body').removeAttribute('style') || true"}],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:"document.querySelector('body').removeAttribute('style') || true"}],test:[{eval:"window.CookieConsent.declined === true"}]},{name:"cookiefirst.com",prehideSelectors:["#cookiefirst-root"],detectCmp:[{exists:"#cookiefirst-root"}],detectPopup:[{visible:"#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:"document.querySelectorAll('button[data-cookiefirst-accent-color=true][role=checkbox]:not([disabled])').forEach(i => (i.getAttribute('aria-checked') == 'true' && i.click())) || true"},{wait:1e3},{click:"button[data-cookiefirst-action=save]"}],else:[{click:"button[data-cookiefirst-action=reject]"}]}],test:[{eval:"let o = JSON.parse(decodeURIComponent(document.cookie.split(';').find(c => c.indexOf('cookiefirst') !== -1).trim()).split('=')[1]); (o.performance === false && o.functional === false && o.advertising === false) ? true : false"}]},{name:"Cookie Information Banner",prehideSelectors:["#cookie-information-template-wrapper"],detectCmp:[{exists:"#cookie-information-template-wrapper"}],detectPopup:[{visible:"#cookie-information-template-wrapper"}],optIn:[{eval:"CookieInformation.submitAllCategories() || true"}],optOut:[{hide:["#cookie-information-template-wrapper"],comment:"some templates don't hide the banner automatically"},{eval:"CookieInformation.declineAllCategories() || true"}],test:[{eval:"document.cookie.includes('CookieInformationConsent=')"}]},{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:"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:"!!document.cookie.match('dm-euconsent-v2')"}]},{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:"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:"!document.cookie.includes('sp_dsgvo_cookie_settings')"}]},{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:"document.cookie.includes('cc_functional=0') && document.cookie.includes('cc_targeting=0')"}]},{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:'document.querySelectorAll(".gdpr-overlay-body input").forEach(toggle => { toggle.checked = false; }) || true'},{eval:"document.querySelector('.gdpr-overlay-view button[data-wt-overlay-close]').click() || true"}],optIn:[{click:"button[data-gdpr-single-choice-accept]"}]},{name:"eu-cookie-compliance-banner",detectCmp:[{exists:".eu-cookie-compliance-banner-info"}],detectPopup:[{exists:".eu-cookie-compliance-popup-open"}],optIn:[{click:".agree-button"}],optOut:[{click:".decline-button,.eu-cookie-compliance-save-preferences-button",optional:!0},{hide:[".eu-cookie-compliance-banner-info","#sliding-popup"]}],test:[{eval:"document.cookie.indexOf('cookie-agreed=2') === -1"}]},{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:"!document.cookie.includes('euCookie')"}]},{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:"ezCMP.handleAcceptAllClick()",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:"!!document.cookie.match(/ezCMPCookieConsent=[^;]+\\|2=0\\|3=0\\|4=0/)"}]},{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:"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."][action$=".com/save"]'}],detectPopup:[{visible:'a[href^="https://policies.google.com/technologies/cookies"'}],optIn:[{waitForThenClick:'form[action^="https://consent."][action$=".com/save"]:has(input[name=set_eom][value=false]) button'}],optOut:[{waitForThenClick:'form[action^="https://consent."][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:"!!document.cookie.match(/SOCS=CAE/)"}]},{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:"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:"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:"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:"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:"document.querySelectorAll('.purposes-item input[type=checkbox]:not([disabled])').forEach(x => {if(x.checked) x.click()}) || true"},{click:"#iubFooterBtn"}],test:[{eval:"!!document.cookie.match(/_iub_cs-\\d+=/)"}]},{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",comment:"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:"!document.cookie.includes('cookies-state=accepted')"}]},{name:"justwatch.com",prehideSelectors:[".consent-banner"],detectCmp:[{exists:".consent-banner"}],detectPopup:[{exists:".consent-banner"}],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",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:[{click:"#lanyard_root button[class^='link']",optional:!0},{if:{exists:"#lanyard_root button[class*='confirmButton']"},then:[{waitForThenClick:"#lanyard_root button[class*='rejectButton']"},{click:"#lanyard_root button[class*='confirmButton']"}],else:[{click:"#lanyard_root div[class^='buttons'] > :nth-child(1)",optional:!0},{waitForThenClick:"#lanyard_root input:checked"},{click:"#consentsTab > div:nth-child(2) > div > div[class^='actions'] > button:nth-child(1)"}]}],test:[]},{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:"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:'document.querySelectorAll("[data-name=\\"mediavine-gdpr-cmp\\"] input[type=checkbox]").forEach(x => x.checked && x.click()) || true',optional:!0},{click:'[data-name="mediavine-gdpr-cmp"] [format="secondary"]'}]},{name:"microsoft.com",prehideSelectors:["#wcpConsentBannerCtrl"],detectCmp:[{exists:"#wcpConsentBannerCtrl"}],detectPopup:[{exists:"#wcpConsentBannerCtrl"}],optOut:[{eval:"Array.from(document.querySelectorAll('div > button')).filter(el => el.innerText.match('Reject|Ablehnen'))[0].click() || true"}],optIn:[{eval:"Array.from(document.querySelectorAll('div > button')).filter(el => el.innerText.match('Accept|Annehmen'))[0].click() || true"}],test:[{eval:"!!document.cookie.match('MSCC')"}]},{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:"document.querySelectorAll('#moove_gdpr_cookie_modal input').forEach(i => { if (!i.disabled && i.name !== 'moove_gdpr_strict_cookies') i.checked = false }) || true"},{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:"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:"osano",prehideSelectors:[".osano-cm-window"],cosmetic:!0,detectCmp:[{exists:".osano-cm-window"}],detectPopup:[{visible:".osano-cm-dialog"}],optIn:[{click:".osano-cm-accept-all",optional:!0}],optOut:[{hide:[".osano-cm-window"]}]},{name:"otto.de",prehideSelectors:[".cookieBanner--visibility"],detectCmp:[{exists:".cookieBanner--visibility"}],detectPopup:[{visible:".cookieBanner__wrapper"}],optIn:[{click:".js_cookieBannerPermissionButton"}],optOut:[{click:".js_cookieBannerProhibitionButton"}]},{name:"paypal-us",prehideSelectors:["#ccpaCookieContent_wrapper, article.ppvx_modal--overpanel"],detectCmp:[{exists:"#ccpaCookieBanner, .privacy-modal-content"}],detectPopup:[{exists:"#ccpaCookieBanner, .privacy-modal-content"}],optIn:[{click:"#acceptAllButton"}],optOut:[{if:{exists:"a#manageCookiesLink"},then:[{click:"a#manageCookiesLink"}],else:[{waitForVisible:".privacy-modal-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:"document.cookie.includes('cookie_prefs') === true"}]},{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:"!document.cookie.includes('cb-enabled=accepted')"}]},{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:"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:"document.cookie.includes('euconsent-v2') && (document.cookie.match(/.YAAAAAAAAAAA/) || document.cookie.match(/.aAAAAAAAAAAA/) || document.cookie.match(/.YAAACFgAAAAA/)) "}]},{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:['section:has(a[href^="https://www.reddit.com/policies/cookies"])'],detectCmp:[{exists:'section:has(a[href^="https://www.reddit.com/policies/cookies"])'}],detectPopup:[{visible:'section:has(a[href^="https://www.reddit.com/policies/cookies"])'}],optIn:[{waitForThenClick:"section:has(a[href^=\"https://www.reddit.com/policies/cookies\"]) section[class^='_'] > section:first-child form button"}],optOut:[{waitForThenClick:"section:has(a[href^=\"https://www.reddit.com/policies/cookies\"]) section[class^='_'] > section:last-child form button"}],test:[{eval:"document.cookie.includes('eu_cookie={%22opted%22:true%2C%22nonessential%22:false}')"}]},{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:"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:"!!window.localStorage.getItem('euconsent-v2')"}]},{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:"document.cookie.includes('euconsent-v2')"}]},{name:"snigel",detectCmp:[{exists:".snigel-cmp-framework"}],detectPopup:[{visible:".snigel-cmp-framework"}],optOut:[{click:"#sn-b-custom"},{click:"#sn-b-save"}],test:[{eval:"!!document.cookie.match('snconsent')"}],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:"JSON.parse(decodeURIComponent(document.cookie.split(';').find(s => s.trim().startsWith('cookieSettings')).split('=')[1])).preference_state === 2"}]},{name:"takealot.com",cosmetic:!0,prehideSelectors:['div[class^="cookies-banner-module_cookie-banner_"]'],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_cookie-banner_"]']}]},{name:"tarteaucitron.js",prehideSelectors:["#tarteaucitronRoot"],detectCmp:[{exists:"#tarteaucitronRoot"}],detectPopup:[{visible:"#tarteaucitronRoot #tarteaucitronAlertSmall,#tarteaucitronRoot #tarteaucitronAlertBig",check:"any"}],optIn:[{eval:"tarteaucitron.userInterface.respondAll(true) || true"}],optOut:[{eval:"tarteaucitron.userInterface.respondAll(false) || true"}],test:[{eval:"document.cookie.match(/tarteaucitron=[^;]*/)[0].includes('false')",comment:"sometimes there are required categories, so we check that at least something is false"}]},{name:"Tealium",prehideSelectors:["#__tealiumGDPRecModal,#__tealiumGDPRcpPrefs,#consent-layer"],detectCmp:[{visible:"#__tealiumGDPRecModal"},{eval:"typeof window.utag !== 'undefined' && typeof utag.gdpr === 'object'"}],detectPopup:[{visible:"#__tealiumGDPRecModal"}],optOut:[{waitForThenClick:"#cm-acceptNone,.js-accept-essential-cookies",timeout:1e3},{eval:"utag.gdpr.setConsentValue(false) || true"}],optIn:[{hide:["#__tealiumGDPRecModal"]},{eval:"utag.gdpr.setConsentValue(true) || true"}],test:[{eval:"utag.gdpr.getConsentState() !== 1"}]},{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:"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:"window.results.results[0] === 'banner_hidden'"}]},{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:"window.results.results[0] === 'button_clicked'"}]},{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:"cmpUi.allowAll() || true"}],optOut:[{eval:"cmpUi.showPurposes() || cmpUi.rejectAll() || true"}]},{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:"document.cookie.includes('_duet_gdpr_acknowledged=1')"}]},{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:"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:"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:"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.p-button"},{waitForThenClick:'xpath///*[@id="cookie-policy-content"]/p[4]/button[2]'}]},{waitForThenClick:".p-switch__input:checked",optional:!0,all:!0},{any:[{waitForThenClick:"div > button"},{waitForThenClick:'xpath///*[@id="modal"]/div/button'}]}],test:[{eval:"document.cookie === '_cookies_accepted=essential'"}]},{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:"!document.cookie.includes('catAccCookies')"}]},{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:"typeof UC_UI === 'object'"},{exists:["#usercentrics-root","[data-testid=uc-container]"]}],optIn:[{eval:"!!UC_UI.acceptAllConsents()"},{eval:"!!UC_UI.closeCMP()"},{eval:"UC_UI.areAllConsentsAccepted() === true"}],optOut:[{eval:"!!UC_UI.closeCMP()"},{eval:"!!UC_UI.denyAllConsents()"}],test:[{eval:"UC_UI.areAllConsentsAccepted() === false"}]},{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:"JSON.parse(localStorage.getItem('usercentrics')).consents.every(c => c.isEssential || !c.consentStatus)"}]},{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:"Array.from(document.querySelectorAll('label[id$=cookies-deny-label]')).forEach(e => e.click()) || true"},{click:"button[data-test=submit]"}],test:[{eval:"document.cookie.includes('wtr_cookies_advertising=0') && document.cookie.includes('wtr_cookies_analytics=0')"}]},{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:"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",comment:"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:"document.cookie.includes('wpl_viewed_cookie=no')"}]},{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:"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:"document.cookie.includes('userConsent=%7B%22marketing%22%3Afalse')"}]},{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:"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:"!!document.cookie.match(/SOCS=CAE/)"}]},{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:"!!document.cookie.match(/SOCS=CAE/)"}]}],F={"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"}]}},I={autoconsent:O,consentomatic:F},T=Object.freeze({__proto__:null,autoconsent:O,consentomatic:F,default:I});const E=new class{constructor(o,c=null,i=null){if(this.id=e(),this.rules=[],this.foundCmp=null,this.state={lifecycle:"loading",prehideOn:!1,findCmpAttempts:0,detectedCmps:[],detectedPopups:[],selfTest:null},t.sendContentMessage=o,this.sendContentMessage=o,this.rules=[...C],this.updateState({lifecycle:"loading"}),c)this.initialize(c,i);else{i&&this.parseRules(i);o({type:"init",url:window.location.href}),this.updateState({lifecycle:"waitingForInitResponse"})}}initialize(e,t){if(this.config=e,e.enabled){if(t&&this.parseRules(t),this.rules=function(e,t){return e.filter((e=>(!t.disabledCmps||!t.disabledCmps.includes(e.name))&&(t.enableCosmeticRules||!e.isCosmetic)))}(this.rules,e),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"})}}parseRules(e){Object.keys(e.consentomatic).forEach((t=>{this.addConsentomaticCMP(t,e.consentomatic[t])})),e.autoconsent.forEach((e=>{this.addCMP(e)}))}addCMP(e){this.rules.push(function(e){return new _(e)}(e))}addConsentomaticCMP(e,t){this.rules.push(new class{constructor(e,t){this.name=e,this.config=t,this.methods=new Map,this.runContext=b,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=>S(e.presentMatcher))).some((e=>!!e))}async detectPopup(){return this.config.detectors.map((e=>S(e.showingMatcher))).some((e=>!!e))}async executeAction(e,t){return!this.methods.has(e)||A(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}}(`com_${e}`,t))}start(){window.requestIdleCallback?window.requestIdleCallback((()=>this._start()),{timeout:500}):this._start()}async _start(){this.updateState({lifecycle:"started"});const e=await this.findCmp(this.config.detectRetries);if(this.updateState({detectedCmps:e.map((e=>e.name))}),0===e.length)return this.config.enablePrehide&&this.undoPrehide(),this.updateState({lifecycle:"nothingDetected"}),!1;this.updateState({lifecycle:"cmpDetected"});let t=await this.detectPopups(e.filter((e=>!e.isCosmetic)));if(0===t.length&&(t=await this.detectPopups(e.filter((e=>e.isCosmetic)))),0===t.length)return this.config.enablePrehide&&this.undoPrehide(),!1;if(this.updateState({lifecycle:"openPopupDetected"}),t.length>1){const e={msg:"Found multiple CMPs, check the detection rules.",cmps:t.map((e=>e.name))};this.sendContentMessage({type:"autoconsentError",details:e})}return this.foundCmp=t[0],"optOut"===this.config.autoAction?await this.doOptOut():"optIn"!==this.config.autoAction||await this.doOptIn()}async findCmp(e){this.updateState({findCmpAttempts:this.state.findCmpAttempts+1});const t=[];for(const e of this.rules)try{if(!e.checkRunContext())continue;await e.detectCmp()&&(this.sendContentMessage({type:"cmpDetected",url:location.href,cmp:e.name}),t.push(e))}catch(e){}return 0===t.length&&e>0?(await m(500),this.findCmp(e-1)):t}async detectPopups(e){const t=[],o=e.map((e=>this.waitForPopup(e).then((o=>{o&&(this.updateState({detectedPopups:this.state.detectedPopups.concat([e.name])}),this.sendContentMessage({type:"popupFound",cmp:e.name,url:location.href}),t.push(e))})).catch((()=>null))));return await Promise.all(o),t}async doOptOut(){let e;return this.updateState({lifecycle:"runningOptOut"}),e=!!this.foundCmp&&await this.foundCmp.optOut(),this.config.enablePrehide&&this.undoPrehide(),this.sendContentMessage({type:"optOutResult",cmp:this.foundCmp?this.foundCmp.name:"none",result:e,scheduleSelfTest:this.foundCmp&&this.foundCmp.hasSelfTest,url:location.href}),e&&!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:e?"optOutSucceeded":"optOutFailed"}),e}async doOptIn(){let e;return this.updateState({lifecycle:"runningOptIn"}),e=!!this.foundCmp&&await this.foundCmp.optIn(),this.config.enablePrehide&&this.undoPrehide(),this.sendContentMessage({type:"optInResult",cmp:this.foundCmp?this.foundCmp.name:"none",result:e,scheduleSelfTest:!1,url:location.href}),e&&!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:e?"optInSucceeded":"optInFailed"}),e}async doSelfTest(){let e;return e=!!this.foundCmp&&await this.foundCmp.test(),this.sendContentMessage({type:"selfTestResult",cmp:this.foundCmp?this.foundCmp.name:"none",result:e,url:location.href}),this.updateState({selfTest:e}),e}async waitForPopup(e,t=5,o=500){const c=await e.detectPopup();return!c&&t>0?(await m(o),this.waitForPopup(e,t-1,o)):c}prehideElements(){const e=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)&&this.undoPrehide()}),this.config.prehideTimeout||2e3),function(e){return i(c("autoconsent-prehide"),e,"opacity")}(e)}undoPrehide(){return this.updateState({prehideOn:!1}),function(){const e=c("autoconsent-prehide");return e&&e.remove(),!!e}()}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){switch(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,o){const c=t.pending.get(e);c?(t.pending.delete(e),c.timer&&window.clearTimeout(c.timer),c.resolve(o)):console.warn("no eval #",e)}(e.id,e.result)}}}((e=>{window.webkit.messageHandlers[e.type]&&window.webkit.messageHandlers[e.type].postMessage(e).then((e=>{E.receiveMessageCallback(e)}))}),null,T);window.autoconsentMessageCallback=e=>{E.receiveMessageCallback(e)}}(); +!function(){"use strict";function e(){return crypto&&void 0!==crypto.randomUUID?crypto.randomUUID():Math.random().toString()}var t={pending:new Map,sendContentMessage:null};function o(o){const c=e();t.sendContentMessage({type:"eval",id:c,code:o});const i=new 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)}}(c);return t.pending.set(i.id,i),i.promise}function c(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 i(e,t,o="display"){const c="opacity"===o?"opacity: 0":"display: none",i=`${t.join(",")} { ${c} !important; z-index: -1 !important; pointer-events: none !important; } `;return e instanceof HTMLStyleElement&&(e.innerText+=i,t.length>0)}async function n(e,t,o){const c=await e();return!c&&t>0?new Promise((c=>{setTimeout((async()=>{c(n(e,t-1,o))}),o)})):Promise.resolve(c)}function a(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 s(e){return o(e).catch((e=>!1))}function r(e,t=!1){const o=k(e);return o.length>0&&(t?o.forEach((e=>e.click())):o[0].click()),o.length>0}function p(e){return k(e).length>0}function l(e,t){const o=k(e),c=new Array(o.length);return o.forEach(((e,t)=>{c[t]=a(e)})),"none"===t?c.every((e=>!e)):0!==c.length&&("any"===t?c.some((e=>e)):c.every((e=>e)))}function d(e,t=1e4){return n((()=>k(e).length>0),Math.ceil(t/200),200)}async function u(e,t=1e4,o=!1){return await d(e,t),r(e,o)}function m(e){return new Promise((t=>{setTimeout((()=>{t(!0)}),e)}))}function h(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))}function k(e){return"string"==typeof e?h(e):function(e){let t,o=document;for(const c of e){if(t=h(c,o),0===t.length)return[];o=t[0]}return t}(e)}var b={main:!0,frame:!1,urlPattern:""},g=class{constructor(e){this.runContext=b,this.name=e}get hasSelfTest(){throw new Error("Not Implemented")}get isIntermediate(){throw new Error("Not Implemented")}get isCosmetic(){throw new Error("Not Implemented")}checkRunContext(){const e={...b,...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)}};async function y(e){const t=[];if(e.exists&&t.push(p(e.exists)),e.visible&&t.push(l(e.visible,e.check)),e.eval){const o=s(e.eval);t.push(o)}var o,a;if(e.waitFor&&t.push(d(e.waitFor,e.timeout)),e.waitForVisible&&t.push(function(e,t=1e4,o="any"){return n((()=>l(e,o)),Math.ceil(t/200),200)}(e.waitForVisible,e.timeout,e.check)),e.click&&t.push(r(e.click,e.all)),e.waitForThenClick&&t.push(u(e.waitForThenClick,e.timeout,e.all)),e.wait&&t.push(m(e.wait)),e.hide&&t.push((o=e.hide,a=e.method,i(c(),o,a))),e.if){if(!e.if.exists&&!e.if.visible)return console.error("invalid conditional rule",e.if),!1;await y(e.if)?t.push(w(e.then)):e.else&&t.push(w(e.else))}if(e.any){for(const t of e.any)if(await y(t))return!0;return!1}if(0===t.length)return!1;return(await Promise.all(t)).reduce(((e,t)=>e&&t),!0)}async function w(e){for(const t of e){if(!await y(t)&&!t.optional)return!1}return!0}var _=class extends g{constructor(e){super(e.name),this.config=e,this.runContext=e.runContext||b}get hasSelfTest(){return!!this.config.test}get isIntermediate(){return!!this.config.intermediate}get isCosmetic(){return!!this.config.cosmetic}get prehideSelectors(){return this.config.prehideSelectors}async detectCmp(){return!!this.config.detectCmp&&async function(e){const t=e.map((e=>y(e)));return(await Promise.all(t)).every((e=>!!e))}(this.config.detectCmp)}async detectPopup(){return!!this.config.detectPopup&&w(this.config.detectPopup)}async optOut(){return!!this.config.optOut&&w(this.config.optOut)}async optIn(){return!!this.config.optIn&&w(this.config.optIn)}async openCmp(){return!!this.config.openCmp&&w(this.config.openCmp)}async test(){return this.hasSelfTest?w(this.config.test):super.test()}},v="#truste-show-consent",f="#truste-consent-track";var C=[new class extends g{constructor(){super("TrustArc-top"),this.prehideSelectors=[".trustarc-banner-container",`.truste_popframe,.truste_overlay,.truste_box_overlay,${f}`],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=p(`${v},${f}`);return e&&(this._shortcutButton=document.querySelector("#truste-consent-required")),e}async detectPopup(){return l(`#truste-consent-content,#trustarc-banner-overlay,${f}`,"all")}openFrame(){r(v)}async optOut(){return this._shortcutButton?(this._shortcutButton.click(),!0):(i(c(),[".truste_popframe",".truste_overlay",".truste_box_overlay",f]),r(v),setTimeout((()=>{c().remove()}),1e4),!0)}async optIn(){return this._optInDone=!0,r("#truste-consent-button")}async openCmp(){return!0}async test(){return await s("window && window.truste && window.truste.eu.bindMap.prefCookie === '0'")}},new class extends g{constructor(){super("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 l("#defaultpreferencemanager","any")&&l(".mainContent","any")}async navigateToSettings(){return await n((async()=>p(".shp")||l(".advance","any")||p(".switch span:first-child")),10,500),p(".shp")&&r(".shp"),await d(".prefPanel",5e3),l(".advance","any")&&r(".advance"),await n((()=>l(".switch span:first-child","any")),5,1e3)}async optOut(){return await n((()=>"complete"===document.readyState),20,100),await d(".mainContent[aria-hidden=false]",5e3),!!r(".rejectAll")||(p(".prefPanel")&&await d('.prefPanel[style="visibility: visible;"]',3e3),r("#catDetails0")?(r(".submit"),!0):(r(".required")||(await this.navigateToSettings(),r(".switch span:nth-child(1):not(.active)",!0),r(".submit"),d("#gwt-debug-close_id",3e5).then((()=>{r("#gwt-debug-close_id")}))),!0))}async optIn(){return r(".call")||(await this.navigateToSettings(),r(".switch span:nth-child(2)",!0),r(".submit"),d("#gwt-debug-close_id",3e5).then((()=>{r("#gwt-debug-close_id")}))),!0}},new class extends g{constructor(){super("Cybotcookiebot"),this.prehideSelectors=["#CybotCookiebotDialog,#dtcookie-container,#cookiebanner,#cb-cookieoverlay"]}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return p("#CybotCookiebotDialogBodyLevelButtonPreferences")}async detectPopup(){return p("#CybotCookiebotDialog,#dtcookie-container,#cookiebanner,#cb-cookiebanner")}async optOut(){return r(".cookie-alert-extended-detail-link")?(await d(".cookie-alert-configuration",2e3),r(".cookie-alert-configuration-input:checked",!0),r(".cookie-alert-extended-button-secondary"),!0):p("#dtcookie-container")?r(".h-dtcookie-decline"):(r(".cookiebot__button--settings")||r("#CybotCookiebotDialogBodyButtonDecline")||(r(".cookiebanner__link--details"),r('.CybotCookiebotDialogBodyLevelButton:checked:enabled,input[id*="CybotCookiebotDialogBodyLevelButton"]:checked:enabled',!0),r("#CybotCookiebotDialogBodyButtonDecline"),r("input[id^=CybotCookiebotDialogBodyLevelButton]:checked",!0),p("#CybotCookiebotDialogBodyButtonAcceptSelected")?r("#CybotCookiebotDialogBodyButtonAcceptSelected"):r("#CybotCookiebotDialogBodyLevelButtonAccept,#CybotCookiebotDialogBodyButtonAccept,#CybotCookiebotDialogBodyLevelButtonLevelOptinAllowallSelection",!0),await s("window.CookieConsent.hasResponse !== true")&&(await s("window.Cookiebot.dialog.submitConsent()"),await m(500)),p("#cb-confirmedSettings")&&await s("endCookieProcess()")),!0)}async optIn(){return p("#dtcookie-container")?r(".h-dtcookie-accept"):(r(".CybotCookiebotDialogBodyLevelButton:not(:checked):enabled",!0),r("#CybotCookiebotDialogBodyLevelButtonAccept"),r("#CybotCookiebotDialogBodyButtonAccept"),!0)}async test(){return s("window.CookieConsent.declined === true")}},new class extends g{constructor(){super("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)&&(e.searchParams.has("message_id")||e.searchParams.has("requestUUID")||e.searchParams.has("consentUUID"))}async detectPopup(){return!!this.ccpaNotice||(this.ccpaPopup?await d(".priv-save-btn",2e3):(await d(".sp_choice_type_11,.sp_choice_type_12,.sp_choice_type_13,.sp_choice_type_ACCEPT_ALL",2e3),!p(".sp_choice_type_9")))}async optIn(){return await d(".sp_choice_type_11,.sp_choice_type_ACCEPT_ALL",2e3),!!r(".sp_choice_type_11")||!!r(".sp_choice_type_ACCEPT_ALL")}isManagerOpen(){return"/privacy-manager/index.html"===location.pathname}async optOut(){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 r(".priv-save-btn")}if(!this.isManagerOpen()){if(!await d(".sp_choice_type_12,.sp_choice_type_13"))return!1;if(!p(".sp_choice_type_12"))return r(".sp_choice_type_13");r(".sp_choice_type_12"),await n((()=>this.isManagerOpen()),200,100)}await d(".type-modal",2e4);try{const e=".sp_choice_type_REJECT_ALL",t=".reject-toggle",o=await Promise.race([d(e,2e3).then((e=>e?0:-1)),d(t,2e3).then((e=>e?1:-1)),d(".pm-features",2e3).then((e=>e?2:-1))]);if(0===o)return await m(1e3),r(e);1===o?r(t):2===o&&(await d(".pm-features",1e4),r(".checked > span",!0),r(".chevron"))}catch(e){}return r(".sp_choice_type_SAVE_AND_EXIT")}},new class extends g{constructor(){super("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 s('window.__cmp && typeof __cmp("getCMPData") === "object"'),!!this.apiAvailable||p("#cmpbox")}async detectPopup(){return this.apiAvailable?(await m(500),await s("!__cmp('consentStatus').userChoiceExists")):l("#cmpbox .cmpmore","any")}async optOut(){return await m(500),this.apiAvailable?await s("__cmp('setConsent', 0)"):!!r(".cmpboxbtnno")||(p(".cmpwelcomeprpsbtn")?(r(".cmpwelcomeprpsbtn > a[aria-checked=true]",!0),r(".cmpboxbtnsave"),!0):(r(".cmpboxbtncustom"),await d(".cmptblbox",2e3),r(".cmptdchoice > a[aria-checked=true]",!0),r(".cmpboxbtnyescustomchoices"),!0))}async optIn(){return this.apiAvailable?await s("__cmp('setConsent', 1)"):r(".cmpboxbtnyes")}async test(){if(this.apiAvailable)return await s("__cmp('consentStatus').userChoiceExists")}},new class extends g{constructor(){super("Evidon")}get hasSelfTest(){return!1}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return p("#_evidon_banner")}async detectPopup(){return l("#_evidon_banner","any")}async optOut(){return r("#_evidon-decline-button")||(i(c(),["#evidon-prefdiag-overlay","#evidon-prefdiag-background"]),r("#_evidon-option-button"),await d("#evidon-prefdiag-overlay",5e3),r("#evidon-prefdiag-decline")),!0}async optIn(){return r("#_evidon-accept-button")}},new class extends g{constructor(){super("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 p("#onetrust-banner-sdk")}async detectPopup(){return l("#onetrust-banner-sdk","all")}async optOut(){return p("#onetrust-pc-btn-handler")?r("#onetrust-pc-btn-handler"):r(".ot-sdk-show-settings,button.js-cookie-settings"),await d("#onetrust-consent-sdk",2e3),await m(1e3),r("#onetrust-consent-sdk input.category-switch-handler:checked,.js-editor-toggle-state:checked",!0),await m(1e3),await d(".save-preference-btn-handler,.js-consent-save",2e3),r(".save-preference-btn-handler,.js-consent-save"),await n((()=>l("#onetrust-banner-sdk","none")),10,500),!0}async optIn(){return r("#onetrust-accept-btn-handler,.js-accept-cookies")}async test(){return await s("window.OnetrustActiveGroups.split(',').filter(s => s.length > 0).length <= 1")}},new class extends g{constructor(){super("Klaro"),this.prehideSelectors=[".klaro"],this.settingsOpen=!1}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return p(".klaro > .cookie-modal")?(this.settingsOpen=!0,!0):p(".klaro > .cookie-notice")}async detectPopup(){return l(".klaro > .cookie-notice,.klaro > .cookie-modal","any")}async optOut(){return!!r(".klaro .cn-decline")||(this.settingsOpen||(r(".klaro .cn-learn-more"),await d(".klaro > .cookie-modal",2e3),this.settingsOpen=!0),!!r(".klaro .cn-decline")||(r(".cm-purpose:not(.cm-toggle-all) > input:not(.half-checked)",!0),r(".cm-btn-accept")))}async optIn(){return!!r(".klaro .cm-btn-accept-all")||(this.settingsOpen?(r(".cm-purpose:not(.cm-toggle-all) > input.half-checked",!0),r(".cm-btn-accept")):r(".klaro .cookie-notice .cm-btn-success"))}async test(){return await s("klaro.getManager().config.services.every(c => c.required || !klaro.getManager().consents[c.name])")}},new class extends g{constructor(){super("Uniconsent")}get prehideSelectors(){return[".unic",".modal:has(.unic)"]}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return p(".unic .unic-box,.unic .unic-bar")}async detectPopup(){return l(".unic .unic-box,.unic .unic-bar","any")}async optOut(){if(await d(".unic button",1e3),document.querySelectorAll(".unic button").forEach((e=>{const t=e.textContent;(t.includes("Manage Options")||t.includes("Optionen verwalten"))&&e.click()})),await d(".unic input[type=checkbox]",1e3)){await d(".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 m(500),!0}}return!1}async optIn(){return u(".unic #unic-agree")}async test(){await m(1e3);return!p(".unic .unic-box,.unic .unic-bar")}},new class extends g{constructor(){super("Conversant"),this.prehideSelectors=[".cmp-root"]}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return p(".cmp-root .cmp-receptacle")}async detectPopup(){return l(".cmp-root .cmp-receptacle","any")}async optOut(){if(!await u(".cmp-main-button:not(.cmp-main-button--primary)"))return!1;if(!await d(".cmp-view-tab-tabs"))return!1;await u(".cmp-view-tab-tabs > :first-child"),await u(".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 n((()=>!!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 r(".cmp-main-button:not(.cmp-main-button--primary)"),!0}async optIn(){return u(".cmp-main-button.cmp-main-button--primary")}async test(){return document.cookie.includes("cmp-data=0")}},new class extends g{constructor(){super("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 p("tiktok-cookie-banner")}async detectPopup(){return a(this.getShadowRoot().querySelector(".tiktok-cookie-banner"))}async optOut(){const e=this.getShadowRoot().querySelector(".button-wrapper button:first-child");return!!e&&(e.click(),!0)}async optIn(){const e=this.getShadowRoot().querySelector(".button-wrapper button:last-child");return!!e&&(e.click(),!0)}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))}},new class extends g{constructor(){super("airbnb"),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 p("div[data-testid=main-cookies-banner-container]")}async detectPopup(){return l("div[data-testid=main-cookies-banner-container","any")}async optOut(){let e;for(await u("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 u("button[data-testid=save-btn]")}async optIn(){return u("div[data-testid=main-cookies-banner-container] button._148dgdpk")}async test(){return await n((()=>!!document.cookie.match("OptanonAlertBoxClosed")),20,200)}}],x=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])}};x.base=null;var S=x;function P(e){const t=S.find(e);return"css"===e.type?!!t.target:"checkbox"===e.type?!!t.target&&t.target.checked:void 0}async function A(e,t){switch(e.type){case"click":return async function(e){const t=S.find(e);null!=t.target&&t.target.click();return O(0)}(e);case"list":return async function(e,t){for(const o of e.actions)await A(o,t)}(e,t);case"consent":return async function(e,t){for(const o of e.consents){const e=-1!==t.indexOf(o.type);if(o.matcher&&o.toggleAction){P(o.matcher)!==e&&await A(o.toggleAction)}else e?await A(o.trueAction):await A(o.falseAction)}}(e,t);case"ifcss":return async function(e,t){const o=S.find(e);o.target?e.falseAction&&await A(e.falseAction,t):e.trueAction&&await A(e.trueAction,t)}(e,t);case"waitcss":return async function(e){await new Promise((t=>{let o=e.retries||10;const c=e.waitTime||250,i=()=>{const n=S.find(e);(e.negated&&n.target||!e.negated&&!n.target)&&o>0?(o-=1,setTimeout(i,c)):t()};i()}))}(e);case"foreach":return async function(e,t){const o=S.find(e,!0),c=S.base;for(const c of o)c.target&&(S.setBase(c.target),await A(e.action,t));S.setBase(c)}(e,t);case"hide":return async function(e){const t=S.find(e);t.target&&t.target.classList.add("Autoconsent-Hidden")}(e);case"slide":return async function(e){const t=S.find(e),o=S.find(e.dragTarget);if(t.target){const e=t.target.getBoundingClientRect(),c=o.target.getBoundingClientRect();let i=c.top-e.top,n=c.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,p=e.top+e.height/2,l=document.createEvent("MouseEvents");l.initMouseEvent("mousedown",!0,!0,window,0,a,s,r,p,!1,!1,!1,!1,0,t.target);const d=document.createEvent("MouseEvents");d.initMouseEvent("mousemove",!0,!0,window,0,a+n,s+i,r+n,p+i,!1,!1,!1,!1,0,t.target);const u=document.createEvent("MouseEvents");u.initMouseEvent("mouseup",!0,!0,window,0,a+n,s+i,r+n,p+i,!1,!1,!1,!1,0,t.target),t.target.dispatchEvent(l),await this.waitTimeout(10),t.target.dispatchEvent(d),await this.waitTimeout(10),t.target.dispatchEvent(u)}}(e);case"close":return async function(){window.close()}();case"wait":return async function(e){await O(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}}function O(e){return new Promise((t=>{setTimeout((()=>{t()}),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:"document.cookie.includes('CC_ADVERTISING=NO') && document.cookie.includes('CC_ANALYTICS=NO')"}]},{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:"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:"!document.cookie.includes('__adroll_fpc')"}]},{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:"document.cookie.includes('serif_manage_cookies_viewed') && !document.cookie.includes('serif_allow_analytics')"}]},{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:"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:"arzt-auskunft.de",prehideSelectors:["#cookiescript_injected"],detectCmp:[{exists:"#cookiescript_injected"}],detectPopup:[{visible:"#cookiescript_injected"}],optOut:[{click:"#cookiescript_reject"}],optIn:[{click:"#cookiescript_accept"}]},{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:"document.cookie.includes('axeptio_authorized_vendors=%2C%2C')"}]},{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:"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:"document.cookie.includes('AL=0') && document.cookie.includes('AD=0') && document.cookie.includes('SM=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:"!JSON.parse(decodeURIComponent(document.cookie.split(';').find(c => c.indexOf('borlabs-cookie') !== -1).split('=', 2)[1])).consents.statistics"}]},{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:"document.cookie.match('cookie-allow-tracking=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:"!document.cookie.includes('gtm_fpc_engagement_event')"}]},{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"]}]},{comment:"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:"document.cookie.includes('__lxG__consent__v2_daisybit=')",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:"document.cookie.includes('ctc_rejected=1')"}]},{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:"JSON.parse(decodeURIComponent(document.cookie.match(/cm_(eu|default)_preferences=([0-9a-zA-Z\\{\\}\\[\\]%:]*);?/)[2])).consent.length <= 1"}]},{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:"document.cookie.includes('cmplz_banner-status=dismissed')"}]},{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:[{click:".cc-accept-all",optional:!0},{click:".cc-allow",optional:!0},{click:".cc-dismiss",optional:!0}],optOut:[{click:".cc-dismiss"}],test:[{eval:"!!document.cookie.match(/cmplz_[^=]+=deny/)"}]},{name:"Complianz notice",prehideSelectors:['.cc-type-info[aria-describedby="cookieconsent:desc"]'],cosmetic:!0,detectCmp:[{exists:'.cc-type-info[aria-describedby="cookieconsent:desc"]'}],detectPopup:[{visible:'.cc-type-info[aria-describedby="cookieconsent:desc"]'}],optIn:[{click:".cc-accept-all",optional:!0},{click:".cc-allow",optional:!0},{click:".cc-dismiss",optional:!0}],optOut:[{hide:['[aria-describedby="cookieconsent:desc"]']}]},{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:[{click:".cc-accept-all",optional:!0},{click:".cc-allow",optional:!0},{click:".cc-dismiss",optional:!0}],optOut:[{click:".cc-settings"},{waitForVisible:'[aria-label="cookies preferences popup"]'},{click:'[aria-label="cookies preferences popup"] input[type=checkbox]:not([disabled]):checked',all:!0,optional:!0},{click:'[aria-label="cookies preferences popup"] [aria-label="Accept Selected"], [aria-label="cookies preferences popup"] [aria-label="Save my choice"], .cc-btn-accept-selected, .cc-deny',optional:!0}],test:[{eval:"!!document.cookie.match(/cookieconsent_preferences_disabled=[^;]+/)"}]},{name:"cookie-law-info",prehideSelectors:["#cookie-law-info-bar"],detectCmp:[{exists:"#cookie-law-info-bar"}],detectPopup:[{visible:"#cookie-law-info-bar"}],optIn:[{click:'[data-cli_action="accept_all"]'}],optOut:[{hide:["#cookie-law-info-bar"]},{eval:"CLI.disableAllCookies() || CLI.reject_close() || true"}],test:[{eval:"document.cookie.indexOf('cookielawinfo-checkbox-non-necessary=yes') === -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:"JSON.parse(document.cookie.split(';').find(c => c.trim().startsWith('CookieLevel')).split('=')[1]).social === false"}]},{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:"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:"document.querySelector('body').removeAttribute('style') || true"}],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:"document.querySelector('body').removeAttribute('style') || true"}],test:[{eval:"window.CookieConsent.declined === true"}]},{name:"cookiefirst.com",prehideSelectors:["#cookiefirst-root"],detectCmp:[{exists:"#cookiefirst-root"}],detectPopup:[{visible:"#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:"document.querySelectorAll('button[data-cookiefirst-accent-color=true][role=checkbox]:not([disabled])').forEach(i => (i.getAttribute('aria-checked') == 'true' && i.click())) || true"},{wait:1e3},{click:"button[data-cookiefirst-action=save]"}],else:[{click:"button[data-cookiefirst-action=reject]"}]}],test:[{eval:"let o = JSON.parse(decodeURIComponent(document.cookie.split(';').find(c => c.indexOf('cookiefirst') !== -1).trim()).split('=')[1]); (o.performance === false && o.functional === false && o.advertising === false) ? true : false"}]},{name:"Cookie Information Banner",prehideSelectors:["#cookie-information-template-wrapper"],detectCmp:[{exists:"#cookie-information-template-wrapper"}],detectPopup:[{visible:"#cookie-information-template-wrapper"}],optIn:[{eval:"CookieInformation.submitAllCategories() || true"}],optOut:[{hide:["#cookie-information-template-wrapper"],comment:"some templates don't hide the banner automatically"},{eval:"CookieInformation.declineAllCategories() || true"}],test:[{eval:"document.cookie.includes('CookieInformationConsent=')"}]},{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:"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:"!!document.cookie.match('dm-euconsent-v2')"}]},{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:"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:"!document.cookie.includes('sp_dsgvo_cookie_settings')"}]},{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:"document.cookie.includes('cc_functional=0') && document.cookie.includes('cc_targeting=0')"}]},{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:'document.querySelectorAll(".gdpr-overlay-body input").forEach(toggle => { toggle.checked = false; }) || true'},{eval:"document.querySelector('.gdpr-overlay-view button[data-wt-overlay-close]').click() || true"}],optIn:[{click:"button[data-gdpr-single-choice-accept]"}]},{name:"eu-cookie-compliance-banner",detectCmp:[{exists:".eu-cookie-compliance-banner-info"}],detectPopup:[{exists:".eu-cookie-compliance-popup-open"}],optIn:[{click:".agree-button"}],optOut:[{click:".decline-button,.eu-cookie-compliance-save-preferences-button",optional:!0},{hide:[".eu-cookie-compliance-banner-info","#sliding-popup"]}],test:[{eval:"document.cookie.indexOf('cookie-agreed=2') === -1"}]},{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:"!document.cookie.includes('euCookie')"}]},{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:"ezCMP.handleAcceptAllClick()",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:"!!document.cookie.match(/ezCMPCookieConsent=[^;]+\\|2=0\\|3=0\\|4=0/)"}]},{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:"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."][action$=".com/save"]'}],detectPopup:[{visible:'a[href^="https://policies.google.com/technologies/cookies"'}],optIn:[{waitForThenClick:'form[action^="https://consent."][action$=".com/save"]:has(input[name=set_eom][value=false]) button'}],optOut:[{waitForThenClick:'form[action^="https://consent."][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:"!!document.cookie.match(/SOCS=CAE/)"}]},{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:"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:"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:"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:"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:"document.querySelectorAll('.purposes-item input[type=checkbox]:not([disabled])').forEach(x => {if(x.checked) x.click()}) || true"},{click:"#iubFooterBtn"}],test:[{eval:"!!document.cookie.match(/_iub_cs-\\d+=/)"}]},{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",comment:"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:"!document.cookie.includes('cookies-state=accepted')"}]},{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",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:[{click:"#lanyard_root button[class^='link']",optional:!0},{if:{exists:"#lanyard_root button[class*='confirmButton']"},then:[{waitForThenClick:"#lanyard_root button[class*='rejectButton']"},{click:"#lanyard_root button[class*='confirmButton']"}],else:[{click:"#lanyard_root div[class^='buttons'] > :nth-child(1)",optional:!0},{waitForThenClick:"#lanyard_root input:checked"},{click:"#consentsTab > div:nth-child(2) > div > div[class^='actions'] > button:nth-child(1)"}]}],test:[]},{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:"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:"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:'document.querySelectorAll("[data-name=\\"mediavine-gdpr-cmp\\"] input[type=checkbox]").forEach(x => x.checked && x.click()) || true',optional:!0},{click:'[data-name="mediavine-gdpr-cmp"] [format="secondary"]'}]},{name:"microsoft.com",prehideSelectors:["#wcpConsentBannerCtrl"],detectCmp:[{exists:"#wcpConsentBannerCtrl"}],detectPopup:[{exists:"#wcpConsentBannerCtrl"}],optOut:[{eval:"Array.from(document.querySelectorAll('div > button')).filter(el => el.innerText.match('Reject|Ablehnen'))[0].click() || true"}],optIn:[{eval:"Array.from(document.querySelectorAll('div > button')).filter(el => el.innerText.match('Accept|Annehmen'))[0].click() || true"}],test:[{eval:"!!document.cookie.match('MSCC')"}]},{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:"document.querySelectorAll('#moove_gdpr_cookie_modal input').forEach(i => { if (!i.disabled && i.name !== 'moove_gdpr_strict_cookies') i.checked = false }) || true"},{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:"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:"osano",prehideSelectors:[".osano-cm-window"],cosmetic:!0,detectCmp:[{exists:".osano-cm-window"}],detectPopup:[{visible:".osano-cm-dialog"}],optIn:[{click:".osano-cm-accept-all",optional:!0}],optOut:[{hide:[".osano-cm-window"]}]},{name:"otto.de",prehideSelectors:[".cookieBanner--visibility"],detectCmp:[{exists:".cookieBanner--visibility"}],detectPopup:[{visible:".cookieBanner__wrapper"}],optIn:[{click:".js_cookieBannerPermissionButton"}],optOut:[{click:".js_cookieBannerProhibitionButton"}]},{name:"paypal-us",prehideSelectors:["#ccpaCookieContent_wrapper, article.ppvx_modal--overpanel"],detectCmp:[{exists:"#ccpaCookieBanner, .privacy-modal-content"}],detectPopup:[{exists:"#ccpaCookieBanner, .privacy-modal-content"}],optIn:[{click:"#acceptAllButton"}],optOut:[{if:{exists:"a#manageCookiesLink"},then:[{click:"a#manageCookiesLink"}],else:[{waitForVisible:".privacy-modal-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:"document.cookie.includes('cookie_prefs') === true"}]},{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:"!document.cookie.includes('cb-enabled=accepted')"}]},{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:"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:"document.cookie.includes('euconsent-v2') && (document.cookie.match(/.YAAAAAAAAAAA/) || document.cookie.match(/.aAAAAAAAAAAA/) || document.cookie.match(/.YAAACFgAAAAA/)) "}]},{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:['section:has(a[href^="https://www.reddit.com/policies/cookies"])'],detectCmp:[{exists:'section:has(a[href^="https://www.reddit.com/policies/cookies"])'}],detectPopup:[{visible:'section:has(a[href^="https://www.reddit.com/policies/cookies"])'}],optIn:[{waitForThenClick:"section:has(a[href^=\"https://www.reddit.com/policies/cookies\"]) section[class^='_'] > section:first-child form button"}],optOut:[{waitForThenClick:"section:has(a[href^=\"https://www.reddit.com/policies/cookies\"]) section[class^='_'] > section:last-child form button"}],test:[{eval:"document.cookie.includes('eu_cookie={%22opted%22:true%2C%22nonessential%22:false}')"}]},{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:"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:"!!window.localStorage.getItem('euconsent-v2')"}]},{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:"document.cookie.includes('euconsent-v2')"}]},{name:"snigel",detectCmp:[{exists:".snigel-cmp-framework"}],detectPopup:[{visible:".snigel-cmp-framework"}],optOut:[{click:"#sn-b-custom"},{click:"#sn-b-save"}],test:[{eval:"!!document.cookie.match('snconsent')"}],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:"JSON.parse(decodeURIComponent(document.cookie.split(';').find(s => s.trim().startsWith('cookieSettings')).split('=')[1])).preference_state === 2"}]},{name:"takealot.com",cosmetic:!0,prehideSelectors:['div[class^="cookies-banner-module_cookie-banner_"]'],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_cookie-banner_"]']}]},{name:"tarteaucitron.js",prehideSelectors:["#tarteaucitronRoot"],detectCmp:[{exists:"#tarteaucitronRoot"}],detectPopup:[{visible:"#tarteaucitronRoot #tarteaucitronAlertSmall,#tarteaucitronRoot #tarteaucitronAlertBig",check:"any"}],optIn:[{eval:"tarteaucitron.userInterface.respondAll(true) || true"}],optOut:[{eval:"tarteaucitron.userInterface.respondAll(false) || true"}],test:[{eval:"document.cookie.match(/tarteaucitron=[^;]*/)[0].includes('false')",comment:"sometimes there are required categories, so we check that at least something is false"}]},{name:"Tealium",prehideSelectors:["#__tealiumGDPRecModal,#__tealiumGDPRcpPrefs,#consent-layer"],detectCmp:[{visible:"#__tealiumGDPRecModal"},{eval:"typeof window.utag !== 'undefined' && typeof utag.gdpr === 'object'"}],detectPopup:[{visible:"#__tealiumGDPRecModal"}],optOut:[{waitForThenClick:"#cm-acceptNone,.js-accept-essential-cookies",timeout:1e3},{eval:"utag.gdpr.setConsentValue(false) || true"}],optIn:[{hide:["#__tealiumGDPRecModal"]},{eval:"utag.gdpr.setConsentValue(true) || true"}],test:[{eval:"utag.gdpr.getConsentState() !== 1"}]},{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:"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:"window.results.results[0] === 'banner_hidden'"}]},{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:"window.results.results[0] === 'button_clicked'"}]},{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:"cmpUi.allowAll() || true"}],optOut:[{eval:"cmpUi.showPurposes() || cmpUi.rejectAll() || true"}]},{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:"document.cookie.includes('_duet_gdpr_acknowledged=1')"}]},{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:"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.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:"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.p-button"},{waitForThenClick:'xpath///*[@id="cookie-policy-content"]/p[4]/button[2]'}]},{waitForThenClick:".p-switch__input:checked",optional:!0,all:!0},{any:[{waitForThenClick:"div > button"},{waitForThenClick:'xpath///*[@id="modal"]/div/button'}]}],test:[{eval:"document.cookie === '_cookies_accepted=essential'"}]},{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:"!document.cookie.includes('catAccCookies')"}]},{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:"typeof UC_UI === 'object'"},{exists:["#usercentrics-root","[data-testid=uc-container]"]}],optIn:[{eval:"!!UC_UI.acceptAllConsents()"},{eval:"!!UC_UI.closeCMP()"},{eval:"UC_UI.areAllConsentsAccepted() === true"}],optOut:[{eval:"!!UC_UI.closeCMP()"},{eval:"!!UC_UI.denyAllConsents()"}],test:[{eval:"UC_UI.areAllConsentsAccepted() === false"}]},{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:"JSON.parse(localStorage.getItem('usercentrics')).consents.every(c => c.isEssential || !c.consentStatus)"}]},{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:"Array.from(document.querySelectorAll('label[id$=cookies-deny-label]')).forEach(e => e.click()) || true"},{click:"button[data-test=submit]"}],test:[{eval:"document.cookie.includes('wtr_cookies_advertising=0') && document.cookie.includes('wtr_cookies_analytics=0')"}]},{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:"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",comment:"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:"document.cookie.includes('wpl_viewed_cookie=no')"}]},{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:"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:"document.cookie.includes('userConsent=%7B%22marketing%22%3Afalse')"}]},{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:"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:"!!document.cookie.match(/SOCS=CAE/)"}]},{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:"!!document.cookie.match(/SOCS=CAE/)"}]}],I={"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"}]}},T={autoconsent:F,consentomatic:I},E=Object.freeze({__proto__:null,autoconsent:F,consentomatic:I,default:T});const B=new class{constructor(o,c=null,i=null){if(this.id=e(),this.rules=[],this.foundCmp=null,this.state={lifecycle:"loading",prehideOn:!1,findCmpAttempts:0,detectedCmps:[],detectedPopups:[],selfTest:null},t.sendContentMessage=o,this.sendContentMessage=o,this.rules=[...C],this.updateState({lifecycle:"loading"}),c)this.initialize(c,i);else{i&&this.parseRules(i);o({type:"init",url:window.location.href}),this.updateState({lifecycle:"waitingForInitResponse"})}}initialize(e,t){if(this.config=e,e.enabled){if(t&&this.parseRules(t),this.rules=function(e,t){return e.filter((e=>(!t.disabledCmps||!t.disabledCmps.includes(e.name))&&(t.enableCosmeticRules||!e.isCosmetic)))}(this.rules,e),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"})}}parseRules(e){Object.keys(e.consentomatic).forEach((t=>{this.addConsentomaticCMP(t,e.consentomatic[t])})),e.autoconsent.forEach((e=>{this.addCMP(e)}))}addCMP(e){this.rules.push(function(e){return new _(e)}(e))}addConsentomaticCMP(e,t){this.rules.push(new class{constructor(e,t){this.name=e,this.config=t,this.methods=new Map,this.runContext=b,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=>P(e.presentMatcher))).some((e=>!!e))}async detectPopup(){return this.config.detectors.map((e=>P(e.showingMatcher))).some((e=>!!e))}async executeAction(e,t){return!this.methods.has(e)||A(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}}(`com_${e}`,t))}start(){window.requestIdleCallback?window.requestIdleCallback((()=>this._start()),{timeout:500}):this._start()}async _start(){this.updateState({lifecycle:"started"});const e=await this.findCmp(this.config.detectRetries);if(this.updateState({detectedCmps:e.map((e=>e.name))}),0===e.length)return this.config.enablePrehide&&this.undoPrehide(),this.updateState({lifecycle:"nothingDetected"}),!1;this.updateState({lifecycle:"cmpDetected"});let t=await this.detectPopups(e.filter((e=>!e.isCosmetic)));if(0===t.length&&(t=await this.detectPopups(e.filter((e=>e.isCosmetic)))),0===t.length)return this.config.enablePrehide&&this.undoPrehide(),!1;if(this.updateState({lifecycle:"openPopupDetected"}),t.length>1){const e={msg:"Found multiple CMPs, check the detection rules.",cmps:t.map((e=>e.name))};this.sendContentMessage({type:"autoconsentError",details:e})}return this.foundCmp=t[0],"optOut"===this.config.autoAction?await this.doOptOut():"optIn"!==this.config.autoAction||await this.doOptIn()}async findCmp(e){this.updateState({findCmpAttempts:this.state.findCmpAttempts+1});const t=[];for(const e of this.rules)try{if(!e.checkRunContext())continue;await e.detectCmp()&&(this.sendContentMessage({type:"cmpDetected",url:location.href,cmp:e.name}),t.push(e))}catch(e){}return 0===t.length&&e>0?(await m(500),this.findCmp(e-1)):t}async detectPopups(e){const t=[],o=e.map((e=>this.waitForPopup(e).then((o=>{o&&(this.updateState({detectedPopups:this.state.detectedPopups.concat([e.name])}),this.sendContentMessage({type:"popupFound",cmp:e.name,url:location.href}),t.push(e))})).catch((()=>null))));return await Promise.all(o),t}async doOptOut(){let e;return this.updateState({lifecycle:"runningOptOut"}),e=!!this.foundCmp&&await this.foundCmp.optOut(),this.config.enablePrehide&&this.undoPrehide(),this.sendContentMessage({type:"optOutResult",cmp:this.foundCmp?this.foundCmp.name:"none",result:e,scheduleSelfTest:this.foundCmp&&this.foundCmp.hasSelfTest,url:location.href}),e&&!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:e?"optOutSucceeded":"optOutFailed"}),e}async doOptIn(){let e;return this.updateState({lifecycle:"runningOptIn"}),e=!!this.foundCmp&&await this.foundCmp.optIn(),this.config.enablePrehide&&this.undoPrehide(),this.sendContentMessage({type:"optInResult",cmp:this.foundCmp?this.foundCmp.name:"none",result:e,scheduleSelfTest:!1,url:location.href}),e&&!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:e?"optInSucceeded":"optInFailed"}),e}async doSelfTest(){let e;return e=!!this.foundCmp&&await this.foundCmp.test(),this.sendContentMessage({type:"selfTestResult",cmp:this.foundCmp?this.foundCmp.name:"none",result:e,url:location.href}),this.updateState({selfTest:e}),e}async waitForPopup(e,t=5,o=500){const c=await e.detectPopup();return!c&&t>0?(await m(o),this.waitForPopup(e,t-1,o)):c}prehideElements(){const e=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)&&this.undoPrehide()}),this.config.prehideTimeout||2e3),function(e){return i(c("autoconsent-prehide"),e,"opacity")}(e)}undoPrehide(){return this.updateState({prehideOn:!1}),function(){const e=c("autoconsent-prehide");return e&&e.remove(),!!e}()}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){switch(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,o){const c=t.pending.get(e);c?(t.pending.delete(e),c.timer&&window.clearTimeout(c.timer),c.resolve(o)):console.warn("no eval #",e)}(e.id,e.result)}}}((e=>{window.webkit.messageHandlers[e.type]&&window.webkit.messageHandlers[e.type].postMessage(e).then((e=>{B.receiveMessageCallback(e)}))}),null,E);window.autoconsentMessageCallback=e=>{B.receiveMessageCallback(e)}}(); diff --git a/package-lock.json b/package-lock.json index 136fe26019..e04d778e7f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,7 @@ "name": "macos-browser", "version": "1.0.0", "dependencies": { - "@duckduckgo/autoconsent": "^5.1.0" + "@duckduckgo/autoconsent": "^5.3.0" }, "devDependencies": { "@rollup/plugin-json": "^4.1.0", @@ -53,9 +53,9 @@ } }, "node_modules/@duckduckgo/autoconsent": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@duckduckgo/autoconsent/-/autoconsent-5.1.0.tgz", - "integrity": "sha512-YWIIteq8noDiPnvK1631xqfoGpPXSxH2ykRb4f3JXdDuhaQFzO0E5wI8N6kuzkXVPngczLF7D4NZOaxUTnMGAA==" + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@duckduckgo/autoconsent/-/autoconsent-5.3.0.tgz", + "integrity": "sha512-/ZUdNt+FLhtT40f53Pl/TwOLX1Rr4vCyzgDXQjtXBHF7vSaQJLRdkDkiEm4P24HAxNbg+WGeleJUiIEyQgfp2A==" }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.2", diff --git a/package.json b/package.json index a56ba9b4cc..7342a038c1 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,6 @@ "rollup-plugin-terser": "^7.0.2" }, "dependencies": { - "@duckduckgo/autoconsent": "^5.1.0" + "@duckduckgo/autoconsent": "^5.3.0" } } From 6588d03d451fc8d4302383c1bff193f2142d1e3c Mon Sep 17 00:00:00 2001 From: Diego Rey Mendez Date: Thu, 10 Aug 2023 16:32:23 +0200 Subject: [PATCH 07/17] Disables a flaky test from App Store unit tests (#1467) Task/Issue URL: https://app.asana.com/0/1201037661562251/1205249621847889/f Tech Design URL: CC: ## Description: Disables a flaky test from App Store unit test runs ([that's already disabled from DeveloperID unit test runs](https://github.com/duckduckgo/macos-browser/pull/1429)). You can see the failure here: https://github.com/duckduckgo/macos-browser/actions/runs/5821469075/job/15783975898?pr=1466 --- .../xcschemes/DuckDuckGo Privacy Browser App Store.xcscheme | 3 +++ 1 file changed, 3 insertions(+) diff --git a/DuckDuckGo.xcodeproj/xcshareddata/xcschemes/DuckDuckGo Privacy Browser App Store.xcscheme b/DuckDuckGo.xcodeproj/xcshareddata/xcschemes/DuckDuckGo Privacy Browser App Store.xcscheme index 3e1ed53948..599d16fb55 100644 --- a/DuckDuckGo.xcodeproj/xcshareddata/xcschemes/DuckDuckGo Privacy Browser App Store.xcscheme +++ b/DuckDuckGo.xcodeproj/xcshareddata/xcschemes/DuckDuckGo Privacy Browser App Store.xcscheme @@ -108,6 +108,9 @@ + + Date: Thu, 10 Aug 2023 15:40:08 +0100 Subject: [PATCH 08/17] Add job to create a task on asana if tests fails on develop (#1461) Task/Issue URL: https://app.asana.com/0/1203301625297703/1205236154771248/f **Description**: Add job to create a task on asana if tests fails on develop --- .github/workflows/pr.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index c8d5f07000..f46657b77e 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -303,3 +303,24 @@ jobs: run: | git update-index --refresh git diff-index --quiet HEAD -- + + asana: + name: Create Asana Task + needs: [swiftlint, tests, release-build, verify-autoconsent-bundle, private-api] + + if: failure() && github.ref_name == 'develop' + + env: + WORKFLOW_URL: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} + + runs-on: ubuntu-latest + + steps: + - name: Create Asana Task + uses: malmstein/github-asana-action@master + with: + asana-pat: ${{ secrets.ASANA_ACCESS_TOKEN }} + asana-project: ${{ vars.MACOS_APP_DEVELOPMENT_ASANA_PROJECT_ID }} + asana-task-name: 'PR Check is failing on develop' + action: create-asana-task + asana-task-description: PR Checks conducted after merging have failed. See ${{ env.WORKFLOW_URL }} \ No newline at end of file From 75862d44b8de739703bc602c4b23feff70241787 Mon Sep 17 00:00:00 2001 From: Dax Mobile <44842493+daxmobile@users.noreply.github.com> Date: Fri, 11 Aug 2023 05:45:23 -0500 Subject: [PATCH 09/17] Update BSK with autofill 8.1.2 (#1471) Task/Issue URL: https://app.asana.com/0/1205255555424025/1205255555424025 Autofill Release: https://github.com/duckduckgo/duckduckgo-autofill/releases/tag/8.1.2 BSK PR: https://github.com/duckduckgo/BrowserServicesKit/pull/457 ## Description Updates Autofill to version [8.1.2](https://github.com/duckduckgo/duckduckgo-autofill/releases/tag/8.1.2) --- DuckDuckGo.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/swiftpm/Package.resolved | 8 ++++---- LocalPackages/NetworkProtectionUI/Package.swift | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index f8d438ff51..854156b43d 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -10203,7 +10203,7 @@ repositoryURL = "https://github.com/duckduckgo/BrowserServicesKit"; requirement = { kind = exactVersion; - version = 73.0.0; + version = 73.0.1; }; }; AA06B6B52672AF8100F541C5 /* XCRemoteSwiftPackageReference "Sparkle" */ = { diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 7cd547027b..3068b87fb3 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -14,8 +14,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/duckduckgo/BrowserServicesKit", "state" : { - "revision" : "b01e0010c7c0618f7329184c11c85a3c64a4307f", - "version" : "73.0.0" + "revision" : "873bb1f0a774cf069c0ceba99be3f93515135baf", + "version" : "73.0.1" } }, { @@ -32,8 +32,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/duckduckgo/duckduckgo-autofill.git", "state" : { - "revision" : "b1160df954eea20cd4cdf9356afc369751981b16", - "version" : "8.0.0" + "revision" : "40bcd0d347b51d14e8114f191df2817d8dcb4284", + "version" : "8.1.2" } }, { diff --git a/LocalPackages/NetworkProtectionUI/Package.swift b/LocalPackages/NetworkProtectionUI/Package.swift index ce732f5f1b..35d5394879 100644 --- a/LocalPackages/NetworkProtectionUI/Package.swift +++ b/LocalPackages/NetworkProtectionUI/Package.swift @@ -15,7 +15,7 @@ let package = Package( targets: ["NetworkProtectionUI"]) ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "73.0.0"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "73.0.1"), .package(path: "../SwiftUIExtensions") ], targets: [ From 378d77246be0bd5711ec9d116d592b0f247e4f09 Mon Sep 17 00:00:00 2001 From: Sabrina Tardio <44158575+SabrinaTardio@users.noreply.github.com> Date: Fri, 11 Aug 2023 13:35:15 +0200 Subject: [PATCH 10/17] don't show day 7 when day 0 has been dismissed (#1468) Task/Issue URL: https://app.asana.com/0/1204186595873227/1205248215850616/f **Description**: Modify the card logic to not show the day7 survey when the day0 card has been dismissed **Steps to test this PR**: 1 Reset card dismissal: Debug -> Reset Data -> Reset Make DuckDuckGo Yours User Settings 2 Change install day to today: Debug -> Reset Data -> Change Activation Date -> Today 3 Open a new new tab page and check the day 0 survey card appear with the correct copy and icon 4 Click on the card and check it takes you to the correct survey. In the url check the atb is passed at the end of the url. 5 Go back to the new tab page and check the card has disappeared. 6 Change install day to during the past week: Debug -> Reset Data -> Change Activation Date -> Less than a week ago 7 Open a new new tab page and check none of the survey card is present. 8 Change install day to during more than a week ago: Debug -> Reset Data -> Change Activation Date -> More than a week ago. 9 Open a new new tab page and check none of the survey card is present. 10 Repeat steps 1 to 3 11 Dismiss the day 0 card 12 repeat steps 6 to 8 13 check the day 7 card does not appear 14 Repeat steps 1 to 3 15 Repeat steps 6 to 8 16 check the day 7 card appears 17 Click on the card and check it takes you to the correct survey. In the url check the atb is passed at the end of the url. --- ###### Internal references: [Pull Request Review Checklist](https://app.asana.com/0/1202500774821704/1203764234894239/f) [Software Engineering Expectations](https://app.asana.com/0/59792373528535/199064865822552) [Technical Design Template](https://app.asana.com/0/59792373528535/184709971311943) [Pull Request Documentation](https://app.asana.com/0/1202500774821704/1204012835277482/f) --- .../HomePage/Model/HomePageContinueSetUpModel.swift | 1 + UnitTests/HomePage/ContinueSetUpModelTests.swift | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/DuckDuckGo/HomePage/Model/HomePageContinueSetUpModel.swift b/DuckDuckGo/HomePage/Model/HomePageContinueSetUpModel.swift index 73f935d9eb..a4d6984817 100644 --- a/DuckDuckGo/HomePage/Model/HomePageContinueSetUpModel.swift +++ b/DuckDuckGo/HomePage/Model/HomePageContinueSetUpModel.swift @@ -320,6 +320,7 @@ extension HomePage.Models { private var shouldSurveyDay7BeVisible: Bool { let oneWeekAgo = Calendar.current.date(byAdding: .weekOfYear, value: -1, to: Date())! return isDay7SurveyEnabled && + shouldShowSurveyDay0 && shouldShowSurveyDay7 && !userInteractedWithSurveyDay0 && firstLaunchDate <= oneWeekAgo diff --git a/UnitTests/HomePage/ContinueSetUpModelTests.swift b/UnitTests/HomePage/ContinueSetUpModelTests.swift index cc69cab15f..2b2589b571 100644 --- a/UnitTests/HomePage/ContinueSetUpModelTests.swift +++ b/UnitTests/HomePage/ContinueSetUpModelTests.swift @@ -144,6 +144,19 @@ final class ContinueSetUpModelTests: XCTestCase { XCTAssertFalse(vm.visibleFeaturesMatrix.reduce([], +).contains(HomePage.Models.FeatureType.surveyDay7)) } + @MainActor func testWhenInstallDateIsMoreThanAWeekAgoAndUserDismissedDay0SurveyDay7SurveyCardIsNotShown() { + let statisticStore = MockStatisticsStore() + vm.statisticsStore = statisticStore + vm.removeItem(for: .surveyDay0) + let aDayAgo = Calendar.current.date(byAdding: .day, value: -7, to: Date())! + userDefaults.set(aDayAgo, forKey: UserDefaultsWrapper.Key.firstLaunchDate.rawValue) + vm = HomePage.Models.ContinueSetUpModel.fixture() + vm.shouldShowAllFeatures = true + + XCTAssertFalse(vm.visibleFeaturesMatrix.reduce([], +).contains(HomePage.Models.FeatureType.surveyDay0)) + XCTAssertFalse(vm.visibleFeaturesMatrix.reduce([], +).contains(HomePage.Models.FeatureType.surveyDay7)) + } + @MainActor func testWhenInitializedNotForTheFirstTimeTheMatrixHasAllElementsInTheRightOrder() { var homePageIsFirstSession = UserDefaultsWrapper(key: .homePageIsFirstSession, defaultValue: true) homePageIsFirstSession.wrappedValue = false From abe6dd16dc7a36a884b24bca78573aef351f6592 Mon Sep 17 00:00:00 2001 From: Graeme Arthur <2030310+graeme@users.noreply.github.com> Date: Fri, 11 Aug 2023 13:53:04 +0200 Subject: [PATCH 11/17] Move Make /Applications symlink build phase to later (#1470) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task/Issue URL: https://app.asana.com/0/1201037661562251/1205251729301936/f **Description**: From @afterxleep : Out of nowhere, I started seeing the following error when building the macOS app (Likely after a DerivedData cleanup). File name changed every time. While it happened, I tried the following, and nothing worked. Reboot Clean Build Clear issues Clean Derived Data Reset Packages Git Clean Reclone Repository Clean User Data Remove Command line tools Reinstall Xcode Throw the laptop out the window @Dominik mentioned that he had to disable the , script in CI, so I did the same, and after cleaning everything up, builds started to work. This script moves the built app from Derived Data to the Applications folder and then creates a symlink. If, for any reason, this symlink is not created, the Copy Resources Phase above fails, causing the error. (Files are not there anymore) On top of that, if the symlink creation fails, the project ends up in an unrecoverable state, and the only solution is to wipe out DerivedData, Reset Package caches, and remove the script from the Build Phases. **Steps to test this PR**: 1. Delete Applications/DEBUG/DuckDuckGo.app 2. Clear Derived Data 3. Go to System Settings -> VPN -> (Blue) DuckDuckGo, click the (i) button and delete the configuration 4. Build the app from Xcode 5. Enable NetP and allow the network extension to be installed. 6. Ensure it functions as expected. — ###### Internal references: [Pull Request Review Checklist](https://app.asana.com/0/1202500774821704/1203764234894239/f) [Software Engineering Expectations](https://app.asana.com/0/59792373528535/199064865822552) [Technical Design Template](https://app.asana.com/0/59792373528535/184709971311943) [Pull Request Documentation](https://app.asana.com/0/1202500774821704/1204012835277482/f) --- DuckDuckGo.xcodeproj/project.pbxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 854156b43d..df82e9fc85 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -6806,13 +6806,13 @@ isa = PBXNativeTarget; buildConfigurationList = AA585DA4248FD31500E9A3E2 /* Build configuration list for PBXNativeTarget "DuckDuckGo Privacy Browser" */; buildPhases = ( - B6F2C8722A7A4C7D000498CF /* Make /Applications symlink, remove app on Clean build */, CBCCF59E299667B700C02DFE /* Assert Xcode version */, 3705272528992C8A000C06A2 /* Check Embedded Config URLs */, AA585D7A248FD31100E9A3E2 /* Sources */, AA8EDF2824925E940071C2E8 /* Swift Lint */, AA585D7B248FD31100E9A3E2 /* Frameworks */, AA585D7C248FD31100E9A3E2 /* Resources */, + B6F2C8722A7A4C7D000498CF /* Make /Applications symlink, remove app on Clean build */, 4B2D065D2A11D2AE00DE1F49 /* Embed Login Items */, 4B5F14F32A14823D0060320F /* Embed NetP Controller Apps */, 4B5F14F62A14825A0060320F /* Replace VPN Controllers with Symlinks */, From dc41168bb7ad05835348658f2b0bd3500c7139e3 Mon Sep 17 00:00:00 2001 From: Dominik Kapusta Date: Fri, 11 Aug 2023 14:42:14 +0200 Subject: [PATCH 12/17] Use MainActor to ensure FutureExtension tests are dispatched on main queue (#1472) Task/Issue URL: https://app.asana.com/0/1199230911884351/1205205535714739/f Description: Replace tests using DispatchQueue.main.async for the entire body with a @MainActor async functions to ensure that they're being run on main thread. --- .../Extensions/FutureExtensionTests.swift | 116 ++++++++---------- 1 file changed, 48 insertions(+), 68 deletions(-) diff --git a/UnitTests/Common/Extensions/FutureExtensionTests.swift b/UnitTests/Common/Extensions/FutureExtensionTests.swift index 855d103569..9aed1ead1e 100644 --- a/UnitTests/Common/Extensions/FutureExtensionTests.swift +++ b/UnitTests/Common/Extensions/FutureExtensionTests.swift @@ -230,64 +230,49 @@ final class FutureExtensionTests: XCTestCase { // - main queue async - - func testUnfulfilledPromiseAsyncDoesNotHaveValue() { - let e = expectation(description: "async job done") - DispatchQueue.main.async { - let promise = Future.promise() - var value: String? - let c = promise.future.sink { - XCTFail("unexpected value") - value = $0 - } - - XCTAssertNil(value) - withExtendedLifetime(c, {}) - - e.fulfill() + @MainActor + func testUnfulfilledPromiseAsyncDoesNotHaveValue() async { + let promise = Future.promise() + var value: String? + let c = promise.future.sink { + XCTFail("unexpected value") + value = $0 } - waitForExpectations(timeout: 1) - } - func testInstantlyFulfilledPromiseAsyncHasValue() { - let e = expectation(description: "async job done") - DispatchQueue.main.async { - let promise = Future.promise() - promise.fulfill("test") - var value: String? - let c = promise.future.sink { - XCTAssertEqual($0, "test") - value = $0 - } - - XCTAssertEqual(value, "test") - withExtendedLifetime(c, {}) + XCTAssertNil(value) + withExtendedLifetime(c, {}) + } - e.fulfill() + @MainActor + func testInstantlyFulfilledPromiseAsyncHasValue() async { + let promise = Future.promise() + promise.fulfill("test") + var value: String? + let c = promise.future.sink { + XCTAssertEqual($0, "test") + value = $0 } - waitForExpectations(timeout: 1) + + XCTAssertEqual(value, "test") + withExtendedLifetime(c, {}) } - func testFulfilledPromiseAsyncHasValue() { - let e = expectation(description: "async job done") + @MainActor + func testFulfilledPromiseAsyncHasValue() async { let eFulfilled = expectation(description: "fulfilled") - DispatchQueue.main.async { - let promise = Future.promise() - var value: String? + let promise = Future.promise() + var value: String? - let c = promise.future.sink { - XCTAssertEqual($0, "test") - value = $0 - eFulfilled.fulfill() - } - - promise.fulfill("test") - self.wait(for: [eFulfilled], timeout: 0) - XCTAssertEqual(value, "test") - withExtendedLifetime(c, {}) - - e.fulfill() + let c = promise.future.sink { + XCTAssertEqual($0, "test") + value = $0 + eFulfilled.fulfill() } - wait(for: [e], timeout: 1) + + promise.fulfill("test") + await fulfillment(of: [eFulfilled], timeout: 0) + XCTAssertEqual(value, "test") + withExtendedLifetime(c, {}) } func testPromiseAsyncFulfilledAsyncHasValue() { @@ -315,29 +300,24 @@ final class FutureExtensionTests: XCTestCase { wait(for: [e], timeout: 1) } - func testPromiseAsyncFulfilledInBackgroundHasValue() { - let e = expectation(description: "async job done") + @MainActor + func testPromiseAsyncFulfilledInBackgroundHasValue() async { let eFulfilled = expectation(description: "fulfilled") - DispatchQueue.main.async { - let promise = Future.promise() - var value: String? + let promise = Future.promise() + var value: String? - let c = promise.future.sink { - XCTAssertEqual($0, "test") - value = $0 - eFulfilled.fulfill() - } - - DispatchQueue.global().async { - promise.fulfill("test") - } - self.wait(for: [eFulfilled], timeout: 1) - XCTAssertEqual(value, "test") - withExtendedLifetime(c, {}) + let c = promise.future.sink { + XCTAssertEqual($0, "test") + value = $0 + eFulfilled.fulfill() + } - e.fulfill() + DispatchQueue.global().async { + promise.fulfill("test") } - wait(for: [e], timeout: 1) + await fulfillment(of: [eFulfilled], timeout: 1) + XCTAssertEqual(value, "test") + withExtendedLifetime(c, {}) } // MARK: - Publishers.First.get() From 029551545df19b0a03f5021b28107201c85a889b Mon Sep 17 00:00:00 2001 From: Dominik Kapusta Date: Fri, 11 Aug 2023 15:29:47 +0200 Subject: [PATCH 13/17] Report failed unit tests to Asana (#1457) Task/Issue URL: https://app.asana.com/0/1199230911884351/1205242009579916/f Description: Update PR Checks workflow so that it searches JUnit test report for failed test cases, and reports every single failed test case as new task in Asana (or a subtask in case of duplicates). --- .github/workflows/pr.yml | 13 ++ .../xcshareddata/swiftpm/Package.resolved | 2 +- scripts/report-failed-unit-test.sh | 187 ++++++++++++++++++ 3 files changed, 201 insertions(+), 1 deletion(-) create mode 100755 scripts/report-failed-unit-test.sh diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index f46657b77e..9861aaeb56 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -154,6 +154,19 @@ jobs: check_name: "Test Report: ${{ matrix.flavor }}" report_paths: ${{ matrix.flavor }}.xml + - name: Update Asana with failed unit tests + if: always() # always run even if the previous step fails + env: + ASANA_ACCESS_TOKEN: ${{ secrets.ASANA_ACCESS_TOKEN }} + WORKFLOW_URL: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} + run: | + # Extract failed tests from the junit report + # Only keep failures unique by classname and name (column 1 and 2 of the yq output) + yq < ${{ matrix.flavor }}.xml -p xml -o json -r \ + $'[.testsuites.testsuite[].testcase] | flatten | map(select(.failure) | .+@classname + " " + .+@name + " \'" + .failure.+@message + "\' ${{ env.WORKFLOW_URL }}") | .[]' \ + | sort -u -k 1,2 \ + | xargs -L 1 ./scripts/report-failed-unit-test.sh + - name: Upload failed test log uses: actions/upload-artifact@v3 if: failure() diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 3068b87fb3..6a6e6f8436 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -129,7 +129,7 @@ { "identity" : "trackerradarkit", "kind" : "remoteSourceControl", - "location" : "https://github.com/duckduckgo/TrackerRadarKit", + "location" : "https://github.com/duckduckgo/TrackerRadarKit.git", "state" : { "revision" : "4684440d03304e7638a2c8086895367e90987463", "version" : "1.2.1" diff --git a/scripts/report-failed-unit-test.sh b/scripts/report-failed-unit-test.sh new file mode 100755 index 0000000000..cacc72bf76 --- /dev/null +++ b/scripts/report-failed-unit-test.sh @@ -0,0 +1,187 @@ +#!/bin/bash + +set -eo pipefail + +if ! [[ $common_sh ]]; then + cwd="$(dirname "${BASH_SOURCE[0]}")" + source "${cwd}/helpers/common.sh" +fi + +print_usage_and_exit() { + local reason=$1 + + cat <<- EOF + Usage: + $ $(basename "$0") [-h] + + Options: + Name of the failing test case class + Name of the failing test case + Failure message + URL of the workflow that failed + -h Print this message + + Note: This script is intended for CI use only. You shouldn't call it directly. + EOF + + die "${reason}" +} + +read_command_line_arguments() { + while getopts 'h' OPTION; do + case "${OPTION}" in + h) + print_usage_and_exit + ;; + *) + print_usage_and_exit "Unknown option '${OPTION}'" + ;; + esac + done + + shift $((OPTIND-1)) + + if (( $# < 4 )); then + print_usage_and_exit "Missing arguments" + fi + + class_name=$1 + testcase_name=$2 + message=$3 + workflow_url=$4 + + shift 4 +} + +workspace_id="137249556945" +project_id="1205237866452338" +occurrences_custom_field_id="1205237866452341" +failing_tests_section_id="1205242009579904" +asana_api_url="https://app.asana.com/api/1.0" + +find_task_and_occurrences() { + local task_name=$1 + curl -s "${asana_api_url}/workspaces/${workspace_id}/tasks/search?text=${task_name}&opt_fields=custom_fields.number_value&resource_subtype=default_task&projects.any=${project_id}&is_subtask=false" \ + -H "Authorization: Bearer ${asana_personal_access_token}" \ + | jq -r "if (.data | length) != 0 then [.data[0].gid, (.data[0].custom_fields[] | select(.gid == \"${occurrences_custom_field_id}\") | (.number_value // 0))] | join(\" \") else empty end" +} + +update_task() { + local task_id=$1 + local occurrences=$2 + local return_code + + return_code="$(curl -X PUT -s "${asana_api_url}/tasks/${task_id}" \ + -H "Authorization: Bearer ${asana_personal_access_token}" \ + -H 'content-type: application/json' \ + --write-out '%{http_code}' \ + --output /dev/null \ + -d "{ + \"data\": { + \"completed\": false, + \"custom_fields\": { + \"${occurrences_custom_field_id}\": \"${occurrences}\" + }, + \"due_on\": \"${due_date}\" + } + }")" + + [[ ${return_code} -eq 200 ]] +} + +create_task() { + local task_name=$1 + local workflow_url=$2 + local message="${3//\"/\\\"}" + local occurrences=1 + local task_id + + task_id=$(curl -X POST -s "${asana_api_url}/tasks?opt_fields=gid" \ + -H "Authorization: Bearer ${asana_personal_access_token}" \ + -H 'content-type: application/json' \ + -d "{ + \"data\": { + \"custom_fields\": { + \"${occurrences_custom_field_id}\": \"${occurrences}\" + }, + \"due_date\": \"${due_date}\", + \"name\": \"${task_name}\", + \"resource_subtype\": \"default_task\", + \"notes\": \"Workflow URL: ${workflow_url}\n\n${message}\", + \"projects\": [ + \"${project_id}\" + ], + \"workspace\": \"${workspace_id}\" + } + }" \ + | jq -r '.data.gid') + + curl -X POST -s "${asana_api_url}/sections/${failing_tests_section_id}/addTask" \ + -H "Authorization: Bearer ${asana_personal_access_token}" \ + -H 'content-type: application/json' \ + --write-out '%{http_code}' \ + --output /dev/null \ + -d "{\"data\": {\"task\": \"${task_id}\"}}" +} + +add_subtask() { + local parent_task_id=$1 + local task_name=$2 + local workflow_url=$3 + local message="${4//\"/\\\"}" + local return_code + + return_code=$(curl -X POST -s "${asana_api_url}/tasks/${parent_task_id}/subtasks" \ + -H "Authorization: Bearer ${asana_personal_access_token}" \ + -H 'content-type: application/json' \ + --write-out '%{http_code}' \ + --output /dev/null \ + -d "{ + \"data\": { + \"name\": \"${task_name}\", + \"resource_subtype\": \"default_task\", + \"notes\": \"Workflow URL: ${workflow_url}\n\n${message}\" + } + } + ") + + [[ ${return_code} -eq 201 ]] +} + +main() { + local class_name + local testcase_name + local message + local workflow_url + local due_date + due_date=$(date -v +30d +%Y-%m-%d) + + source "${cwd}/helpers/keychain.sh" + read_command_line_arguments "$@" + + # Load Asana-related functions. This calls `_asana_preflight` which + # will check for Asana access token if needed (if asana task was passed to the script). + source "${cwd}/helpers/asana.sh" + + _asana_get_token + + local task_name="${class_name}.${testcase_name}" + echo "Processing ${task_name}" + + local task_and_occurrences + task_and_occurrences=$(find_task_and_occurrences "${task_name}") + if [[ -n "${task_and_occurrences}" ]]; then + local task_id + local occurrences + task_id=$(cut -d ' ' -f 1 <<< "${task_and_occurrences}") + occurrences=$(cut -d ' ' -f 2 <<< "${task_and_occurrences}") + occurrences=$((occurrences+1)) + + update_task "${task_id}" "${occurrences}" + add_subtask "${task_id}" "${task_name}" "${workflow_url}" "${message}" + else + create_task "${task_name}" "${workflow_url}" "${message}" + fi +} + +main "$@" From 05fb8450c84a26271c5e02d6563d505b6fdb1412 Mon Sep 17 00:00:00 2001 From: Lorenzo Mattei Date: Fri, 11 Aug 2023 15:57:18 +0200 Subject: [PATCH 14/17] Bump version to 1.51.1 (48) --- Configuration/AppStoreBuildNumber.xcconfig | 2 +- Configuration/Version.xcconfig | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Configuration/AppStoreBuildNumber.xcconfig b/Configuration/AppStoreBuildNumber.xcconfig index cc77b45085..0781fe2817 100644 --- a/Configuration/AppStoreBuildNumber.xcconfig +++ b/Configuration/AppStoreBuildNumber.xcconfig @@ -1 +1 @@ -CURRENT_PROJECT_VERSION = 47 +CURRENT_PROJECT_VERSION = 48 diff --git a/Configuration/Version.xcconfig b/Configuration/Version.xcconfig index ebc558f9a2..d573985333 100644 --- a/Configuration/Version.xcconfig +++ b/Configuration/Version.xcconfig @@ -1 +1 @@ -MARKETING_VERSION = 1.51.0 +MARKETING_VERSION = 1.51.1 From e5fcb8ba730baff6e100e5772d5e8c67149f2004 Mon Sep 17 00:00:00 2001 From: Lorenzo Mattei Date: Fri, 4 Aug 2023 10:55:40 +0200 Subject: [PATCH 15/17] Fix dsym upload to S3 --- .github/workflows/build_notarized.yml | 5 +++++ .github/workflows/release.yml | 2 ++ 2 files changed, 7 insertions(+) diff --git a/.github/workflows/build_notarized.yml b/.github/workflows/build_notarized.yml index 96a112d18d..79ecc9d6b6 100644 --- a/.github/workflows/build_notarized.yml +++ b/.github/workflows/build_notarized.yml @@ -81,6 +81,11 @@ on: required: true MM_WEBHOOK_URL: required: true + AWS_ACCESS_KEY_ID: + required: true + AWS_SECRET_ACCESS_KEY: + required: true + jobs: export-notarized-app: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9b43a4b824..2033f2ce9f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -39,6 +39,8 @@ jobs: ASANA_ACCESS_TOKEN: ${{ secrets.ASANA_ACCESS_TOKEN }} MM_HANDLES_BASE64: ${{ secrets.MM_HANDLES_BASE64 }} MM_WEBHOOK_URL: ${{ secrets.MM_WEBHOOK_URL }} + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} appstore-release: name: Prepare AppStore Release From 0739bfa369bb40c92bdf0f530b085ce953061843 Mon Sep 17 00:00:00 2001 From: Dominik Kapusta Date: Fri, 11 Aug 2023 17:52:00 +0200 Subject: [PATCH 16/17] Add support for syncing Credentials (#1368) Task/Issue URL: https://app.asana.com/0/72649045549333/1204029872658872/f Tech Design URL: https://app.asana.com/0/481882893211075/1204906306298609/f Description: Update Autofill UI data models and related code to account's domain and username being optional. Move Bookmarks Database Cleaner to SyncBookmarksAdapter. Add SyncCredentialsAdapter (and it also contains CredentialsDatabaseCleaner). Added sync triggers to Autofill UI and fixed UI to preserve Logins list selection when it's reloaded, e.g. by sync. --- DuckDuckGo.xcodeproj/project.pbxproj | 14 +- .../xcshareddata/swiftpm/Package.resolved | 4 +- DuckDuckGo/AppDelegate/AppDelegate.swift | 9 + .../Bookmarks/Model/BookmarkManager.swift | 11 +- .../Model/BookmarksCleanupErrorHandling.swift | 10 +- .../Services/LocalBookmarkStore.swift | 4 +- .../View/SwiftUI/LoginFaviconView.swift | 11 +- DuckDuckGo/DataExport/CSVLoginExporter.swift | 29 ++-- .../SecureVaultLoginImporter.swift | 39 +++-- .../View/DataImportViewController.swift | 9 + DuckDuckGo/Fire/Model/Fire.swift | 4 +- .../Model/DataImportStatusProviding.swift | 14 +- .../PasswordManagerCoordinator.swift | 14 +- .../PasswordManagementItemListModel.swift | 13 +- .../Model/PasswordManagementLoginModel.swift | 2 +- .../PasswordManagementViewController.swift | 155 ++++++++++++------ .../View/SaveCredentialsViewController.swift | 38 +++-- .../View/SaveIdentityViewController.swift | 1 + .../SavePaymentMethodViewController.swift | 1 + DuckDuckGo/Statistics/PixelEvent.swift | 18 +- .../CredentialsCleanupErrorHandling.swift | 42 +++++ DuckDuckGo/Sync/SyncCredentialsAdapter.swift | 93 +++++++++++ DuckDuckGo/Sync/SyncDataProviders.swift | 18 +- .../NetworkProtectionUI/Package.swift | 2 +- .../Sources/SyncUI/Views/ManagementView.swift | 2 +- .../Sources/SyncUI/internal/UserText.swift | 10 +- UnitTests/DataExport/MockSecureVault.swift | 98 ++++++++++- 27 files changed, 523 insertions(+), 142 deletions(-) create mode 100644 DuckDuckGo/Sync/CredentialsCleanupErrorHandling.swift create mode 100644 DuckDuckGo/Sync/SyncCredentialsAdapter.swift diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index df82e9fc85..79f588ed82 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -910,6 +910,8 @@ 37197EAC294244D600394917 /* FutureExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = B634DBE6293C98C500C3C99E /* FutureExtension.swift */; }; 371C0A2927E33EDC0070591F /* FeedbackPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 371C0A2827E33EDC0070591F /* FeedbackPresenter.swift */; }; 371D00E129D8509400EC8598 /* OpenSSL in Frameworks */ = {isa = PBXBuildFile; productRef = 371D00E029D8509400EC8598 /* OpenSSL */; }; + 372BC2A12A4AFA47001D8FD5 /* SyncCredentialsAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 372BC2A02A4AFA47001D8FD5 /* SyncCredentialsAdapter.swift */; }; + 372BC2A22A4AFA47001D8FD5 /* SyncCredentialsAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 372BC2A02A4AFA47001D8FD5 /* SyncCredentialsAdapter.swift */; }; 3739326529AE4B39009346AE /* DDGSync in Frameworks */ = {isa = PBXBuildFile; productRef = 3739326429AE4B39009346AE /* DDGSync */; }; 3739326729AE4B42009346AE /* DDGSync in Frameworks */ = {isa = PBXBuildFile; productRef = 3739326629AE4B42009346AE /* DDGSync */; }; 373A1AA8283ED1B900586521 /* BookmarkHTMLReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373A1AA7283ED1B900586521 /* BookmarkHTMLReader.swift */; }; @@ -989,6 +991,8 @@ 37CD54CE27F2FDD100F1F7B9 /* PreferencesSidebarModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37CD54C627F2FDD100F1F7B9 /* PreferencesSidebarModel.swift */; }; 37CD54CF27F2FDD100F1F7B9 /* AppearancePreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37CD54C727F2FDD100F1F7B9 /* AppearancePreferences.swift */; }; 37CD54D027F2FDD100F1F7B9 /* DefaultBrowserPreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37CD54C827F2FDD100F1F7B9 /* DefaultBrowserPreferences.swift */; }; + 37CEFCA92A6737A2001EF741 /* CredentialsCleanupErrorHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37CEFCA82A6737A2001EF741 /* CredentialsCleanupErrorHandling.swift */; }; + 37CEFCAA2A6737A2001EF741 /* CredentialsCleanupErrorHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37CEFCA82A6737A2001EF741 /* CredentialsCleanupErrorHandling.swift */; }; 37D2377A287EB8CA00BCE03B /* TabIndex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37D23779287EB8CA00BCE03B /* TabIndex.swift */; }; 37D2377C287EBDA300BCE03B /* TabIndexTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37D2377B287EBDA300BCE03B /* TabIndexTests.swift */; }; 37D23780287EFEE200BCE03B /* PinnedTabsManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37D2377F287EFEE200BCE03B /* PinnedTabsManagerTests.swift */; }; @@ -2208,6 +2212,7 @@ 3714B1E628EDB7FA0056C57A /* DuckPlayerPreferencesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DuckPlayerPreferencesTests.swift; sourceTree = ""; }; 3714B1E828EDBAAB0056C57A /* DuckPlayerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DuckPlayerTests.swift; sourceTree = ""; }; 371C0A2827E33EDC0070591F /* FeedbackPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbackPresenter.swift; sourceTree = ""; }; + 372BC2A02A4AFA47001D8FD5 /* SyncCredentialsAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncCredentialsAdapter.swift; sourceTree = ""; }; 373A1AA7283ED1B900586521 /* BookmarkHTMLReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarkHTMLReader.swift; sourceTree = ""; }; 373A1AA9283ED86C00586521 /* BookmarksHTMLReaderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarksHTMLReaderTests.swift; sourceTree = ""; }; 373A1AAF2842C4EA00586521 /* BookmarkHTMLImporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarkHTMLImporter.swift; sourceTree = ""; }; @@ -2281,6 +2286,7 @@ 37CD54C627F2FDD100F1F7B9 /* PreferencesSidebarModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreferencesSidebarModel.swift; sourceTree = ""; }; 37CD54C727F2FDD100F1F7B9 /* AppearancePreferences.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppearancePreferences.swift; sourceTree = ""; }; 37CD54C827F2FDD100F1F7B9 /* DefaultBrowserPreferences.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DefaultBrowserPreferences.swift; sourceTree = ""; }; + 37CEFCA82A6737A2001EF741 /* CredentialsCleanupErrorHandling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CredentialsCleanupErrorHandling.swift; sourceTree = ""; }; 37D23779287EB8CA00BCE03B /* TabIndex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabIndex.swift; sourceTree = ""; }; 37D2377B287EBDA300BCE03B /* TabIndexTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabIndexTests.swift; sourceTree = ""; }; 37D2377F287EFEE200BCE03B /* PinnedTabsManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PinnedTabsManagerTests.swift; sourceTree = ""; }; @@ -3580,7 +3586,9 @@ 373D9B4729EEAC1B00381FDD /* SyncMetadataDatabase.swift */, 37445F982A1566420029F789 /* SyncDataProviders.swift */, 37445F9B2A1569F00029F789 /* SyncBookmarksAdapter.swift */, + 372BC2A02A4AFA47001D8FD5 /* SyncCredentialsAdapter.swift */, 37FD78102A29EBD100B36DB1 /* SyncErrorHandler.swift */, + 37CEFCA82A6737A2001EF741 /* CredentialsCleanupErrorHandling.swift */, ); path = Sync; sourceTree = ""; @@ -7726,6 +7734,7 @@ 3706FB64293F65D500E42796 /* PixelDataModel.xcdatamodeld in Sources */, B626A75B29921FAA00053070 /* NavigationActionPolicyExtension.swift in Sources */, 3706FB65293F65D500E42796 /* PrivacyDashboardWebView.swift in Sources */, + 372BC2A22A4AFA47001D8FD5 /* SyncCredentialsAdapter.swift in Sources */, 3706FB66293F65D500E42796 /* AppearancePreferences.swift in Sources */, 3706FB67293F65D500E42796 /* DownloadListCoordinator.swift in Sources */, 3706FB68293F65D500E42796 /* NSNotificationName+Debug.swift in Sources */, @@ -7872,6 +7881,7 @@ 3706FBDB293F65D500E42796 /* DefaultBrowserPreferences.swift in Sources */, 3706FBDC293F65D500E42796 /* Permissions.xcdatamodeld in Sources */, 3706FBDD293F65D500E42796 /* PaddedImageButton.swift in Sources */, + 37CEFCAA2A6737A2001EF741 /* CredentialsCleanupErrorHandling.swift in Sources */, 3706FBDE293F65D500E42796 /* EncryptionKeyStoring.swift in Sources */, 4B4D60E32A0C883A00BCD287 /* Main.swift in Sources */, 37197EA12942441700394917 /* Tab+UIDelegate.swift in Sources */, @@ -8651,6 +8661,7 @@ 37AFCE9227DB8CAD00471A10 /* PreferencesAboutView.swift in Sources */, 9826B0A02747DF3D0092F683 /* ContentBlocking.swift in Sources */, 4B379C2227BDBA29008A968E /* LocalAuthenticationService.swift in Sources */, + 37CEFCA92A6737A2001EF741 /* CredentialsCleanupErrorHandling.swift in Sources */, 4BB99D0326FE191E001E4761 /* SafariBookmarksReader.swift in Sources */, 1DA6D0FD2A1FF9A100540406 /* HTTPCookie.swift in Sources */, AACF6FD626BC366D00CF09F9 /* SafariVersionReader.swift in Sources */, @@ -8936,6 +8947,7 @@ 85CC1D7B26A05ECF0062F04E /* PasswordManagementItemListModel.swift in Sources */, AABEE6A924AB4B910043105B /* SuggestionTableCellView.swift in Sources */, AA6820F125503DA9005ED0D5 /* FireViewModel.swift in Sources */, + 372BC2A12A4AFA47001D8FD5 /* SyncCredentialsAdapter.swift in Sources */, AAA0CC6A253CC43C0079BC96 /* WKUserContentControllerExtension.swift in Sources */, 4BE65479271FCD41008D1D63 /* EditableTextView.swift in Sources */, AA9FF95D24A1FA1C0039E328 /* TabCollection.swift in Sources */, @@ -10203,7 +10215,7 @@ repositoryURL = "https://github.com/duckduckgo/BrowserServicesKit"; requirement = { kind = exactVersion; - version = 73.0.1; + version = 74.0.0; }; }; AA06B6B52672AF8100F541C5 /* XCRemoteSwiftPackageReference "Sparkle" */ = { diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 6a6e6f8436..c9b8311d08 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -14,8 +14,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/duckduckgo/BrowserServicesKit", "state" : { - "revision" : "873bb1f0a774cf069c0ceba99be3f93515135baf", - "version" : "73.0.1" + "revision" : "557c745b3df8694115c5384625e1dc9a5d02e70e", + "version" : "74.0.0" } }, { diff --git a/DuckDuckGo/AppDelegate/AppDelegate.swift b/DuckDuckGo/AppDelegate/AppDelegate.swift index b3ce41b6d5..6ae7e3f113 100644 --- a/DuckDuckGo/AppDelegate/AppDelegate.swift +++ b/DuckDuckGo/AppDelegate/AppDelegate.swift @@ -289,6 +289,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, FileDownloadManagerDel .removeDuplicates() .sink { isSyncDisabled in LocalBookmarkManager.shared.updateBookmarkDatabaseCleanupSchedule(shouldEnable: isSyncDisabled) + syncDataProviders.credentialsAdapter.updateDatabaseCleanupSchedule(shouldEnable: isSyncDisabled) } // This is also called in applicationDidBecomeActive, but we're also calling it here, since @@ -300,6 +301,14 @@ final class AppDelegate: NSObject, NSApplicationDelegate, FileDownloadManagerDel self.syncDataProviders = syncDataProviders self.syncService = syncService + + bookmarksManager.bookmarkDatabaseCleaner.isSyncActive = { [weak self] in + self?.syncService?.authState == .active + } + + syncDataProviders.credentialsAdapter.databaseCleaner.isSyncActive = { [weak self] in + self?.syncService?.authState == .active + } } // MARK: - Network Protection diff --git a/DuckDuckGo/Bookmarks/Model/BookmarkManager.swift b/DuckDuckGo/Bookmarks/Model/BookmarkManager.swift index a9a998acda..2238b33a0f 100644 --- a/DuckDuckGo/Bookmarks/Model/BookmarkManager.swift +++ b/DuckDuckGo/Bookmarks/Model/BookmarkManager.swift @@ -65,16 +65,17 @@ final class LocalBookmarkManager: BookmarkManager { self.faviconManagement = faviconManagement } + let bookmarkDatabaseCleaner = BookmarkDatabaseCleaner( + bookmarkDatabase: BookmarkDatabase.shared.db, + errorEvents: BookmarksCleanupErrorHandling(), + log: .bookmarks + ) + @Published private(set) var list: BookmarkList? var listPublisher: Published.Publisher { $list } private lazy var bookmarkStore: BookmarkStore = LocalBookmarkStore(bookmarkDatabase: BookmarkDatabase.shared) private lazy var faviconManagement: FaviconManagement = FaviconManager.shared - private lazy var bookmarkDatabaseCleaner = BookmarkDatabaseCleaner( - bookmarkDatabase: BookmarkDatabase.shared.db, - errorEvents: BookmarksCleanupErrorHandling(), - log: .bookmarks - ) // MARK: - Bookmarks diff --git a/DuckDuckGo/Bookmarks/Model/BookmarksCleanupErrorHandling.swift b/DuckDuckGo/Bookmarks/Model/BookmarksCleanupErrorHandling.swift index eb2c9c44d4..815dc8ecf9 100644 --- a/DuckDuckGo/Bookmarks/Model/BookmarksCleanupErrorHandling.swift +++ b/DuckDuckGo/Bookmarks/Model/BookmarksCleanupErrorHandling.swift @@ -25,10 +25,14 @@ public class BookmarksCleanupErrorHandling: EventMapping public init() { super.init { event, _, _, _ in - let processedErrors = CoreDataErrorsParser.parse(error: event.coreDataError as NSError) - let params = processedErrors.errorPixelParameters + if event.cleanupError is BookmarksCleanupCancelledError { + Pixel.fire(.debug(event: .bookmarksCleanupAttemptedWhileSyncWasEnabled)) + } else { + let processedErrors = CoreDataErrorsParser.parse(error: event.cleanupError as NSError) + let params = processedErrors.errorPixelParameters - Pixel.fire(.debug(event: .bookmarksCleanupFailed, error: event.coreDataError), withAdditionalParameters: params) + Pixel.fire(.debug(event: .bookmarksCleanupFailed, error: event.cleanupError), withAdditionalParameters: params) + } } } diff --git a/DuckDuckGo/Bookmarks/Services/LocalBookmarkStore.swift b/DuckDuckGo/Bookmarks/Services/LocalBookmarkStore.swift index 6940ac217f..22b04715a4 100644 --- a/DuckDuckGo/Bookmarks/Services/LocalBookmarkStore.swift +++ b/DuckDuckGo/Bookmarks/Services/LocalBookmarkStore.swift @@ -306,7 +306,7 @@ final class LocalBookmarkStore: BookmarkStore { func remove(objectsWithUUIDs identifiers: [String], completion: @escaping (Bool, Error?) -> Void) { applyChangesAndSave(changes: { [weak self] context in - guard let self = self else { + guard self != nil else { throw BookmarkStoreError.storeDeallocated } @@ -488,7 +488,7 @@ final class LocalBookmarkStore: BookmarkStore { let context = makeContext() context.performAndWait { [weak self] in - guard let self = self else { + guard self != nil else { assertionFailure("Couldn't get strong self") return } diff --git a/DuckDuckGo/Common/View/SwiftUI/LoginFaviconView.swift b/DuckDuckGo/Common/View/SwiftUI/LoginFaviconView.swift index 93723b03c5..adb9621c81 100644 --- a/DuckDuckGo/Common/View/SwiftUI/LoginFaviconView.swift +++ b/DuckDuckGo/Common/View/SwiftUI/LoginFaviconView.swift @@ -20,14 +20,12 @@ import SwiftUI struct LoginFaviconView: View { - let domain: String + let domain: String? let faviconManagement: FaviconManagement = FaviconManager.shared var body: some View { - let favicon = faviconManagement.getCachedFavicon(for: domain, sizeCategory: .small)?.image ?? NSImage(named: "Login") - if let image = favicon { Image(nsImage: image) .resizable() @@ -38,4 +36,11 @@ struct LoginFaviconView: View { } + var favicon: NSImage? { + guard let domain else { + return NSImage(named: "Login") + } + return faviconManagement.getCachedFavicon(for: domain, sizeCategory: .small)?.image ?? NSImage(named: "Login") + } + } diff --git a/DuckDuckGo/DataExport/CSVLoginExporter.swift b/DuckDuckGo/DataExport/CSVLoginExporter.swift index bcf3c10589..fe8b4a3af6 100644 --- a/DuckDuckGo/DataExport/CSVLoginExporter.swift +++ b/DuckDuckGo/DataExport/CSVLoginExporter.swift @@ -34,20 +34,23 @@ final class CSVLoginExporter { } func exportVaultLogins(to url: URL) throws { - guard let accounts = try? secureVault.accounts() else { - return - } - var credentialsToExport: [SecureVaultModels.WebsiteCredentials] = [] - for account in accounts { - guard let accountID = account.id, let accountIDInt = Int64(accountID) else { - continue - } + do { + let accounts = try secureVault.accounts() + + for account in accounts { + guard let accountID = account.id, let accountIDInt = Int64(accountID) else { + continue + } - if let credentials = try? secureVault.websiteCredentialsFor(accountId: accountIDInt) { - credentialsToExport.append(credentials) + if let credentials = try secureVault.websiteCredentialsFor(accountId: accountIDInt) { + credentialsToExport.append(credentials) + } } + } catch { + Pixel.fire(.debug(event: .secureVaultError, error: error)) + throw error } try save(credentials: credentialsToExport, to: url) @@ -56,9 +59,9 @@ final class CSVLoginExporter { private func save(credentials: [SecureVaultModels.WebsiteCredentials], to url: URL) throws { let credentialsAsCSVRows: [String] = credentials.compactMap { credential in let title = credential.account.title ?? "" - let domain = credential.account.domain - let username = credential.account.username - let password = credential.password.utf8String() ?? "" + let domain = credential.account.domain ?? "" + let username = credential.account.username ?? "" + let password = credential.password?.utf8String() ?? "" // Ensure that exported passwords escape any quotes they contain let escapedPassword = password.replacingOccurrences(of: "\"", with: "\\\"") diff --git a/DuckDuckGo/DataImport/Logins/SecureVault/SecureVaultLoginImporter.swift b/DuckDuckGo/DataImport/Logins/SecureVault/SecureVaultLoginImporter.swift index b02945992d..aedf2bfb03 100644 --- a/DuckDuckGo/DataImport/Logins/SecureVault/SecureVaultLoginImporter.swift +++ b/DuckDuckGo/DataImport/Logins/SecureVault/SecureVaultLoginImporter.swift @@ -35,26 +35,31 @@ final class SecureVaultLoginImporter: LoginImporter { var duplicates: [String] = [] var failed: [String] = [] - for login in logins { - let title = login.title - let account = SecureVaultModels.WebsiteAccount(title: title, username: login.username, domain: login.url) - let credentials = SecureVaultModels.WebsiteCredentials(account: account, password: login.password.data(using: .utf8)!) - let importSummaryValue: String + let encryptionKey = try vault.getEncryptionKey() + let hashingSalt = try vault.getHashingSalt() - if let title = account.title { - importSummaryValue = "\(title): \(credentials.account.domain) (\(credentials.account.username))" - } else { - importSummaryValue = "\(credentials.account.domain) (\(credentials.account.username))" - } + try vault.inDatabaseTransaction { database in + for login in logins { + let title = login.title + let account = SecureVaultModels.WebsiteAccount(title: title, username: login.username, domain: login.url) + let credentials = SecureVaultModels.WebsiteCredentials(account: account, password: login.password.data(using: .utf8)!) + let importSummaryValue: String - do { - _ = try vault.storeWebsiteCredentials(credentials) - successful.append(importSummaryValue) - } catch { - if case .duplicateRecord = error as? SecureStorageError { - duplicates.append(importSummaryValue) + if let title = account.title { + importSummaryValue = "\(title): \(credentials.account.domain ?? "") (\(credentials.account.username ?? ""))" } else { - failed.append(importSummaryValue) + importSummaryValue = "\(credentials.account.domain ?? "") (\(credentials.account.username ?? ""))" + } + + do { + _ = try vault.storeWebsiteCredentials(credentials, in: database, encryptedUsing: encryptionKey, hashedUsing: hashingSalt) + successful.append(importSummaryValue) + } catch { + if case .duplicateRecord = error as? SecureStorageError { + duplicates.append(importSummaryValue) + } else { + failed.append(importSummaryValue) + } } } } diff --git a/DuckDuckGo/DataImport/View/DataImportViewController.swift b/DuckDuckGo/DataImport/View/DataImportViewController.swift index ade84b383e..248ad17fae 100644 --- a/DuckDuckGo/DataImport/View/DataImportViewController.swift +++ b/DuckDuckGo/DataImport/View/DataImportViewController.swift @@ -404,6 +404,7 @@ final class DataImportViewController: NSViewController { } else { self.bookmarkCount += summary.bookmarksResult?.successful ?? 0 self.viewState.interactionState = .completedImport(summary) + self.requestSync() } NotificationCenter.default.post(name: .dataImportComplete, object: nil) @@ -447,6 +448,14 @@ final class DataImportViewController: NSViewController { } } + private func requestSync() { + guard let syncService = (NSApp.delegate as? AppDelegate)?.syncService else { + return + } + os_log(.debug, log: OSLog.sync, "Requesting sync if enabled") + syncService.scheduler.requestSyncImmediately() + } + } extension DataImportViewController: FileImportViewControllerDelegate { diff --git a/DuckDuckGo/Fire/Model/Fire.swift b/DuckDuckGo/Fire/Model/Fire.swift index c95f8bc944..f88e40b831 100644 --- a/DuckDuckGo/Fire/Model/Fire.swift +++ b/DuckDuckGo/Fire/Model/Fire.swift @@ -390,7 +390,7 @@ final class Fire { let accounts = try? vault.accounts() else { return [] } - return Set(accounts.map { $0.domain }) + return Set(accounts.compactMap { $0.domain }) } private func burnFavicons(completion: @escaping () -> Void) { @@ -519,7 +519,7 @@ final class Fire { private func burnDeletedBookmarks() { if syncService?.authState == .inactive { - LocalBookmarkManager.shared.cleanUpBookmarksDatabase() + bookmarkManager.cleanUpBookmarksDatabase() } } } diff --git a/DuckDuckGo/HomePage/Model/DataImportStatusProviding.swift b/DuckDuckGo/HomePage/Model/DataImportStatusProviding.swift index 9989b445fc..026e1582f0 100644 --- a/DuckDuckGo/HomePage/Model/DataImportStatusProviding.swift +++ b/DuckDuckGo/HomePage/Model/DataImportStatusProviding.swift @@ -68,17 +68,17 @@ final class BookmarksAndPasswordsImportStatusProvider: DataImportStatusProviding } var dates: [Date] = [] - if let accountsDates = try? secureVault.accounts().map({ $0.created }) { + do { + let accountsDates = try secureVault.accounts().map(\.created) dates.append(contentsOf: accountsDates) - } - if let noteDates = try? secureVault.notes().map({ $0.created }) { + let noteDates = try secureVault.notes().map(\.created) dates.append(contentsOf: noteDates) - } - if let cardDates = try? secureVault.creditCards().map({ $0.created }) { + let cardDates = try secureVault.creditCards().map(\.created) dates.append(contentsOf: cardDates) - } - if let identitiesDates = try? secureVault.identities().map({ $0.created }) { + let identitiesDates = try secureVault.identities().map(\.created) dates.append(contentsOf: identitiesDates) + } catch { + Pixel.fire(.debug(event: .secureVaultError, error: error)) } guard dates.count >= 2 else { return false diff --git a/DuckDuckGo/PasswordManager/PasswordManagerCoordinator.swift b/DuckDuckGo/PasswordManager/PasswordManagerCoordinator.swift index ef523ea714..c141191234 100644 --- a/DuckDuckGo/PasswordManager/PasswordManagerCoordinator.swift +++ b/DuckDuckGo/PasswordManager/PasswordManagerCoordinator.swift @@ -213,13 +213,13 @@ final class PasswordManagerCoordinator: PasswordManagerCoordinating { if bitwardenCredential.credentialId == nil { bitwardenManagement.create(credential: bitwardenCredential) { [weak self] error in - self?.websiteCredentialsFor(domain: credentials.account.domain) { _, _ in + self?.websiteCredentialsFor(domain: credentials.account.domain ?? "") { _, _ in completion(error) } } } else { bitwardenManagement.update(credential: bitwardenCredential) { [weak self] error in - self?.websiteCredentialsFor(domain: credentials.account.domain) { _, _ in + self?.websiteCredentialsFor(domain: credentials.account.domain ?? "") { _, _ in completion(error) } } @@ -315,12 +315,16 @@ extension BrowserServicesKit.SecureVaultModels.WebsiteCredentials { extension BWCredential { init?(from websiteCredentials: BrowserServicesKit.SecureVaultModels.WebsiteCredentials, vault: BWVault) { + guard let domain = websiteCredentials.account.domain else { + return nil + } + self.init(userId: vault.id, credentialId: websiteCredentials.account.id, - credentialName: websiteCredentials.account.domain, + credentialName: domain, username: websiteCredentials.account.username, - password: websiteCredentials.password.utf8String() ?? "", - domain: websiteCredentials.account.domain) + password: websiteCredentials.password?.utf8String() ?? "", + domain: domain) } } diff --git a/DuckDuckGo/SecureVault/Model/PasswordManagementItemListModel.swift b/DuckDuckGo/SecureVault/Model/PasswordManagementItemListModel.swift index 34fdb4baad..c7a33820db 100644 --- a/DuckDuckGo/SecureVault/Model/PasswordManagementItemListModel.swift +++ b/DuckDuckGo/SecureVault/Model/PasswordManagementItemListModel.swift @@ -99,10 +99,9 @@ enum SecureVaultItem: Equatable, Identifiable, Comparable { func item(matches filter: String) -> Bool { switch self { case .account(let account): - return account.domain.lowercased().contains(filter) || - account.username.lowercased().contains(filter) || - account.title?.lowercased().contains(filter) ?? false || - account.notes?.lowercased().contains(filter) ?? false + return account.domain?.lowercased().contains(filter) == true || + account.username?.lowercased().contains(filter) == true || + account.title?.lowercased().contains(filter) == true case .card(let card): return card.title.localizedCaseInsensitiveContains(filter) case .identity(let identity): @@ -110,7 +109,7 @@ enum SecureVaultItem: Equatable, Identifiable, Comparable { case .note(let note): return note.title.localizedCaseInsensitiveContains(filter) || note.text.localizedCaseInsensitiveContains(filter) || - (note.associatedDomain?.localizedCaseInsensitiveContains(filter) ?? false) + (note.associatedDomain?.localizedCaseInsensitiveContains(filter) == true) } } @@ -131,7 +130,7 @@ enum SecureVaultItem: Equatable, Identifiable, Comparable { var displaySubtitle: String { switch self { case .account(let account): - return account.username + return account.username ?? "" case .card(let creditCard): return creditCard.displayName case .identity(let identity): @@ -307,8 +306,10 @@ final class PasswordManagementItemListModel: ObservableObject { for section in displayedItems { if let first = section.items.first(where: { $0 == item }) { selected(item: first, notify: notify) + return } } + selectFirst() } func selectLoginWithDomainOrFirst(domain: String, notify: Bool = true) { diff --git a/DuckDuckGo/SecureVault/Model/PasswordManagementLoginModel.swift b/DuckDuckGo/SecureVault/Model/PasswordManagementLoginModel.swift index 157690c867..7e7cec66d1 100644 --- a/DuckDuckGo/SecureVault/Model/PasswordManagementLoginModel.swift +++ b/DuckDuckGo/SecureVault/Model/PasswordManagementLoginModel.swift @@ -54,7 +54,7 @@ final class PasswordManagementLoginModel: ObservableObject, PasswordManagementIt @Published var isNew = false var isDirty: Bool { - username != "" || password != "" || domain != "" + title != "" || username != "" || password != "" || domain != "" || notes != "" } var lastUpdatedDate: String = "" diff --git a/DuckDuckGo/SecureVault/View/PasswordManagementViewController.swift b/DuckDuckGo/SecureVault/View/PasswordManagementViewController.swift index b3e4de0d98..a071ff67f4 100644 --- a/DuckDuckGo/SecureVault/View/PasswordManagementViewController.swift +++ b/DuckDuckGo/SecureVault/View/PasswordManagementViewController.swift @@ -18,6 +18,8 @@ import Foundation import Combine +import Common +import DDGSync import SwiftUI import BrowserServicesKit import SecureStorage @@ -101,6 +103,7 @@ final class PasswordManagementViewController: NSViewController { var emptyStateCancellable: AnyCancellable? var editingCancellable: AnyCancellable? var appearanceCancellable: AnyCancellable? + var reloadDataAfterSyncCancellable: AnyCancellable? var domain: String? var isEditing = false @@ -152,6 +155,7 @@ final class PasswordManagementViewController: NSViewController { createLoginItemView() appearanceCancellable = view.subscribeForAppApperanceUpdates() + reloadDataAfterSyncCancellable = bindSyncDidFinish() emptyStateTitle.attributedStringValue = NSAttributedString.make(emptyStateTitle.stringValue, lineHeight: 1.14, kern: -0.23) emptyStateMessage.attributedStringValue = NSAttributedString.make(emptyStateMessage.stringValue, lineHeight: 1.05, kern: -0.08) @@ -173,9 +177,18 @@ final class PasswordManagementViewController: NSViewController { } } + private func bindSyncDidFinish() -> AnyCancellable? { + (NSApp.delegate as? AppDelegate)?.syncDataProviders?.credentialsAdapter.syncDidCompletePublisher + .receive(on: DispatchQueue.main) + .sink { [weak self] in + self?.refreshData() + } + } + private func toggleLockScreen(hidden: Bool) { if hidden { hideLockScreen() + requestSync() } else { displayLockScreen() } @@ -299,6 +312,8 @@ final class PasswordManagementViewController: NSViewController { } else if self?.isDirty == false { if let selectItemMatchingDomain = selectItemMatchingDomain { self?.listModel?.selectLoginWithDomainOrFirst(domain: selectItemMatchingDomain) + } else if let selectedItem = self?.listModel?.selected { + self?.listModel?.select(item: selectedItem) } else { self?.listModel?.selectFirst() } @@ -448,7 +463,17 @@ final class PasswordManagementViewController: NSViewController { private func doSaveCredentials(_ credentials: SecureVaultModels.WebsiteCredentials) { let isNew = credentials.account.id == nil + func showDuplicateAlert() { + if let window = view.window { + NSAlert.passwordManagerDuplicateLogin().beginSheetModal(for: window) + } + } + do { + if try secureVault?.hasAccountFor(username: credentials.account.username, domain: credentials.account.domain) == true && isNew { + showDuplicateAlert() + return + } guard let id = try secureVault?.storeWebsiteCredentials(credentials), let savedCredentials = try secureVault?.websiteCredentialsFor(accountId: id) else { return @@ -463,10 +488,13 @@ final class PasswordManagementViewController: NSViewController { syncModelsOnCredentials(savedCredentials) } postChange() + requestSync() } catch { - if let window = view.window, case SecureStorageError.duplicateRecord = error { - NSAlert.passwordManagerDuplicateLogin().beginSheetModal(for: window) + if case SecureStorageError.duplicateRecord = error { + showDuplicateAlert() + } else { + Pixel.fire(.debug(event: .secureVaultError, error: error)) } } } @@ -489,7 +517,7 @@ final class PasswordManagementViewController: NSViewController { postChange() } catch { - // Which errors can occur when saving identities? + Pixel.fire(.debug(event: .secureVaultError, error: error)) } } @@ -533,7 +561,7 @@ final class PasswordManagementViewController: NSViewController { postChange() } catch { - // Which errors can occur when saving cards? + Pixel.fire(.debug(event: .secureVaultError, error: error)) } } @@ -547,8 +575,13 @@ final class PasswordManagementViewController: NSViewController { switch response { case .alertFirstButtonReturn: - try? self.secureVault?.deleteWebsiteCredentialsFor(accountId: id) - self.refreshData() + do { + try self.secureVault?.deleteWebsiteCredentialsFor(accountId: id) + self.requestSync() + self.refreshData() + } catch { + Pixel.fire(.debug(event: .secureVaultError, error: error)) + } default: break // cancel, do nothing @@ -566,8 +599,12 @@ final class PasswordManagementViewController: NSViewController { switch response { case .alertFirstButtonReturn: - try? self.secureVault?.deleteIdentityFor(identityId: id) - self.refreshData() + do { + try self.secureVault?.deleteIdentityFor(identityId: id) + self.refreshData() + } catch { + Pixel.fire(.debug(event: .secureVaultError, error: error)) + } default: break // cancel, do nothing @@ -604,8 +641,12 @@ final class PasswordManagementViewController: NSViewController { switch response { case .alertFirstButtonReturn: - try? self.secureVault?.deleteCreditCardFor(cardId: id) - self.refreshData() + do { + try self.secureVault?.deleteCreditCardFor(cardId: id) + self.refreshData() + } catch { + Pixel.fire(.debug(event: .secureVaultError, error: error)) + } default: break // cancel, do nothing @@ -635,23 +676,27 @@ final class PasswordManagementViewController: NSViewController { } func loadNewItemWithID() { - switch newValue { - case .account: - guard let credentials = try? self?.secureVault?.websiteCredentialsFor(accountId: id) else { return } - self?.createLoginItemView() - self?.syncModelsOnCredentials(credentials) - case .card: - guard let card = try? self?.secureVault?.creditCardFor(id: id) else { return } - self?.createCreditCardItemView() - self?.syncModelsOnCreditCard(card) - case .identity: - guard let identity = try? self?.secureVault?.identityFor(id: id) else { return } - self?.createIdentityItemView() - self?.syncModelsOnIdentity(identity) - case .note: - guard let note = try? self?.secureVault?.noteFor(id: id) else { return } - self?.createNoteItemView() - self?.syncModelsOnNote(note) + do { + switch newValue { + case .account: + guard let credentials = try self?.secureVault?.websiteCredentialsFor(accountId: id) else { return } + self?.createLoginItemView() + self?.syncModelsOnCredentials(credentials) + case .card: + guard let card = try self?.secureVault?.creditCardFor(id: id) else { return } + self?.createCreditCardItemView() + self?.syncModelsOnCreditCard(card) + case .identity: + guard let identity = try self?.secureVault?.identityFor(id: id) else { return } + self?.createIdentityItemView() + self?.syncModelsOnIdentity(identity) + case .note: + guard let note = try self?.secureVault?.noteFor(id: id) else { return } + self?.createNoteItemView() + self?.syncModelsOnNote(note) + } + } catch { + Pixel.fire(.debug(event: .secureVaultError, error: error)) } } @@ -730,28 +775,32 @@ final class PasswordManagementViewController: NSViewController { private func fetchSecureVaultItems(category: SecureVaultSorting.Category = .allItems, completion: @escaping ([SecureVaultItem]) -> Void) { DispatchQueue.global(qos: .userInitiated).async { - var items: [SecureVaultItem] - - switch category { - case .allItems: - let accounts = (try? self.secureVault?.accounts()) ?? [] - let cards = (try? self.secureVault?.creditCards()) ?? [] - let notes = (try? self.secureVault?.notes()) ?? [] - let identities = (try? self.secureVault?.identities()) ?? [] - - items = accounts.map(SecureVaultItem.account) + - cards.map(SecureVaultItem.card) + - notes.map(SecureVaultItem.note) + - identities.map(SecureVaultItem.identity) - case .logins: - let accounts = (try? self.secureVault?.accounts()) ?? [] - items = accounts.map(SecureVaultItem.account) - case .identities: - let identities = (try? self.secureVault?.identities()) ?? [] - items = identities.map(SecureVaultItem.identity) - case .cards: - let cards = (try? self.secureVault?.creditCards()) ?? [] - items = cards.map(SecureVaultItem.card) + var items: [SecureVaultItem] = [] + + do { + switch category { + case .allItems: + let accounts = try self.secureVault?.accounts() ?? [] + let cards = try self.secureVault?.creditCards() ?? [] + let notes = try self.secureVault?.notes() ?? [] + let identities = try self.secureVault?.identities() ?? [] + + items = accounts.map(SecureVaultItem.account) + + cards.map(SecureVaultItem.card) + + notes.map(SecureVaultItem.note) + + identities.map(SecureVaultItem.identity) + case .logins: + let accounts = try self.secureVault?.accounts() ?? [] + items = accounts.map(SecureVaultItem.account) + case .identities: + let identities = try self.secureVault?.identities() ?? [] + items = identities.map(SecureVaultItem.identity) + case .cards: + let cards = try self.secureVault?.creditCards() ?? [] + items = cards.map(SecureVaultItem.card) + } + } catch { + Pixel.fire(.debug(event: .secureVaultError, error: error)) } DispatchQueue.main.async { @@ -910,6 +959,14 @@ final class PasswordManagementViewController: NSViewController { emptyStateButton.isHidden = true } + private func requestSync() { + guard let syncService = (NSApp.delegate as? AppDelegate)?.syncService else { + return + } + os_log(.debug, log: OSLog.sync, "Requesting sync if enabled") + syncService.scheduler.requestSyncImmediately() + } + } extension PasswordManagementViewController: NSMenuDelegate { diff --git a/DuckDuckGo/SecureVault/View/SaveCredentialsViewController.swift b/DuckDuckGo/SecureVault/View/SaveCredentialsViewController.swift index 39d0e4fabe..3e1ec33832 100644 --- a/DuckDuckGo/SecureVault/View/SaveCredentialsViewController.swift +++ b/DuckDuckGo/SecureVault/View/SaveCredentialsViewController.swift @@ -100,16 +100,20 @@ final class SaveCredentialsViewController: NSViewController { /// Note that if the credentials.account.id is not nil, then we consider this an update rather than a save. func update(credentials: SecureVaultModels.WebsiteCredentials, automaticallySaved: Bool) { self.credentials = credentials - self.domainLabel.stringValue = credentials.account.domain - self.usernameField.stringValue = credentials.account.username - self.hiddenPasswordField.stringValue = String(data: credentials.password, encoding: .utf8) ?? "" + self.domainLabel.stringValue = credentials.account.domain ?? "" + self.usernameField.stringValue = credentials.account.username ?? "" + self.hiddenPasswordField.stringValue = String(data: credentials.password ?? Data(), encoding: .utf8) ?? "" self.visiblePasswordField.stringValue = self.hiddenPasswordField.stringValue self.loadFaviconForDomain(credentials.account.domain) - fireproofCheck.state = FireproofDomains.shared.isFireproof(fireproofDomain: credentials.account.domain) ? .on : .off + if let domain = credentials.account.domain, FireproofDomains.shared.isFireproof(fireproofDomain: domain) { + fireproofCheck.state = .on + } else { + fireproofCheck.state = .off + } // Only use the non-editable state if a credential was automatically saved and it didn't already exist. - let condition = credentials.account.id != nil && !credentials.account.username.isEmpty && automaticallySaved + let condition = credentials.account.id != nil && !(credentials.account.username?.isEmpty ?? true) && automaticallySaved updateViewState(editable: !condition) } @@ -173,22 +177,28 @@ final class SaveCredentialsViewController: NSViewController { } } else { _ = try AutofillSecureVaultFactory.makeVault(errorReporter: SecureVaultErrorReporter.shared).storeWebsiteCredentials(credentials) + (NSApp.delegate as? AppDelegate)?.syncService?.scheduler.notifyDataChanged() + os_log(.debug, log: OSLog.sync, "Requesting sync if enabled") } } catch { os_log("%s:%s: failed to store credentials %s", type: .error, className, #function, error.localizedDescription) + Pixel.fire(.debug(event: .secureVaultError, error: error)) } Pixel.fire(.autofillItemSaved(kind: .password)) + if passwordManagerCoordinator.isEnabled { passwordManagerCoordinator.reportPasswordSave() } - if self.fireproofCheck.state == .on { - FireproofDomains.shared.add(domain: account.domain) - } else { - // If the Fireproof checkbox has been unchecked, and the domain is Fireproof, then un-Fireproof it. - guard FireproofDomains.shared.isFireproof(fireproofDomain: account.domain) else { return } - FireproofDomains.shared.remove(domain: account.domain) + if let domain = account.domain { + if self.fireproofCheck.state == .on { + FireproofDomains.shared.add(domain: domain) + } else { + // If the Fireproof checkbox has been unchecked, and the domain is Fireproof, then un-Fireproof it. + guard FireproofDomains.shared.isFireproof(fireproofDomain: domain) else { return } + FireproofDomains.shared.remove(domain: domain) + } } } @@ -245,7 +255,11 @@ final class SaveCredentialsViewController: NSViewController { updatePasswordFieldVisibility(visible: !hiddenPasswordField.isHidden) } - func loadFaviconForDomain(_ domain: String) { + func loadFaviconForDomain(_ domain: String?) { + guard let domain else { + faviconImage.image = NSImage(named: NSImage.Name("Web")) + return + } faviconImage.image = faviconManagement.getCachedFavicon(for: domain, sizeCategory: .small)?.image ?? NSImage(named: NSImage.Name("Web")) } diff --git a/DuckDuckGo/SecureVault/View/SaveIdentityViewController.swift b/DuckDuckGo/SecureVault/View/SaveIdentityViewController.swift index d11ca73d3b..01bf91e6bd 100644 --- a/DuckDuckGo/SecureVault/View/SaveIdentityViewController.swift +++ b/DuckDuckGo/SecureVault/View/SaveIdentityViewController.swift @@ -72,6 +72,7 @@ final class SaveIdentityViewController: NSViewController { Pixel.fire(.autofillItemSaved(kind: .identity)) } catch { os_log("%s:%s: failed to store identity %s", type: .error, className, #function, error.localizedDescription) + Pixel.fire(.debug(event: .secureVaultError, error: error)) } } diff --git a/DuckDuckGo/SecureVault/View/SavePaymentMethodViewController.swift b/DuckDuckGo/SecureVault/View/SavePaymentMethodViewController.swift index d5a8b7b8af..9d686a6788 100644 --- a/DuckDuckGo/SecureVault/View/SavePaymentMethodViewController.swift +++ b/DuckDuckGo/SecureVault/View/SavePaymentMethodViewController.swift @@ -87,6 +87,7 @@ final class SavePaymentMethodViewController: NSViewController { try AutofillSecureVaultFactory.makeVault(errorReporter: SecureVaultErrorReporter.shared).storeCreditCard(paymentMethod) } catch { os_log("%s:%s: failed to store payment method %s", type: .error, className, #function, error.localizedDescription) + Pixel.fire(.debug(event: .secureVaultError, error: error)) } } diff --git a/DuckDuckGo/Statistics/PixelEvent.swift b/DuckDuckGo/Statistics/PixelEvent.swift index 590cc28df0..5e7c5042d2 100644 --- a/DuckDuckGo/Statistics/PixelEvent.swift +++ b/DuckDuckGo/Statistics/PixelEvent.swift @@ -251,7 +251,6 @@ extension Pixel { case missingParent case bookmarksSaveFailed case bookmarksSaveFailedOnImport - case bookmarksCleanupFailed case orphanedBookmarksPresent case bookmarksCouldNotLoadDatabase @@ -266,6 +265,14 @@ extension Pixel { case syncMetadataCouldNotLoadDatabase case syncBookmarksProviderInitializationFailed case syncBookmarksFailed + case syncCredentialsProviderInitializationFailed + case syncCredentialsFailed + + case bookmarksCleanupFailed + case bookmarksCleanupAttemptedWhileSyncWasEnabled + + case credentialsDatabaseCleanupFailed + case credentialsCleanupAttemptedWhileSyncWasEnabled case invalidPayload(Configuration) @@ -591,7 +598,6 @@ extension Pixel.Event.Debug { case .missingParent: return "bookmark_missing_parent" case .bookmarksSaveFailed: return "bookmarks_save_failed" case .bookmarksSaveFailedOnImport: return "bookmarks_save_failed_on_import" - case .bookmarksCleanupFailed: return "bookmarks_cleanup_failed" case .orphanedBookmarksPresent: return "bookmarks_orphans_present" case .bookmarksCouldNotLoadDatabase: return "bookmarks_could_not_load_database" @@ -607,6 +613,14 @@ extension Pixel.Event.Debug { case .syncMetadataCouldNotLoadDatabase: return "sync_metadata_could_not_load_database" case .syncBookmarksProviderInitializationFailed: return "sync_bookmarks_provider_initialization_failed" case .syncBookmarksFailed: return "sync_bookmarks_failed" + case .syncCredentialsProviderInitializationFailed: return "sync_credentials_provider_initialization_failed" + case .syncCredentialsFailed: return "sync_credentials_failed" + + case .bookmarksCleanupFailed: return "bookmarks_cleanup_failed" + case .bookmarksCleanupAttemptedWhileSyncWasEnabled: return "bookmarks_cleanup_attempted_while_sync_was_enabled" + + case .credentialsDatabaseCleanupFailed: return "credentials_database_cleanup_failed" + case .credentialsCleanupAttemptedWhileSyncWasEnabled: return "credentials_cleanup_attempted_while_sync_was_enabled" case .invalidPayload(let configuration): return "m_d_\(configuration.rawValue)_invalid_payload".lowercased() diff --git a/DuckDuckGo/Sync/CredentialsCleanupErrorHandling.swift b/DuckDuckGo/Sync/CredentialsCleanupErrorHandling.swift new file mode 100644 index 0000000000..bf5a66377a --- /dev/null +++ b/DuckDuckGo/Sync/CredentialsCleanupErrorHandling.swift @@ -0,0 +1,42 @@ +// +// CredentialsCleanupErrorHandling.swift +// +// Copyright © 2023 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation +import BrowserServicesKit +import Common +import Persistence + +public class CredentialsCleanupErrorHandling: EventMapping { + + public init() { + super.init { event, _, _, _ in + if event.cleanupError is CredentialsCleanupErrorHandling { + Pixel.fire(.debug(event: .credentialsCleanupAttemptedWhileSyncWasEnabled)) + } else { + let processedErrors = CoreDataErrorsParser.parse(error: event.cleanupError as NSError) + let params = processedErrors.errorPixelParameters + + Pixel.fire(.debug(event: .credentialsDatabaseCleanupFailed, error: event.cleanupError), withAdditionalParameters: params) + } + } + } + + override init(mapping: @escaping EventMapping.Mapping) { + fatalError("Use init()") + } +} diff --git a/DuckDuckGo/Sync/SyncCredentialsAdapter.swift b/DuckDuckGo/Sync/SyncCredentialsAdapter.swift new file mode 100644 index 0000000000..a44400966b --- /dev/null +++ b/DuckDuckGo/Sync/SyncCredentialsAdapter.swift @@ -0,0 +1,93 @@ +// +// SyncCredentialsAdapter.swift +// +// Copyright © 2023 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import BrowserServicesKit +import Combine +import Common +import DDGSync +import Persistence +import SyncDataProviders + +final class SyncCredentialsAdapter { + + private(set) var provider: CredentialsProvider? + let databaseCleaner: CredentialsDatabaseCleaner + let syncDidCompletePublisher: AnyPublisher + + init(secureVaultFactory: AutofillVaultFactory = AutofillSecureVaultFactory) { + syncDidCompletePublisher = syncDidCompleteSubject.eraseToAnyPublisher() + databaseCleaner = CredentialsDatabaseCleaner( + secureVaultFactory: secureVaultFactory, + secureVaultErrorReporter: SecureVaultErrorReporter.shared, + errorEvents: CredentialsCleanupErrorHandling(), + log: .passwordManager + ) + } + + func updateDatabaseCleanupSchedule(shouldEnable: Bool) { + databaseCleaner.cleanUpDatabaseNow() + if shouldEnable { + databaseCleaner.scheduleRegularCleaning() + } else { + databaseCleaner.cancelCleaningSchedule() + } + } + + func setUpProviderIfNeeded(secureVaultFactory: AutofillVaultFactory, metadataStore: SyncMetadataStore) { + guard provider == nil else { + return + } + + do { + let provider = try CredentialsProvider( + secureVaultFactory: secureVaultFactory, + secureVaultErrorReporter: SecureVaultErrorReporter.shared, + metadataStore: metadataStore, + syncDidUpdateData: { [weak self] in + self?.syncDidCompleteSubject.send() + } + ) + + syncErrorCancellable = provider.syncErrorPublisher + .sink { error in + switch error { + case let syncError as SyncError: + Pixel.fire(.debug(event: .syncCredentialsFailed, error: syncError)) + default: + let nsError = error as NSError + if nsError.domain != NSURLErrorDomain { + let processedErrors = CoreDataErrorsParser.parse(error: error as NSError) + let params = processedErrors.errorPixelParameters + Pixel.fire(.debug(event: .syncCredentialsFailed, error: error), withAdditionalParameters: params) + } + } + os_log(.error, log: OSLog.sync, "Credentials Sync error: %{public}s", String(reflecting: error)) + } + + self.provider = provider + + } catch let error as NSError { + let processedErrors = CoreDataErrorsParser.parse(error: error) + let params = processedErrors.errorPixelParameters + Pixel.fire(.debug(event: .syncCredentialsProviderInitializationFailed, error: error), withAdditionalParameters: params) + } + } + + private var syncDidCompleteSubject = PassthroughSubject() + private var syncErrorCancellable: AnyCancellable? +} diff --git a/DuckDuckGo/Sync/SyncDataProviders.swift b/DuckDuckGo/Sync/SyncDataProviders.swift index 100cbf7f6a..8f2f11ba08 100644 --- a/DuckDuckGo/Sync/SyncDataProviders.swift +++ b/DuckDuckGo/Sync/SyncDataProviders.swift @@ -16,6 +16,7 @@ // limitations under the License. // +import BrowserServicesKit import Common import DDGSync import Persistence @@ -23,6 +24,7 @@ import SyncDataProviders final class SyncDataProviders: DataProvidersSource { public let bookmarksAdapter: SyncBookmarksAdapter + public let credentialsAdapter: SyncCredentialsAdapter func makeDataProviders() -> [DataProviding] { initializeMetadataDatabaseIfNeeded() @@ -32,12 +34,21 @@ final class SyncDataProviders: DataProvidersSource { } bookmarksAdapter.setUpProviderIfNeeded(database: bookmarksDatabase, metadataStore: syncMetadata) - return [bookmarksAdapter.provider].compactMap { $0 } + credentialsAdapter.setUpProviderIfNeeded(secureVaultFactory: secureVaultFactory, metadataStore: syncMetadata) + + let providers: [Any] = [ + bookmarksAdapter.provider as Any, + credentialsAdapter.provider as Any + ] + + return providers.compactMap { $0 as? DataProviding } } - init(bookmarksDatabase: CoreDataDatabase) { + init(bookmarksDatabase: CoreDataDatabase, secureVaultFactory: AutofillVaultFactory = AutofillSecureVaultFactory) { self.bookmarksDatabase = bookmarksDatabase + self.secureVaultFactory = secureVaultFactory bookmarksAdapter = SyncBookmarksAdapter() + credentialsAdapter = SyncCredentialsAdapter(secureVaultFactory: secureVaultFactory) } private func initializeMetadataDatabaseIfNeeded() { @@ -54,7 +65,7 @@ final class SyncDataProviders: DataProvidersSource { } Thread.sleep(forTimeInterval: 1) - fatalError("Could not create Bookmarks database stack: \(error?.localizedDescription ?? "err")") + fatalError("Could not create Sync Metadata database stack: \(error?.localizedDescription ?? "err")") } } syncMetadata = LocalSyncMetadataStore(database: syncMetadataDatabase.db) @@ -66,4 +77,5 @@ final class SyncDataProviders: DataProvidersSource { private let syncMetadataDatabase: SyncMetadataDatabase = SyncMetadataDatabase() private let bookmarksDatabase: CoreDataDatabase + private let secureVaultFactory: AutofillVaultFactory } diff --git a/LocalPackages/NetworkProtectionUI/Package.swift b/LocalPackages/NetworkProtectionUI/Package.swift index 35d5394879..5eb6f0f614 100644 --- a/LocalPackages/NetworkProtectionUI/Package.swift +++ b/LocalPackages/NetworkProtectionUI/Package.swift @@ -15,7 +15,7 @@ let package = Package( targets: ["NetworkProtectionUI"]) ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "73.0.1"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "74.0.0"), .package(path: "../SwiftUIExtensions") ], targets: [ diff --git a/LocalPackages/SyncUI/Sources/SyncUI/Views/ManagementView.swift b/LocalPackages/SyncUI/Sources/SyncUI/Views/ManagementView.swift index 0ecd45c67c..8c51854651 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/Views/ManagementView.swift +++ b/LocalPackages/SyncUI/Sources/SyncUI/Views/ManagementView.swift @@ -62,7 +62,7 @@ public struct ManagementView: View where ViewModel: ManagementViewMod .foregroundColor(.black) // swiftlint:disable line_length - Text("This feature is viewable to internal users only and is still being developed and tested. Currently you can create accounts, connect and manage devices, and sync bookmarks and favorites. **[More Info](https://app.asana.com/0/1201493110486074/1203756800930481/f)**") + Text("This feature is viewable to internal users only and is still being developed and tested. Currently you can create accounts, connect and manage devices, and sync bookmarks, favorites and Autofill logins. **[More Info](https://app.asana.com/0/1201493110486074/1203756800930481/f)**") .foregroundColor(.black) .font(.system(size: 11, weight: .regular)) // swiftlint:enable line_length diff --git a/LocalPackages/SyncUI/Sources/SyncUI/internal/UserText.swift b/LocalPackages/SyncUI/Sources/SyncUI/internal/UserText.swift index 57f657c098..3a5ce1fa72 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/internal/UserText.swift +++ b/LocalPackages/SyncUI/Sources/SyncUI/internal/UserText.swift @@ -30,7 +30,7 @@ enum UserText { static let sync = NSLocalizedString("preferences.sync", value: "Sync", comment: "Show sync preferences") - static let syncSetupExplanation = NSLocalizedString("preferences.sync.setup-explanation", value: "Sync your bookmarks across your devices and save an encrypted backup on DuckDuckGo’s servers.", comment: "Sync setup explanation") + static let syncSetupExplanation = NSLocalizedString("preferences.sync.setup-explanation", value: "Sync your bookmarks and Autofill logins across your devices and save an encrypted backup on DuckDuckGo’s servers.", comment: "Sync setup explanation") static let turnOnSync = NSLocalizedString("preferences.sync.turn-on", value: "Turn on Sync", comment: "Enable sync button caption") static let turnOnSyncWithEllipsis = NSLocalizedString("preferences.sync.turn-on-ellipsis", value: "Turn on Sync...", comment: "Enable sync button caption") static let turnOff = NSLocalizedString("preferences.sync.turn-off", value: "Turn Off", comment: "Turn off sync confirmation dialog button title") @@ -61,15 +61,15 @@ enum UserText { static let saveRecoveryPDF = NSLocalizedString("prefrences.sync.save-recovery-pdf", value: "Save Recovery PDF", comment: "Caption for a button to save Sync recovery PDF") static let turnOnSyncQuestion = NSLocalizedString("preferences.sync.turn-on-question", value: "Turn on Sync?", comment: "Sync setup dialog title") - static let turnOnSyncExplanation1 = NSLocalizedString("preferences.sync.turn-on-explanation1", value: "This will save an encrypted backup of your bookmarks on DuckDuckGo’s servers, which can be synced with your other devices.", comment: "Sync setup dialog content") + static let turnOnSyncExplanation1 = NSLocalizedString("preferences.sync.turn-on-explanation1", value: "This will save an encrypted backup of your bookmarks and Autofill logins on DuckDuckGo’s servers, which can be synced with your other devices.", comment: "Sync setup dialog content") static let turnOnSyncExplanation2 = NSLocalizedString("preferences.sync.turn-on-explanation2", value: "The decryption key is stored on your device and cannot be read by DuckDuckGo.", comment: "Sync setup dialog content") static let recoverSyncedDataTitle = NSLocalizedString("preferences.sync.recover-synced-data", value: "Recover Synced Data", comment: "Sync setup dialog title") static let recoverSyncedDataExplanation = NSLocalizedString("preferences.sync.recover-synced-data-explanation", value: "Enter the code on your recovery PDF or another synced device below to recover your synced data.", comment: "Sync setup dialog content") static let syncAnotherDeviceTitle = NSLocalizedString("preferences.sync.sync-another-device-question", value: "Sync Another Device?", comment: "Sync setup dialog title") - static let syncAnotherDeviceExplanation1 = NSLocalizedString("preferences.sync.sync-another-device-explanation1", value: "Your bookmarks will be backed up! Would you like to sync with another device now?", comment: "Sync setup dialog content") - static let syncAnotherDeviceExplanation2 = NSLocalizedString("preferences.sync.sync-another-device-explanation2", value: "If you’ve already set up Sync on another device, this will allow you to combine bookmarks from both devices into a single backup.", comment: "Sync setup dialog content") + static let syncAnotherDeviceExplanation1 = NSLocalizedString("preferences.sync.sync-another-device-explanation1", value: "Your bookmarks and Autofill logins will be backed up! Would you like to sync with another device now?", comment: "Sync setup dialog content") + static let syncAnotherDeviceExplanation2 = NSLocalizedString("preferences.sync.sync-another-device-explanation2", value: "If you’ve already set up Sync on another device, this will allow you to combine bookmarks and Autofill logins from both devices into a single backup.", comment: "Sync setup dialog content") static let syncAnotherDevice = NSLocalizedString("preferences.sync.sync-another-device", value: "Sync Another Device", comment: "Button caption") static let showCode = NSLocalizedString("prefrences.sync.show-code", value: "Show Code", comment: "Button caption in Sync's add new device screen") @@ -78,7 +78,7 @@ enum UserText { static let syncNewDeviceEnterCodeInstructions = NSLocalizedString("prefrences.sync.sync-new-device-enter-code-instructions", value: "Enter the code on your Recovery PDF, or another synced device below to recover your synced data.", comment: "Instructions for adding a new device to sync") static let deviceSynced = NSLocalizedString("prefrences.sync.device-synced", value: "Device Synced!", comment: "Sync setup dialog title") - static let deviceSyncedExplanation = NSLocalizedString("prefrences.sync.device-synced-explanation", value: "Your bookmarks are now syncing with this device.", comment: "Sync setup completion confirmation") + static let deviceSyncedExplanation = NSLocalizedString("prefrences.sync.device-synced-explanation", value: "Your bookmarks and Autofill logins are now syncing with this device.", comment: "Sync setup completion confirmation") static let recoveryPDFExplanation1 = NSLocalizedString("prefrences.sync.recovery-pdf-explanation1", value: "If you lose access to your devices, you will need the code recover your synced data. You can save this code to your device as a PDF.", comment: "Sync recovery PDF explanation") static let recoveryPDFExplanation2 = NSLocalizedString("prefrences.sync.recovery-pdf-explanation2", value: "Anyone with access to this code can access your synced data, so please keep it in a safe place.", comment: "Sync recovery PDF explanation") diff --git a/UnitTests/DataExport/MockSecureVault.swift b/UnitTests/DataExport/MockSecureVault.swift index 77d38254fc..d178d01ca6 100644 --- a/UnitTests/DataExport/MockSecureVault.swift +++ b/UnitTests/DataExport/MockSecureVault.swift @@ -61,8 +61,8 @@ final class MockSecureVault: AutofillSecureVault { return storedAccounts.filter { $0.domain == domain } } - func accountsWithPartialMatchesFor(eTLDplus1: String) throws -> [BrowserServicesKit.SecureVaultModels.WebsiteAccount] { - return storedAccounts.filter { $0.domain.contains(eTLDplus1) } + func accountsWithPartialMatchesFor(eTLDplus1: String) throws -> [SecureVaultModels.WebsiteAccount] { + return storedAccounts.filter { $0.domain?.contains(eTLDplus1) ?? false } } func websiteCredentialsFor(accountId: Int64) throws -> SecureVaultModels.WebsiteCredentials? { @@ -139,6 +139,63 @@ final class MockSecureVault: AutofillSecureVault { return nil } + func inDatabaseTransaction(_ block: @escaping (Database) throws -> Void) throws {} + + func modifiedSyncableCredentials() throws -> [SecureVaultModels.SyncableCredentials] { + [] + } + + func deleteSyncableCredentials(_ syncableCredentials: SecureVaultModels.SyncableCredentials, in database: Database) throws { + if let accountId = syncableCredentials.metadata.objectId { + try deleteWebsiteCredentialsFor(accountId: accountId) + } + } + + func storeWebsiteCredentials(_ credentials: SecureVaultModels.WebsiteCredentials, clearModifiedAt: Bool) throws -> Int64 { + try storeWebsiteCredentials(credentials) + } + + func storeSyncableCredentials(_ syncableCredentials: SecureVaultModels.SyncableCredentials, in database: Database) throws {} + + func syncableCredentialsForSyncIds(_ syncIds: any Sequence, in database: Database) throws -> [SecureVaultModels.SyncableCredentials] { + [] + } + + func syncableCredentialsForAccountId(_ accountId: Int64, in database: Database) throws -> SecureVaultModels.SyncableCredentials? { + nil + } + + func accountsForDomain(_ domain: String, in database: Database) throws -> [SecureVaultModels.WebsiteAccount] { + try accountsFor(domain: domain) + } + + func getEncryptionKey() throws -> Data { + Data() + } + + func encrypt(_ data: Data, using key: Data) throws -> Data { + data + } + + func decrypt(_ data: Data, using key: Data) throws -> Data { + data + } + + func hasAccountFor(username: String?, domain: String?) throws -> Bool { + storedAccounts.contains(where: { $0.username == username && $0.domain == domain }) + } + + func getHashingSalt() throws -> Data? { + nil + } + + func storeWebsiteCredentials(_ credentials: SecureVaultModels.WebsiteCredentials, in database: Database, encryptedUsing l2Key: Data, hashedUsing salt: Data?) throws -> Int64 { + 1 + } + + func storeSyncableCredentials(_ syncableCredentials: SecureVaultModels.SyncableCredentials, in database: Database, encryptedUsing l2Key: Data, hashedUsing salt: Data?) throws { + } + } // MARK: - Mock Providers @@ -254,6 +311,43 @@ class MockDatabaseProvider: AutofillDatabaseProvider { func deleteCreditCardForCreditCardId(_ cardId: Int64) throws { _creditCards.removeValue(forKey: cardId) } + + func hasAccountFor(username: String?, domain: String?) throws -> Bool { + _accounts.contains(where: { $0.username == username && $0.domain == domain }) + } + + func inTransaction(_ block: @escaping (Database) throws -> Void) throws { + try db.write(block) + } + + func storeWebsiteCredentials(_ credentials: SecureVaultModels.WebsiteCredentials, in database: Database) throws -> Int64 { + 1 + } + + func modifiedSyncableCredentials() throws -> [SecureVaultModels.SyncableCredentials] { + [] + } + + func syncableCredentialsForSyncIds(_ syncIds: any Sequence, in database: Database) throws -> [SecureVaultModels.SyncableCredentials] { + [] + } + + func websiteCredentialsForAccountId(_ accountId: Int64, in database: Database) throws -> SecureVaultModels.WebsiteCredentials? { + nil + } + + func syncableCredentialsForAccountId(_ accountId: Int64, in database: Database) throws -> SecureVaultModels.SyncableCredentials? { + nil + } + + func storeSyncableCredentials(_ syncableCredentials: SecureVaultModels.SyncableCredentials, in database: Database) throws { + } + + func deleteSyncableCredentials(_ syncableCredentials: SecureVaultModels.SyncableCredentials, in database: Database) throws { + } + + func updateSyncTimestamp(in database: Database, tableName: String, objectId: Int64, timestamp: Date?) throws { + } } class MockCryptoProvider: SecureStorageCryptoProvider { From 0ad1a914697a0925fd5507f9381703f5dc0feb52 Mon Sep 17 00:00:00 2001 From: Sam Symons Date: Sun, 13 Aug 2023 12:04:07 -0700 Subject: [PATCH 17/17] Update NetP endpoint (#1459) Task/Issue URL: https://app.asana.com/0/1199230911884351/1205234536443673/f Tech Design URL: CC: Description: This PR updates the NetP endpoint via duckduckgo/BrowserServicesKit#456 --- DuckDuckGo.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/swiftpm/Package.resolved | 6 +++--- LocalPackages/NetworkProtectionUI/Package.swift | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 79f588ed82..48d0fec15c 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -10215,7 +10215,7 @@ repositoryURL = "https://github.com/duckduckgo/BrowserServicesKit"; requirement = { kind = exactVersion; - version = 74.0.0; + version = 74.0.1; }; }; AA06B6B52672AF8100F541C5 /* XCRemoteSwiftPackageReference "Sparkle" */ = { diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index c9b8311d08..caa11bcaaa 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -14,8 +14,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/duckduckgo/BrowserServicesKit", "state" : { - "revision" : "557c745b3df8694115c5384625e1dc9a5d02e70e", - "version" : "74.0.0" + "revision" : "da6b1eac1d4b4416eb82fb5648519aa5cb363a17", + "version" : "74.0.1" } }, { @@ -129,7 +129,7 @@ { "identity" : "trackerradarkit", "kind" : "remoteSourceControl", - "location" : "https://github.com/duckduckgo/TrackerRadarKit.git", + "location" : "https://github.com/duckduckgo/TrackerRadarKit", "state" : { "revision" : "4684440d03304e7638a2c8086895367e90987463", "version" : "1.2.1" diff --git a/LocalPackages/NetworkProtectionUI/Package.swift b/LocalPackages/NetworkProtectionUI/Package.swift index 5eb6f0f614..ee4f861bdf 100644 --- a/LocalPackages/NetworkProtectionUI/Package.swift +++ b/LocalPackages/NetworkProtectionUI/Package.swift @@ -15,7 +15,7 @@ let package = Package( targets: ["NetworkProtectionUI"]) ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "74.0.0"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "74.0.1"), .package(path: "../SwiftUIExtensions") ], targets: [