This repository was archived by the owner on Feb 24, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 16
/
Copy pathRulesCompilationMonitor.swift
116 lines (96 loc) · 4.14 KB
/
RulesCompilationMonitor.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
//
// RulesCompilationMonitor.swift
//
// Copyright © 2022 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
typealias ContentBlockingAssetsCompilationTimeReporter = AbstractContentBlockingAssetsCompilationTimeReporter<UInt64>
extension ContentBlockingAssetsCompilationTimeReporter {
static let shared = ContentBlockingAssetsCompilationTimeReporter()
}
final class AbstractContentBlockingAssetsCompilationTimeReporter<Caller: Hashable>: NSObject {
var currentTime: () -> TimeInterval = CACurrentMediaTime
private var waitStart: TimeInterval?
private var waiters = Set<Caller>()
private var isFinished = false
@UserDefaultsWrapper(key: .onboardingFinished, defaultValue: false)
private var onboardingFinished: Bool
private var onboardingShown: Bool!
override init() {
super.init()
self.onboardingShown = !onboardingFinished
NotificationCenter.default.addObserver(self,
selector: #selector(applicationWillTerminate(_:)),
name: NSApplication.willTerminateNotification,
object: nil)
}
/// Called when a Tab is going to wait for Content Blocking Rules compilation
func tabWillWaitForRulesCompilation(_ tab: Caller) {
guard !isFinished else { return }
waiters.insert(tab)
if waitStart == nil {
waitStart = currentTime()
}
}
private func report(waitTime: TimeInterval, result: GeneralPixel.WaitResult, completionHandler: @escaping ((Error?) -> Void) = { _ in }) {
// report only once
isFinished = true
completionHandler(nil)
// This is temporarily disabled:
//
// PixelKit.fire(GeneralPixel.compileRulesWait(onboardingShown: self.onboardingShown, waitTime: waitTime, result: result),
// withAdditionalParameters: ["waitTime": String(waitTime)],
// onComplete: completionHandler)
}
/// Called when Rules compilation finishes
func reportWaitTimeForTabFinishedWaitingForRules(_ tab: Caller) {
defer { waiters.remove(tab) }
guard waiters.contains(tab),
!isFinished,
let waitStart = waitStart
else { return }
report(waitTime: currentTime() - waitStart, result: .success)
}
/// If Tab is going to close while the rules are still being compiled: report wait time with Tab .closed argument
func tabWillClose(_ tab: Caller) {
defer { waiters.remove(tab) }
guard waiters.contains(tab),
!isFinished,
let waitStart = self.waitStart
else { return }
report(waitTime: currentTime() - waitStart, result: .closed)
}
/// If App is going to close while the rules are still being compiled: report wait time with .quit argument
@objc func applicationWillTerminate(_: Notification) {
guard !isFinished,
waiters.count > 0,
let waitStart = self.waitStart
else { return }
// Run the loop until Pixel is sent
let condition = RunLoop.ResumeCondition()
report(waitTime: currentTime() - waitStart, result: .quit) { _ in
condition.resolve()
}
RunLoop.current.run(until: condition)
}
/// When Navigation while Content Blocking Rules are already available
func reportNavigationDidNotWaitForRules() {
guard !isFinished else { return }
report(waitTime: 0, result: .success)
}
deinit {
NotificationCenter.default.removeObserver(self)
}
}