1616// limitations under the License.
1717//
1818
19- import os . log
19+ import BrowserServicesKit
2020import Combine
2121import Common
2222import ContentBlocking
2323import Foundation
24- import BrowserServicesKit
24+ import Navigation
25+ import os. log
2526import PrivacyDashboard
27+ import TrackerRadarKit
2628import WebKit
2729
2830protocol AdClickAttributionDependencies {
@@ -46,18 +48,48 @@ protocol UserContentControllerProtocol: AnyObject {
4648 func installLocalContentRuleList( _ ruleList: WKContentRuleList , identifier: String )
4749}
4850
51+ protocol AdClickAttributionDetecting {
52+ func onStartNavigation( url: URL ? )
53+ func on2XXResponse( url: URL ? )
54+ func onDidFinishNavigation( url: URL ? )
55+ func onDidFailNavigation( )
56+ }
57+ extension AdClickAttributionDetection : AdClickAttributionDetecting { }
58+
59+ protocol AdClickLogicProtocol : AnyObject {
60+ var state : AdClickAttributionLogic . State { get }
61+ var delegate : AdClickAttributionLogicDelegate ? { get set }
62+
63+ func applyInheritedAttribution( state: AdClickAttributionLogic . State ? )
64+ func onRulesChanged( latestRules: [ ContentBlockerRulesManager . Rules ] )
65+ func onRequestDetected( request: DetectedRequest )
66+
67+ func onBackForwardNavigation( mainFrameURL: URL ? )
68+ func onProvisionalNavigation( ) async
69+ func onDidFinishNavigation( host: String ? , currentTime: Date )
70+ }
71+ extension AdClickAttributionLogic : AdClickLogicProtocol { }
72+
73+ protocol ContentBlockerScriptProtocol : AnyObject {
74+ var currentAdClickAttributionVendor : String ? { get set }
75+ var supplementaryTrackerData : [ TrackerData ] { get set }
76+ }
77+ extension ContentBlockerRulesUserScript : ContentBlockerScriptProtocol { }
78+
4979final class AdClickAttributionTabExtension : TabExtension {
5080
51- private static func makeAdClickAttributionDetection( with dependencies: some AdClickAttributionDependencies ) -> AdClickAttributionDetection {
52- return AdClickAttributionDetection ( feature: dependencies. adClickAttribution,
53- tld: dependencies. tld,
54- eventReporting: dependencies. attributionEvents,
55- errorReporting: dependencies. attributionDebugEvents,
56- log: OSLog . attribution)
81+ private static func makeAdClickAttributionDetection( with dependencies: any AdClickAttributionDependencies , delegate: AdClickAttributionLogic ) -> AdClickAttributionDetection {
82+ let detection = AdClickAttributionDetection ( feature: dependencies. adClickAttribution,
83+ tld: dependencies. tld,
84+ eventReporting: dependencies. attributionEvents,
85+ errorReporting: dependencies. attributionDebugEvents,
86+ log: OSLog . attribution)
87+ detection. delegate = delegate
88+ return detection
5789
5890 }
5991
60- private static func makeAdClickAttributionLogic( with dependencies: some AdClickAttributionDependencies ) -> AdClickAttributionLogic {
92+ private static func makeAdClickAttributionLogic( with dependencies: any AdClickAttributionDependencies ) -> AdClickAttributionLogic {
6193 return AdClickAttributionLogic ( featureConfig: dependencies. adClickAttribution,
6294 rulesProvider: dependencies. adClickAttributionRulesProvider,
6395 tld: dependencies. tld,
@@ -66,33 +98,40 @@ final class AdClickAttributionTabExtension: TabExtension {
6698 log: OSLog . attribution)
6799 }
68100
101+ private static func makeAdClickAttribution( with dependencies: any AdClickAttributionDependencies ) -> ( AdClickLogicProtocol , AdClickAttributionDetecting ) {
102+ let logic = makeAdClickAttributionLogic ( with: dependencies)
103+ let detection = makeAdClickAttributionDetection ( with: dependencies, delegate: logic)
104+ return ( logic, detection)
105+ }
106+
69107 private let dependencies : any AdClickAttributionDependencies
70108
71109 private weak var userContentController : UserContentControllerProtocol ?
72- private weak var contentBlockerRulesScript : ContentBlockerRulesUserScript ?
110+ private weak var contentBlockerRulesScript : ContentBlockerScriptProtocol ?
111+ private let dateTimeProvider : ( ) -> Date
73112
74- // to be made private
75- let detection : AdClickAttributionDetection
76- let logic : AdClickAttributionLogic
113+ private let detection : AdClickAttributionDetecting
114+ private let logic : AdClickLogicProtocol
77115
78- public var currentAttributionState : AdClickAttributionLogic . State ? {
116+ public var currentAttributionState : AdClickAttributionLogic . State {
79117 logic. state
80118 }
81119
82120 private var cancellables = Set < AnyCancellable > ( )
83121
84122 init ( inheritedAttribution: AdClickAttributionLogic . State ? ,
85123 userContentControllerFuture: Future < UserContentControllerProtocol , Never > ,
86- contentBlockerRulesScriptPublisher: some Publisher < ContentBlockerRulesUserScript ? , Never > ,
124+ contentBlockerRulesScriptPublisher: some Publisher < ( any ContentBlockerScriptProtocol ) ? , Never > ,
87125 trackerInfoPublisher: some Publisher < DetectedRequest , Never > ,
88- dependencies: some AdClickAttributionDependencies ) {
126+ dependencies: some AdClickAttributionDependencies ,
127+ dateTimeProvider: @escaping ( ) -> Date = Date . init,
128+ logicsProvider: ( AdClickAttributionDependencies ) -> ( AdClickLogicProtocol , AdClickAttributionDetecting ) = AdClickAttributionTabExtension . makeAdClickAttribution) {
89129
90130 self . dependencies = dependencies
91- self . detection = Self . makeAdClickAttributionDetection ( with: dependencies)
92- self . logic = Self . makeAdClickAttributionLogic ( with: dependencies)
131+ self . dateTimeProvider = dateTimeProvider
93132
94- logic. delegate = self
95- detection . delegate = logic
133+ ( self . logic, self . detection ) = logicsProvider ( dependencies )
134+ self . logic . delegate = self
96135
97136 // delay firing up until UserContentController is published
98137 userContentControllerFuture. sink { [ weak self] userContentController in
@@ -103,7 +142,7 @@ final class AdClickAttributionTabExtension: TabExtension {
103142 } . store ( in: & cancellables)
104143 }
105144
106- private func delayedInitialization( with userContentController: UserContentControllerProtocol , inheritedAttribution: AdClickAttributionLogic . State ? , contentBlockerRulesScriptPublisher: some Publisher < ContentBlockerRulesUserScript ? , Never > , trackerInfoPublisher: some Publisher < DetectedRequest , Never > ) {
145+ private func delayedInitialization( with userContentController: UserContentControllerProtocol , inheritedAttribution: AdClickAttributionLogic . State ? , contentBlockerRulesScriptPublisher: some Publisher < ( any ContentBlockerScriptProtocol ) ? , Never > , trackerInfoPublisher: some Publisher < DetectedRequest , Never > ) {
107146
108147 cancellables. removeAll ( )
109148 self . userContentController = userContentController
@@ -173,14 +212,48 @@ extension AdClickAttributionTabExtension: AdClickAttributionLogicDelegate {
173212
174213}
175214
176- extension AppContentBlocking : AdClickAttributionDependencies { }
215+ extension AdClickAttributionTabExtension : NavigationResponder {
216+
217+ func decidePolicy( for navigationAction: NavigationAction , preferences: inout NavigationPreferences ) async -> NavigationActionPolicy ? {
218+ if navigationAction. isForMainFrame, navigationAction. navigationType. isBackForward {
219+ logic. onBackForwardNavigation ( mainFrameURL: navigationAction. url)
220+ }
221+ return . next
222+ }
223+
224+ func didStart( _ navigation: Navigation ) {
225+ detection. onStartNavigation ( url: navigation. url)
226+ }
227+
228+ func decidePolicy( for navigationResponse: NavigationResponse ) async -> NavigationResponsePolicy ? {
229+ if navigationResponse. isForMainFrame,
230+ let currentNavigation = navigationResponse. mainFrameNavigation,
231+ navigationResponse. isSuccessful == true {
232+ detection. on2XXResponse ( url: currentNavigation. url)
233+ }
234+
235+ await logic. onProvisionalNavigation ( )
236+
237+ return . next
238+ }
239+
240+ func navigationDidFinish( _ navigation: Navigation ) {
241+ guard navigation. isCurrent else { return }
242+ detection. onDidFinishNavigation ( url: navigation. url)
243+ logic. onDidFinishNavigation ( host: navigation. url. host, currentTime: dateTimeProvider ( ) )
244+ }
245+
246+ func navigation( _ navigation: Navigation , didFailWith error: WKError ) {
247+ guard navigation. isCurrent else { return }
248+ detection. onDidFailNavigation ( )
249+ }
250+
251+ }
177252
178- protocol AdClickAttributionProtocol {
179- var currentAttributionState : AdClickAttributionLogic . State ? { get }
253+ extension AppContentBlocking : AdClickAttributionDependencies { }
180254
181- // to be removed
182- var detection : AdClickAttributionDetection { get }
183- var logic : AdClickAttributionLogic { get }
255+ protocol AdClickAttributionProtocol : AnyObject , NavigationResponder {
256+ var currentAttributionState : AdClickAttributionLogic . State { get }
184257}
185258
186259extension AdClickAttributionTabExtension : AdClickAttributionProtocol {
0 commit comments