<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

import Foundation
import LocalAuthentication

class TipManager {
    
    struct Tip: Equatable {
        var title: String
        var description: String?
        var identifier: String
        var showVc: Bool
        
        init(title: String, description: String? = nil, identifier: String, showVc: Bool = false) {
            self.title = title
            self.identifier = identifier
            self.description = description
            self.showVc = showVc
        }
        
        static func == (lhs: Tip, rhs: Tip) -> Bool {
            return lhs.identifier == rhs.identifier
        }
    }
    
    class TipKey {
        static let autocompleteTip = "autocompleteTip"
        static let sitesNotWorkingTip = "sitesNotWorkingTip"
        static let biometricTip = "biometricTip"
        static let siriFavoriteTip = "siriFavoriteTip"
        static let shareTrackersTip = "shareTrackersTip"
        static let requestDesktopTip = "requestDesktopTip"
        static let siriEraseTip = "siriEraseTip"
    }
    
    static let shared = TipManager()
    private var possibleTips: [Tip]
    private let laContext = LAContext()
    var currentTip: Tip?
    
    init() {
        possibleTips = [Tip]()
        addAllTips()
    }
    
    private func addAllTips() {
        possibleTips.append(autocompleteTip)
        possibleTips.append(sitesNotWorkingTip)
        possibleTips.append(requestDesktopTip)
        possibleTips.append(siriFavoriteTip)
        possibleTips.append(siriEraseTip)
        possibleTips.append(shareTrackersTip)
        if laContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil) {
            possibleTips.append(biometricTip)
        }
    }
    
    lazy var autocompleteTip = Tip(title: UIConstants.strings.autocompleteTipTitle, description: UIConstants.strings.autocompleteTipDescription, identifier: TipKey.autocompleteTip)
    
    lazy var sitesNotWorkingTip = Tip(title: UIConstants.strings.sitesNotWorkingTipTitle, description: UIConstants.strings.sitesNotWorkingTipDescription, identifier: TipKey.sitesNotWorkingTip)
    
    lazy var biometricTip: Tip = {
        if laContext.biometryType == .faceID {
            return Tip(title: UIConstants.strings.biometricTipTitle, description: UIConstants.strings.biometricTipFaceIdDescription, identifier: TipKey.biometricTip, showVc: true)
        } else {
            return Tip(title: UIConstants.strings.biometricTipTitle, description: UIConstants.strings.biometricTipTouchIdDescription, identifier: TipKey.biometricTip, showVc: true)
        }
    }()
    
    lazy var requestDesktopTip = Tip(title: UIConstants.strings.requestDesktopTipTitle, description: UIConstants.strings.requestDesktopTipDescription, identifier: TipKey.requestDesktopTip)
    
    lazy var siriFavoriteTip = Tip(title: UIConstants.strings.siriFavoriteTipTitle, description: UIConstants.strings.siriFavoriteTipDescription, identifier: TipKey.siriFavoriteTip, showVc: true)
    
    lazy var siriEraseTip = Tip(title: UIConstants.strings.siriEraseTipTitle, description: UIConstants.strings.siriEraseTipDescription, identifier: TipKey.siriEraseTip, showVc: true)
    
    lazy var shareTrackersTip = Tip(title: UIConstants.strings.shareTrackersTipTitle, identifier: TipKey.shareTrackersTip)
    
    func fetchTip() -> Tip? {
        guard let tip = possibleTips.randomElement(), let indexToRemove = possibleTips.index(of: tip) else { return nil }
        if tip.identifier != TipKey.shareTrackersTip {
            possibleTips.remove(at: indexToRemove)
        }
        if canShowTip(with: tip.identifier) {
            return tip
        } else {
            return fetchTip()
        }
    }
    
    private func canShowTip(with id: String) -> Bool {
        let defaults = UserDefaults.standard
        switch id {
        case TipKey.siriFavoriteTip, TipKey.siriEraseTip:
            guard #available(iOS 12.0, *) else { return false }
        case TipKey.shareTrackersTip:
            guard UserDefaults.standard.integer(forKey: BrowserViewController.userDefaultsTrackersBlockedKey) >= 10 else { return false }
        default:
            break
        }
        return defaults.bool(forKey: id)
    }
}
<commit_msg>Fixes #1400: TipManager infinite loop (#1402)<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

import Foundation
import LocalAuthentication

class TipManager {
    
