Skip to content
This repository has been archived by the owner on May 10, 2024. It is now read-only.

Commit

Permalink
Fix #7352: Add tracking and ad block level setting
Browse files Browse the repository at this point in the history
  • Loading branch information
cuba committed May 10, 2023
1 parent c234c08 commit 9a16ace
Show file tree
Hide file tree
Showing 13 changed files with 588 additions and 46 deletions.
1 change: 1 addition & 0 deletions Sources/Brave/Frontend/Browser/BrowserViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2995,6 +2995,7 @@ extension BrowserViewController: PreferencesObserver {
case Preferences.General.enablePullToRefresh.key:
tabManager.selectedTab?.updatePullToRefreshVisibility()
case Preferences.Shields.blockAdsAndTracking.key,
Preferences.Shields.blockAdsAndTrackingAggressive.key,
Preferences.Shields.blockScripts.key,
Preferences.Shields.blockPhishingAndMalware.key,
Preferences.Shields.blockImages.key,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -583,7 +583,7 @@ extension PlaylistWebLoader: WKNavigationDelegate {

// Force adblocking on
domainForShields.shield_allOff = 0
domainForShields.shield_adblockAndTp = true
domainForShields.adBlockAndTPShieldLevel = .standard

// Load block lists
let ruleLists = await ContentBlockerManager.shared.ruleLists(for: domainForShields)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,14 +105,7 @@ class BraveShieldsAndPrivacySettingsController: TableViewController {
// MARK: - P3A

private func recordGlobalAdBlockShieldsP3A() {
// Q46 What is the global ad blocking shields setting?
enum Answer: Int, CaseIterable {
case disabled = 0
case standard = 1
case aggressive = 2
}
let answer: Answer = Preferences.Shields.blockAdsAndTracking.value ? .standard : .disabled
UmaHistogramEnumeration("Brave.Shields.AdBlockSetting", sample: answer)
UmaHistogramEnumeration("Brave.Shields.AdBlockSetting", sample: Preferences.Shields.blockAdsAndTrackingLevel.p3AAnswer)
}

private func recordGlobalFingerprintingShieldsP3A() {
Expand All @@ -132,7 +125,20 @@ class BraveShieldsAndPrivacySettingsController: TableViewController {
var shields = Section(
header: .title(Strings.shieldsDefaults),
rows: [
.boolRow(title: Strings.blockAdsAndTracking, detailText: Strings.blockAdsAndTrackingDescription, option: Preferences.Shields.blockAdsAndTracking),
.pickerRow(
title: Strings.trackersAndAdsBlocking,
detailText: Strings.trackersAndAdsBlockingDescription,
options: Preferences.Shields.ShieldLevel.allCases,
selectedValue: Preferences.Shields.blockAdsAndTrackingLevel,
valueChange: { value in
guard let shieldLevel = Preferences.Shields.ShieldLevel(rawValue: value.id) else {
assertionFailure()
return
}

Preferences.Shields.blockAdsAndTrackingLevel = shieldLevel
}
),
.boolRow(title: Strings.HTTPSEverywhere, detailText: Strings.HTTPSEverywhereDescription, option: Preferences.Shields.httpsEverywhere),
.boolRow(title: Strings.blockPhishingAndMalware, option: Preferences.Shields.blockPhishingAndMalware),
.boolRow(title: Strings.autoRedirectAMPPages, detailText: Strings.autoRedirectAMPPagesDescription, option: Preferences.Shields.autoRedirectAMPPages),
Expand Down Expand Up @@ -524,3 +530,17 @@ class BraveShieldsAndPrivacySettingsController: TableViewController {
_toggleFolderAccessForBlockCookies(locked: true)
}
}

extension Preferences.Shields.ShieldLevel: PickerAccessoryViewValue {
public var id: String {
return rawValue
}

public var localizedTitle: String {
switch self {
case .aggressive: return Strings.trackersAndAdsBlockingAggressive
case .disabled: return Strings.trackersAndAdsBlockingDisabled
case .standard: return Strings.trackersAndAdsBlockingStandard
}
}
}
75 changes: 74 additions & 1 deletion Sources/Brave/Frontend/Shields/AdvancedShieldsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,32 @@ import UIKit
import Shared
import BraveShared
import BraveUI
import Preferences

class AdvancedShieldsView: UIStackView {
let siteTitle = HeaderTitleView()
let adsTrackersControl = ToggleView(title: Strings.blockAdsAndTracking)
let blockMalwareControl = ToggleView(title: Strings.blockPhishing)
let blockScriptsControl = ToggleView(title: Strings.blockScripts)
let fingerprintingControl = ToggleView(title: Strings.fingerprintingProtection)
let globalControlsTitleView = HeaderTitleView().then {
$0.titleLabel.text = Strings.Shields.globalControls.uppercased()
}
let globalControlsButton = ChangeGlobalDefaultsView()
var adsTrackerValueChange: ((Preferences.Shields.ShieldLevel) -> Void)?

lazy var adsTrackersControl: PickerView = {
return PickerView(
title: Strings.trackersAndAdsBlocking,
options: Preferences.Shields.ShieldLevel.allCases,
selectedValue: Preferences.Shields.blockAdsAndTrackingLevel) { [weak self] value in
guard let shieldLevel = Preferences.Shields.ShieldLevel(rawValue: value.id) else {
assertionFailure()
return
}

self?.adsTrackerValueChange?(shieldLevel)
}
}()

override init(frame: CGRect) {
super.init(frame: frame)
Expand Down Expand Up @@ -145,6 +160,64 @@ extension AdvancedShieldsView {
valueToggled?(toggleSwitch.isOn)
}
}

/// A container displaying a toggle for the user
class PickerView: UIView {
private let titleLabel: UILabel = {
let label = UILabel()
label.font = .systemFont(ofSize: 15.0)
label.numberOfLines = 0
label.textColor = .braveLabel
return label
}()

let pickerView: PickerAccessoryView
var selectedValue: PickerAccessoryView.ValueChange?

override var accessibilityLabel: String? {
get { titleLabel.accessibilityLabel }
set { assertionFailure() } // swiftlint:disable:this unused_setter_value
}

override var accessibilityValue: String? {
get { pickerView.accessibilityValue }
set { assertionFailure() } // swiftlint:disable:this unused_setter_value
}

init(title: String, options: [PickerAccessoryViewValue], selectedValue: PickerAccessoryViewValue, valueChange: @escaping PickerAccessoryView.ValueChange) {
self.pickerView = PickerAccessoryView(
options: options,
selectedValue: selectedValue,
valueChange: valueChange
)

super.init(frame: .zero)

let stackView = UIStackView(arrangedSubviews: [titleLabel, pickerView])
stackView.spacing = 12.0
stackView.alignment = .center
stackView.distribution = .equalSpacing
addSubview(stackView)

snp.makeConstraints {
$0.height.greaterThanOrEqualTo(44)
}

stackView.snp.makeConstraints {
$0.horizontalEdges.equalToSuperview().inset(16)
$0.verticalEdges.equalToSuperview()
}

titleLabel.text = title
isAccessibilityElement = true
accessibilityTraits.insert(.button)
}

@available(*, unavailable)
required init?(coder aDecoder: NSCoder) {
fatalError()
}
}

class SeparatorView: UIView {
override init(frame: CGRect) {
Expand Down
20 changes: 19 additions & 1 deletion Sources/Brave/Frontend/Shields/ShieldsViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,10 @@ class ShieldsViewController: UIViewController, PopoverContentComponent {

if let domain = domain {
shieldsUpSwitch.isOn = !domain.isShieldExpected(.AllOff, considerAllShieldsOption: false)
shieldsView.advancedShieldView.adsTrackersControl.pickerView.selectedValue = domain.adBlockAndTPShieldLevel
} else {
shieldsUpSwitch.isOn = true
shieldsView.advancedShieldView.adsTrackersControl.pickerView.selectedValue = Preferences.Shields.blockAdsAndTrackingLevel
}

shieldControlMapping.forEach { shield, view, option in
Expand All @@ -85,6 +87,7 @@ class ShieldsViewController: UIViewController, PopoverContentComponent {
view.toggleSwitch.isOn = domain.isShieldExpected(shield, considerAllShieldsOption: false)
}
}

updateGlobalShieldState(shieldsUpSwitch.isOn)
}

Expand Down Expand Up @@ -212,7 +215,6 @@ class ShieldsViewController: UIViewController, PopoverContentComponent {

/// Groups the shield types with their control and global preference
private lazy var shieldControlMapping: [(BraveShield, AdvancedShieldsView.ToggleView, Preferences.Option<Bool>?)] = [
(.AdblockAndTp, shieldsView.advancedShieldView.adsTrackersControl, Preferences.Shields.blockAdsAndTracking),
(.SafeBrowsing, shieldsView.advancedShieldView.blockMalwareControl, Preferences.Shields.blockPhishingAndMalware),
(.NoScript, shieldsView.advancedShieldView.blockScriptsControl, Preferences.Shields.blockScripts),
(.FpProtection, shieldsView.advancedShieldView.fingerprintingControl, Preferences.Shields.fingerprintingProtection),
Expand Down Expand Up @@ -264,6 +266,22 @@ class ShieldsViewController: UIViewController, PopoverContentComponent {
shieldsView.advancedControlsBar.isShowingAdvancedControls = true
updatePreferredContentSize()
}

shieldsView.advancedShieldView.adsTrackerValueChange = { [weak self] shieldLevel in
guard let self = self, let url = self.url else { return }

Domain.setAdAndTP(
shieldLevel: shieldLevel,
for: url,
isPrivateBrowsing: PrivateBrowsingManager.shared.isPrivateBrowsing
)

// Wait a fraction of a second to allow DB write to complete otherwise it will not use the
// updated shield settings when reloading the page
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
self.shieldsSettingsChanged?(self, .AdblockAndTp)
}
}

shieldControlMapping.forEach { shield, toggle, option in
toggle.valueToggled = { [weak self] on in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import Foundation
import WebKit
import Shared
import Preferences
import os.log

class SiteStateListenerScriptHandler: TabContentScript {
Expand Down Expand Up @@ -67,8 +68,12 @@ class SiteStateListenerScriptHandler: TabContentScript {
if domain.areAllShieldsOff { return }

let models = await AdBlockStats.shared.cosmeticFilterModels(forFrameURL: frameURL, domain: domain)
let args = try self.makeArgs(from: models, frameURL: frameURL)
let source = try ScriptFactory.shared.makeScriptSource(of: .selectorsPoller).replacingOccurrences(of: "$<args>", with: args)
let args = try self.makeArgs(
from: models, frameURL: frameURL,
isAggressive: domain.adBlockAndTPShieldLevel.isAggressive
)
let source = try ScriptFactory.shared.makeScriptSource(of: .selectorsPoller)
.replacingOccurrences(of: "$<args>", with: args)

let secureSource = CosmeticFiltersScriptHandler.secureScript(
handlerNamesMap: [
Expand Down Expand Up @@ -97,7 +102,7 @@ class SiteStateListenerScriptHandler: TabContentScript {
}
}

@MainActor private func makeArgs(from modelTuples: [CachedAdBlockEngine.CosmeticFilterModelTuple], frameURL: URL) throws -> String {
@MainActor private func makeArgs(from modelTuples: [CachedAdBlockEngine.CosmeticFilterModelTuple], frameURL: URL, isAggressive: Bool) throws -> String {
var standardSelectors: Set<String> = []
var agressiveSelectors: Set<String> = []
var styleSelectors: [String: Set<String>] = [:]
Expand Down Expand Up @@ -126,7 +131,7 @@ class SiteStateListenerScriptHandler: TabContentScript {
// (i.e. we don't hide first party content on standard mode)
let setup = UserScriptType.SelectorsPollerSetup(
frameURL: frameURL,
hideFirstPartyContent: false,
hideFirstPartyContent: isAggressive,
genericHide: modelTuples.contains { $0.model.genericHide },
firstSelectorsPollingDelayMs: nil,
switchToSelectorsPollingThreshold: 1000,
Expand Down
2 changes: 1 addition & 1 deletion Sources/BraveShields/BraveShield.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public enum BraveShield {
case .AllOff:
return false
case .AdblockAndTp:
return Preferences.Shields.blockAdsAndTracking.value
return Preferences.Shields.blockAdsAndTrackingLevel.isEnabled
case .SafeBrowsing:
return Preferences.Shields.blockPhishingAndMalware.value
case .FpProtection:
Expand Down
8 changes: 6 additions & 2 deletions Sources/BraveStrings/BraveStrings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1036,8 +1036,12 @@ extension Strings {
public static let privateBrowsingOnly = NSLocalizedString("PrivateBrowsingOnly", tableName: "BraveShared", bundle: .module, value: "Private Browsing Only", comment: "Setting to keep app in private mode")
public static let shieldsDefaults = NSLocalizedString("ShieldsDefaults", tableName: "BraveShared", bundle: .module, value: "Brave Shields Global Defaults", comment: "Section title for adbblock, tracking protection, HTTPS-E, and cookies")
public static let shieldsDefaultsFooter = NSLocalizedString("ShieldsDefaultsFooter", tableName: "BraveShared", bundle: .module, value: "These are the default Shields settings for new sites. Changing these won't affect your existing per-site settings.", comment: "Section footer for global shields defaults")
public static let blockAdsAndTracking = NSLocalizedString("BlockAdsAndTracking", tableName: "BraveShared", bundle: .module, value: "Block Cross-Site Trackers", comment: "")
public static let blockAdsAndTrackingDescription = NSLocalizedString("BlockAdsAndTrackingDescription", tableName: "BraveShared", bundle: .module, value: "Prevents ads, popups, and trackers from loading.", comment: "")
public static let trackersAndAdsBlocking = NSLocalizedString("TrackersAndAdsBlocking", tableName: "BraveShared", bundle: .module, value: "Trackers & Ads Blocking", comment: "A label for a shield option that allows you to switch between different blocking levels for tracker and ads blocking. Options include disabled, standard and aggressive.")
public static let trackersAndAdsBlockingDescription = NSLocalizedString("BlockAdsAndTrackingDescription", tableName: "BraveShared", bundle: .module, value: "Prevents ads, popups, and trackers from loading.", comment: "")
public static let trackersAndAdsBlockingDisabled = NSLocalizedString("BlockAdsAndTrackingDisabled", tableName: "BraveShared", bundle: .module, value: "Disabled", comment: "The option the user can select to disable ad and tracker blocking")
public static let trackersAndAdsBlockingAggressive = NSLocalizedString("BlockAdsAndTrackingAggressive", tableName: "BraveShared", bundle: .module, value: "Aggressive", comment: "The option the user can select to do aggressive ad and tracker blocking")
public static let trackersAndAdsBlockingStandard = NSLocalizedString("BlockAdsAndTrackingStandard", tableName: "BraveShared", bundle: .module, value: "Standard", comment: "The option the user can select to do standard (non-aggressive) ad and tracker blocking")

public static let HTTPSEverywhere = NSLocalizedString("HTTPSEverywhere", tableName: "BraveShared", bundle: .module, value: "Upgrade Connections to HTTPS", comment: "")
public static let HTTPSEverywhereDescription = NSLocalizedString("HTTPSEverywhereDescription", tableName: "BraveShared", bundle: .module, value: "Opens sites using secure HTTPS instead of HTTP when possible.", comment: "")
public static let blockPhishingAndMalware = NSLocalizedString("BlockPhishingAndMalware", tableName: "BraveShared", bundle: .module, value: "Block Phishing and Malware", comment: "")
Expand Down
Loading

0 comments on commit 9a16ace

Please sign in to comment.