[go: nahoru, domu]

blob: c6c9bae3d1f521096413de6d6f5a965253dc9bbc [file] [log] [blame]
// Copyright 2020 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/page_info/page_info_view_controller.h"
#import "base/apple/foundation_util.h"
#import "base/metrics/user_metrics.h"
#import "base/metrics/user_metrics_action.h"
#import "base/notreached.h"
#import "base/strings/sys_string_conversions.h"
#import "ios/chrome/browser/net/crurl.h"
#import "ios/chrome/browser/shared/model/url/chrome_url_constants.h"
#import "ios/chrome/browser/shared/public/commands/page_info_commands.h"
#import "ios/chrome/browser/shared/ui/symbols/symbols.h"
#import "ios/chrome/browser/shared/ui/table_view/cells/table_view_attributed_string_header_footer_item.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_link_header_footer_item.h"
#import "ios/chrome/browser/shared/ui/table_view/cells/table_view_multi_detail_text_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/cells/table_view_text_item.h"
#import "ios/chrome/browser/shared/ui/table_view/cells/table_view_text_link_item.h"
#import "ios/chrome/browser/shared/ui/table_view/table_view_utils.h"
#import "ios/chrome/browser/ui/keyboard/UIKeyCommand+Chrome.h"
#import "ios/chrome/browser/ui/page_info/page_info_constants.h"
#import "ios/chrome/browser/ui/permissions/permission_info.h"
#import "ios/chrome/browser/ui/permissions/permissions_constants.h"
#import "ios/chrome/browser/ui/permissions/permissions_delegate.h"
#import "ios/chrome/common/string_util.h"
#import "ios/chrome/common/ui/colors/semantic_color_names.h"
#import "ios/chrome/common/ui/table_view/table_view_cells_constants.h"
#import "ios/chrome/grit/ios_strings.h"
#import "ios/web/public/permissions/permissions.h"
#import "ui/base/l10n/l10n_util.h"
#import "url/gurl.h"
namespace {
typedef NS_ENUM(NSInteger, SectionIdentifier) {
SectionIdentifierSecurityContent = kSectionIdentifierEnumZero,
SectionIdentifierPermissions,
};
typedef NS_ENUM(NSInteger, ItemType) {
ItemTypeSecurityHeader = kItemTypeEnumZero,
ItemTypeSecurityDescription,
ItemTypePermissionsHeader,
ItemTypePermissionsCamera,
ItemTypePermissionsMicrophone,
ItemTypePermissionsDescription,
};
// The vertical padding between the navigation bar and the Security header.
float kPaddingSecurityHeader = 28.0f;
// The minimum scale factor of the title label showing the URL.
float kTitleLabelMinimumScaleFactor = 0.7f;
} // namespace
@interface PageInfoViewController () <TableViewLinkHeaderFooterItemDelegate>
// The page info security description.
@property(nonatomic, strong)
PageInfoSiteSecurityDescription* pageInfoSecurityDescription;
// The list of permissions info used to create switches.
@property(nonatomic, copy) NSArray<PermissionInfo*>* permissionsInfo;
@end
@implementation PageInfoViewController
#pragma mark - UIViewController
- (instancetype)initWithSiteSecurityDescription:
(PageInfoSiteSecurityDescription*)siteSecurityDescription {
UITableViewStyle style = ChromeTableViewStyle();
self = [super initWithStyle:style];
if (self) {
_pageInfoSecurityDescription = siteSecurityDescription;
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.titleView =
[self titleViewLabelForURL:self.pageInfoSecurityDescription.siteURL];
self.title = l10n_util::GetNSString(IDS_IOS_PAGE_INFO_SITE_INFORMATION);
self.tableView.accessibilityIdentifier = kPageInfoViewAccessibilityIdentifier;
self.navigationController.navigationBar.accessibilityIdentifier =
kPageInfoViewNavigationBarAccessibilityIdentifier;
UIBarButtonItem* dismissButton = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemDone
target:self.pageInfoCommandsHandler
action:@selector(hidePageInfo)];
self.navigationItem.rightBarButtonItem = dismissButton;
self.tableView.separatorInset =
UIEdgeInsetsMake(0, kTableViewSeparatorInset, 0, 0);
self.tableView.allowsSelection = NO;
if (self.pageInfoSecurityDescription.isEmpty) {
[self addEmptyTableViewWithMessage:self.pageInfoSecurityDescription.message
image:nil];
self.tableView.alwaysBounceVertical = NO;
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
return;
}
[self loadModel];
}
#pragma mark - ChromeTableViewController
- (void)loadModel {
[super loadModel];
[self.tableViewModel
addSectionWithIdentifier:SectionIdentifierSecurityContent];
// Site Security section.
TableViewDetailIconItem* securityHeader =
[[TableViewDetailIconItem alloc] initWithType:ItemTypeSecurityHeader];
securityHeader.text = l10n_util::GetNSString(IDS_IOS_PAGE_INFO_SITE_SECURITY);
securityHeader.detailText = self.pageInfoSecurityDescription.status;
securityHeader.iconImage = self.pageInfoSecurityDescription.iconImage;
securityHeader.iconTintColor = UIColor.whiteColor;
securityHeader.iconBackgroundColor =
self.pageInfoSecurityDescription.iconBackgroundColor;
securityHeader.iconCornerRadius = kColorfulBackgroundSymbolCornerRadius;
[self.tableViewModel addItem:securityHeader
toSectionWithIdentifier:SectionIdentifierSecurityContent];
TableViewLinkHeaderFooterItem* securityDescription =
[[TableViewLinkHeaderFooterItem alloc]
initWithType:ItemTypeSecurityDescription];
securityDescription.text = self.pageInfoSecurityDescription.message;
securityDescription.urls =
@[ [[CrURL alloc] initWithGURL:GURL(kPageInfoHelpCenterURL)] ];
[self.tableViewModel setFooter:securityDescription
forSectionWithIdentifier:SectionIdentifierSecurityContent];
// Permissions section.
if (@available(iOS 15.0, *)) {
if ([self.permissionsInfo count]) {
[self loadPermissionsModel];
}
}
}
// Loads the "Permissions" section in this table view.
- (void)loadPermissionsModel API_AVAILABLE(ios(15.0)) {
[self.tableViewModel addSectionWithIdentifier:SectionIdentifierPermissions];
TableViewTextHeaderFooterItem* permissionsHeaderItem =
[[TableViewTextHeaderFooterItem alloc]
initWithType:ItemTypePermissionsHeader];
permissionsHeaderItem.text =
l10n_util::GetNSString(IDS_IOS_PAGE_INFO_PERMISSIONS_HEADER);
[self.tableViewModel setHeader:permissionsHeaderItem
forSectionWithIdentifier:SectionIdentifierPermissions];
for (id permission in self.permissionsInfo) {
[self updateSwitchForPermission:permission tableViewLoaded:NO];
}
TableViewAttributedStringHeaderFooterItem* permissionsDescription =
[[TableViewAttributedStringHeaderFooterItem alloc]
initWithType:ItemTypePermissionsDescription];
NSString* description = l10n_util::GetNSStringF(
IDS_IOS_PERMISSIONS_INFOBAR_MODAL_DESCRIPTION,
base::SysNSStringToUTF16(self.pageInfoSecurityDescription.siteURL));
NSMutableAttributedString* descriptionAttributedString =
[[NSMutableAttributedString alloc]
initWithAttributedString:PutBoldPartInString(
description, UIFontTextStyleFootnote)];
[descriptionAttributedString
addAttributes:@{
NSForegroundColorAttributeName :
[UIColor colorNamed:kTextSecondaryColor]
}
range:NSMakeRange(0, descriptionAttributedString.length)];
permissionsDescription.attributedString = descriptionAttributedString;
[self.tableViewModel setFooter:permissionsDescription
forSectionWithIdentifier:SectionIdentifierPermissions];
}
#pragma mark - UITableViewDelegate
- (CGFloat)tableView:(UITableView*)tableView
heightForHeaderInSection:(NSInteger)section {
return section == SectionIdentifierSecurityContent
? kPaddingSecurityHeader
: UITableViewAutomaticDimension;
}
- (UIView*)tableView:(UITableView*)tableView
viewForFooterInSection:(NSInteger)section {
UIView* view = [super tableView:tableView viewForFooterInSection:section];
NSInteger sectionIdentifier =
[self.tableViewModel sectionIdentifierForSectionIndex:section];
switch (sectionIdentifier) {
case SectionIdentifierSecurityContent: {
TableViewLinkHeaderFooterView* linkView =
base::apple::ObjCCastStrict<TableViewLinkHeaderFooterView>(view);
linkView.delegate = self;
} break;
}
return view;
}
#pragma mark - UITableViewDataSource
- (UITableViewCell*)tableView:(UITableView*)tableView
cellForRowAtIndexPath:(NSIndexPath*)indexPath {
UITableViewCell* cell = [super tableView:tableView
cellForRowAtIndexPath:indexPath];
ItemType itemType =
(ItemType)[self.tableViewModel itemTypeForIndexPath:indexPath];
switch (itemType) {
case ItemTypePermissionsCamera:
case ItemTypePermissionsMicrophone: {
TableViewSwitchCell* switchCell =
base::apple::ObjCCastStrict<TableViewSwitchCell>(cell);
switchCell.switchView.tag = itemType;
[switchCell.switchView addTarget:self
action:@selector(permissionSwitchToggled:)
forControlEvents:UIControlEventValueChanged];
break;
}
case ItemTypeSecurityHeader:
case ItemTypeSecurityDescription:
case ItemTypePermissionsHeader:
case ItemTypePermissionsDescription: {
// Not handled.
break;
}
}
return cell;
}
#pragma mark - TableViewLinkHeaderFooterItemDelegate
- (void)view:(TableViewLinkHeaderFooterView*)view didTapLinkURL:(CrURL*)URL {
DCHECK(URL.gurl == GURL(kPageInfoHelpCenterURL));
[self.pageInfoCommandsHandler showSecurityHelpPage];
}
#pragma mark - UIAdaptivePresentationControllerDelegate
- (void)presentationControllerDidDismiss:
(UIPresentationController*)presentationController {
[self.pageInfoCommandsHandler hidePageInfo];
}
#pragma mark - UIResponder
// To always be able to register key commands via -keyCommands, the VC must be
// able to become first responder.
- (BOOL)canBecomeFirstResponder {
return YES;
}
- (NSArray*)keyCommands {
return @[ UIKeyCommand.cr_close ];
}
- (void)keyCommand_close {
base::RecordAction(base::UserMetricsAction("MobileKeyCommandClose"));
[self.pageInfoCommandsHandler hidePageInfo];
}
#pragma mark - Private
// Returns the navigationItem titleView for `siteURL`.
- (UILabel*)titleViewLabelForURL:(NSString*)siteURL {
UILabel* labelURL = [[UILabel alloc] init];
labelURL.lineBreakMode = NSLineBreakByTruncatingHead;
labelURL.font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
labelURL.text = siteURL;
labelURL.adjustsFontSizeToFitWidth = YES;
labelURL.minimumScaleFactor = kTitleLabelMinimumScaleFactor;
return labelURL;
}
// Updates the switch of the given permission.
- (void)updateSwitchForPermission:(PermissionInfo*)permissionInfo
tableViewLoaded:(BOOL)tableViewLoaded {
switch (permissionInfo.permission) {
case web::PermissionCamera:
[self updateSwitchForPermissionState:permissionInfo.state
withLabel:l10n_util::GetNSString(
IDS_IOS_PERMISSIONS_CAMERA)
toItem:ItemTypePermissionsCamera
tableViewLoaded:tableViewLoaded];
break;
case web::PermissionMicrophone:
[self updateSwitchForPermissionState:permissionInfo.state
withLabel:l10n_util::GetNSString(
IDS_IOS_PERMISSIONS_MICROPHONE)
toItem:ItemTypePermissionsMicrophone
tableViewLoaded:tableViewLoaded];
break;
}
}
// Invoked when a permission switch is toggled.
- (void)permissionSwitchToggled:(UISwitch*)sender {
if (@available(iOS 15.0, *)) {
web::Permission permission;
switch (sender.tag) {
case ItemTypePermissionsCamera:
permission = web::PermissionCamera;
break;
case ItemTypePermissionsMicrophone:
permission = web::PermissionMicrophone;
break;
case ItemTypePermissionsDescription:
NOTREACHED();
return;
}
PermissionInfo* permissionsDescription = [[PermissionInfo alloc] init];
permissionsDescription.permission = permission;
permissionsDescription.state =
sender.isOn ? web::PermissionStateAllowed : web::PermissionStateBlocked;
[self.permissionsDelegate updateStateForPermission:permissionsDescription];
}
}
// Adds or removes a switch depending on the value of the PermissionState.
- (void)updateSwitchForPermissionState:(web::PermissionState)state
withLabel:(NSString*)label
toItem:(ItemType)itemType
tableViewLoaded:(BOOL)tableViewLoaded {
if ([self.tableViewModel hasItemForItemType:itemType
sectionIdentifier:SectionIdentifierPermissions]) {
NSIndexPath* index = [self.tableViewModel indexPathForItemType:itemType];
// Remove the switch item if the permission is not accessible.
if (state == web::PermissionStateNotAccessible) {
[self removeFromModelItemAtIndexPaths:@[ index ]];
[self.tableView deleteRowsAtIndexPaths:@[ index ]
withRowAnimation:UITableViewRowAnimationAutomatic];
} else {
TableViewSwitchItem* currentItem =
base::apple::ObjCCastStrict<TableViewSwitchItem>(
[self.tableViewModel itemAtIndexPath:index]);
TableViewSwitchCell* currentCell =
base::apple::ObjCCastStrict<TableViewSwitchCell>(
[self.tableView cellForRowAtIndexPath:index]);
currentItem.on = state == web::PermissionStateAllowed;
// Reload the switch cell if its value is outdated.
if (currentItem.isOn != currentCell.switchView.isOn) {
[self.tableView
reloadRowsAtIndexPaths:@[ index ]
withRowAnimation:UITableViewRowAnimationAutomatic];
}
}
return;
}
// Don't add a switch item if the permission is not accessible.
if (state == web::PermissionStateNotAccessible) {
return;
}
TableViewSwitchItem* switchItem =
[[TableViewSwitchItem alloc] initWithType:itemType];
switchItem.text = label;
switchItem.on = state == web::PermissionStateAllowed;
switchItem.accessibilityIdentifier =
itemType == ItemTypePermissionsCamera
? kPageInfoCameraSwitchAccessibilityIdentifier
: kPageInfoMicrophoneSwitchAccessibilityIdentifier;
// If ItemTypePermissionsMicrophone is already added, insert the
// ItemTypePermissionsCamera before the ItemTypePermissionsMicrophone.
if (itemType == ItemTypePermissionsCamera &&
[self.tableViewModel hasItemForItemType:ItemTypePermissionsMicrophone
sectionIdentifier:SectionIdentifierPermissions]) {
NSIndexPath* index = [self.tableViewModel
indexPathForItemType:ItemTypePermissionsMicrophone];
[self.tableViewModel insertItem:switchItem
inSectionWithIdentifier:SectionIdentifierPermissions
atIndex:index.row];
} else {
[self.tableViewModel addItem:switchItem
toSectionWithIdentifier:SectionIdentifierPermissions];
}
if (tableViewLoaded) {
NSIndexPath* index = [self.tableViewModel indexPathForItemType:itemType];
[self.tableView insertRowsAtIndexPaths:@[ index ]
withRowAnimation:UITableViewRowAnimationAutomatic];
}
}
#pragma mark - PermissionsConsumer
- (void)setPermissionsInfo:(NSArray<PermissionInfo*>*)permissionsInfo {
_permissionsInfo = permissionsInfo;
}
- (void)permissionStateChanged:(PermissionInfo*)permissionInfo {
if (@available(iOS 15.0, *)) {
// Add the Permissions section if it doesn't exist.
if (![self.tableViewModel
hasSectionForSectionIdentifier:SectionIdentifierPermissions]) {
[self loadPermissionsModel];
NSUInteger index = [self.tableViewModel
sectionForSectionIdentifier:SectionIdentifierPermissions];
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:index]
withRowAnimation:UITableViewRowAnimationAutomatic];
}
[self updateSwitchForPermission:permissionInfo tableViewLoaded:YES];
}
}
@end