Skip to content

Commit

Permalink
Add visted links support (#3261)
Browse files Browse the repository at this point in the history
  • Loading branch information
mallexxx authored Sep 13, 2024
1 parent db29c39 commit 5c141ba
Show file tree
Hide file tree
Showing 14 changed files with 238 additions and 27 deletions.
8 changes: 7 additions & 1 deletion DuckDuckGo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1777,6 +1777,8 @@
848648A22C76F4B20082282D /* BookmarksBarMenuViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 848648A02C76F4B20082282D /* BookmarksBarMenuViewController.swift */; };
84DC715A2C1C1E9000033B8C /* UserDefaultsWrapperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84DC71582C1C1E8A00033B8C /* UserDefaultsWrapperTests.swift */; };
84DC715B2C1C1E9000033B8C /* UserDefaultsWrapperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84DC71582C1C1E8A00033B8C /* UserDefaultsWrapperTests.swift */; };
84DDB90A2C92B66E008C997B /* WKVisitedLinkStoreWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84DDB9092C92B667008C997B /* WKVisitedLinkStoreWrapper.swift */; };
84DDB90B2C92B66E008C997B /* WKVisitedLinkStoreWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84DDB9092C92B667008C997B /* WKVisitedLinkStoreWrapper.swift */; };
84F1C8CF2C7705B500716446 /* BookmarksBarMenuPopover.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F1C8CE2C7705B500716446 /* BookmarksBarMenuPopover.swift */; };
84F1C8D02C7705B500716446 /* BookmarksBarMenuPopover.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F1C8CE2C7705B500716446 /* BookmarksBarMenuPopover.swift */; };
84F1C8DE2C774D4200716446 /* NSTableViewExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F1C8DD2C774D4200716446 /* NSTableViewExtension.swift */; };
Expand Down Expand Up @@ -3830,6 +3832,7 @@
843965142C737022004C8899 /* NSPasteboardExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSPasteboardExtension.swift; sourceTree = "<group>"; };
848648A02C76F4B20082282D /* BookmarksBarMenuViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarksBarMenuViewController.swift; sourceTree = "<group>"; };
84DC71582C1C1E8A00033B8C /* UserDefaultsWrapperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaultsWrapperTests.swift; sourceTree = "<group>"; };
84DDB9092C92B667008C997B /* WKVisitedLinkStoreWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WKVisitedLinkStoreWrapper.swift; sourceTree = "<group>"; };
84F1C8CE2C7705B500716446 /* BookmarksBarMenuPopover.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarksBarMenuPopover.swift; sourceTree = "<group>"; };
84F1C8DA2C774CA900716446 /* BookmarkListPopover.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarkListPopover.swift; sourceTree = "<group>"; };
84F1C8DD2C774D4200716446 /* NSTableViewExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSTableViewExtension.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -8230,6 +8233,7 @@
B65C7DFA2B886CF0001E2D5C /* WKPDFHUDViewWrapper.swift */,
B645D8F529FA95440024461F /* WKProcessPoolExtension.swift */,
AAA0CC69253CC43C0079BC96 /* WKUserContentControllerExtension.swift */,
84DDB9092C92B667008C997B /* WKVisitedLinkStoreWrapper.swift */,
4B7A60A0273E0BE400BBDFEB /* WKWebsiteDataStoreExtension.swift */,
B63D466725BEB6C200874977 /* WKWebView+Private.h */,
B63D466825BEB6C200874977 /* WKWebView+SessionState.swift */,
Expand Down Expand Up @@ -10751,6 +10755,7 @@
1E559BB22BBCA9F1002B4AF6 /* RedirectNavigationResponder.swift in Sources */,
3706FB6C293F65D500E42796 /* FaviconStore.swift in Sources */,
3706FB6D293F65D500E42796 /* SuggestionListCharacteristics.swift in Sources */,
84DDB90B2C92B66E008C997B /* WKVisitedLinkStoreWrapper.swift in Sources */,
F1C70D7A2BFF50A400599292 /* DataBrokerProtectionLoginItemInterface.swift in Sources */,
377D801F2AB48191002AF251 /* FavoritesDisplayModeSyncHandler.swift in Sources */,
3706FB6F293F65D500E42796 /* BookmarkListViewController.swift in Sources */,
Expand Down Expand Up @@ -12335,6 +12340,7 @@
B69A14F22B4D6FE800B9417D /* AddBookmarkFolderPopoverViewModel.swift in Sources */,
4BE65478271FCD41008D1D63 /* PasswordManagementNoteItemView.swift in Sources */,
AA5C8F632591021700748EB7 /* NSApplicationExtension.swift in Sources */,
84DDB90A2C92B66E008C997B /* WKVisitedLinkStoreWrapper.swift in Sources */,
AA9E9A5625A3AE8400D1959D /* NSWindowExtension.swift in Sources */,
7BD3AF5D2A8E7AF1006F9F56 /* KeychainType+ClientDefault.swift in Sources */,
370A34B12AB24E3700C77F7C /* SyncDebugMenu.swift in Sources */,
Expand Down Expand Up @@ -14146,7 +14152,7 @@
repositoryURL = "https://github.com/duckduckgo/BrowserServicesKit";
requirement = {
kind = exactVersion;
version = 193.2.1;
version = 194.0.0;
};
};
9FF521422BAA8FF300B9819B /* XCRemoteSwiftPackageReference "lottie-spm" */ = {
Expand Down
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" : {
"branch" : "193.2.1",
"revision" : "e304d397d61f74a43453748bdc86f933e3fe5425"
"branch" : "194.0.0",
"revision" : "026acbd36fb80c95e0bfc6a9080e369dd85db66f"
}
},
{
Expand Down Expand Up @@ -75,7 +75,7 @@
{
"identity" : "lottie-spm",
"kind" : "remoteSourceControl",
"location" : "https://github.com/airbnb/lottie-spm.git",
"location" : "https://github.com/airbnb/lottie-spm",
"state" : {
"revision" : "1d29eccc24cc8b75bff9f6804155112c0ffc9605",
"version" : "4.4.3"
Expand Down
76 changes: 76 additions & 0 deletions DuckDuckGo/Common/Extensions/WKVisitedLinkStoreWrapper.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
//
// WKVisitedLinkStoreWrapper.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.
//

struct WKVisitedLinkStoreWrapper {

fileprivate let visitedLinkStore: NSObject

init?(visitedLinkStore: NSObject) {
guard visitedLinkStore.responds(to: Selector.removeVisitedLinkWithURL) else {
assertionFailure("\(visitedLinkStore) does not respond to \(Selector.removeVisitedLinkWithURL)")
return nil
}
guard visitedLinkStore.responds(to: Selector.removeAll) else {
assertionFailure("\(visitedLinkStore) does not respond to \(Selector.removeAll)")
return nil
}
self.visitedLinkStore = visitedLinkStore
}

@MainActor
func removeVisitedLink(with url: URL) {
visitedLinkStore.perform(Selector.removeVisitedLinkWithURL, with: url as NSURL)
}

@MainActor
func removeAll() {
visitedLinkStore.perform(Selector.removeAll)
}

enum Selector {
static let removeAll = NSSelectorFromString("removeAll")
static let removeVisitedLinkWithURL = NSSelectorFromString("removeVisitedLinkWithURL:")
}

}

extension WKWebViewConfiguration {

var visitedLinkStore: WKVisitedLinkStoreWrapper? {
get {
guard self.responds(to: Selector.visitedLinkStore) else {
assertionFailure("WKWebView doesn‘t respond to _visitedLinkStore")
return nil
}
return (self.value(forKey: NSStringFromSelector(Selector.visitedLinkStore)) as? NSObject).flatMap(WKVisitedLinkStoreWrapper.init)
}
set {
guard self.responds(to: Selector.setVisitedLinkStore) else {
assertionFailure("WKWebView doesn‘t respond to _setVisitedLinkStore:")
return
}
self.perform(Selector.setVisitedLinkStore, with: newValue?.visitedLinkStore)
}
}

enum Selector {
static let visitedLinkStore = NSSelectorFromString("_visitedLinkStore")
static let setVisitedLinkStore = NSSelectorFromString("_setVisitedLinkStore:")
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,23 @@ import os.log

extension WKWebViewConfiguration {

static var sharedVisitedLinkStore: WKVisitedLinkStoreWrapper?

@MainActor
func applyStandardConfiguration(contentBlocking: some ContentBlockingProtocol, burnerMode: BurnerMode, earlyAccessHandlers: [UserScript] = []) {
if case .burner(let websiteDataStore) = burnerMode {
self.websiteDataStore = websiteDataStore
// Fire Window: disable audio/video item info reporting to macOS Control Center / Lock Screen
preferences[.mediaSessionEnabled] = false

} else if let sharedVisitedLinkStore = Self.sharedVisitedLinkStore {
// share visited link store between regular tabs
self.visitedLinkStore = sharedVisitedLinkStore
} else {
// set shared object if not set yet
Self.sharedVisitedLinkStore = self.visitedLinkStore
}

allowsAirPlayForMediaPlayback = true
if #available(macOS 12.3, *) {
preferences.isElementFullscreenEnabled = true
Expand Down Expand Up @@ -62,7 +72,7 @@ extension WKWebViewConfiguration {
self.processPool.geolocationProvider = GeolocationProvider(processPool: self.processPool)

_=NSPopover.swizzleShowRelativeToRectOnce
}
}

}

Expand Down
19 changes: 19 additions & 0 deletions DuckDuckGo/Common/Extensions/WKWebViewExtension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -348,12 +348,31 @@ extension WKWebView {
try await evaluateJavaScript("window.getSelection().removeAllRanges()") as Void?
}

var addsVisitedLinks: Bool {
get {
guard self.responds(to: Selector.addsVisitedLinks) else {
assertionFailure("WKWebView doesn‘t respond to _addsVisitedLinks")
return false
}
return self.value(forKey: NSStringFromSelector(Selector.addsVisitedLinks)) as? Bool ?? false
}
set {
guard self.responds(to: Selector.addsVisitedLinks) else {
assertionFailure("WKWebView doesn‘t respond to _setAddsVisitedLinks:")
return
}
self.perform(Selector.setAddsVisitedLinks, with: newValue ? true : nil)
}
}

enum Selector {
static let fullScreenPlaceholderView = NSSelectorFromString("_fullScreenPlaceholderView")
static let printOperationWithPrintInfoForFrame = NSSelectorFromString("_printOperationWithPrintInfo:forFrame:")
static let loadAlternateHTMLString = NSSelectorFromString("_loadAlternateHTMLString:baseURL:forUnreachableURL:")
static let mediaMutedState = NSSelectorFromString("_mediaMutedState")
static let setPageMuted = NSSelectorFromString("_setPageMuted:")
static let setAddsVisitedLinks = NSSelectorFromString("_setAddsVisitedLinks:")
static let addsVisitedLinks = NSSelectorFromString("_addsVisitedLinks")
}

}
47 changes: 39 additions & 8 deletions DuckDuckGo/Fire/Model/Fire.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ final class Fire {
let tabCleanupPreparer = TabCleanupPreparer()
let secureVaultFactory: AutofillVaultFactory
let tld: TLD
let getVisitedLinkStore: () -> WKVisitedLinkStoreWrapper?

private var dispatchGroup: DispatchGroup?

Expand Down Expand Up @@ -102,7 +103,8 @@ final class Fire {
bookmarkManager: BookmarkManager = LocalBookmarkManager.shared,
syncService: DDGSyncing? = nil,
syncDataProviders: SyncDataProviders? = nil,
secureVaultFactory: AutofillVaultFactory = AutofillSecureVaultFactory
secureVaultFactory: AutofillVaultFactory = AutofillSecureVaultFactory,
getVisitedLinkStore: (() -> WKVisitedLinkStoreWrapper?)? = nil
) {
self.webCacheManager = cacheManager
self.historyCoordinating = historyCoordinating
Expand All @@ -118,6 +120,7 @@ final class Fire {
self.syncDataProviders = syncDataProviders ?? NSApp.delegateTyped.syncDataProviders
self.secureVaultFactory = secureVaultFactory
self.tld = tld
self.getVisitedLinkStore = getVisitedLinkStore ?? { WKWebViewConfiguration.sharedVisitedLinkStore }
self.autoconsentManagement = autoconsentManagement ?? AutoconsentManagement.shared
if let stateRestorationManager = stateRestorationManager {
self.stateRestorationManager = stateRestorationManager
Expand Down Expand Up @@ -210,7 +213,8 @@ final class Fire {
self.burnTabs(burningEntity: .allWindows(mainWindowControllers: windowControllers, selectedDomains: Set())) {
Task { @MainActor in
await self.burnWebCache()
self.burnHistory {
self.burnAllVisitedLinks()
self.burnAllHistory {
self.burnPermissions {
self.burnFavicons {
self.burnDownloads()
Expand Down Expand Up @@ -260,6 +264,7 @@ final class Fire {
// Convert to eTLD+1 domains
domains = domains.convertedToETLDPlus1(tld: tld)

burnVisitedLinks(visits)
historyCoordinating.burnVisits(visits) {
let entity: BurningEntity

Expand Down Expand Up @@ -345,37 +350,63 @@ final class Fire {

// MARK: - History

private func burnHistory(completion: @escaping () -> Void) {
historyCoordinating.burnAll(completion: completion)
}

@MainActor
private func burnHistory(ofEntity entity: BurningEntity, completion: @escaping () -> Void) {
let visits: [Visit]
switch entity {
case .none(selectedDomains: let domains):
burnHistory(of: domains, completion: completion)
burnHistory(of: domains) { urls in
self.burnVisitedLinks(urls)
completion()
}
return
case .tab(tabViewModel: let tabViewModel, selectedDomains: _, parentTabCollectionViewModel: _):
visits = tabViewModel.tab.localHistory
case .window(tabCollectionViewModel: let tabCollectionViewModel, selectedDomains: _):
visits = tabCollectionViewModel.localHistory

case .allWindows:
burnAllVisitedLinks()
burnAllHistory(completion: completion)
return
}

burnVisitedLinks(visits)
historyCoordinating.burnVisits(visits, completion: completion)
}

private func burnHistory(of baseDomains: Set<String>, completion: @escaping () -> Void) {
private func burnHistory(of baseDomains: Set<String>, completion: @escaping (Set<URL>) -> Void) {
historyCoordinating.burnDomains(baseDomains, tld: ContentBlocking.shared.tld, completion: completion)
}

private func burnAllHistory(completion: @escaping () -> Void) {
historyCoordinating.burnAll(completion: completion)
}

// MARK: - Visited links

@MainActor
private func burnAllVisitedLinks() {
getVisitedLinkStore()?.removeAll()
}

@MainActor
private func burnVisitedLinks(_ visits: [Visit]) {
guard let visitedLinkStore = getVisitedLinkStore() else { return }
for visit in visits {
guard let url = visit.historyEntry?.url else { continue }
visitedLinkStore.removeVisitedLink(with: url)
}
}

@MainActor
private func burnVisitedLinks(_ urls: Set<URL>) {
guard let visitedLinkStore = getVisitedLinkStore() else { return }
for url in urls {
visitedLinkStore.removeVisitedLink(with: url)
}
}

// MARK: - Zoom levels

private func burnZoomLevels() {
Expand Down
2 changes: 2 additions & 0 deletions DuckDuckGo/Tab/Model/Tab.swift
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,8 @@ protocol NewWindowPolicyDecisionMaker {

webView = WebView(frame: CGRect(origin: .zero, size: webViewSize), configuration: configuration)
webView.allowsLinkPreview = false
webView.addsVisitedLinks = true

permissions = PermissionModel(permissionManager: permissionManager,
geolocationService: geolocationService)

Expand Down
2 changes: 1 addition & 1 deletion LocalPackages/DataBrokerProtection/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ let package = Package(
targets: ["DataBrokerProtection"])
],
dependencies: [
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", branch: "193.2.1"),
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", branch: "194.0.0"),
.package(path: "../SwiftUIExtensions"),
.package(path: "../XPCHelper"),
],
Expand Down
2 changes: 1 addition & 1 deletion LocalPackages/NetworkProtectionMac/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ let package = Package(
.library(name: "VPNAppLauncher", targets: ["VPNAppLauncher"]),
],
dependencies: [
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "193.2.1"),
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "194.0.0"),
.package(url: "https://github.com/airbnb/lottie-spm", exact: "4.4.3"),
.package(path: "../AppLauncher"),
.package(path: "../UDSHelper"),
Expand Down
2 changes: 1 addition & 1 deletion LocalPackages/SubscriptionUI/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ let package = Package(
targets: ["SubscriptionUI"]),
],
dependencies: [
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "193.2.1"),
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "194.0.0"),
.package(path: "../SwiftUIExtensions")
],
targets: [
Expand Down
Loading

0 comments on commit 5c141ba

Please sign in to comment.