Skip to content

Commit

Permalink
Merge branch 'main' into sam/vpn-snooze-initial-support
Browse files Browse the repository at this point in the history
* main:
  Bump Privacy Dashboard to v5.0.0 (#919)
  Push domain exclusions to internal release (#928)
  allow to register early scripts (#930)
  Handle contingency settings state on remote config (#926)
  VPN Domain exclusions (internal release) (#918)
  • Loading branch information
samsymons committed Aug 7, 2024
2 parents 01f19a2 + ff87c19 commit 71a50fc
Show file tree
Hide file tree
Showing 9 changed files with 437 additions and 8 deletions.
4 changes: 2 additions & 2 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/duckduckgo/privacy-dashboard",
"state" : {
"revision" : "348594efe2cd40ef156e915c272d02ec22f1903f",
"version" : "4.2.0"
"revision" : "36dc07cba4bc1e7e0c1d1fb679c3cd077694a072",
"version" : "5.0.0"
}
},
{
Expand Down
5 changes: 3 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ let package = Package(
.package(url: "https://github.com/duckduckgo/sync_crypto", exact: "0.2.0"),
.package(url: "https://github.com/gumob/PunycodeSwift.git", exact: "2.1.0"),
.package(url: "https://github.com/duckduckgo/content-scope-scripts", exact: "6.4.0"),
.package(url: "https://github.com/duckduckgo/privacy-dashboard", exact: "4.2.0"),
.package(url: "https://github.com/duckduckgo/privacy-dashboard", exact: "5.0.0"),
.package(url: "https://github.com/httpswift/swifter.git", exact: "1.5.0"),
.package(url: "https://github.com/duckduckgo/bloom_cpp.git", exact: "3.0.0"),
.package(url: "https://github.com/duckduckgo/wireguard-apple", exact: "1.1.3"),
Expand Down Expand Up @@ -384,7 +384,8 @@ let package = Package(
.target(
name: "DuckPlayer",
dependencies: [
"Common"
"Common",
"BrowserServicesKit"
],
swiftSettings: [
.define("DEBUG", .when(configuration: .debug))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,13 +112,17 @@ final public class UserContentController: WKUserContentController {
@MainActor
private let scriptMessageHandler = PermanentScriptMessageHandler()

/// if earlyAccessHandlers (WKScriptMessageHandlers) are provided they are installed without waiting for contentBlockingAssets to be loaded if.
@MainActor
public init<Pub, Content>(assetsPublisher: Pub, privacyConfigurationManager: PrivacyConfigurationManaging)
public init<Pub, Content>(assetsPublisher: Pub, privacyConfigurationManager: PrivacyConfigurationManaging, earlyAccessHandlers: [UserScript] = [])
where Pub: Publisher, Content: UserContentControllerNewContent, Pub.Output == Content, Pub.Failure == Never {

self.privacyConfigurationManager = privacyConfigurationManager
super.init()

// Install initial WKScriptMessageHandlers if any. Currently, no WKUserScript are provided at initialization.
installUserScripts([], handlers: earlyAccessHandlers)

assetsPublisherCancellable = assetsPublisher.sink { [weak self, selfDescr=self.debugDescription] content in
os_log(.debug, log: .contentBlocking, "\(selfDescr): 📚 received content blocking assets")
Task.detached { [weak self] in
Expand Down
71 changes: 71 additions & 0 deletions Sources/DuckPlayer/DuckPlayerContingencyHandler.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
//
// DuckPlayerContingencyHandler.swift
//
// Copyright © 2024 DuckDuckGo. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

import Foundation
import Common
import BrowserServicesKit

/// A protocol that defines the requirements for handling DuckPlayer contingency scenarios.
/// >Tech Design: https://app.asana.com/0/481882893211075/1207926753747908/f
public protocol DuckPlayerContingencyHandler {
/// A Boolean value indicating whether a contingency message should be displayed.
var shouldDisplayContingencyMessage: Bool { get }

/// A URL pointing to a "Learn More" page for additional information.
var learnMoreURL: URL? { get }
}

/// A default implementation of the `DuckPlayerContingencyHandler` protocol uses `PrivacyConfigurationManaging` to define its values.
public struct DefaultDuckPlayerContingencyHandler: DuckPlayerContingencyHandler {
private let privacyConfigurationManager: PrivacyConfigurationManaging

/// A Boolean value indicating whether a contingency message should be displayed.
/// The message should be displayed if the `learnMoreURL` is not nil and the DuckPlayer feature is not enabled.
public var shouldDisplayContingencyMessage: Bool {
learnMoreURL != nil && !isDuckPlayerFeatureEnabled
}

private var isDuckPlayerFeatureEnabled: Bool {
privacyConfigurationManager.privacyConfig.isEnabled(featureKey: .duckPlayer)
}

/// A URL pointing to a "Learn More" page for additional information.
/// The URL is derived from the privacy configuration settings.
public var learnMoreURL: URL? {
let settings = privacyConfigurationManager.privacyConfig.settings(for: .duckPlayer)
guard let link = settings[.duckPlayerDisabledHelpPageLink] as? String,
let pageLink = URL(string: link) else { return nil }
return pageLink
}

public init(privacyConfigurationManager: PrivacyConfigurationManaging) {
self.privacyConfigurationManager = privacyConfigurationManager
}
}

// MARK: - Settings key for Dictionary extension

private enum SettingsKey: String {
case duckPlayerDisabledHelpPageLink
}

private extension Dictionary where Key == String {
subscript(key: SettingsKey) -> Value? {
return self[key.rawValue]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public enum VPNCommand: Codable {
case removeSystemExtension
case removeVPNConfiguration
case sendTestNotification
case restartAdapter
case uninstallVPN
case disableConnectOnDemandAndShutDown
case quitAgent
Expand Down
23 changes: 23 additions & 0 deletions Sources/NetworkProtection/PacketTunnelProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -923,6 +923,7 @@ open class PacketTunnelProvider: NEPacketTunnelProvider {
excludedRoutes: settings.excludedRanges,
dnsSettings: settings.dnsSettings,
regenerateKey: regenerateKey)

case .useConfiguration(let newTunnelConfiguration):
tunnelConfiguration = newTunnelConfiguration
}
Expand Down Expand Up @@ -1159,6 +1160,8 @@ open class PacketTunnelProvider: NEPacketTunnelProvider {
case .removeVPNConfiguration:
// Since the VPN configuration is being removed we may as well reset all state
handleResetAllState(completionHandler: completionHandler)
case .restartAdapter:
handleRestartAdapter(completionHandler: completionHandler)
case .uninstallVPN:
// Since the VPN configuration is being removed we may as well reset all state
handleResetAllState(completionHandler: completionHandler)
Expand Down Expand Up @@ -1190,6 +1193,26 @@ open class PacketTunnelProvider: NEPacketTunnelProvider {
}
}

private func handleRestartAdapter(completionHandler: ((Data?) -> Void)? = nil) {
Task {
do {
let tunnelConfiguration = try await generateTunnelConfiguration(serverSelectionMethod: currentServerSelectionMethod,
includedRoutes: includedRoutes ?? [],
excludedRoutes: settings.excludedRanges,
dnsSettings: settings.dnsSettings,
regenerateKey: false)

try await updateTunnelConfiguration(updateMethod: .useConfiguration(tunnelConfiguration),
reassert: false,
regenerateKey: false)

completionHandler?(nil)
} catch {
completionHandler?(nil)
}
}
}

private func handleGetLastErrorMessage(completionHandler: ((Data?) -> Void)? = nil) {
let response = controllerErrorStore.lastErrorMessage.map(ExtensionMessageString.init)
completionHandler?(response?.rawValue)
Expand Down
3 changes: 0 additions & 3 deletions Sources/NetworkProtection/WireGuardKit/WireGuardAdapter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -405,9 +405,6 @@ public class WireGuardAdapter {
}

if reassert {
// Tell the system that the tunnel is going to reconnect using new WireGuard
// configuration.
// This will broadcast the `NEVPNStatusDidChange` notification to the GUI process.
self.packetTunnelProvider?.reasserting = true
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ final class UserContentControllerTests: XCTestCase {
@MainActor
override func setUp() async throws {
_=WKUserContentController.swizzleContentRuleListsMethodsOnce
_=WKUserContentController.swizzleScriptMessageHandlerMethodsOnce
ucc = UserContentController(assetsPublisher: assetsSubject, privacyConfigurationManager: PrivacyConfigurationManagerMock())
ucc.delegate = self
}
Expand All @@ -69,6 +70,16 @@ final class UserContentControllerTests: XCTestCase {
}

// MARK: - Tests
@MainActor
func testWhenUserContentControllerInitialisedWithEarlyAccessScriptsThenHandlersAreRegistered() async throws {
let script1 = MockUserScript(messageNames: ["message1"])
let script2 = MockUserScript(messageNames: ["message2"])
ucc = UserContentController(assetsPublisher: assetsSubject, privacyConfigurationManager: PrivacyConfigurationManagerMock(), earlyAccessHandlers: [script1, script2])
ucc.delegate = self

XCTAssertTrue(ucc.registeredScriptHandlerNames.contains("message1"))
XCTAssertTrue(ucc.registeredScriptHandlerNames.contains("message2"))
}

@MainActor
func testWhenContentBlockingAssetsPublished_contentRuleListsAreInstalled() async throws {
Expand Down Expand Up @@ -250,6 +261,34 @@ extension WKUserContentController {
}
}

extension WKUserContentController {
private static let scriptHandlersKey = UnsafeRawPointer(bitPattern: "scriptHandlersKey".hashValue)!

private static var installedScriptHandlers: [(WKScriptMessageHandler, WKContentWorld, String)] {
get {
objc_getAssociatedObject(self, scriptHandlersKey) as? [(WKScriptMessageHandler, WKContentWorld, String)] ?? []
}
set {
objc_setAssociatedObject(self, scriptHandlersKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}

static let swizzleScriptMessageHandlerMethodsOnce: Void = {
let originalAddMethod = class_getInstanceMethod(WKUserContentController.self, #selector(WKUserContentController.add(_:contentWorld:name:)))!
let swizzledAddMethod = class_getInstanceMethod(WKUserContentController.self, #selector(swizzled_add(_:contentWorld:name:)))!
method_exchangeImplementations(originalAddMethod, swizzledAddMethod)
}()

@objc dynamic private func swizzled_add(_ scriptMessageHandler: WKScriptMessageHandler, contentWorld: WKContentWorld, name: String) {
Self.installedScriptHandlers.append((scriptMessageHandler, contentWorld, name))
swizzled_add(scriptMessageHandler, contentWorld: contentWorld, name: name) // calling the original method
}

var registeredScriptHandlerNames: [String] {
return Self.installedScriptHandlers.map { $0.2 }
}
}

extension UserContentControllerTests: UserContentControllerDelegate {
func userContentController(_ userContentController: UserContentController, didInstallContentRuleLists contentRuleLists: [String: WKContentRuleList], userScripts: any UserScriptsProvider, updateEvent: ContentBlockerRulesManager.UpdateEvent) {
onAssetsInstalled?((contentRuleLists, userScripts, updateEvent))
Expand Down Expand Up @@ -305,3 +344,17 @@ class PrivacyConfigurationMock: PrivacyConfiguration {
func userEnabledProtection(forDomain: String) {}
func userDisabledProtection(forDomain: String) {}
}

class MockUserScript: NSObject, UserScript {
var source: String = "MockUserScript"
var injectionTime: WKUserScriptInjectionTime = .atDocumentEnd
var forMainFrameOnly: Bool = false
var messageNames: [String]

init(messageNames: [String]) {
self.messageNames = messageNames
}

func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
}
}
Loading

0 comments on commit 71a50fc

Please sign in to comment.