| // Copyright 2015 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/privacy_table_view_controller.h" |
| |
| #import <LocalAuthentication/LocalAuthentication.h> |
| |
| #import "base/apple/foundation_util.h" |
| #import "base/check.h" |
| #import "base/metrics/histogram_macros.h" |
| #import "base/metrics/user_metrics.h" |
| #import "base/metrics/user_metrics_action.h" |
| #import "components/content_settings/core/common/features.h" |
| #import "components/handoff/pref_names_ios.h" |
| #import "components/prefs/ios/pref_observer_bridge.h" |
| #import "components/prefs/pref_change_registrar.h" |
| #import "components/prefs/pref_service.h" |
| #import "components/safe_browsing/core/common/safe_browsing_prefs.h" |
| #import "components/strings/grit/components_strings.h" |
| #import "components/supervised_user/core/common/supervised_user_utils.h" |
| #import "components/sync/service/sync_service.h" |
| #import "ios/chrome/browser/browsing_data/browsing_data_features.h" |
| #import "ios/chrome/browser/net/crurl.h" |
| #import "ios/chrome/browser/policy/policy_util.h" |
| #import "ios/chrome/browser/shared/model/application_context/application_context.h" |
| #import "ios/chrome/browser/shared/model/browser/browser.h" |
| #import "ios/chrome/browser/shared/model/browser_state/chrome_browser_state.h" |
| #import "ios/chrome/browser/shared/model/prefs/pref_names.h" |
| #import "ios/chrome/browser/shared/public/commands/open_new_tab_command.h" |
| #import "ios/chrome/browser/shared/public/features/features.h" |
| #import "ios/chrome/browser/shared/ui/table_view/cells/table_view_detail_icon_item.h" |
| #import "ios/chrome/browser/shared/ui/table_view/cells/table_view_info_button_cell.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_link_header_footer_item.h" |
| #import "ios/chrome/browser/shared/ui/table_view/cells/table_view_switch_cell.h" |
| #import "ios/chrome/browser/shared/ui/table_view/cells/table_view_switch_item.h" |
| #import "ios/chrome/browser/shared/ui/table_view/cells/table_view_text_header_footer_item.h" |
| #import "ios/chrome/browser/shared/ui/table_view/table_view_utils.h" |
| #import "ios/chrome/browser/sync/sync_service_factory.h" |
| #import "ios/chrome/browser/ui/incognito_interstitial/incognito_interstitial_constants.h" |
| #import "ios/chrome/browser/ui/settings/elements/enterprise_info_popover_view_controller.h" |
| #import "ios/chrome/browser/ui/settings/elements/info_popover_view_controller.h" |
| #import "ios/chrome/browser/ui/settings/elements/supervised_user_info_popover_view_controller.h" |
| #import "ios/chrome/browser/ui/settings/privacy/privacy_constants.h" |
| #import "ios/chrome/browser/ui/settings/privacy/privacy_navigation_commands.h" |
| #import "ios/chrome/browser/ui/settings/settings_navigation_controller.h" |
| #import "ios/chrome/browser/ui/settings/settings_table_view_controller_constants.h" |
| #import "ios/chrome/browser/ui/settings/utils/pref_backed_boolean.h" |
| #import "ios/chrome/browser/web/features.h" |
| #import "ios/chrome/common/string_util.h" |
| #import "ios/chrome/common/ui/colors/semantic_color_names.h" |
| #import "ios/chrome/common/ui/reauthentication/reauthentication_protocol.h" |
| #import "ios/chrome/grit/ios_chromium_strings.h" |
| #import "ios/chrome/grit/ios_strings.h" |
| #import "ios/components/security_interstitials/https_only_mode/feature.h" |
| #import "ui/base/l10n/l10n_util.h" |
| #import "ui/base/l10n/l10n_util_mac.h" |
| #import "url/gurl.h" |
| |
| namespace { |
| |
| typedef NS_ENUM(NSInteger, SectionIdentifier) { |
| SectionIdentifierPrivacyContent = kSectionIdentifierEnumZero, |
| SectionIdentifierSafeBrowsing, |
| SectionIdentifierHTTPSOnlyMode, |
| SectionIdentifierWebServices, |
| SectionIdentifierIncognitoAuth, |
| SectionIdentifierIncognitoInterstitial, |
| SectionIdentifierLockdownMode, |
| }; |
| |
| typedef NS_ENUM(NSInteger, ItemType) { |
| ItemTypeClearBrowsingDataClear = kItemTypeEnumZero, |
| // Footer to suggest the user to open Sync and Google services settings. |
| ItemTypePrivacyFooter, |
| ItemTypeOtherDevicesHandoff, |
| ItemTypeIncognitoReauth, |
| ItemTypeIncognitoReauthDisabled, |
| ItemTypePrivacySafeBrowsing, |
| ItemTypeHTTPSOnlyMode, |
| ItemTypeIncognitoInterstitial, |
| ItemTypeIncognitoInterstitialDisabled, |
| ItemTypeLockdownMode, |
| }; |
| |
| // Used to open the Sync and Google Services settings. |
| // These links should not be dispatched. |
| const char kGoogleServicesSettingsURL[] = "settings://open_google_services"; |
| const char kSyncSettingsURL[] = "settings://open_sync"; |
| |
| } // namespace |
| |
| @interface PrivacyTableViewController () <BooleanObserver, |
| PrefObserverDelegate, |
| PopoverLabelViewControllerDelegate> { |
| ChromeBrowserState* _browserState; // weak |
| |
| // Pref observer to track changes to prefs. |
| std::unique_ptr<PrefObserverBridge> _prefObserverBridge; |
| // Registrar for pref changes notifications. |
| PrefChangeRegistrar _prefChangeRegistrar; |
| |
| // Updatable Items. |
| TableViewDetailIconItem* _handoffDetailItem; |
| // Safe Browsing item. |
| TableViewDetailIconItem* _safeBrowsingDetailItem; |
| // Locdown Mode item. |
| TableViewDetailIconItem* _lockdownModeDetailItem; |
| |
| // Whether Settings have been dismissed. |
| BOOL _settingsAreDismissed; |
| } |
| |
| // Accessor for the incognito reauth pref. |
| @property(nonatomic, strong) PrefBackedBoolean* incognitoReauthPref; |
| |
| // Switch item for toggling incognito reauth. |
| @property(nonatomic, strong) TableViewSwitchItem* incognitoReauthItem; |
| |
| // Authentication module used when the user toggles the biometric auth on. |
| @property(nonatomic, strong) id<ReauthenticationProtocol> reauthModule; |
| |
| // Accessor for the HTTPS-Only Mode pref. |
| @property(nonatomic, strong) PrefBackedBoolean* HTTPSOnlyModePref; |
| |
| // The item related to the switch for the "HTTPS Only Mode" setting. |
| @property(nonatomic, strong) TableViewSwitchItem* HTTPSOnlyModeItem; |
| |
| // Accessor for the Incognito interstitial pref. |
| @property(nonatomic, strong) PrefBackedBoolean* incognitoInterstitialPref; |
| |
| // The item related to the Incognito interstitial setting. |
| @property(nonatomic, strong) TableViewSwitchItem* incognitoInterstitialItem; |
| |
| @end |
| |
| @implementation PrivacyTableViewController |
| |
| #pragma mark - Initialization |
| |
| - (instancetype)initWithBrowser:(Browser*)browser |
| reauthenticationModule:(id<ReauthenticationProtocol>)reauthModule { |
| DCHECK(browser); |
| |
| self = [super initWithStyle:ChromeTableViewStyle()]; |
| if (self) { |
| _reauthModule = reauthModule; |
| _browserState = browser->GetBrowserState(); |
| self.title = l10n_util::GetNSString(IDS_IOS_SETTINGS_PRIVACY_TITLE); |
| |
| PrefService* prefService = _browserState->GetPrefs(); |
| |
| _prefChangeRegistrar.Init(prefService); |
| _prefObserverBridge.reset(new PrefObserverBridge(self)); |
| // Register to observe any changes on Perf backed values displayed by the |
| // screen. |
| _prefObserverBridge->ObserveChangesForPreference( |
| prefs::kIosHandoffToOtherDevices, &_prefChangeRegistrar); |
| _prefObserverBridge->ObserveChangesForPreference( |
| prefs::kSafeBrowsingEnabled, &_prefChangeRegistrar); |
| _prefObserverBridge->ObserveChangesForPreference( |
| prefs::kSafeBrowsingEnhanced, &_prefChangeRegistrar); |
| _prefObserverBridge->ObserveChangesForPreference( |
| prefs::kBrowserLockdownModeEnabled, &_prefChangeRegistrar); |
| |
| _incognitoReauthPref = [[PrefBackedBoolean alloc] |
| initWithPrefService:GetApplicationContext()->GetLocalState() |
| prefName:prefs::kIncognitoAuthenticationSetting]; |
| [_incognitoReauthPref setObserver:self]; |
| |
| _HTTPSOnlyModePref = [[PrefBackedBoolean alloc] |
| initWithPrefService:prefService |
| prefName:prefs::kHttpsOnlyModeEnabled]; |
| [_HTTPSOnlyModePref setObserver:self]; |
| |
| _incognitoInterstitialPref = [[PrefBackedBoolean alloc] |
| initWithPrefService:browser->GetBrowserState()->GetPrefs() |
| prefName:prefs::kIncognitoInterstitialEnabled]; |
| [_incognitoInterstitialPref setObserver:self]; |
| } |
| return self; |
| } |
| |
| #pragma mark - UIViewController |
| |
| - (void)viewDidLoad { |
| [super viewDidLoad]; |
| self.tableView.accessibilityIdentifier = kPrivacyTableViewId; |
| |
| [self loadModel]; |
| } |
| |
| - (void)didMoveToParentViewController:(UIViewController*)parent { |
| [super didMoveToParentViewController:parent]; |
| if (!parent) { |
| [self.presentationDelegate privacyTableViewControllerWasRemoved:self]; |
| } |
| } |
| |
| #pragma mark - ChromeTableViewController |
| |
| - (void)loadModel { |
| [super loadModel]; |
| if (_settingsAreDismissed) |
| return; |
| |
| TableViewModel* model = self.tableViewModel; |
| [model addSectionWithIdentifier:SectionIdentifierPrivacyContent]; |
| [model addSectionWithIdentifier:SectionIdentifierSafeBrowsing]; |
| |
| if (base::FeatureList::IsEnabled( |
| security_interstitials::features::kHttpsOnlyMode)) { |
| [model addSectionWithIdentifier:SectionIdentifierHTTPSOnlyMode]; |
| [model addItem:self.HTTPSOnlyModeItem |
| toSectionWithIdentifier:SectionIdentifierHTTPSOnlyMode]; |
| } |
| |
| [model addSectionWithIdentifier:SectionIdentifierWebServices]; |
| [model addSectionWithIdentifier:SectionIdentifierIncognitoAuth]; |
| [model addSectionWithIdentifier:SectionIdentifierIncognitoInterstitial]; |
| |
| if (web::IsBrowserLockdownModeEnabled()) { |
| [model addSectionWithIdentifier:SectionIdentifierLockdownMode]; |
| } |
| |
| // Clear Browsing item. |
| [model addItem:[self clearBrowsingDetailItem] |
| toSectionWithIdentifier:SectionIdentifierPrivacyContent]; |
| |
| // Privacy Safe Browsing item. |
| [model addItem:[self safeBrowsingDetailItem] |
| toSectionWithIdentifier:SectionIdentifierSafeBrowsing]; |
| |
| // Web Services item. |
| [model addItem:[self handoffDetailItem] |
| toSectionWithIdentifier:SectionIdentifierWebServices]; |
| |
| // Incognito reauth item is added. If Incognito mode is disabled, or device |
| // authentication is not supported, a disabled version is shown instead with |
| // relevant information as a popover. |
| TableViewItem* incognitoReauthItem = |
| (IsIncognitoModeDisabled(_browserState->GetPrefs()) || |
| ![self deviceSupportsAuthentication]) |
| ? self.incognitoReauthItemDisabled |
| : self.incognitoReauthItem; |
| [model addItem:incognitoReauthItem |
| toSectionWithIdentifier:SectionIdentifierIncognitoAuth]; |
| |
| // Show "Ask to Open Links from Other Apps in Incognito" setting. |
| // Incognito interstitial item is added. If Incognito mode is |
| // disabled or forced, a disabled version is shown with information |
| // to learn more. |
| TableViewItem* incognitoInterstitialItem = |
| (IsIncognitoModeDisabled(_browserState->GetPrefs()) || |
| IsIncognitoModeForced(_browserState->GetPrefs())) |
| ? self.incognitoInterstitialItemDisabled |
| : self.incognitoInterstitialItem; |
| [model addItem:incognitoInterstitialItem |
| toSectionWithIdentifier:SectionIdentifierIncognitoInterstitial]; |
| |
| // Lockdown Mode item. |
| if (web::IsBrowserLockdownModeEnabled()) { |
| [model addItem:[self lockdownModeDetailItem] |
| toSectionWithIdentifier:SectionIdentifierLockdownMode]; |
| [model setFooter:[self showPrivacyFooterItem] |
| forSectionWithIdentifier:SectionIdentifierLockdownMode]; |
| } else { |
| [model setFooter:[self showPrivacyFooterItem] |
| forSectionWithIdentifier:SectionIdentifierIncognitoInterstitial]; |
| } |
| } |
| |
| #pragma mark - Model Objects |
| |
| - (TableViewSwitchItem*)HTTPSOnlyModeItem { |
| if (!_HTTPSOnlyModeItem) { |
| _HTTPSOnlyModeItem = |
| [[TableViewSwitchItem alloc] initWithType:ItemTypeHTTPSOnlyMode]; |
| |
| _HTTPSOnlyModeItem.text = |
| l10n_util::GetNSString(IDS_IOS_SETTINGS_HTTPS_ONLY_MODE_TITLE); |
| _HTTPSOnlyModeItem.detailText = |
| l10n_util::GetNSString(IDS_IOS_SETTINGS_HTTPS_ONLY_MODE_DESCRIPTION); |
| _HTTPSOnlyModeItem.on = [self.HTTPSOnlyModePref value]; |
| _HTTPSOnlyModeItem.accessibilityIdentifier = kSettingsHttpsOnlyModeCellId; |
| } |
| return _HTTPSOnlyModeItem; |
| } |
| |
| - (TableViewSwitchItem*)incognitoInterstitialItem { |
| if (!_incognitoInterstitialItem) { |
| _incognitoInterstitialItem = [[TableViewSwitchItem alloc] |
| initWithType:ItemTypeIncognitoInterstitial]; |
| _incognitoInterstitialItem.text = |
| l10n_util::GetNSString(IDS_IOS_OPTIONS_ENABLE_INCOGNITO_INTERSTITIAL); |
| _incognitoInterstitialItem.on = self.incognitoInterstitialPref.value; |
| _incognitoInterstitialItem.enabled = YES; |
| _incognitoInterstitialItem.accessibilityIdentifier = |
| kSettingsIncognitoInterstitialId; |
| } |
| return _incognitoInterstitialItem; |
| } |
| |
| - (TableViewInfoButtonItem*)incognitoInterstitialItemDisabled { |
| TableViewInfoButtonItem* itemDisabled = [[TableViewInfoButtonItem alloc] |
| initWithType:ItemTypeIncognitoInterstitialDisabled]; |
| itemDisabled.text = |
| l10n_util::GetNSString(IDS_IOS_OPTIONS_ENABLE_INCOGNITO_INTERSTITIAL); |
| itemDisabled.statusText = l10n_util::GetNSString(IDS_IOS_SETTING_OFF); |
| itemDisabled.accessibilityIdentifier = |
| kSettingsIncognitoInterstitialDisabledId; |
| itemDisabled.iconTintColor = [UIColor colorNamed:kGrey300Color]; |
| itemDisabled.textColor = [UIColor colorNamed:kTextSecondaryColor]; |
| return itemDisabled; |
| } |
| |
| - (TableViewItem*)handoffDetailItem { |
| NSString* detailText = |
| _browserState->GetPrefs()->GetBoolean(prefs::kIosHandoffToOtherDevices) |
| ? l10n_util::GetNSString(IDS_IOS_SETTING_ON) |
| : l10n_util::GetNSString(IDS_IOS_SETTING_OFF); |
| _handoffDetailItem = [self |
| detailItemWithType:ItemTypeOtherDevicesHandoff |
| titleId:IDS_IOS_OPTIONS_ENABLE_HANDOFF_TO_OTHER_DEVICES |
| detailText:detailText |
| accessibilityIdentifier:kSettingsHandoffCellId]; |
| |
| return _handoffDetailItem; |
| } |
| |
| // Creates TableViewHeaderFooterItem instance to show a link to open the Sync |
| // and Google services settings. |
| - (TableViewHeaderFooterItem*)showPrivacyFooterItem { |
| TableViewLinkHeaderFooterItem* showPrivacyFooterItem = |
| [[TableViewLinkHeaderFooterItem alloc] |
| initWithType:ItemTypePrivacyFooter]; |
| |
| NSString* privacyFooterText; |
| |
| syncer::SyncService* syncService = |
| SyncServiceFactory::GetInstance()->GetForBrowserState(_browserState); |
| |
| NSMutableArray* urls = [[NSMutableArray alloc] init]; |
| // TODO(crbug.com/1462552): Remove IsSyncFeatureEnabled() usage after kSync |
| // users are migrated to kSignin in phase 3. See ConsentLevel::kSync for more |
| // details. |
| if (syncService->IsSyncFeatureEnabled()) { |
| privacyFooterText = |
| l10n_util::GetNSString(IDS_IOS_PRIVACY_SYNC_AND_GOOGLE_SERVICES_FOOTER); |
| [urls addObject:[[CrURL alloc] initWithGURL:GURL(kSyncSettingsURL)]]; |
| } else { |
| privacyFooterText = |
| l10n_util::GetNSString(IDS_IOS_PRIVACY_GOOGLE_SERVICES_FOOTER); |
| } |
| [urls |
| addObject:[[CrURL alloc] initWithGURL:GURL(kGoogleServicesSettingsURL)]]; |
| |
| showPrivacyFooterItem.text = privacyFooterText; |
| showPrivacyFooterItem.urls = urls; |
| return showPrivacyFooterItem; |
| } |
| |
| - (TableViewItem*)clearBrowsingDetailItem { |
| return [self detailItemWithType:ItemTypeClearBrowsingDataClear |
| titleId:IDS_IOS_CLEAR_BROWSING_DATA_TITLE |
| detailText:nil |
| accessibilityIdentifier:kSettingsClearBrowsingDataCellId]; |
| } |
| |
| - (TableViewItem*)safeBrowsingDetailItem { |
| NSString* detailText = [self safeBrowsingDetailText]; |
| _safeBrowsingDetailItem = |
| [self detailItemWithType:ItemTypePrivacySafeBrowsing |
| titleId:IDS_IOS_PRIVACY_SAFE_BROWSING_TITLE |
| detailText:detailText |
| accessibilityIdentifier:kSettingsPrivacySafeBrowsingCellId]; |
| return _safeBrowsingDetailItem; |
| } |
| |
| - (TableViewItem*)lockdownModeDetailItem { |
| NSString* detailText = |
| _browserState->GetPrefs()->GetBoolean(prefs::kBrowserLockdownModeEnabled) |
| ? l10n_util::GetNSString(IDS_IOS_SETTING_ON) |
| : l10n_util::GetNSString(IDS_IOS_SETTING_OFF); |
| _lockdownModeDetailItem = |
| [self detailItemWithType:ItemTypeLockdownMode |
| titleId:IDS_IOS_LOCKDOWN_MODE_TITLE |
| detailText:detailText |
| accessibilityIdentifier:kPrivacyLockdownModeCellId]; |
| return _lockdownModeDetailItem; |
| } |
| |
| - (TableViewSwitchItem*)incognitoReauthItem { |
| if (_incognitoReauthItem) { |
| return _incognitoReauthItem; |
| } |
| _incognitoReauthItem = |
| [[TableViewSwitchItem alloc] initWithType:ItemTypeIncognitoReauth]; |
| _incognitoReauthItem.text = |
| l10n_util::GetNSString(IDS_IOS_INCOGNITO_REAUTH_SETTING_NAME); |
| _incognitoReauthItem.on = self.incognitoReauthPref.value; |
| _incognitoReauthItem.enabled = YES; |
| return _incognitoReauthItem; |
| } |
| |
| - (TableViewInfoButtonItem*)incognitoReauthItemDisabled { |
| TableViewInfoButtonItem* itemDisabled = [[TableViewInfoButtonItem alloc] |
| initWithType:ItemTypeIncognitoReauthDisabled]; |
| itemDisabled.text = |
| l10n_util::GetNSString(IDS_IOS_INCOGNITO_REAUTH_SETTING_NAME); |
| itemDisabled.statusText = l10n_util::GetNSString(IDS_IOS_SETTING_OFF); |
| itemDisabled.iconTintColor = [UIColor colorNamed:kGrey300Color]; |
| itemDisabled.textColor = [UIColor colorNamed:kTextSecondaryColor]; |
| return itemDisabled; |
| } |
| |
| - (TableViewDetailIconItem*)detailItemWithType:(NSInteger)type |
| titleId:(NSInteger)titleId |
| detailText:(NSString*)detailText |
| accessibilityIdentifier: |
| (NSString*)accessibilityIdentifier { |
| TableViewDetailIconItem* detailItem = |
| [[TableViewDetailIconItem alloc] initWithType:type]; |
| detailItem.text = l10n_util::GetNSString(titleId); |
| detailItem.detailText = detailText; |
| detailItem.accessoryType = UITableViewCellAccessoryDisclosureIndicator; |
| detailItem.accessibilityTraits |= UIAccessibilityTraitButton; |
| detailItem.accessibilityIdentifier = accessibilityIdentifier; |
| |
| return detailItem; |
| } |
| |
| #pragma mark - SettingsControllerProtocol |
| |
| - (void)reportDismissalUserAction { |
| base::RecordAction(base::UserMetricsAction("MobilePrivacySettingsClose")); |
| } |
| |
| - (void)reportBackUserAction { |
| base::RecordAction(base::UserMetricsAction("MobilePrivacySettingsBack")); |
| } |
| |
| - (void)settingsWillBeDismissed { |
| DCHECK(!_settingsAreDismissed); |
| |
| // Stop observable prefs. |
| [_incognitoReauthPref stop]; |
| _incognitoReauthPref.observer = nil; |
| _incognitoReauthPref = nil; |
| [_HTTPSOnlyModePref stop]; |
| _HTTPSOnlyModePref.observer = nil; |
| _HTTPSOnlyModePref = nil; |
| [_incognitoInterstitialPref stop]; |
| _incognitoInterstitialPref.observer = nil; |
| _incognitoInterstitialPref = nil; |
| |
| // Remove pref changes registrations. |
| _prefChangeRegistrar.RemoveAll(); |
| |
| // Remove observer bridges. |
| _prefObserverBridge.reset(); |
| |
| // Clear C++ ivars. |
| _browserState = nullptr; |
| |
| _settingsAreDismissed = YES; |
| } |
| |
| #pragma mark - UITableViewDelegate |
| |
| - (UIView*)tableView:(UITableView*)tableView |
| viewForFooterInSection:(NSInteger)section { |
| UIView* footerView = [super tableView:tableView |
| viewForFooterInSection:section]; |
| TableViewLinkHeaderFooterView* footer = |
| base::apple::ObjCCast<TableViewLinkHeaderFooterView>(footerView); |
| if (footer) { |
| footer.delegate = self; |
| } |
| return footerView; |
| } |
| |
| - (void)tableView:(UITableView*)tableView |
| didSelectRowAtIndexPath:(NSIndexPath*)indexPath { |
| [super tableView:tableView didSelectRowAtIndexPath:indexPath]; |
| NSInteger itemType = [self.tableViewModel itemTypeForIndexPath:indexPath]; |
| switch (itemType) { |
| case ItemTypeOtherDevicesHandoff: |
| [self.handler showHandoff]; |
| break; |
| case ItemTypeClearBrowsingDataClear: |
| [self.handler showClearBrowsingData]; |
| break; |
| case ItemTypePrivacySafeBrowsing: |
| base::RecordAction(base::UserMetricsAction( |
| "SafeBrowsing.Settings.ShowedFromParentSettings")); |
| [self.handler showSafeBrowsing]; |
| break; |
| case ItemTypeLockdownMode: |
| [self.handler showLockdownMode]; |
| break; |
| default: |
| break; |
| } |
| [tableView deselectRowAtIndexPath:indexPath animated:YES]; |
| } |
| |
| #pragma mark - UITableViewDataSource |
| |
| - (UITableViewCell*)tableView:(UITableView*)tableView |
| cellForRowAtIndexPath:(NSIndexPath*)indexPath { |
| UITableViewCell* cell = [super tableView:tableView |
| cellForRowAtIndexPath:indexPath]; |
| |
| ItemType itemType = static_cast<ItemType>( |
| [self.tableViewModel itemTypeForIndexPath:indexPath]); |
| |
| if (itemType == ItemTypeIncognitoReauth) { |
| TableViewSwitchCell* switchCell = |
| base::apple::ObjCCastStrict<TableViewSwitchCell>(cell); |
| [switchCell.switchView addTarget:self |
| action:@selector(switchTapped:) |
| forControlEvents:UIControlEventTouchUpInside]; |
| } else if (itemType == ItemTypeIncognitoReauthDisabled) { |
| TableViewInfoButtonCell* managedCell = |
| base::apple::ObjCCastStrict<TableViewInfoButtonCell>(cell); |
| [managedCell.trailingButton |
| addTarget:self |
| action:@selector(didTapIncognitoReauthDisabledInfoButton:) |
| forControlEvents:UIControlEventTouchUpInside]; |
| } else if (itemType == ItemTypeHTTPSOnlyMode) { |
| TableViewSwitchCell* switchCell = |
| base::apple::ObjCCastStrict<TableViewSwitchCell>(cell); |
| [switchCell.switchView addTarget:self |
| action:@selector(HTTPSOnlyModeTapped:) |
| forControlEvents:UIControlEventTouchUpInside]; |
| } else if (itemType == ItemTypeIncognitoInterstitial) { |
| TableViewSwitchCell* switchCell = |
| base::apple::ObjCCastStrict<TableViewSwitchCell>(cell); |
| [switchCell.switchView |
| addTarget:self |
| action:@selector(incognitoInterstitialSwitchTapped:) |
| forControlEvents:UIControlEventTouchUpInside]; |
| } else if (itemType == ItemTypeIncognitoInterstitialDisabled) { |
| TableViewInfoButtonCell* managedCell = |
| base::apple::ObjCCastStrict<TableViewInfoButtonCell>(cell); |
| [managedCell.trailingButton |
| addTarget:self |
| action:@selector |
| (didTapIncognitoInterstitialDisabledInfoButton:) |
| forControlEvents:UIControlEventTouchUpInside]; |
| } |
| return cell; |
| } |
| |
| #pragma mark - PrefObserverDelegate |
| |
| - (void)onPreferenceChanged:(const std::string&)preferenceName { |
| if (_settingsAreDismissed) |
| return; |
| |
| if (preferenceName == prefs::kIosHandoffToOtherDevices) { |
| NSString* detailText = _browserState->GetPrefs()->GetBoolean(preferenceName) |
| ? l10n_util::GetNSString(IDS_IOS_SETTING_ON) |
| : l10n_util::GetNSString(IDS_IOS_SETTING_OFF); |
| _handoffDetailItem.detailText = detailText; |
| [self reconfigureCellsForItems:@[ _handoffDetailItem ]]; |
| return; |
| } |
| |
| DCHECK(_safeBrowsingDetailItem); |
| if (preferenceName == prefs::kSafeBrowsingEnabled || |
| preferenceName == prefs::kSafeBrowsingEnhanced) { |
| _safeBrowsingDetailItem.detailText = [self safeBrowsingDetailText]; |
| [self reconfigureCellsForItems:@[ _safeBrowsingDetailItem ]]; |
| } |
| |
| if (preferenceName == prefs::kBrowserLockdownModeEnabled) { |
| NSString* detailText = _browserState->GetPrefs()->GetBoolean(preferenceName) |
| ? l10n_util::GetNSString(IDS_IOS_SETTING_ON) |
| : l10n_util::GetNSString(IDS_IOS_SETTING_OFF); |
| _lockdownModeDetailItem.detailText = detailText; |
| [self reconfigureCellsForItems:@[ _lockdownModeDetailItem ]]; |
| return; |
| } |
| } |
| |
| #pragma mark - BooleanObserver |
| |
| - (void)booleanDidChange:(id<ObservableBoolean>)observableBoolean { |
| // Update the cells. |
| self.incognitoReauthItem.on = self.incognitoReauthPref.value; |
| [self reconfigureCellsForItems:@[ self.incognitoReauthItem ]]; |
| |
| self.HTTPSOnlyModeItem.on = self.HTTPSOnlyModePref.value; |
| [self reconfigureCellsForItems:@[ self.HTTPSOnlyModeItem ]]; |
| |
| self.incognitoInterstitialItem.on = self.incognitoInterstitialPref.value; |
| [self reconfigureCellsForItems:@[ self.incognitoInterstitialItem ]]; |
| } |
| |
| #pragma mark - TableViewLinkHeaderFooterItemDelegate |
| |
| - (void)view:(TableViewLinkHeaderFooterView*)view didTapLinkURL:(CrURL*)URL { |
| if (URL.gurl == GURL(kGoogleServicesSettingsURL)) { |
| // kGoogleServicesSettingsURL is not a realy link. It should be handled |
| // with a special case. |
| [self.dispatcher showGoogleServicesSettingsFromViewController:self]; |
| } else if (URL.gurl == GURL(kSyncSettingsURL)) { |
| [self.dispatcher showSyncSettingsFromViewController:self]; |
| } else { |
| [super view:view didTapLinkURL:URL]; |
| } |
| } |
| |
| #pragma mark - PopoverLabelViewControllerDelegate |
| |
| - (void)didTapLinkURL:(NSURL*)URL { |
| [super view:nil didTapLinkURL:[[CrURL alloc] initWithNSURL:URL]]; |
| } |
| |
| #pragma mark - Private |
| |
| // Called when the user taps on the information button of the disabled Incognito |
| // reauth setting's UI cell. |
| - (void)didTapIncognitoReauthDisabledInfoButton:(UIButton*)buttonView { |
| InfoPopoverViewController* popover; |
| if (supervised_user::IsSubjectToParentalControls(_browserState->GetPrefs())) { |
| popover = [[SupervisedUserInfoPopoverViewController alloc] |
| initWithMessage: |
| l10n_util::GetNSString( |
| IDS_IOS_SNACKBAR_MESSAGE_INCOGNITO_DISABLED_BY_PARENT)]; |
| } else if (IsIncognitoModeDisabled(_browserState->GetPrefs())) { |
| popover = [[EnterpriseInfoPopoverViewController alloc] |
| initWithMessage:l10n_util::GetNSString( |
| IDS_IOS_SNACKBAR_MESSAGE_INCOGNITO_DISABLED) |
| enterpriseName:nil]; |
| } else { |
| popover = [[InfoPopoverViewController alloc] |
| initWithMessage:l10n_util::GetNSString( |
| IDS_IOS_INCOGNITO_REAUTH_SET_UP_PASSCODE_HINT)]; |
| } |
| |
| [self showInfoPopover:popover forInfoButton:buttonView]; |
| } |
| |
| // Called when the user taps on the information button of the disabled Incognito |
| // interstitial setting's UI cell. |
| - (void)didTapIncognitoInterstitialDisabledInfoButton:(UIButton*)buttonView { |
| InfoPopoverViewController* popover; |
| if (supervised_user::IsSubjectToParentalControls(_browserState->GetPrefs())) { |
| popover = [[SupervisedUserInfoPopoverViewController alloc] |
| initWithMessage: |
| l10n_util::GetNSString( |
| IDS_IOS_SNACKBAR_MESSAGE_INCOGNITO_DISABLED_BY_PARENT)]; |
| } else { |
| NSString* popoverMessage = |
| IsIncognitoModeDisabled(_browserState->GetPrefs()) |
| ? l10n_util::GetNSString( |
| IDS_IOS_SNACKBAR_MESSAGE_INCOGNITO_DISABLED) |
| : l10n_util::GetNSString(IDS_IOS_SNACKBAR_MESSAGE_INCOGNITO_FORCED); |
| |
| popover = [[EnterpriseInfoPopoverViewController alloc] |
| initWithMessage:popoverMessage |
| enterpriseName:nil]; |
| } |
| |
| [self showInfoPopover:popover forInfoButton:buttonView]; |
| } |
| |
| // Shows a contextual bubble explaining that the tapped setting is managed and |
| // includes a link to the chrome://management page. |
| - (void)showInfoPopover:(PopoverLabelViewController*)popover |
| forInfoButton:(UIButton*)buttonView { |
| popover.delegate = self; |
| |
| // Disable the button when showing the bubble. |
| // The button will be enabled when closing the bubble in |
| // (void)popoverPresentationControllerDidDismissPopover: of |
| // EnterpriseInfoPopoverViewController. |
| buttonView.enabled = NO; |
| |
| // Set the anchor and arrow direction of the bubble. |
| popover.popoverPresentationController.sourceView = buttonView; |
| popover.popoverPresentationController.sourceRect = buttonView.bounds; |
| popover.popoverPresentationController.permittedArrowDirections = |
| UIPopoverArrowDirectionAny; |
| |
| [self presentViewController:popover animated:YES completion:nil]; |
| } |
| |
| // Called from the HTTPS-Only Mode setting's UIControlEventTouchUpInside. |
| // When this is called, `switchView` already has the updated value: |
| // If the switch was off, and user taps it, when this method is called, |
| // switchView.on is YES. |
| - (void)HTTPSOnlyModeTapped:(UISwitch*)switchView { |
| BOOL isOn = switchView.isOn; |
| [_HTTPSOnlyModePref setValue:isOn]; |
| } |
| |
| // Called from the Incognito interstitial setting's UIControlEventTouchUpInside. |
| // When this is called, `switchView` already has the updated value: |
| // If the switch was off, and user taps it, when this method is called, |
| // switchView.on is YES. |
| - (void)incognitoInterstitialSwitchTapped:(UISwitch*)switchView { |
| self.incognitoInterstitialPref.value = switchView.on; |
| UMA_HISTOGRAM_ENUMERATION( |
| kIncognitoInterstitialSettingsActionsHistogram, |
| switchView.on ? IncognitoInterstitialSettingsActions::kEnabled |
| : IncognitoInterstitialSettingsActions::kDisabled); |
| } |
| |
| // Called from the reauthentication setting's UIControlEventTouchUpInside. |
| // When this is called, `switchView` already has the updated value: |
| // If the switch was off, and user taps it, when this method is called, |
| // switchView.on is YES. |
| - (void)switchTapped:(UISwitch*)switchView { |
| if (switchView.isOn && ![self.reauthModule canAttemptReauth]) { |
| // This should normally not happen: the switch should not even be enabled. |
| // Fallback behaviour added just in case. |
| switchView.on = false; |
| return; |
| } |
| |
| __weak PrivacyTableViewController* weakSelf = self; |
| [self.reauthModule |
| attemptReauthWithLocalizedReason: |
| l10n_util::GetNSString( |
| IDS_IOS_INCOGNITO_REAUTH_SET_UP_SYSTEM_DIALOG_REASON) |
| canReusePreviousAuth:false |
| handler:^(ReauthenticationResult result) { |
| BOOL enabled = switchView.on; |
| if (result != |
| ReauthenticationResult::kSuccess) { |
| // Revert the switch if authentication wasn't |
| // successful. |
| enabled = !enabled; |
| } |
| [switchView setOn:enabled animated:YES]; |
| weakSelf.incognitoReauthPref.value = enabled; |
| }]; |
| } |
| |
| // Checks if the device has Passcode, Face ID, or Touch ID set up. |
| - (BOOL)deviceSupportsAuthentication { |
| LAContext* context = [[LAContext alloc] init]; |
| return [context canEvaluatePolicy:LAPolicyDeviceOwnerAuthentication |
| error:nil]; |
| } |
| |
| // Returns the proper detail text for the safe browsing item depending on the |
| // safe browsing and enhanced protection preference values. |
| - (NSString*)safeBrowsingDetailText { |
| PrefService* prefService = _browserState->GetPrefs(); |
| if (safe_browsing::IsEnhancedProtectionEnabled(*prefService)) { |
| return l10n_util::GetNSString( |
| IDS_IOS_SAFE_BROWSING_ENHANCED_PROTECTION_TITLE); |
| } else if (safe_browsing::IsSafeBrowsingEnabled(*prefService)) { |
| return l10n_util::GetNSString( |
| IDS_IOS_PRIVACY_SAFE_BROWSING_STANDARD_PROTECTION_TITLE); |
| } |
| return l10n_util::GetNSString( |
| IDS_IOS_PRIVACY_SAFE_BROWSING_NO_PROTECTION_DETAIL_TITLE); |
| } |
| |
| @end |