feat(privacy): Use Content Settings for some Shields Settings on iOS (#30608)
* Add feature flag - Add an iOS-only feature flag for using content settings for persisting Brave Shields settings. * Update Shields default/global settings. - Use content settings when the feature flag is enabled for default settings. * Use content settings for shields when flag is enabled. - Update BraveShieldsTabHelper to use content settings when the flag is enabled. * Use core P3A when feature flag is enabled.
This commit is contained in:
@@ -105,6 +105,10 @@ BASE_FEATURE(kBraveShredCacheData,
|
||||
#else
|
||||
base::FEATURE_DISABLED_BY_DEFAULT);
|
||||
#endif
|
||||
// When enabled, brave will use content settings for Shields on iOS.
|
||||
BASE_FEATURE(kBraveShieldsContentSettingsIOS,
|
||||
"BraveShieldsContentSettingsIOS",
|
||||
base::FEATURE_DISABLED_BY_DEFAULT);
|
||||
// When enabled, will display debug menu for adblock features in the Shields
|
||||
// panel.
|
||||
BASE_FEATURE(kBraveIOSDebugAdblock,
|
||||
|
||||
@@ -34,6 +34,7 @@ BASE_DECLARE_FEATURE(kBraveLocalhostAccessPermission);
|
||||
BASE_DECLARE_FEATURE(kBraveReduceLanguage);
|
||||
BASE_DECLARE_FEATURE(kBraveShredFeature);
|
||||
BASE_DECLARE_FEATURE(kBraveShredCacheData);
|
||||
BASE_DECLARE_FEATURE(kBraveShieldsContentSettingsIOS);
|
||||
BASE_DECLARE_FEATURE(kBraveIOSDebugAdblock);
|
||||
BASE_DECLARE_FEATURE(kBraveIOSEnableFarblingPlugins);
|
||||
BASE_DECLARE_FEATURE(kBraveShowStrictFingerprintingMode);
|
||||
|
||||
@@ -158,6 +158,8 @@ extension BrowserViewController {
|
||||
}
|
||||
|
||||
@MainActor private func recordShieldsLevelUpdateP3A(buckets: [Bucket]) {
|
||||
// if the flag is enabled, P3A is recorded in core shields functions.
|
||||
guard FeatureList.kBraveShieldsContentSettings.enabled else { return }
|
||||
// Q51 On how many domains has the user set the adblock setting to be lower (block less) than the default?
|
||||
let adsBelowGlobalCount = Domain.totalDomainsWithAdblockShieldsLoweredFromGlobal()
|
||||
UmaHistogramRecordValueToBucket(
|
||||
@@ -175,6 +177,8 @@ extension BrowserViewController {
|
||||
}
|
||||
|
||||
func recordFinterprintProtectionP3A(buckets: [Bucket]) {
|
||||
// if the flag is enabled, P3A is recorded in core shields functions.
|
||||
guard FeatureList.kBraveShieldsContentSettings.enabled else { return }
|
||||
// Q53 On how many domains has the user set the FP setting to be lower (block less) than the default?
|
||||
let fingerprintingBelowGlobalCount =
|
||||
Domain.totalDomainsWithFingerprintingProtectionLoweredFromGlobal()
|
||||
@@ -194,6 +198,8 @@ extension BrowserViewController {
|
||||
}
|
||||
|
||||
func recordGlobalAdBlockShieldsP3A() {
|
||||
// if the flag is enabled, P3A is recorded in core shields functions.
|
||||
guard FeatureList.kBraveShieldsContentSettings.enabled else { return }
|
||||
// Q46 What is the global ad blocking shields setting?
|
||||
enum Answer: Int, CaseIterable {
|
||||
case disabled = 0
|
||||
@@ -213,6 +219,8 @@ extension BrowserViewController {
|
||||
}
|
||||
|
||||
func recordGlobalFingerprintingShieldsP3A() {
|
||||
// if the flag is enabled, P3A is recorded in core shields functions.
|
||||
guard FeatureList.kBraveShieldsContentSettings.enabled else { return }
|
||||
// Q47 What is the global fingerprinting shields setting?
|
||||
enum Answer: Int, CaseIterable {
|
||||
case disabled = 0
|
||||
|
||||
+6
-1
@@ -30,7 +30,12 @@ extension BrowserViewController: TabManagerDelegate {
|
||||
tab.pageMetadataHelper = .init(tab: tab)
|
||||
tab.faviconTabHelper = .init(tab: tab)
|
||||
tab.userActivityHelper = .init(tab: tab)
|
||||
let braveShieldsHelper: BraveShieldsTabHelper = .init(tab: tab)
|
||||
let profile =
|
||||
tab.isPrivate ? profileController.profile.offTheRecordProfile : profileController.profile
|
||||
let braveShieldsHelper: BraveShieldsTabHelper = .init(
|
||||
tab: tab,
|
||||
braveShieldsSettings: BraveShieldsSettingsFactory.create(for: profile)
|
||||
)
|
||||
tab.braveShieldsHelper = braveShieldsHelper
|
||||
// When `BraveShieldsTabHelper+TabPolicyDecider` is moved to `BraveShields` target,
|
||||
// we should add it as a policy decider at initialization.
|
||||
|
||||
+1
@@ -520,6 +520,7 @@ extension BrowserViewController: TopToolbarDelegate {
|
||||
tabManager: self.tabManager,
|
||||
feedDataSource: self.feedDataSource,
|
||||
debounceService: DebounceServiceFactory.get(privateMode: false),
|
||||
braveShieldsSettings: BraveShieldsSettingsFactory.create(for: profileController.profile),
|
||||
braveCore: profileController,
|
||||
p3aUtils: braveCore.p3aUtils,
|
||||
rewards: rewards,
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
import BraveCore
|
||||
import BraveShields
|
||||
import Data
|
||||
import Shared
|
||||
@@ -42,7 +43,14 @@ class LinkPreviewViewController: UIViewController {
|
||||
tab.miscDelegate = browserController
|
||||
tab.createWebView()
|
||||
tab.addPolicyDecider(browserController)
|
||||
let braveShieldsTabHelper: BraveShieldsTabHelper = .init(tab: tab)
|
||||
let profile =
|
||||
tab.isPrivate
|
||||
? browserController.profileController.profile.offTheRecordProfile
|
||||
: browserController.profileController.profile
|
||||
let braveShieldsTabHelper: BraveShieldsTabHelper = .init(
|
||||
tab: tab,
|
||||
braveShieldsSettings: BraveShieldsSettingsFactory.create(for: profile)
|
||||
)
|
||||
tab.braveShieldsHelper = braveShieldsTabHelper
|
||||
tab.addPolicyDecider(braveShieldsTabHelper)
|
||||
tab.delegate = browserController
|
||||
|
||||
@@ -1262,7 +1262,7 @@ class TabManager: NSObject {
|
||||
} else {
|
||||
// Don't access `shredLevel` directly, but `TabState` is unavailable
|
||||
// to access via `BraveShieldsTabHelper`. Temporarily access here until
|
||||
// we switch to using `BraveShieldsUtilsIOS` with brave-browser#47350
|
||||
// we switch to using `BraveShieldsSettings` with brave-browser#47350
|
||||
let siteDomain = Domain.getOrCreate(
|
||||
forUrl: url,
|
||||
persistent: !isPrivate
|
||||
|
||||
+41
-1
@@ -65,9 +65,35 @@ import os
|
||||
}
|
||||
@Published var adBlockAndTrackingPreventionLevel: ShieldLevel {
|
||||
didSet {
|
||||
guard oldValue != adBlockAndTrackingPreventionLevel else { return }
|
||||
if FeatureList.kBraveShieldsContentSettings.enabled {
|
||||
braveShieldsSettings.defaultAdBlockMode = adBlockAndTrackingPreventionLevel.adBlockMode
|
||||
}
|
||||
// Also assign to existing pref until deprecated so reverse migration is not required
|
||||
ShieldPreferences.blockAdsAndTrackingLevel = adBlockAndTrackingPreventionLevel
|
||||
}
|
||||
}
|
||||
@Published var isBlockScriptsEnabled: Bool {
|
||||
didSet {
|
||||
guard oldValue != isBlockScriptsEnabled else { return }
|
||||
if FeatureList.kBraveShieldsContentSettings.enabled {
|
||||
braveShieldsSettings.isBlockScriptsEnabledByDefault = isBlockScriptsEnabled
|
||||
}
|
||||
// Also assign to existing pref until deprecated so reverse migration is not required
|
||||
Preferences.Shields.blockScripts.value = isBlockScriptsEnabled
|
||||
}
|
||||
}
|
||||
@Published var isBlockFingerprintingEnabled: Bool {
|
||||
didSet {
|
||||
guard oldValue != isBlockFingerprintingEnabled else { return }
|
||||
if FeatureList.kBraveShieldsContentSettings.enabled {
|
||||
braveShieldsSettings.defaultFingerprintMode =
|
||||
isBlockFingerprintingEnabled ? .standardMode : .allowMode
|
||||
}
|
||||
// Also assign to existing pref until deprecated so reverse migration is not required
|
||||
Preferences.Shields.fingerprintingProtection.value = isBlockFingerprintingEnabled
|
||||
}
|
||||
}
|
||||
@Published var httpsUpgradeLevel: HTTPSUpgradeLevel {
|
||||
didSet {
|
||||
ShieldPreferences.httpsUpgradeLevel = httpsUpgradeLevel
|
||||
@@ -79,6 +105,7 @@ import os
|
||||
}
|
||||
@Published var shredLevel: SiteShredLevel {
|
||||
didSet {
|
||||
// TODO: Support AutoShred via content settings brave-browser#47753
|
||||
ShieldPreferences.shredLevel = shredLevel
|
||||
}
|
||||
}
|
||||
@@ -107,6 +134,7 @@ import os
|
||||
private let p3aUtilities: BraveP3AUtils
|
||||
private let deAmpPrefs: DeAmpPrefs
|
||||
private let debounceService: (any DebounceService)?
|
||||
private let braveShieldsSettings: any BraveShieldsSettings
|
||||
private let rewards: BraveRewards?
|
||||
private let clearDataCallback: ClearDataCallback
|
||||
private let webcompatReporterHandler: WebcompatReporterWebcompatReporterHandler?
|
||||
@@ -117,6 +145,7 @@ import os
|
||||
tabManager: TabManager,
|
||||
feedDataSource: FeedDataSource,
|
||||
debounceService: (any DebounceService)?,
|
||||
braveShieldsSettings: any BraveShieldsSettings,
|
||||
braveCore: BraveProfileController,
|
||||
p3aUtils: BraveP3AUtils,
|
||||
rewards: BraveRewards?,
|
||||
@@ -126,14 +155,25 @@ import os
|
||||
self.p3aUtilities = p3aUtils
|
||||
self.deAmpPrefs = braveCore.deAmpPrefs
|
||||
self.debounceService = debounceService
|
||||
self.braveShieldsSettings = braveShieldsSettings
|
||||
self.tabManager = tabManager
|
||||
self.isP3AEnabled = p3aUtilities.isP3AEnabled
|
||||
self.rewards = rewards
|
||||
self.clearDataCallback = clearDataCallback
|
||||
self.adBlockAndTrackingPreventionLevel = ShieldPreferences.blockAdsAndTrackingLevel
|
||||
if FeatureList.kBraveShieldsContentSettings.enabled {
|
||||
self.adBlockAndTrackingPreventionLevel = braveShieldsSettings.defaultAdBlockMode.shieldLevel
|
||||
self.isBlockScriptsEnabled = braveShieldsSettings.isBlockScriptsEnabledByDefault
|
||||
self.isBlockFingerprintingEnabled =
|
||||
braveShieldsSettings.defaultFingerprintMode == .standardMode
|
||||
} else {
|
||||
self.adBlockAndTrackingPreventionLevel = ShieldPreferences.blockAdsAndTrackingLevel
|
||||
self.isBlockScriptsEnabled = Preferences.Shields.blockScripts.value
|
||||
self.isBlockFingerprintingEnabled = Preferences.Shields.fingerprintingProtection.value
|
||||
}
|
||||
self.httpsUpgradeLevel = ShieldPreferences.httpsUpgradeLevel
|
||||
self.isDeAmpEnabled = deAmpPrefs.isDeAmpEnabled
|
||||
self.isDebounceEnabled = debounceService?.isEnabled ?? false
|
||||
// TODO: Support AutoShred via content settings brave-browser#47753
|
||||
self.shredLevel = ShieldPreferences.shredLevel
|
||||
self.webcompatReporterHandler = webcompatReporterHandler
|
||||
self.isSurveyPanelistEnabled = rewards?.ads.isSurveyPanelistEnabled ?? false
|
||||
|
||||
+5
-5
@@ -72,16 +72,16 @@ struct DefaultShieldsSectionView: View {
|
||||
subtitle: Strings.autoRedirectTrackingURLsDescription,
|
||||
toggle: $settings.isDebounceEnabled
|
||||
)
|
||||
OptionToggleView(
|
||||
|
||||
ToggleView(
|
||||
title: Strings.blockScripts,
|
||||
subtitle: Strings.blockScriptsDescription,
|
||||
option: Preferences.Shields.blockScripts
|
||||
toggle: $settings.isBlockScriptsEnabled
|
||||
)
|
||||
|
||||
OptionToggleView(
|
||||
ToggleView(
|
||||
title: Strings.fingerprintingProtection,
|
||||
subtitle: Strings.fingerprintingProtectionDescription,
|
||||
option: Preferences.Shields.fingerprintingProtection
|
||||
toggle: $settings.isBlockFingerprintingEnabled
|
||||
)
|
||||
|
||||
ToggleView(
|
||||
|
||||
@@ -364,6 +364,7 @@ class SettingsViewController: TableViewController {
|
||||
tabManager: self.tabManager,
|
||||
feedDataSource: self.feedDataSource,
|
||||
debounceService: DebounceServiceFactory.get(privateMode: false),
|
||||
braveShieldsSettings: BraveShieldsSettingsFactory.create(for: braveCore.profile),
|
||||
braveCore: braveCore,
|
||||
p3aUtils: p3aUtilities,
|
||||
rewards: rewards,
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
import BraveCore
|
||||
import Data
|
||||
import Foundation
|
||||
import Preferences
|
||||
@@ -21,25 +22,40 @@ extension TabDataValues {
|
||||
@MainActor
|
||||
public class BraveShieldsTabHelper {
|
||||
private weak var tab: (any TabState)?
|
||||
private let braveShieldsSettings: any BraveShieldsSettings
|
||||
|
||||
public init(tab: some TabState) {
|
||||
public init(
|
||||
tab: some TabState,
|
||||
braveShieldsSettings: any BraveShieldsSettings
|
||||
) {
|
||||
self.tab = tab
|
||||
self.braveShieldsSettings = braveShieldsSettings
|
||||
}
|
||||
|
||||
public func isBraveShieldsEnabled(for url: URL?) -> Bool {
|
||||
guard let url = url ?? tab?.visibleURL, let isPrivate = tab?.isPrivate else { return false }
|
||||
if FeatureList.kBraveShieldsContentSettings.enabled {
|
||||
return braveShieldsSettings.isBraveShieldsEnabled(for: url)
|
||||
}
|
||||
let domain = Domain.getOrCreate(forUrl: url, persistent: !isPrivate)
|
||||
return !domain.areAllShieldsOff
|
||||
}
|
||||
|
||||
public func setBraveShieldsEnabled(_ isEnabled: Bool, for url: URL?) {
|
||||
guard let url = url ?? tab?.visibleURL, let isPrivate = tab?.isPrivate else { return }
|
||||
if FeatureList.kBraveShieldsContentSettings.enabled {
|
||||
braveShieldsSettings.setBraveShieldsEnabled(isEnabled, for: url)
|
||||
}
|
||||
// Also assign to Domain until deprecated so reverse migration is required
|
||||
let domain = Domain.getOrCreate(forUrl: url, persistent: !isPrivate)
|
||||
domain.shield_allOff = NSNumber(booleanLiteral: !isEnabled)
|
||||
}
|
||||
|
||||
public func shieldLevel(for url: URL?, considerAllShieldsOption: Bool) -> ShieldLevel {
|
||||
guard let url = url ?? tab?.visibleURL, let isPrivate = tab?.isPrivate else { return .disabled }
|
||||
if FeatureList.kBraveShieldsContentSettings.enabled {
|
||||
return braveShieldsSettings.adBlockMode(for: url).shieldLevel
|
||||
}
|
||||
let domain = Domain.getOrCreate(forUrl: url, persistent: !isPrivate)
|
||||
if considerAllShieldsOption {
|
||||
return domain.globalBlockAdsAndTrackingLevel
|
||||
@@ -50,18 +66,30 @@ public class BraveShieldsTabHelper {
|
||||
|
||||
public func setShieldLevel(_ shieldLevel: ShieldLevel, for url: URL?) {
|
||||
guard let url = url ?? tab?.visibleURL, let isPrivate = tab?.isPrivate else { return }
|
||||
if FeatureList.kBraveShieldsContentSettings.enabled {
|
||||
braveShieldsSettings.setAdBlockMode(shieldLevel.adBlockMode, for: url)
|
||||
}
|
||||
// Also assign to Domain until deprecated so reverse migration is required
|
||||
let domain = Domain.getOrCreate(forUrl: url, persistent: !isPrivate)
|
||||
domain.domainBlockAdsAndTrackingLevel = shieldLevel
|
||||
}
|
||||
|
||||
public func setBlockScriptsEnabled(_ isEnabled: Bool, for url: URL?) {
|
||||
guard let url = url ?? tab?.visibleURL, let isPrivate = tab?.isPrivate else { return }
|
||||
if FeatureList.kBraveShieldsContentSettings.enabled {
|
||||
braveShieldsSettings.setBlockScriptsEnabled(isEnabled, for: url)
|
||||
}
|
||||
// Also assign to Domain until deprecated so reverse migration is required
|
||||
let domain = Domain.getOrCreate(forUrl: url, persistent: !isPrivate)
|
||||
domain.shield_noScript = NSNumber(booleanLiteral: isEnabled)
|
||||
}
|
||||
|
||||
public func setBlockFingerprintingEnabled(_ isEnabled: Bool, for url: URL?) {
|
||||
guard let url = url ?? tab?.visibleURL, let isPrivate = tab?.isPrivate else { return }
|
||||
if FeatureList.kBraveShieldsContentSettings.enabled {
|
||||
braveShieldsSettings.setFingerprintMode(isEnabled ? .standardMode : .allowMode, for: url)
|
||||
}
|
||||
// Also assign to Domain until deprecated so reverse migration is required
|
||||
let domain = Domain.getOrCreate(forUrl: url, persistent: !isPrivate)
|
||||
domain.shield_fpProtection = NSNumber(booleanLiteral: isEnabled)
|
||||
}
|
||||
@@ -73,6 +101,20 @@ public class BraveShieldsTabHelper {
|
||||
considerAllShieldsOption: Bool
|
||||
) -> Bool {
|
||||
guard let url = url ?? tab?.visibleURL, let isPrivate = tab?.isPrivate else { return false }
|
||||
if FeatureList.kBraveShieldsContentSettings.enabled {
|
||||
if considerAllShieldsOption && !isBraveShieldsEnabled(for: url) {
|
||||
// Shields is disabled for this url
|
||||
return false
|
||||
}
|
||||
switch shield {
|
||||
case .allOff:
|
||||
return braveShieldsSettings.isBraveShieldsEnabled(for: url)
|
||||
case .fpProtection:
|
||||
return braveShieldsSettings.fingerprintMode(for: url) == .standardMode
|
||||
case .noScript:
|
||||
return braveShieldsSettings.isBlockScriptsEnabled(for: url)
|
||||
}
|
||||
}
|
||||
let domain = Domain.getOrCreate(forUrl: url, persistent: !isPrivate)
|
||||
return domain.isShieldExpected(shield, considerAllShieldsOption: considerAllShieldsOption)
|
||||
}
|
||||
@@ -81,6 +123,7 @@ public class BraveShieldsTabHelper {
|
||||
for url: URL?
|
||||
) -> SiteShredLevel {
|
||||
guard let url = url ?? tab?.visibleURL, let isPrivate = tab?.isPrivate else { return .never }
|
||||
// TODO: Support AutoShred via content settings brave-browser#47753
|
||||
let domain = Domain.getOrCreate(forUrl: url, persistent: !isPrivate)
|
||||
return domain.shredLevel
|
||||
}
|
||||
@@ -90,6 +133,7 @@ public class BraveShieldsTabHelper {
|
||||
for url: URL?
|
||||
) {
|
||||
guard let url = url ?? tab?.visibleURL, let isPrivate = tab?.isPrivate else { return }
|
||||
// TODO: Support AutoShred via content settings brave-browser#47753
|
||||
let domain = Domain.getOrCreate(forUrl: url, persistent: !isPrivate)
|
||||
domain.shredLevel = shredLevel
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
import BraveCore
|
||||
import Foundation
|
||||
|
||||
/// A 3 part option for shield levels varying in strength of blocking content
|
||||
@@ -37,4 +38,25 @@ public enum ShieldLevel: String, CaseIterable, Hashable {
|
||||
case .disabled, .standard: return false
|
||||
}
|
||||
}
|
||||
|
||||
public var adBlockMode: BraveShields.AdBlockMode {
|
||||
switch self {
|
||||
case .aggressive: return .aggressive
|
||||
case .standard: return .standard
|
||||
case .disabled: return .allow
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension BraveShields.AdBlockMode {
|
||||
public var shieldLevel: ShieldLevel {
|
||||
switch self {
|
||||
case .aggressive: return .aggressive
|
||||
case .standard: return .standard
|
||||
case .allow: return .disabled
|
||||
@unknown default:
|
||||
assertionFailure("Unexpected AdBlockMode")
|
||||
return .standard
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,6 +53,7 @@ OBJC_EXPORT
|
||||
@property(class, nonatomic, readonly) Feature* kBraveSearchDefaultAPIFeature;
|
||||
@property(class, nonatomic, readonly) Feature* kBraveShredFeature;
|
||||
@property(class, nonatomic, readonly) Feature* kBraveShredCacheData;
|
||||
@property(class, nonatomic, readonly) Feature* kBraveShieldsContentSettings;
|
||||
@property(class, nonatomic, readonly) Feature* kBraveIOSDebugAdblock;
|
||||
@property(class, nonatomic, readonly) Feature* kBraveIOSEnableFarblingPlugins;
|
||||
@property(class, nonatomic, readonly)
|
||||
|
||||
@@ -211,6 +211,11 @@
|
||||
initWithFeature:&brave_shields::features::kBraveShredCacheData];
|
||||
}
|
||||
|
||||
+ (Feature*)kBraveShieldsContentSettings {
|
||||
return [[Feature alloc] initWithFeature:&brave_shields::features::
|
||||
kBraveShieldsContentSettingsIOS];
|
||||
}
|
||||
|
||||
+ (Feature*)kBraveIOSDebugAdblock {
|
||||
return [[Feature alloc]
|
||||
initWithFeature:&brave_shields::features::kBraveIOSDebugAdblock];
|
||||
|
||||
@@ -90,6 +90,15 @@
|
||||
flags_ui::kOsIos, \
|
||||
FEATURE_VALUE_TYPE(brave_shields::features::kBraveShredCacheData), \
|
||||
}, \
|
||||
{ \
|
||||
"brave-shields-content-settings", \
|
||||
"Brave Shields use Content Settings", \
|
||||
"Brave Shields will use content settings for persisting Shields " \
|
||||
"preferences", \
|
||||
flags_ui::kOsIos, \
|
||||
FEATURE_VALUE_TYPE( \
|
||||
brave_shields::features::kBraveShieldsContentSettingsIOS), \
|
||||
}, \
|
||||
{ \
|
||||
"https-by-default", \
|
||||
"Use HTTPS by Default", \
|
||||
|
||||
Reference in New Issue
Block a user