Skip to content

Commit

Permalink
Merge release/1.125.0 into main
Browse files Browse the repository at this point in the history
  • Loading branch information
daxmobile authored Feb 4, 2025
2 parents 9df11fb + 78d074e commit 21b261c
Show file tree
Hide file tree
Showing 14 changed files with 216 additions and 26 deletions.
2 changes: 1 addition & 1 deletion Configuration/BuildNumber.xcconfig
Original file line number Diff line number Diff line change
@@ -1 +1 @@
CURRENT_PROJECT_VERSION = 354
CURRENT_PROJECT_VERSION = 356
6 changes: 6 additions & 0 deletions DuckDuckGo-macOS.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1099,6 +1099,8 @@
370C230F2C76A3D600A80A3E /* SettingsGrid.swift in Sources */ = {isa = PBXBuildFile; fileRef = 370C23042C76A31F00A80A3E /* SettingsGrid.swift */; };
370C23112C7747E200A80A3E /* ImageProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 370C23102C7747E200A80A3E /* ImageProcessor.swift */; };
370C23122C7747E200A80A3E /* ImageProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 370C23102C7747E200A80A3E /* ImageProcessor.swift */; };
370DB6E22D521D130055D988 /* NewTabPageConfigurationErrorHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 370DB6E12D521D0A0055D988 /* NewTabPageConfigurationErrorHandler.swift */; };
370DB6E32D521D130055D988 /* NewTabPageConfigurationErrorHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 370DB6E12D521D0A0055D988 /* NewTabPageConfigurationErrorHandler.swift */; };
370E70A32D4691560077D4F3 /* RecentActivityProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 370E70A22D46914A0077D4F3 /* RecentActivityProviderTests.swift */; };
370E70A42D4691560077D4F3 /* RecentActivityProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 370E70A22D46914A0077D4F3 /* RecentActivityProviderTests.swift */; };
371209212C232E3F003ADF3D /* RemoteMessagingClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 371209202C232E3F003ADF3D /* RemoteMessagingClient.swift */; };
Expand Down Expand Up @@ -3769,6 +3771,7 @@
370C23082C76A39900A80A3E /* BackgroundCategoryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundCategoryView.swift; sourceTree = "<group>"; };
370C230A2C76A3BC00A80A3E /* BackgroundThumbnailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundThumbnailView.swift; sourceTree = "<group>"; };
370C23102C7747E200A80A3E /* ImageProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageProcessor.swift; sourceTree = "<group>"; };
370DB6E12D521D0A0055D988 /* NewTabPageConfigurationErrorHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewTabPageConfigurationErrorHandler.swift; sourceTree = "<group>"; };
370E70A22D46914A0077D4F3 /* RecentActivityProviderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecentActivityProviderTests.swift; sourceTree = "<group>"; };
371209202C232E3F003ADF3D /* RemoteMessagingClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteMessagingClient.swift; sourceTree = "<group>"; };
371209292C2333A0003ADF3D /* RemoteMessagingDatabase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteMessagingDatabase.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -6241,6 +6244,7 @@
379E4D632D439F9A00A901B1 /* Features */ = {
isa = PBXGroup;
children = (
370DB6E12D521D0A0055D988 /* NewTabPageConfigurationErrorHandler.swift */,
376E8C192D41186200D5D2EC /* DefaultRecentActivityActionsHandler.swift */,
37F9AEB02D1316FE007FD19B /* DefaultHomePageSettingsModelNavigator+NewTabPageLinkOpening.swift */,
3758A38B2D108229001CEAA1 /* NewTabPageFreemiumDBPBannerProvider.swift */,
Expand Down Expand Up @@ -11622,6 +11626,7 @@
F1D042A22BFBB4DD00A31506 /* DataBrokerProtectionSettings+Environment.swift in Sources */,
370270BD2C78B6D3002E44E4 /* NewTabBackgroundPixel.swift in Sources */,
BB7BA64C2D11FC170020B47E /* TabBarRemoteMessagePresenting.swift in Sources */,
370DB6E32D521D130055D988 /* NewTabPageConfigurationErrorHandler.swift in Sources */,
3706FAA3293F65D500E42796 /* CrashReportPromptViewController.swift in Sources */,
BD88A83F2C4F3E4300460A26 /* FeedbackCategoryProviding.swift in Sources */,
3706FAA4293F65D500E42796 /* ContextMenuManager.swift in Sources */,
Expand Down Expand Up @@ -13780,6 +13785,7 @@
316C48F02CC2B232000B08C1 /* AIChatPreferencesStorage.swift in Sources */,
B6830961274CDE99004B46BB /* FireproofDomainsContainer.swift in Sources */,
85B49AFA2CD1B7C5007FAA2A /* SystemInfo.swift in Sources */,
370DB6E22D521D130055D988 /* NewTabPageConfigurationErrorHandler.swift in Sources */,
B687B7CC2947A1E9001DEA6F /* ExternalAppSchemeHandler.swift in Sources */,
B65536AE2685E17200085A79 /* GeolocationService.swift in Sources */,
4B02198925E05FAC00ED7DEA /* FireproofingURLExtensions.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,7 @@ final class NetworkProtectionTunnelController: TunnelController, TunnelSessionPr

public var session: NETunnelProviderSession? {
get async {
guard let manager = internalManager,
guard let manager = await manager,
let session = manager.connection as? NETunnelProviderSession else {

// The active connection is not running, so there's no session, this is acceptable
Expand Down Expand Up @@ -596,10 +596,6 @@ final class NetworkProtectionTunnelController: TunnelController, TunnelSessionPr
)
}

if await isConnected {
await stop()
}

// Always keep the first error message shown, as it's the more actionable one.
if controllerErrorStore.lastErrorMessage == nil {
controllerErrorStore.lastErrorMessage = error.localizedDescription
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//
// NewTabPageConfigurationErrorHandler.swift
//
// Copyright © 2025 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 Common
import NewTabPage
import PixelKit

final class NewTabPageConfigurationErrorHandler: EventMapping<NewTabPageConfigurationEvent> {

init() {
super.init { event, _, _, _ in
switch event {
case .newTabPageError(let message):
PixelKit.fire(DebugEvent(NewTabPagePixel.newTabPageExceptionReported(message: message)), frequency: .dailyAndStandard)
}
}
}

override init(mapping: @escaping EventMapping<NewTabPageConfigurationEvent>.Mapping) {
fatalError("Use init()")
}
}
14 changes: 5 additions & 9 deletions DuckDuckGo/NewTabPage/Features/RecentActivityProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,6 @@ final class RecentActivityProvider: NewTabPageRecentActivityProviding {
var activityItems = [DomainActivityRef]()
var activityItemsByDomain = [String: DomainActivityRef]()

let oneWeekAgo = Date.weekAgo

browsingHistory
.filter(\.isValidForRecentActivity)
.sorted(by: { $0.lastVisit > $1.lastVisit })
Expand All @@ -129,7 +127,7 @@ final class RecentActivityProvider: NewTabPageRecentActivityProviding {
return newItemRef
}()

activityItem?.activity.addBlockedEntities(historyEntry.blockedTrackingEntities)
activityItem?.activity.addBlockedEntities(from: historyEntry)
activityItem?.activity.addPage(fromHistory: historyEntry, dateFormatter: Self.relativeTime)
}

Expand Down Expand Up @@ -192,16 +190,14 @@ extension NewTabPageDataModel.DomainActivity {
favicon: favicon,
favorite: urlFavoriteStatusProvider.isUrlFavorited(url: rootURL),
trackersFound: historyEntry.trackersFound,
trackingStatus: .init(
totalCount: Int64(historyEntry.numberOfTrackersBlocked),
trackerCompanies: historyEntry.blockedTrackingEntities.map(NewTabPageDataModel.TrackingStatus.TrackerCompany.init)
),
trackingStatus: .init(totalCount: 0, trackerCompanies: []), // keep this empty because it's updated separately
history: []
)
}

mutating func addBlockedEntities(_ entities: Set<String>) {
let trackerCompanies = Set(entities.map(NewTabPageDataModel.TrackingStatus.TrackerCompany.init))
mutating func addBlockedEntities(from entry: HistoryEntry) {
let trackerCompanies = Set(entry.blockedTrackingEntities.filter({ !$0.isEmpty }).map(NewTabPageDataModel.TrackingStatus.TrackerCompany.init))
trackingStatus.totalCount += Int64(entry.numberOfTrackersBlocked)
trackingStatus.trackerCompanies = Array(Set(trackingStatus.trackerCompanies).union(trackerCompanies))
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ extension NewTabPageActionsManager {
sectionsAvailabilityProvider: NewTabPageModeDecider(),
sectionsVisibilityProvider: appearancePreferences,
customBackgroundProvider: customizationProvider,
linkOpener: DefaultHomePageSettingsModelNavigator()
linkOpener: DefaultHomePageSettingsModelNavigator(),
eventMapper: NewTabPageConfigurationErrorHandler()
),
NewTabPageCustomBackgroundClient(model: customizationProvider),
NewTabPageRMFClient(remoteMessageProvider: activeRemoteMessageModel),
Expand Down
5 changes: 5 additions & 0 deletions DuckDuckGo/Statistics/NewTabPagePixel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ enum NewTabPagePixel: PixelKitEventV2 {
*/
case privacyStatsDatabaseError

case newTabPageExceptionReported(message: String)

var name: String {
switch self {
case .newTabPageShown: return "m_mac_newtab_shown"
Expand All @@ -148,6 +150,7 @@ enum NewTabPagePixel: PixelKitEventV2 {
case .blockedTrackingAttemptsShowMore: return "m_mac_new-tab-page_blocked-tracking-attempts_show-more"
case .privacyStatsCouldNotLoadDatabase: return "new-tab-page_privacy-stats_could-not-load-database"
case .privacyStatsDatabaseError: return "new-tab-page_privacy-stats_database_error"
case .newTabPageExceptionReported: return "new-tab-page_exception-reported"
}
}

Expand All @@ -165,6 +168,8 @@ enum NewTabPagePixel: PixelKitEventV2 {
parameters["blocked-tracking-attempts"] = String(privacyStats)
}
return parameters
case .newTabPageExceptionReported(let message):
return [PixelKit.Parameters.assertionMessage: message]
case .favoriteSectionHidden,
.recentActivitySectionHidden,
.blockedTrackingAttemptsSectionHidden,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ public protocol NewTabPageLinkOpening {
func openLink(_ target: NewTabPageDataModel.OpenAction.Target) async
}

public enum NewTabPageConfigurationEvent: Equatable {
case newTabPageError(message: String)
}

public final class NewTabPageConfigurationClient: NewTabPageUserScriptClient {

private var cancellables = Set<AnyCancellable>()
Expand All @@ -50,19 +54,22 @@ public final class NewTabPageConfigurationClient: NewTabPageUserScriptClient {
private let customBackgroundProvider: NewTabPageCustomBackgroundProviding
private let contextMenuPresenter: NewTabPageContextMenuPresenting
private let linkOpener: NewTabPageLinkOpening
private let eventMapper: EventMapping<NewTabPageConfigurationEvent>?

public init(
sectionsAvailabilityProvider: NewTabPageSectionsAvailabilityProviding,
sectionsVisibilityProvider: NewTabPageSectionsVisibilityProviding,
customBackgroundProvider: NewTabPageCustomBackgroundProviding,
contextMenuPresenter: NewTabPageContextMenuPresenting = DefaultNewTabPageContextMenuPresenter(),
linkOpener: NewTabPageLinkOpening
linkOpener: NewTabPageLinkOpening,
eventMapper: EventMapping<NewTabPageConfigurationEvent>?
) {
self.sectionsAvailabilityProvider = sectionsAvailabilityProvider
self.sectionsVisibilityProvider = sectionsVisibilityProvider
self.customBackgroundProvider = customBackgroundProvider
self.contextMenuPresenter = contextMenuPresenter
self.linkOpener = linkOpener
self.eventMapper = eventMapper
super.init()

Publishers.Merge3(
Expand Down Expand Up @@ -237,10 +244,11 @@ public final class NewTabPageConfigurationClient: NewTabPageUserScriptClient {
}

private func reportException(params: Any, original: WKScriptMessage) async throws -> Encodable? {
guard let params = params as? [String: String] else { return nil }
let message = params["message"] ?? ""
let id = params["id"] ?? ""
Logger.general.error("New Tab Page error: \("\(id): \(message)", privacy: .public)")
guard let exception: NewTabPageDataModel.Exception = DecodableHelper.decode(from: params) else {
return nil
}
eventMapper?.fire(.newTabPageError(message: exception.message))
Logger.general.error("New Tab Page error: \("\(exception.message)", privacy: .public)")
return nil
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ extension NewTabPageDataModel {
}
}

struct Exception: Codable, Equatable {
let message: String
}

struct NewTabPageConfiguration: Encodable {
var widgets: [Widget]
var widgetConfigs: [WidgetConfig]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//
// CapturingNewTabPageConfigurationErrorHandler.swift
//
// Copyright © 2025 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 Common
import NewTabPage

final class CapturingNewTabPageConfigurationEventHandler: EventMapping<NewTabPageConfigurationEvent> {
var events: [NewTabPageConfigurationEvent] = []

init() {
let localEvents = PassthroughSubject<NewTabPageConfigurationEvent, Never>()
super.init { event, _, _, _ in
localEvents.send(event)
}

cancellable = localEvents
.sink { [weak self] value in
self?.events.append(value)
}
}

override init(mapping: @escaping EventMapping<NewTabPageConfigurationEvent>.Mapping) {
fatalError("Use init()")
}

private var cancellable: AnyCancellable?
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,21 @@ final class NewTabPageConfigurationClientTests: XCTestCase {
private var contextMenuPresenter: CapturingNewTabPageContextMenuPresenter!
private var userScript: NewTabPageUserScript!
private var messageHelper: MessageHelper<NewTabPageConfigurationClient.MessageName>!
private var eventMapper: CapturingNewTabPageConfigurationEventHandler!

override func setUpWithError() throws {
try super.setUpWithError()
sectionsAvailabilityProvider = MockNewTabPageSectionsAvailabilityProvider()
sectionsVisibilityProvider = MockNewTabPageSectionsVisibilityProvider()
contextMenuPresenter = CapturingNewTabPageContextMenuPresenter()
eventMapper = CapturingNewTabPageConfigurationEventHandler()
client = NewTabPageConfigurationClient(
sectionsAvailabilityProvider: sectionsAvailabilityProvider,
sectionsVisibilityProvider: sectionsVisibilityProvider,
customBackgroundProvider: CapturingNewTabPageCustomBackgroundProvider(),
contextMenuPresenter: contextMenuPresenter,
linkOpener: CapturingNewTabPageLinkOpener()
linkOpener: CapturingNewTabPageLinkOpener(),
eventMapper: eventMapper
)

userScript = NewTabPageUserScript()
Expand Down Expand Up @@ -159,4 +162,22 @@ final class NewTabPageConfigurationClientTests: XCTestCase {
XCTAssertEqual(sectionsVisibilityProvider.isPrivacyStatsVisible, false)
XCTAssertEqual(sectionsVisibilityProvider.isRecentActivityVisible, true)
}

// MARK: - reportInitException

func testThatReportInitExceptionForwardsEventToTheMapper() async throws {
let exception = NewTabPageDataModel.Exception(message: "sample message")
try await messageHelper.handleMessageExpectingNilResponse(named: .reportInitException, parameters: exception)

XCTAssertEqual(eventMapper.events, [.newTabPageError(message: "sample message")])
}

// MARK: - reportPageException

func testThatReportPageExceptionForwardsEventToTheMapper() async throws {
let exception = NewTabPageDataModel.Exception(message: "sample message")
try await messageHelper.handleMessageExpectingNilResponse(named: .reportPageException, parameters: exception)

XCTAssertEqual(eventMapper.events, [.newTabPageError(message: "sample message")])
}
}
8 changes: 4 additions & 4 deletions UITests/BookmarksAndFavoritesTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ class BookmarksAndFavoritesTests: UITestCase {
contextualMenuRemoveBookmarkFromFavoritesMenuItem = app.menuItems["ContextualMenu.removeBookmarkFromFavoritesMenuItem"]
defaultBookmarkDialogButton = app.buttons["BookmarkDialogButtonsView.defaultButton"]
defaultBookmarkOtherButton = app.buttons["BookmarkDialogButtonsView.otherButton"]
favoriteGridAddFavoriteButton = app.staticTexts["HomePage.Models.FavoriteModel.addButton"]
favoriteGridAddFavoriteButton = app.buttons["Add Favorite"]
favoriteThisPageMenuItem = app.menuItems["MainMenu.favoriteThisPage"]
manageBookmarksMenuItem = app.menuItems["MainMenu.manageBookmarksMenuItem"]
openBookmarksMenuItem = app.menuItems["MoreOptionsMenu.openBookmarks"]
Expand Down Expand Up @@ -297,7 +297,7 @@ class BookmarksAndFavoritesTests: UITestCase {
let urlForAddFavoriteDialog = try XCTUnwrap(urlForBookmarksBar, "Couldn't unwrap page url")
app.typeText("\(pageTitleForAddFavoriteDialog)\t")
app.typeURL(urlForAddFavoriteDialog)
let newFavorite = app.otherElements.staticTexts[pageTitleForAddFavoriteDialog]
let newFavorite = app.links[pageTitleForAddFavoriteDialog]

XCTAssertTrue(
newFavorite.waitForExistence(timeout: UITests.Timeouts.elementExistence),
Expand Down Expand Up @@ -433,7 +433,7 @@ class BookmarksAndFavoritesTests: UITestCase {

toggleBookmarksBarShowFavoritesOn()
let unwrappedPageTitle = try XCTUnwrap(pageTitle, "It wasn't possible to unwrap pageTitle")
let firstFavoriteInGridMatchingTitle = app.staticTexts["HomePage.Models.FavoriteModel.\(unwrappedPageTitle)"].firstMatch
let firstFavoriteInGridMatchingTitle = app.staticTexts[unwrappedPageTitle].firstMatch

XCTAssertTrue(
firstFavoriteInGridMatchingTitle.waitForExistence(timeout: UITests.Timeouts.elementExistence),
Expand Down Expand Up @@ -545,7 +545,7 @@ class BookmarksAndFavoritesTests: UITestCase {
app.typeKey("n", modifierFlags: .command) // New window

let unwrappedPageTitle = try XCTUnwrap(pageTitle, "It wasn't possible to unwrap pageTitle")
let firstFavoriteInGridMatchingTitle = app.staticTexts["HomePage.Models.FavoriteModel.\(unwrappedPageTitle)"].firstMatch
let firstFavoriteInGridMatchingTitle = app.links[unwrappedPageTitle].firstMatch
XCTAssertTrue(
firstFavoriteInGridMatchingTitle.waitForExistence(timeout: UITests.Timeouts.elementExistence),
"The favorited item in the grid did not become available in a reasonable timeframe."
Expand Down
1 change: 1 addition & 0 deletions UITests/UI Tests.xctestplan
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"skippedTests" : [
"BookmarksAndFavoritesTests\/test_bookmarks_canBeAddedTo_byClickingBookmarksButtonInAddressBar()",
"BookmarksAndFavoritesTests\/test_favorites_canBeAddedTo_byClickingAddFavoriteInAddBookmarkPopover()",
"BookmarksAndFavoritesTests\/test_favorites_canBeRemovedFromNewTabViaContextClick()",
"PermissionsTests"
],
"target" : {
Expand Down
Loading

0 comments on commit 21b261c

Please sign in to comment.