    struct Tip: Equatable {
        var title: String
        var description: String?
        var identifier: String
        var showVc: Bool
        
        init(title: String, description: String? = nil, identifier: String, showVc: Bool = false) {
            self.title = title
            self.identifier = identifier
            self.description = description
            self.showVc = showVc
        }
        
        static func == (lhs: Tip, rhs: Tip) -> Bool {
            return lhs.identifier == rhs.identifier
        }
    }
    
    class TipKey {
        static let autocompleteTip = "autocompleteTip"
        static let sitesNotWorkingTip = "sitesNotWorkingTip"
        static let biometricTip = "biometricTip"
        static let siriFavoriteTip = "siriFavoriteTip"
        static let shareTrackersTip = "shareTrackersTip"
        static let requestDesktopTip = "requestDesktopTip"
        static let siriEraseTip = "siriEraseTip"
    }
    
    static let shared = TipManager()
    private var possibleTips: [Tip]
    private let laContext = LAContext()
    var currentTip: Tip?
    
    init() {
        possibleTips = [Tip]()
        addAllTips()
    }
    
    private func addAllTips() {
        possibleTips.append(autocompleteTip)
        possibleTips.append(sitesNotWorkingTip)
        possibleTips.append(requestDesktopTip)
        possibleTips.append(siriFavoriteTip)
        possibleTips.append(siriEraseTip)
        possibleTips.append(shareTrackersTip)
        if laContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil) {
            possibleTips.append(biometricTip)
        }
    }
    
    lazy var autocompleteTip = Tip(title: UIConstants.strings.autocompleteTipTitle, description: UIConstants.strings.autocompleteTipDescription, identifier: TipKey.autocompleteTip)
    
    lazy var sitesNotWorkingTip = Tip(title: UIConstants.strings.sitesNotWorkingTipTitle, description: UIConstants.strings.sitesNotWorkingTipDescription, identifier: TipKey.sitesNotWorkingTip)
    
    lazy var biometricTip: Tip = {
        if laContext.biometryType == .faceID {
            return Tip(title: UIConstants.strings.biometricTipTitle, description: UIConstants.strings.biometricTipFaceIdDescription, identifier: TipKey.biometricTip, showVc: true)
        } else {
            return Tip(title: UIConstants.strings.biometricTipTitle, description: UIConstants.strings.biometricTipTouchIdDescription, identifier: TipKey.biometricTip, showVc: true)
        }
    }()
    
    lazy var requestDesktopTip = Tip(title: UIConstants.strings.requestDesktopTipTitle, description: UIConstants.strings.requestDesktopTipDescription, identifier: TipKey.requestDesktopTip)
    
    lazy var siriFavoriteTip = Tip(title: UIConstants.strings.siriFavoriteTipTitle, description: UIConstants.strings.siriFavoriteTipDescription, identifier: TipKey.siriFavoriteTip, showVc: true)
    
    lazy var siriEraseTip = Tip(title: UIConstants.strings.siriEraseTipTitle, description: UIConstants.strings.siriEraseTipDescription, identifier: TipKey.siriEraseTip, showVc: true)
    
    lazy var shareTrackersTip = Tip(title: UIConstants.strings.shareTrackersTipTitle, identifier: TipKey.shareTrackersTip)
    
    func fetchTip() -> Tip? {
        guard let tip = possibleTips.randomElement(), let indexToRemove = possibleTips.index(of: tip) else { return nil }
        if tip.identifier != TipKey.shareTrackersTip {
            possibleTips.remove(at: indexToRemove)
        }
        if canShowTip(with: tip.identifier) {
            return tip
        } else if possibleTips.count == 1 {
            return nil
        } else {
            return fetchTip()
        }
    }
    
    private func canShowTip(with id: String) -> Bool {
        let defaults = UserDefaults.standard
        switch id {
        case TipKey.siriFavoriteTip, TipKey.siriEraseTip:
            guard #available(iOS 12.0, *) else { return false }
        case TipKey.shareTrackersTip:
            guard UserDefaults.standard.integer(forKey: BrowserViewController.userDefaultsTrackersBlockedKey) >= 10 else { return false }
        default:
            break
        }
        return defaults.bool(forKey: id)
    }
}

{'fraction_non_alphanumeric': 0.07833537331701346, 'fraction_numerical': 0.003004339601646823, 'mean_word_length': 2.887543252595156, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4121436', 'n_tokens_mistral': 2543, 'n_tokens_neox': 2457, 'n_words': 591}