Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Push domain exclusions to internal release #3076

Merged
merged 4 commits into from
Aug 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 69 additions & 1 deletion DuckDuckGo.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/duckduckgo/BrowserServicesKit",
"state" : {
"revision" : "92ecebfb4172ab9561959a07d7ef7037aea8c6e1",
"version" : "180.0.0"
"revision" : "1dabef4fd0afa84317625d8327ee2bf30b173872",
"version" : "180.0.0-1"
}
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,12 @@
<Test
Identifier = "NavigationProtectionIntegrationTests/testReferrerTrimming()">
</Test>
<Test
Identifier = "TabContentTests/testWhenPDFContextMenuPrintChosen_printDialogOpens()">
</Test>
<Test
Identifier = "TabContentTests/testWhenPDFMainMenuPrintChosen_printDialogOpens()">
</Test>
Comment on lines +148 to +153
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flaky tests, there are tasks for these.

</SkippedTests>
</TestableReference>
<TestableReference
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,12 @@
<Test
Identifier = "NavigationProtectionIntegrationTests/testReferrerTrimming()">
</Test>
<Test
Identifier = "TabContentTests/testWhenPDFContextMenuPrintChosen_printDialogOpens()">
</Test>
<Test
Identifier = "TabContentTests/testWhenPDFContextMenuSaveAsChosen_saveDialogOpens()">
</Test>
</SkippedTests>
</TestableReference>
<TestableReference
Expand Down
39 changes: 6 additions & 33 deletions DuckDuckGo/Application/URLEventHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ import DataBrokerProtection
// @MainActor
final class URLEventHandler {

@MainActor
private static let vpnURLEventHandler = VPNURLEventHandler()

private let handler: (URL) -> Void

private var didFinishLaunching = false
Expand Down Expand Up @@ -109,7 +112,9 @@ final class URLEventHandler {

private static func openURL(_ url: URL) {
if url.scheme?.isNetworkProtectionScheme == true {
handleNetworkProtectionURL(url)
Task { @MainActor in
await vpnURLEventHandler.handle(url)
}
}

#if DBP
Expand Down Expand Up @@ -141,38 +146,6 @@ final class URLEventHandler {
}
}

/// Handles NetP URLs
private static func handleNetworkProtectionURL(_ url: URL) {
DispatchQueue.main.async {
switch url {
case VPNAppLaunchCommand.showStatus.launchURL:
Task {
await WindowControllersManager.shared.showNetworkProtectionStatus()
}
case VPNAppLaunchCommand.showSettings.launchURL:
WindowControllersManager.shared.showPreferencesTab(withSelectedPane: .vpn)
case VPNAppLaunchCommand.shareFeedback.launchURL:
WindowControllersManager.shared.showShareFeedbackModal()
case VPNAppLaunchCommand.justOpen.launchURL:
WindowControllersManager.shared.showMainWindow()
case VPNAppLaunchCommand.showVPNLocations.launchURL:
WindowControllersManager.shared.showPreferencesTab(withSelectedPane: .vpn)
WindowControllersManager.shared.showLocationPickerSheet()
case VPNAppLaunchCommand.showPrivacyPro.launchURL:
let url = Application.appDelegate.subscriptionManager.url(for: .purchase)
WindowControllersManager.shared.showTab(with: .subscription(url))
PixelKit.fire(PrivacyProPixel.privacyProOfferScreenImpression)
#if !APPSTORE && !DEBUG
case VPNAppLaunchCommand.moveAppToApplications.launchURL:
// this should be run after NSApplication.shared is set
PFMoveToApplicationsFolderIfNecessary(false)
#endif
default:
return
}
}
}

#if DBP
/// Handles DBP URLs
///
Expand Down
12 changes: 12 additions & 0 deletions DuckDuckGo/Common/Localizables/UserText+NetworkProtection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ extension UserText {
// MARK: - Setting Titles
// "vpn.location.title" - Location section title in VPN settings
static let vpnLocationTitle = "Location"
// "vpn.excluded.sites.title" - Excluded Sites title in VPN settings
static let vpnExcludedSitesTitle = "Excluded Sites"
// "vpn.general.title" - General section title in VPN settings
static let vpnGeneralTitle = "General"
// "vpn.shortcuts.settings.title" - Shortcuts section title in VPN settings
Expand Down Expand Up @@ -161,6 +163,16 @@ extension UserText {
return String(format: message, count)
}

// MARK: - Excluded Domains
// "vpn.setting.excluded.domains.description" - Excluded Sites description
static let vpnExcludedDomainsDescription = "Websites you selected to be excluded even when the VPN is connected."
// "vpn.setting.excluded.domains.manage.button.title" - Excluded Sites management button title
static let vpnExcludedDomainsManageButtonTitle = "Manage Excluded Sites…"
// "vpn.excluded.domains.add.domain" - Add Domain button for the excluded sites view
static let vpnExcludedDomainsAddDomain = "Add Website"
// "vpn.excluded.domains.title" - Title for the excluded sites view
static let vpnExcludedDomainsTitle = "Excluded Websites"

// MARK: - DNS
// "vpn.dns.server.title" - Title of the DNS Server section
static let vpnDnsServerTitle = "DNS Server"
Expand Down
20 changes: 14 additions & 6 deletions DuckDuckGo/NavigationBar/View/NavigationBarPopovers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ protocol PopoverPresenter {
func show(_ popover: NSPopover, positionedBelow view: NSView)
}

@MainActor
protocol NetPPopoverManager: AnyObject {
var isShown: Bool { get }

Expand Down Expand Up @@ -133,8 +134,10 @@ final class NavigationBarPopovers: NSObject, PopoverPresenter {
}

func toggleNetworkProtectionPopover(from button: MouseOverButton, withDelegate delegate: NSPopoverDelegate) {
if let popover = networkProtectionPopoverManager.toggle(positionedBelow: button, withDelegate: delegate) {
bindIsMouseDownState(of: button, to: popover)
Task { @MainActor in
if let popover = networkProtectionPopoverManager.toggle(positionedBelow: button, withDelegate: delegate) {
bindIsMouseDownState(of: button, to: popover)
}
}
}

Expand Down Expand Up @@ -199,8 +202,10 @@ final class NavigationBarPopovers: NSObject, PopoverPresenter {
downloadsPopover?.close()
}

if networkProtectionPopoverManager.isShown {
networkProtectionPopoverManager.close()
Task { @MainActor in
if networkProtectionPopoverManager.isShown {
networkProtectionPopoverManager.close()
}
}

if bookmarkPopover?.isShown ?? false {
Expand Down Expand Up @@ -432,8 +437,11 @@ final class NavigationBarPopovers: NSObject, PopoverPresenter {
// MARK: - VPN

func showNetworkProtectionPopover(positionedBelow button: MouseOverButton, withDelegate delegate: NSPopoverDelegate) {
let popover = networkProtectionPopoverManager.show(positionedBelow: button, withDelegate: delegate)
bindIsMouseDownState(of: button, to: popover)

Task { @MainActor in
let popover = networkProtectionPopoverManager.show(positionedBelow: button, withDelegate: delegate)
bindIsMouseDownState(of: button, to: popover)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
//
// ActiveDomainPublisher.swift
//
// Copyright © 2024 DuckDuckGo. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

import Combine
import Foundation

/// A convenience class for publishing the active domain
///
/// The active domain is the domain loaded in the last active tab within the last active window.
///
final class ActiveDomainPublisher {

private let windowControllersManager: WindowControllersManager
private var activeWindowControllerCancellable: AnyCancellable?
private var activeTabViewModelCancellable: AnyCancellable?
private var activeTabContentCancellable: AnyCancellable?

@MainActor
@Published
private var activeWindowController: MainWindowController? {
didSet {
subscribeToActiveTabViewModel()
}
}

@MainActor
@Published
private var activeTab: Tab? {
didSet {
subscribeToActiveTabContentChanges()
}
}

init(windowControllersManager: WindowControllersManager) {
self.windowControllersManager = windowControllersManager

Task { @MainActor in
subscribeToKeyWindowControllerChanges()
}
}

@Published
private(set) var activeDomain: String?

@MainActor
private func subscribeToKeyWindowControllerChanges() {
activeWindowControllerCancellable = windowControllersManager
.didChangeKeyWindowController
.prepend(windowControllersManager.lastKeyMainWindowController)
.assign(to: \.activeWindowController, onWeaklyHeld: self)
}

@MainActor
private func subscribeToActiveTabViewModel() {
activeTabViewModelCancellable = activeWindowController?.mainViewController.tabCollectionViewModel.$selectedTabViewModel
.map(\.?.tab)
.assign(to: \.activeTab, onWeaklyHeld: self)
}

@MainActor
private func subscribeToActiveTabContentChanges() {
activeTabContentCancellable = activeTab?.$content
.map(domain(from:))
.removeDuplicates()
.assign(to: \.activeDomain, onWeaklyHeld: self)
}

private func domain(from tabContent: Tab.TabContent) -> String? {
if case .url(let url, _, _) = tabContent {

return url.host
} else {
return nil
}
}
}

extension ActiveDomainPublisher: Publisher {
typealias Output = String?
typealias Failure = Never

func receive<S>(subscriber: S) where S: Subscriber, Never == S.Failure, String? == S.Input {
$activeDomain.subscribe(subscriber)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,14 @@ final class NetworkProtectionDebugMenu: NSMenu {

NSMenuItem.separator()

NSMenuItem(title: "Adapter") {
NSMenuItem(title: "Restart Adapter", action: #selector(NetworkProtectionDebugMenu.restartAdapter(_:)))
.targetting(self)

NSMenuItem(title: "Re-create Adapter", action: #selector(NetworkProtectionDebugMenu.restartAdapter(_:)))
.targetting(self)
}

NSMenuItem(title: "Tunnel Settings") {
shouldIncludeAllNetworksMenuItem
.targetting(self)
Expand Down Expand Up @@ -218,6 +226,18 @@ final class NetworkProtectionDebugMenu: NSMenu {
}
}

/// Removes the system extension and agents for DuckDuckGo VPN.
///
@objc func restartAdapter(_ sender: Any?) {
Task { @MainActor in
do {
try await debugUtilities.restartAdapter()
} catch {
await NSAlert(error: error).runModal()
}
}
}

/// Sends a test user notification.
///
@objc func sendTestNotification(_ sender: Any?) {
Expand Down Expand Up @@ -449,8 +469,8 @@ final class NetworkProtectionDebugMenu: NSMenu {
private let ddgBrowserAppIdentifier = Bundle.main.bundleIdentifier!

private func updateExclusionsMenu() {
excludeDBPTrafficFromVPN.state = transparentProxySettings.isExcluding(dbpBackgroundAppIdentifier) ? .on : .off
excludeDDGBrowserTrafficFromVPN.state = transparentProxySettings.isExcluding(ddgBrowserAppIdentifier) ? .on : .off
excludeDBPTrafficFromVPN.state = transparentProxySettings.isExcluding(appIdentifier: dbpBackgroundAppIdentifier) ? .on : .off
excludeDDGBrowserTrafficFromVPN.state = transparentProxySettings.isExcluding(appIdentifier: ddgBrowserAppIdentifier) ? .on : .off
}

@objc private func toggleExcludeDBPBackgroundAgent() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ final class NetworkProtectionDebugUtilities {

// MARK: - Debug commands for the extension

func restartAdapter() async throws {
try await ipcClient.command(.restartAdapter)
}

func resetAllState(keepAuthToken: Bool) async throws {
try await vpnUninstaller.uninstall(removeSystemExtension: true)

Expand Down
Loading
Loading