| // Copyright 2022 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #import "ios/chrome/browser/ui/settings/privacy/safe_browsing/safe_browsing_standard_protection_mediator.h" |
| |
| #import "base/apple/foundation_util.h" |
| #import "base/notreached.h" |
| #import "components/password_manager/core/common/password_manager_pref_names.h" |
| #import "components/prefs/pref_service.h" |
| #import "components/safe_browsing/core/common/features.h" |
| #import "components/safe_browsing/core/common/safe_browsing_prefs.h" |
| #import "components/signin/public/identity_manager/objc/identity_manager_observer_bridge.h" |
| #import "ios/chrome/browser/shared/ui/list_model/list_model.h" |
| #import "ios/chrome/browser/shared/ui/symbols/symbols.h" |
| #import "ios/chrome/browser/shared/ui/table_view/cells/table_view_info_button_item.h" |
| #import "ios/chrome/browser/shared/ui/table_view/cells/table_view_switch_item.h" |
| #import "ios/chrome/browser/signin/authentication_service.h" |
| #import "ios/chrome/browser/ui/settings/cells/safe_browsing_header_item.h" |
| #import "ios/chrome/browser/ui/settings/cells/sync_switch_item.h" |
| #import "ios/chrome/browser/ui/settings/privacy/safe_browsing/safe_browsing_constants.h" |
| #import "ios/chrome/browser/ui/settings/privacy/safe_browsing/safe_browsing_standard_protection_consumer.h" |
| #import "ios/chrome/browser/ui/settings/utils/observable_boolean.h" |
| #import "ios/chrome/browser/ui/settings/utils/pref_backed_boolean.h" |
| #import "ios/chrome/common/ui/colors/semantic_color_names.h" |
| #import "ios/chrome/grit/ios_google_chrome_strings.h" |
| #import "ios/chrome/grit/ios_strings.h" |
| #import "ui/base/l10n/l10n_util.h" |
| |
| using ItemArray = NSArray<TableViewItem*>*; |
| |
| namespace { |
| |
| // List of item types. |
| typedef NS_ENUM(NSInteger, ItemType) { |
| ItemTypeShieldIcon = kItemTypeEnumZero, |
| ItemTypeMetricIcon, |
| ItemTypePasswordLeakCheckSwitch, |
| ItemTypeSafeBrowsingExtendedReporting, |
| ItemTypeSafeBrowsingManagedExtendedReporting, |
| }; |
| |
| // The size of the symbols. |
| const CGFloat kSymbolSize = 20; |
| |
| } // namespace |
| |
| @interface SafeBrowsingStandardProtectionMediator () < |
| BooleanObserver, |
| IdentityManagerObserverBridgeDelegate> { |
| std::unique_ptr<signin::IdentityManagerObserverBridge> |
| _identityManagerObserver; |
| } |
| |
| // User pref service used to check if a specific pref is managed by enterprise |
| // policies. |
| @property(nonatomic, assign, readonly) PrefService* userPrefService; |
| |
| // Authentication service. |
| @property(nonatomic, assign, readonly) AuthenticationService* authService; |
| |
| // Returns YES if the user has selected Standard Protection for the Safe |
| // Browsing choice. |
| @property(nonatomic, assign, readonly) BOOL inSafeBrowsingStandardProtection; |
| |
| // Preference value for the Safe Browsing Enhanced Protection feature. |
| @property(nonatomic, strong, readonly) |
| PrefBackedBoolean* safeBrowsingEnhancedProtectionPreference; |
| |
| // Preference value for the "Safe Browsing" feature. |
| @property(nonatomic, strong, readonly) |
| PrefBackedBoolean* safeBrowsingStandardProtectionPreference; |
| |
| // Preference value for Safe Browsing Extended Reporting. |
| @property(nonatomic, strong, readonly) |
| PrefBackedBoolean* safeBrowsingExtendedReportingPreference; |
| |
| // The observable boolean that binds to the password leak check settings |
| // state. |
| @property(nonatomic, strong, readonly) |
| PrefBackedBoolean* passwordLeakCheckPreference; |
| |
| // The item related to the switch for the automatic password leak detection |
| // setting. |
| @property(nonatomic, strong, null_resettable) |
| TableViewSwitchItem* passwordLeakCheckItem; |
| |
| // Header that has shield icon. |
| @property(nonatomic, strong) SafeBrowsingHeaderItem* shieldIconHeader; |
| |
| // Second header which has a metric icon. |
| @property(nonatomic, strong) SafeBrowsingHeaderItem* metricIconHeader; |
| |
| // All the items for the standard safe browsing section. |
| @property(nonatomic, strong, readonly) |
| ItemArray safeBrowsingStandardProtectionItems; |
| |
| @end |
| |
| @implementation SafeBrowsingStandardProtectionMediator |
| |
| @synthesize safeBrowsingStandardProtectionItems = |
| _safeBrowsingStandardProtectionItems; |
| |
| - (instancetype)initWithUserPrefService:(PrefService*)userPrefService |
| authService:(AuthenticationService*)authService |
| identityManager: |
| (signin::IdentityManager*)identityManager { |
| self = [super init]; |
| if (self) { |
| DCHECK(userPrefService); |
| _userPrefService = userPrefService; |
| _authService = authService; |
| _identityManagerObserver = |
| std::make_unique<signin::IdentityManagerObserverBridge>(identityManager, |
| self); |
| |
| _safeBrowsingEnhancedProtectionPreference = [[PrefBackedBoolean alloc] |
| initWithPrefService:userPrefService |
| prefName:prefs::kSafeBrowsingEnhanced]; |
| _safeBrowsingEnhancedProtectionPreference.observer = self; |
| _safeBrowsingStandardProtectionPreference = [[PrefBackedBoolean alloc] |
| initWithPrefService:userPrefService |
| prefName:prefs::kSafeBrowsingEnabled]; |
| _safeBrowsingStandardProtectionPreference.observer = self; |
| _safeBrowsingExtendedReportingPreference = [[PrefBackedBoolean alloc] |
| initWithPrefService:userPrefService |
| prefName:prefs::kSafeBrowsingScoutReportingEnabled]; |
| _safeBrowsingExtendedReportingPreference.observer = self; |
| _passwordLeakCheckPreference = [[PrefBackedBoolean alloc] |
| initWithPrefService:userPrefService |
| prefName:password_manager::prefs:: |
| kPasswordLeakDetectionEnabled]; |
| _passwordLeakCheckPreference.observer = self; |
| } |
| return self; |
| } |
| |
| - (void)disconnect { |
| _identityManagerObserver = nil; |
| } |
| |
| #pragma mark - Properties |
| |
| - (ItemArray)safeBrowsingStandardProtectionItems { |
| if (!_safeBrowsingStandardProtectionItems) { |
| NSMutableArray* items = [NSMutableArray array]; |
| if (self.userPrefService->IsManagedPreference( |
| prefs::kSafeBrowsingEnabled)) { |
| TableViewInfoButtonItem* safeBrowsingManagedExtendedReportingItem = [self |
| tableViewInfoButtonItemType: |
| ItemTypeSafeBrowsingManagedExtendedReporting |
| textStringID: |
| IDS_IOS_SAFE_BROWSING_STANDARD_PROTECTION_EXTENDED_REPORTING_TITLE |
| detailStringID: |
| IDS_IOS_SAFE_BROWSING_STANDARD_PROTECTION_EXTENDED_REPORTING_SUMMARY |
| status: |
| self.safeBrowsingStandardProtectionPreference |
| .value]; |
| [items addObject:safeBrowsingManagedExtendedReportingItem]; |
| } else { |
| SyncSwitchItem* safeBrowsingExtendedReportingItem = [self |
| switchItemWithItemType:ItemTypeSafeBrowsingExtendedReporting |
| textStringID: |
| IDS_IOS_SAFE_BROWSING_STANDARD_PROTECTION_EXTENDED_REPORTING_TITLE |
| detailStringID: |
| IDS_IOS_SAFE_BROWSING_STANDARD_PROTECTION_EXTENDED_REPORTING_SUMMARY |
| defaultState:safe_browsing::IsExtendedReportingEnabled( |
| *self.userPrefService) |
| enabled:self.inSafeBrowsingStandardProtection]; |
| safeBrowsingExtendedReportingItem.accessibilityIdentifier = |
| kSafeBrowsingExtendedReportingCellId; |
| [items addObject:safeBrowsingExtendedReportingItem]; |
| } |
| [items addObject:self.passwordLeakCheckItem]; |
| |
| _safeBrowsingStandardProtectionItems = items; |
| } |
| return _safeBrowsingStandardProtectionItems; |
| } |
| |
| - (SafeBrowsingHeaderItem*)shieldIconHeader { |
| if (!_shieldIconHeader) { |
| UIImage* shieldIcon; |
| shieldIcon = CustomSymbolWithPointSize(kPrivacySymbol, kSymbolSize); |
| SafeBrowsingHeaderItem* shieldIconItem = [self |
| detailItemWithType:ItemTypeShieldIcon |
| detailText: |
| IDS_IOS_SAFE_BROWSING_STANDARD_PROTECTION_BULLET_ONE |
| image:shieldIcon |
| accessibilityIdentifier:kSafeBrowsingStandardProtectionShieldCellId]; |
| _shieldIconHeader = shieldIconItem; |
| } |
| return _shieldIconHeader; |
| } |
| |
| - (SafeBrowsingHeaderItem*)metricIconHeader { |
| if (!_metricIconHeader) { |
| UIImage* metricIcon = |
| DefaultSymbolWithPointSize(kCheckmarkCircleSymbol, kSymbolSize); |
| SafeBrowsingHeaderItem* metricIconItem = [self |
| detailItemWithType:ItemTypeMetricIcon |
| detailText: |
| IDS_IOS_SAFE_BROWSING_STANDARD_PROTECTION_BULLET_TWO |
| image:metricIcon |
| accessibilityIdentifier:kSafeBrowsingStandardProtectionMetricCellId]; |
| _metricIconHeader = metricIconItem; |
| } |
| return _metricIconHeader; |
| } |
| |
| - (TableViewSwitchItem*)passwordLeakCheckItem { |
| if (!_passwordLeakCheckItem) { |
| TableViewSwitchItem* passwordLeakCheckItem = [[TableViewSwitchItem alloc] |
| initWithType:ItemTypePasswordLeakCheckSwitch]; |
| passwordLeakCheckItem.text = l10n_util::GetNSString( |
| IDS_IOS_SAFE_BROWSING_STANDARD_PROTECTION_LEAK_CHECK_TITLE); |
| passwordLeakCheckItem.accessibilityIdentifier = |
| kSafeBrowsingStandardProtectionPasswordLeakCellId; |
| [self configureLeakCheckItem:passwordLeakCheckItem]; |
| |
| _passwordLeakCheckItem = passwordLeakCheckItem; |
| } |
| return _passwordLeakCheckItem; |
| } |
| |
| - (void)setConsumer:(id<SafeBrowsingStandardProtectionConsumer>)consumer { |
| if (_consumer == consumer) |
| return; |
| _consumer = consumer; |
| [_consumer setSafeBrowsingStandardProtectionItems: |
| self.safeBrowsingStandardProtectionItems]; |
| [_consumer setShieldIconHeader:self.shieldIconHeader]; |
| [_consumer setMetricIconHeader:self.metricIconHeader]; |
| } |
| |
| - (BOOL)inSafeBrowsingStandardProtection { |
| return safe_browsing::GetSafeBrowsingState(*self.userPrefService) == |
| safe_browsing::SafeBrowsingState::STANDARD_PROTECTION; |
| } |
| |
| #pragma mark - Private |
| |
| // Creates header in Standard Protection view. |
| - (SafeBrowsingHeaderItem*)detailItemWithType:(NSInteger)type |
| detailText:(NSInteger)detailText |
| image:(UIImage*)image |
| accessibilityIdentifier: |
| (NSString*)accessibilityIdentifier { |
| SafeBrowsingHeaderItem* detailItem = |
| [[SafeBrowsingHeaderItem alloc] initWithType:type]; |
| detailItem.text = l10n_util::GetNSString(detailText); |
| detailItem.image = image; |
| detailItem.imageViewTintColor = [UIColor colorNamed:kGrey600Color]; |
| detailItem.accessibilityIdentifier = accessibilityIdentifier; |
| |
| return detailItem; |
| } |
| |
| // Creates a TableViewInfoButtonItem instance used for items that the user is |
| // not allowed to switch on or off (enterprise reason for example). |
| - (TableViewInfoButtonItem*)tableViewInfoButtonItemType:(NSInteger)itemType |
| textStringID:(int)textStringID |
| detailStringID:(int)detailStringID |
| status:(BOOL)status { |
| TableViewInfoButtonItem* managedItem = |
| [[TableViewInfoButtonItem alloc] initWithType:itemType]; |
| managedItem.text = l10n_util::GetNSString(textStringID); |
| managedItem.detailText = l10n_util::GetNSString(detailStringID); |
| managedItem.statusText = status ? l10n_util::GetNSString(IDS_IOS_SETTING_ON) |
| : l10n_util::GetNSString(IDS_IOS_SETTING_OFF); |
| if (!status) { |
| managedItem.iconTintColor = [UIColor colorNamed:kGrey300Color]; |
| |
| // This item is not controllable, then set the color opacity to 40%. |
| managedItem.textColor = |
| [[UIColor colorNamed:kTextPrimaryColor] colorWithAlphaComponent:0.4f]; |
| managedItem.detailTextColor = |
| [[UIColor colorNamed:kTextSecondaryColor] colorWithAlphaComponent:0.4f]; |
| |
| managedItem.accessibilityHint = l10n_util::GetNSString( |
| IDS_IOS_TOGGLE_SETTING_MANAGED_ACCESSIBILITY_HINT); |
| } |
| return managedItem; |
| } |
| |
| // Creates an item with a switch toggle. |
| - (SyncSwitchItem*)switchItemWithItemType:(NSInteger)itemType |
| textStringID:(int)textStringID |
| detailStringID:(int)detailStringID |
| defaultState:(BOOL)defaultState |
| enabled:(BOOL)enabled { |
| SyncSwitchItem* switchItem = [[SyncSwitchItem alloc] initWithType:itemType]; |
| switchItem.text = l10n_util::GetNSString(textStringID); |
| if (detailStringID) |
| switchItem.detailText = l10n_util::GetNSString(detailStringID); |
| switchItem.on = defaultState; |
| switchItem.enabled = enabled; |
| return switchItem; |
| } |
| |
| // Updates switches' on and enabled status. |
| - (void)updateSafeBrowsingStandardProtectionModel { |
| for (TableViewItem* item in self.safeBrowsingStandardProtectionItems) { |
| ItemType type = static_cast<ItemType>(item.type); |
| switch (type) { |
| case ItemTypePasswordLeakCheckSwitch: |
| [self configureLeakCheckItem:item]; |
| break; |
| case ItemTypeSafeBrowsingExtendedReporting: { |
| SyncSwitchItem* syncSwitchItem = |
| base::apple::ObjCCastStrict<SyncSwitchItem>(item); |
| syncSwitchItem.on = |
| safe_browsing::IsExtendedReportingEnabled(*self.userPrefService); |
| syncSwitchItem.enabled = self.inSafeBrowsingStandardProtection; |
| break; |
| } |
| case ItemTypeSafeBrowsingManagedExtendedReporting: |
| base::apple::ObjCCastStrict<TableViewInfoButtonItem>(item).statusText = |
| self.safeBrowsingExtendedReportingPreference.value |
| ? l10n_util::GetNSString(IDS_IOS_SETTING_ON) |
| : l10n_util::GetNSString(IDS_IOS_SETTING_OFF); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| [self.consumer reloadCellsForItems]; |
| } |
| |
| // Returns a boolean indicating if the switch should appear as "On" or "Off" |
| // based on the sync preference, the sign in status, and if the user has |
| // selected Standard Protection as the Safe Browsing option. |
| - (BOOL)passwordLeakCheckItemOnState { |
| return self.safeBrowsingEnhancedProtectionPreference.value || |
| (self.safeBrowsingStandardProtectionPreference.value && |
| self.passwordLeakCheckPreference.value); |
| } |
| |
| // Updates the detail text and on state of the leak check item based on the |
| // state. |
| - (void)configureLeakCheckItem:(TableViewItem*)item { |
| TableViewSwitchItem* leakCheckItem = |
| base::apple::ObjCCastStrict<TableViewSwitchItem>(item); |
| leakCheckItem.enabled = self.inSafeBrowsingStandardProtection; |
| leakCheckItem.on = [self passwordLeakCheckItemOnState]; |
| if (base::FeatureList::IsEnabled( |
| safe_browsing::kFriendlierSafeBrowsingSettingsStandardProtection)) { |
| leakCheckItem.detailText = l10n_util::GetNSString( |
| IDS_IOS_SAFE_BROWSING_STANDARD_PROTECTION_LEAK_CHECK_FRIENDLIER_SUMMARY); |
| } else { |
| leakCheckItem.detailText = l10n_util::GetNSString( |
| IDS_IOS_SAFE_BROWSING_STANDARD_PROTECTION_LEAK_CHECK_SUMMARY); |
| } |
| } |
| |
| #pragma mark - SafeBrowsingStandardProtectionViewControllerDelegate |
| |
| - (void)toggleSwitchItem:(TableViewItem*)item withValue:(BOOL)value { |
| SyncSwitchItem* syncSwitchItem = base::apple::ObjCCast<SyncSwitchItem>(item); |
| syncSwitchItem.on = value; |
| ItemType type = static_cast<ItemType>(item.type); |
| switch (type) { |
| case ItemTypeSafeBrowsingExtendedReporting: |
| self.safeBrowsingExtendedReportingPreference.value = value; |
| break; |
| case ItemTypePasswordLeakCheckSwitch: |
| self.passwordLeakCheckPreference.value = value; |
| break; |
| default: |
| // Not a switch. |
| NOTREACHED(); |
| break; |
| } |
| } |
| |
| #pragma mark - BooleanObserver |
| |
| - (void)booleanDidChange:(id<ObservableBoolean>)observableBoolean { |
| [self updateSafeBrowsingStandardProtectionModel]; |
| } |
| |
| #pragma mark - IdentityManagerObserverBridgeDelegate |
| |
| // Used to update model when signing in/out is completed. |
| - (void)onPrimaryAccountChanged: |
| (const signin::PrimaryAccountChangeEvent&)event { |
| [self updateSafeBrowsingStandardProtectionModel]; |
| } |
| |
| @end |