Skip to content

Commit

Permalink
Merge branch 'main' into sam/ship-review-build
Browse files Browse the repository at this point in the history
* main:
  Bug fixes for refreshing and updating subscription state (#2473)
  Add additional parameters to breakage reports (#2416)
  Main thread issue for NetworkProtection nav bar icon update (#2466)
  Update the last version run store usage (#2441)
  • Loading branch information
samsymons committed Mar 21, 2024
2 parents 7a8d561 + e7d0897 commit 5201316
Show file tree
Hide file tree
Showing 21 changed files with 216 additions and 121 deletions.
2 changes: 1 addition & 1 deletion DuckDuckGo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -14109,7 +14109,7 @@
repositoryURL = "https://github.com/duckduckgo/BrowserServicesKit";
requirement = {
kind = exactVersion;
version = 127.1.1;
version = 129.0.0;
};
};
AA06B6B52672AF8100F541C5 /* XCRemoteSwiftPackageReference "Sparkle" */ = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,17 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/duckduckgo/BrowserServicesKit",
"state" : {
"revision" : "1ba3e3682b9231a1d8685f1d16f950e7910b8593",
"version" : "127.1.1"
"revision" : "3a6d8f3f3fe278fb316fe927c4ef005a19d03d71",
"version" : "129.0.0"
}
},
{
"identity" : "content-scope-scripts",
"kind" : "remoteSourceControl",
"location" : "https://github.com/duckduckgo/content-scope-scripts",
"state" : {
"revision" : "f6241631fc14cc2d0f47950bfdc4d6c30bf90130",
"version" : "5.4.0"
"revision" : "edd96481d49b094c260f9a79e078abdbdd3a83fb",
"version" : "5.6.0"
}
},
{
Expand Down Expand Up @@ -165,7 +165,7 @@
{
"identity" : "trackerradarkit",
"kind" : "remoteSourceControl",
"location" : "https://github.com/duckduckgo/TrackerRadarKit",
"location" : "https://github.com/duckduckgo/TrackerRadarKit.git",
"state" : {
"revision" : "a6b7ba151d9dc6684484f3785293875ec01cc1ff",
"version" : "1.2.2"
Expand Down
15 changes: 3 additions & 12 deletions DuckDuckGo/Application/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -261,19 +261,10 @@ final class AppDelegate: NSObject, NSApplicationDelegate, FileDownloadManagerDel

Task {
let accountManager = AccountManager()
do {
try accountManager.migrateAccessTokenToNewStore()
} catch {
if let error = error as? AccountManager.MigrationError {
switch error {
case AccountManager.MigrationError.migrationFailed:
os_log(.default, log: .subscription, "Access token migration failed")
case AccountManager.MigrationError.noMigrationNeeded:
os_log(.default, log: .subscription, "No access token migration needed")
}
}
if let token = accountManager.accessToken {
_ = await SubscriptionService.getSubscription(accessToken: token, cachePolicy: .reloadIgnoringLocalCacheData)
_ = await accountManager.fetchEntitlements(cachePolicy: .reloadIgnoringLocalCacheData)
}
await accountManager.checkSubscriptionState()
}
#endif

Expand Down
6 changes: 6 additions & 0 deletions DuckDuckGo/LoginItems/LoginItemsManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ final class LoginItemsManager {
}
}

func isAnyEnabled(_ items: Set<LoginItem>) -> Bool {
return items.contains(where: { item in
item.status == .enabled
})
}

private func handleError(for item: LoginItem, action: Action, error: NSError) {
let event = Pixel.Event.Debug.loginItemUpdateError(
loginItemBundleID: item.agentBundleID,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,19 +85,15 @@ final class NetworkProtectionAppEvents {
}

private func restartNetworkProtectionIfVersionChanged(using loginItemsManager: LoginItemsManager) {
let versionStore = NetworkProtectionLastVersionRunStore()

// should‘ve been run at least once with NetP enabled
guard versionStore.lastVersionRun != nil else {
os_log(.info, log: .networkProtection, "No last version found for the NetP login items, skipping update")
return
}

// We want to restart the VPN menu app to make sure it's always on the latest.
restartNetworkProtectionMenu(using: loginItemsManager)
}

private func restartNetworkProtectionMenu(using loginItemsManager: LoginItemsManager) {
guard loginItemsManager.isAnyEnabled(LoginItemsManager.networkProtectionLoginItems) else {
return
}

loginItemsManager.restartLoginItems(LoginItemsManager.networkProtectionLoginItems, log: .networkProtection)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,11 @@ final class NetworkProtectionNavBarButtonModel: NSObject, ObservableObject {
}

private func setupIconSubscription() {
iconPublisherCancellable = iconPublisher.$icon.sink { [weak self] icon in
self?.buttonImage = self?.buttonImageFromWaitlistState(icon: icon)
}
iconPublisherCancellable = iconPublisher.$icon
.receive(on: DispatchQueue.main)
.sink { [weak self] icon in
self?.buttonImage = self?.buttonImageFromWaitlistState(icon: icon)
}
}

/// Temporary override used for the NetP waitlist beta, as a different asset is used for users who are invited to join the beta but haven't yet accepted.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -258,11 +258,13 @@ extension PrivacyDashboardViewController: PrivacyDashboardReportBrokenSiteDelega
didRequestSubmitBrokenSiteReportWithCategory category: String,
description: String) {
let source: BrokenSiteReport.Source = privacyDashboardController.initDashboardMode == .report ? .appMenu : .dashboard
do {
let report = try makeBrokenSiteReport(category: category, description: description, source: source)
try brokenSiteReporter.report(report, reportMode: .regular)
} catch {
os_log("Failed to generate or send the broken site report: \(error.localizedDescription)", type: .error)
Task { @MainActor in
do {
let report = try await makeBrokenSiteReport(category: category, description: description, source: source)
try brokenSiteReporter.report(report, reportMode: .regular)
} catch {
os_log("Failed to generate or send the broken site report: \(error.localizedDescription)", type: .error)
}
}
}

Expand All @@ -281,13 +283,15 @@ extension PrivacyDashboardViewController: PrivacyDashboardToggleReportDelegate {
didRequestSubmitToggleReportWithSource source: BrokenSiteReport.Source,
didOpenReportInfo: Bool,
toggleReportCounter: Int?) {
do {
let report = try makeBrokenSiteReport(source: source,
didOpenReportInfo: didOpenReportInfo,
toggleReportCounter: toggleReportCounter)
try toggleProtectionsOffReporter.report(report, reportMode: .toggle)
} catch {
os_log("Failed to generate or send the broken site report: %@", type: .error, error.localizedDescription)
Task { @MainActor in
do {
let report = try await makeBrokenSiteReport(source: source,
didOpenReportInfo: didOpenReportInfo,
toggleReportCounter: toggleReportCounter)
try toggleProtectionsOffReporter.report(report, reportMode: .toggle)
} catch {
os_log("Failed to generate or send the broken site report: %@", type: .error, error.localizedDescription)
}
}
}

Expand All @@ -301,11 +305,25 @@ extension PrivacyDashboardViewController {
case failedToFetchTheCurrentURL
}

private func calculateWebVitals(performanceMetrics: PerformanceMetricsSubfeature?, privacyConfig: PrivacyConfiguration) async -> [Double]? {
var webVitalsResult: [Double]?
if privacyConfig.isEnabled(featureKey: .performanceMetrics) {
webVitalsResult = await withCheckedContinuation({ continuation in
guard let performanceMetrics else { continuation.resume(returning: nil); return }
performanceMetrics.notifyHandler { result in
continuation.resume(returning: result)
}
})
}

return webVitalsResult
}

private func makeBrokenSiteReport(category: String = "",
description: String = "",
source: BrokenSiteReport.Source,
didOpenReportInfo: Bool = false,
toggleReportCounter: Int? = nil) throws -> BrokenSiteReport {
toggleReportCounter: Int? = nil) async throws -> BrokenSiteReport {

// ⚠️ To limit privacy risk, site URL is trimmed to not include query and fragment
guard let currentTab = tabViewModel?.tab,
Expand All @@ -321,12 +339,14 @@ extension PrivacyDashboardViewController {
let configuration = ContentBlocking.shared.privacyConfigurationManager.privacyConfig
let protectionsState = configuration.isFeature(.contentBlocking, enabledForDomain: currentTab.content.url?.host)

let webVitals = await calculateWebVitals(performanceMetrics: currentTab.performanceMetrics, privacyConfig: configuration)

var errors: [Error]?
var statusCodes: [Int]?
if let error = tabViewModel?.tab.lastWebError {
if let error = currentTab.lastWebError {
errors = [error]
}
if let httpStatusCode = tabViewModel?.tab.lastHttpStatusCode {
if let httpStatusCode = currentTab.lastHttpStatusCode {
statusCodes = [httpStatusCode]
}

Expand All @@ -346,6 +366,10 @@ extension PrivacyDashboardViewController {
reportFlow: source,
errors: errors,
httpStatusCodes: statusCodes,
openerContext: currentTab.inferredOpenerContext,
vpnOn: currentTab.tunnelController?.isConnected ?? false,
jsPerformance: webVitals,
userRefreshCount: currentTab.refreshCountSinceLoad,
didOpenReportInfo: didOpenReportInfo,
toggleReportCounter: toggleReportCounter)
return websiteBreakage
Expand Down
43 changes: 42 additions & 1 deletion DuckDuckGo/Tab/Model/Tab.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import Navigation
import UserScript
import WebKit
import History
import PrivacyDashboard

#if SUBSCRIPTION
import Subscription
Expand Down Expand Up @@ -344,7 +345,7 @@ protocol NewWindowPolicyDecisionMaker {
let pinnedTabsManager: PinnedTabsManager

#if NETWORK_PROTECTION
private var tunnelController: NetworkProtectionIPCTunnelController?
private(set) var tunnelController: NetworkProtectionIPCTunnelController?
#endif

private let webViewConfiguration: WKWebViewConfiguration
Expand Down Expand Up @@ -779,6 +780,10 @@ protocol NewWindowPolicyDecisionMaker {
@Published private(set) var lastWebError: Error?
@Published private(set) var lastHttpStatusCode: Int?

@Published private(set) var inferredOpenerContext: BrokenSiteReport.OpenerContext?
@Published private(set) var refreshCountSinceLoad: Int = 0
private (set) var performanceMetrics: PerformanceMetricsSubfeature?

@Published private(set) var isLoading: Bool = false
@Published private(set) var loadingProgress: Double = 0.0

Expand Down Expand Up @@ -1028,6 +1033,8 @@ protocol NewWindowPolicyDecisionMaker {
return nil
}

refreshCountSinceLoad += 1

self.content = content.forceReload()
if webView.url == nil, content.isUrl {
// load from cache or interactionStateData when called by lazy loader
Expand Down Expand Up @@ -1287,6 +1294,9 @@ extension Tab: UserContentControllerDelegate {
userScripts.faviconScript.delegate = self
userScripts.pageObserverScript.delegate = self
userScripts.printingUserScript.delegate = self

performanceMetrics = PerformanceMetricsSubfeature(targetWebview: webView)
userScripts.contentScopeUserScriptIsolated.registerSubfeature(delegate: performanceMetrics!)
}

}
Expand Down Expand Up @@ -1357,11 +1367,32 @@ extension Tab/*: NavigationResponder*/ { // to be moved to Tab+Navigation.swift
committedURL = navigation.url
}

func resetRefreshCountIfNeeded(action: NavigationAction) {
switch action.navigationType {
case .reload, .other:
break
default:
refreshCountSinceLoad = 0
}
}

func setOpenerContextIfNeeded(action: NavigationAction) {
switch action.navigationType {
case .linkActivated, .formSubmitted:
inferredOpenerContext = .navigation
default:
break
}
}

@MainActor
func decidePolicy(for navigationAction: NavigationAction, preferences: inout NavigationPreferences) async -> NavigationActionPolicy? {
// allow local file navigations
if navigationAction.url.isFileURL { return .allow }

resetRefreshCountIfNeeded(action: navigationAction)
setOpenerContextIfNeeded(action: navigationAction)

// when navigating to a URL with basic auth username/password, cache it and redirect to a trimmed URL
if let mainFrame = navigationAction.mainFrameTarget,
let credential = navigationAction.url.basicAuthCredential {
Expand Down Expand Up @@ -1416,6 +1447,10 @@ extension Tab/*: NavigationResponder*/ { // to be moved to Tab+Navigation.swift
error = nil
}

if inferredOpenerContext != .external {
inferredOpenerContext = nil
}

invalidateInteractionStateData()
}

Expand All @@ -1425,6 +1460,12 @@ extension Tab/*: NavigationResponder*/ { // to be moved to Tab+Navigation.swift
webViewDidFinishNavigationPublisher.send()
statisticsLoader?.refreshRetentionAtb(isSearch: navigation.url.isDuckDuckGoSearch)

Task { @MainActor in
if await webView.isCurrentSiteReferredFromDuckDuckGo {
inferredOpenerContext = .serp
}
}

#if NETWORK_PROTECTION
if navigation.url.isDuckDuckGoSearch, tunnelController?.isConnected == true {
DailyPixel.fire(pixel: .networkProtectionEnabledOnSearch, frequency: .dailyAndCount)
Expand Down
4 changes: 3 additions & 1 deletion DuckDuckGo/Tab/View/BrowserTabViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,9 @@ final class BrowserTabViewController: NSViewController {
#if DBP
@objc
private func onDBPFeatureDisabled(_ notification: Notification) {
tabCollectionViewModel.removeAll(with: .dataBrokerProtection)
Task { @MainActor in
tabCollectionViewModel.removeAll(with: .dataBrokerProtection)
}
}

@objc
Expand Down
8 changes: 5 additions & 3 deletions DuckDuckGo/VPNFeedbackForm/VPNMetadataCollector.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ struct VPNMetadata: Encodable {

struct AppInfo: Encodable {
let appVersion: String
let lastVersionRun: String
let lastAgentVersionRun: String
let lastExtensionVersionRun: String
let isInternalUser: Bool
let isInApplicationsDirectory: Bool
}
Expand Down Expand Up @@ -154,13 +155,14 @@ final class DefaultVPNMetadataCollector: VPNMetadataCollector {

private func collectAppInfoMetadata() -> VPNMetadata.AppInfo {
let appVersion = AppVersion.shared.versionAndBuildNumber
let versionStore = NetworkProtectionLastVersionRunStore()
let versionStore = NetworkProtectionLastVersionRunStore(userDefaults: .netP)
let isInternalUser = NSApp.delegateTyped.internalUserDecider.isInternalUser
let isInApplicationsDirectory = Bundle.main.isInApplicationsDirectory

return .init(
appVersion: appVersion,
lastVersionRun: versionStore.lastVersionRun ?? "Unknown",
lastAgentVersionRun: versionStore.lastAgentVersionRun ?? "none",
lastExtensionVersionRun: versionStore.lastExtensionVersionRun ?? "none",
isInternalUser: isInternalUser,
isInApplicationsDirectory: isInApplicationsDirectory
)
Expand Down
9 changes: 7 additions & 2 deletions DuckDuckGoVPN/NetworkExtensionController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
//

import Foundation
import NetworkProtection
import NetworkProtectionUI

#if NETP_SYSTEM_EXTENSION
Expand All @@ -32,11 +33,13 @@ final class NetworkExtensionController {

#if NETP_SYSTEM_EXTENSION
private let systemExtensionManager: SystemExtensionManager
private let defaults: UserDefaults
#endif

init(extensionBundleID: String) {
init(extensionBundleID: String, defaults: UserDefaults = .netP) {
#if NETP_SYSTEM_EXTENSION
systemExtensionManager = SystemExtensionManager(extensionBundleID: extensionBundleID)
self.defaults = defaults
#endif
}

Expand All @@ -46,9 +49,11 @@ extension NetworkExtensionController {

func activateSystemExtension(waitingForUserApproval: @escaping () -> Void) async throws {
#if NETP_SYSTEM_EXTENSION
try await systemExtensionManager.activate(
let extensionVersion = try await systemExtensionManager.activate(
waitingForUserApproval: waitingForUserApproval)

NetworkProtectionLastVersionRunStore(userDefaults: defaults).lastExtensionVersionRun = extensionVersion

try? await Task.sleep(nanoseconds: 300 * NSEC_PER_MSEC)
#endif
}
Expand Down
Loading

0 comments on commit 5201316

Please sign in to comment.