| Index: chrome/browser/ui/views/apps/app_info_dialog/app_info_permissions_panel.cc
|
| diff --git a/chrome/browser/ui/views/apps/app_info_dialog/app_info_permissions_panel.cc b/chrome/browser/ui/views/apps/app_info_dialog/app_info_permissions_panel.cc
|
| index 0366a433cc8d8554e2b519b862c25a52e85971a6..fdfb8109759643bb82de56851251e5dc8b34a108 100644
|
| --- a/chrome/browser/ui/views/apps/app_info_dialog/app_info_permissions_panel.cc
|
| +++ b/chrome/browser/ui/views/apps/app_info_dialog/app_info_permissions_panel.cc
|
| @@ -17,220 +17,240 @@
|
| #include "extensions/common/permissions/api_permission.h"
|
| #include "extensions/common/permissions/permissions_data.h"
|
| #include "ui/base/l10n/l10n_util.h"
|
| -#include "ui/views/controls/button/label_button.h"
|
| +#include "ui/base/resource/resource_bundle.h"
|
| +#include "ui/resources/grit/ui_resources.h"
|
| +#include "ui/views/border.h"
|
| +#include "ui/views/controls/button/button.h"
|
| +#include "ui/views/controls/button/image_button.h"
|
| #include "ui/views/controls/label.h"
|
| #include "ui/views/layout/box_layout.h"
|
| #include "ui/views/layout/grid_layout.h"
|
| #include "ui/views/layout/layout_constants.h"
|
| #include "ui/views/view.h"
|
|
|
| -AppInfoPermissionsPanel::AppInfoPermissionsPanel(
|
| - Profile* profile,
|
| - const extensions::Extension* app)
|
| - : AppInfoPanel(profile, app),
|
| - permissions_heading_(NULL),
|
| - permissions_list_(NULL),
|
| - retained_files_heading_(NULL),
|
| - retained_files_list_(NULL),
|
| - revoke_file_permissions_button_(NULL),
|
| - retained_devices_heading_(NULL),
|
| - retained_devices_list_(NULL),
|
| - revoke_device_permissions_button_(NULL) {
|
| - // Create UI elements.
|
| - CreateActivePermissionsControl();
|
| - CreateRetainedFilesControl();
|
| - CreateRetainedDevicesControl();
|
| -
|
| - // Layout elements.
|
| - SetLayoutManager(
|
| - new views::BoxLayout(views::BoxLayout::kVertical,
|
| - 0,
|
| - 0,
|
| - views::kUnrelatedControlVerticalSpacing));
|
| -
|
| - LayoutActivePermissionsControl();
|
| - LayoutRetainedFilesControl();
|
| - LayoutRetainedDevicesControl();
|
| -}
|
| -
|
| -AppInfoPermissionsPanel::~AppInfoPermissionsPanel() {
|
| - // Destroy view children before their models.
|
| - RemoveAllChildViews(true);
|
| -}
|
| +namespace {
|
| +
|
| +// IDs for the two bullet column sets.
|
| +const int kBulletColumnSetId = 1;
|
| +const int kNestedBulletColumnSetId = 2;
|
| +
|
| +// Pixel spacing measurements for different parts of the permissions list.
|
| +const int kSpacingBetweenBulletAndStartOfText = 5;
|
| +const int kSpacingBetweenTextAndRevokeButton = 15;
|
| +const int kIndentationBeforeNestedBullet = 13;
|
| +
|
| +// Creates a close button that calls |callback| on click and can be placed to
|
| +// the right of a bullet in the permissions list.
|
| +class RevokeButton : public views::ImageButton, public views::ButtonListener {
|
| + public:
|
| + explicit RevokeButton(const base::Closure& callback)
|
| + : views::ImageButton(this), callback_(callback) {
|
| + ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
|
| + SetImage(views::CustomButton::STATE_NORMAL,
|
| + rb.GetImageNamed(IDR_DISABLE).ToImageSkia());
|
| + SetImage(views::CustomButton::STATE_HOVERED,
|
| + rb.GetImageNamed(IDR_DISABLE_H).ToImageSkia());
|
| + SetImage(views::CustomButton::STATE_PRESSED,
|
| + rb.GetImageNamed(IDR_DISABLE_P).ToImageSkia());
|
| + SetBorder(scoped_ptr<views::Border>());
|
| + SetSize(GetPreferredSize());
|
| + }
|
| + virtual ~RevokeButton() {}
|
| +
|
| + private:
|
| + // Overridden from views::ButtonListener.
|
| + virtual void ButtonPressed(views::Button* sender,
|
| + const ui::Event& event) override {
|
| + DCHECK_EQ(this, sender);
|
| + if (!callback_.is_null())
|
| + callback_.Run();
|
| + }
|
|
|
| -// Given a list of strings, returns a view containing a list of these strings
|
| -// as bulleted items.
|
| -views::View* AppInfoPermissionsPanel::CreateBulletedListView(
|
| - const std::vector<base::string16>& messages,
|
| - bool allow_multiline,
|
| - gfx::ElideBehavior elide_behavior) {
|
| - const int kSpacingBetweenBulletAndStartOfText = 5;
|
| -
|
| - views::View* list_view = new views::View();
|
| - views::GridLayout* layout = new views::GridLayout(list_view);
|
| - list_view->SetLayoutManager(layout);
|
| -
|
| - // Create 2 columns: one for the bullet, one for the bullet text.
|
| - static const int kColumnSetId = 1;
|
| - views::ColumnSet* column_set = layout->AddColumnSet(kColumnSetId);
|
| - column_set->AddColumn(views::GridLayout::FILL,
|
| - views::GridLayout::LEADING,
|
| - 0,
|
| - views::GridLayout::USE_PREF,
|
| - 0,
|
| - 0);
|
| - column_set->AddPaddingColumn(0, kSpacingBetweenBulletAndStartOfText);
|
| - column_set->AddColumn(views::GridLayout::FILL,
|
| - views::GridLayout::LEADING,
|
| - 1,
|
| - views::GridLayout::USE_PREF,
|
| - 0,
|
| - 0);
|
| -
|
| - for (std::vector<base::string16>::const_iterator it = messages.begin();
|
| - it != messages.end();
|
| - ++it) {
|
| - views::Label* permission_label = new views::Label(*it);
|
| + const base::Closure callback_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(RevokeButton);
|
| +};
|
| +
|
| +// A bulleted list of permissions.
|
| +// TODO(sashab): Fix BoxLayout to correctly display multi-line strings and then
|
| +// remove this class (since the GridLayout will no longer be needed).
|
| +class BulletedPermissionsList : public views::View {
|
| + public:
|
| + BulletedPermissionsList() {
|
| + layout_ = new views::GridLayout(this);
|
| + SetLayoutManager(layout_);
|
| +
|
| + // Create 3 columns: the bullet, the bullet text, and the revoke button.
|
| + views::ColumnSet* column_set = layout_->AddColumnSet(kBulletColumnSetId);
|
| + column_set->AddColumn(views::GridLayout::FILL,
|
| + views::GridLayout::LEADING,
|
| + 0,
|
| + views::GridLayout::USE_PREF,
|
| + 0,
|
| + 0);
|
| + column_set->AddPaddingColumn(0, kSpacingBetweenBulletAndStartOfText);
|
| + column_set->AddColumn(views::GridLayout::FILL,
|
| + views::GridLayout::LEADING,
|
| + 1 /* stretch to fill space */,
|
| + views::GridLayout::USE_PREF,
|
| + 0,
|
| + 0);
|
| + column_set->AddPaddingColumn(0, kSpacingBetweenTextAndRevokeButton);
|
| + column_set->AddColumn(views::GridLayout::FILL,
|
| + views::GridLayout::LEADING,
|
| + 0,
|
| + views::GridLayout::USE_PREF,
|
| + 0,
|
| + 0);
|
| +
|
| + views::ColumnSet* nested_column_set =
|
| + layout_->AddColumnSet(kNestedBulletColumnSetId);
|
| + nested_column_set->AddPaddingColumn(0, kIndentationBeforeNestedBullet);
|
| + nested_column_set->AddColumn(views::GridLayout::FILL,
|
| + views::GridLayout::LEADING,
|
| + 0,
|
| + views::GridLayout::USE_PREF,
|
| + 0,
|
| + 0);
|
| + nested_column_set->AddPaddingColumn(0, kSpacingBetweenBulletAndStartOfText);
|
| + nested_column_set->AddColumn(views::GridLayout::FILL,
|
| + views::GridLayout::LEADING,
|
| + 1 /* stretch to fill space */,
|
| + views::GridLayout::USE_PREF,
|
| + 0,
|
| + 0);
|
| + nested_column_set->AddPaddingColumn(0, kSpacingBetweenTextAndRevokeButton);
|
| + nested_column_set->AddColumn(views::GridLayout::FILL,
|
| + views::GridLayout::LEADING,
|
| + 0,
|
| + views::GridLayout::USE_PREF,
|
| + 0,
|
| + 0);
|
| + }
|
| + virtual ~BulletedPermissionsList() {}
|
| +
|
| + // Given a set of strings for a given permission (|message| for the topmost
|
| + // bullet and a potentially-empty |submessages| for sub-bullets), adds these
|
| + // bullets to the given BulletedPermissionsList. If |revoke_callback| is
|
| + // provided, also adds an X button next to the bullet which calls the callback
|
| + // when clicked.
|
| + void AddPermissionBullets(base::string16 message,
|
| + std::vector<base::string16> submessages,
|
| + gfx::ElideBehavior elide_behavior_for_submessages,
|
| + const base::Closure& revoke_callback) {
|
| + RevokeButton* revoke_button = NULL;
|
| + if (!revoke_callback.is_null())
|
| + revoke_button = new RevokeButton(revoke_callback);
|
| +
|
| + views::Label* permission_label = new views::Label(message);
|
| permission_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
|
| + permission_label->SetMultiLine(true);
|
| + AddSinglePermissionBullet(false, permission_label, revoke_button);
|
| +
|
| + for (const auto& submessage : submessages) {
|
| + views::Label* sub_permission_label = new views::Label(submessage);
|
| + sub_permission_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
|
| + sub_permission_label->SetElideBehavior(elide_behavior_for_submessages);
|
| + AddSinglePermissionBullet(true, sub_permission_label, NULL);
|
| + }
|
| + }
|
|
|
| - if (allow_multiline)
|
| - permission_label->SetMultiLine(true);
|
| - else
|
| - permission_label->SetElideBehavior(elide_behavior);
|
| + private:
|
| + void AddSinglePermissionBullet(bool is_nested,
|
| + views::Label* permission_label,
|
| + RevokeButton* revoke_button) {
|
| + // Add a padding row before every item except the first.
|
| + if (has_children())
|
| + layout_->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
|
|
|
| - // Extract only the bullet from the IDS_EXTENSION_PERMISSION_LINE text.
|
| - views::Label* bullet_label = new views::Label(l10n_util::GetStringFUTF16(
|
| - IDS_EXTENSION_PERMISSION_LINE, base::string16()));
|
| + const base::char16 bullet_point[] = {0x2022, 0};
|
| + views::Label* bullet_label = new views::Label(base::string16(bullet_point));
|
|
|
| - // Add a padding row before every item except the first
|
| - if (it != messages.begin())
|
| - layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
|
| + layout_->StartRow(
|
| + 1, is_nested ? kNestedBulletColumnSetId : kBulletColumnSetId);
|
| + layout_->AddView(bullet_label);
|
| + layout_->AddView(permission_label);
|
|
|
| - layout->StartRow(1, kColumnSetId);
|
| - layout->AddView(bullet_label);
|
| - layout->AddView(permission_label);
|
| + if (revoke_button != NULL)
|
| + layout_->AddView(revoke_button);
|
| + else
|
| + layout_->SkipColumns(1);
|
| }
|
|
|
| - return list_view;
|
| + views::GridLayout* layout_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(BulletedPermissionsList);
|
| +};
|
| +
|
| +} // namespace
|
| +
|
| +AppInfoPermissionsPanel::AppInfoPermissionsPanel(
|
| + Profile* profile,
|
| + const extensions::Extension* app)
|
| + : AppInfoPanel(profile, app) {
|
| + SetLayoutManager(new views::BoxLayout(views::BoxLayout::kVertical,
|
| + 0,
|
| + 0,
|
| + views::kRelatedControlVerticalSpacing));
|
| +
|
| + CreatePermissionsList();
|
| }
|
|
|
| -void AppInfoPermissionsPanel::CreateActivePermissionsControl() {
|
| - permissions_heading_ = CreateHeading(
|
| +AppInfoPermissionsPanel::~AppInfoPermissionsPanel() {
|
| +}
|
| +
|
| +void AppInfoPermissionsPanel::CreatePermissionsList() {
|
| + views::View* permissions_heading = CreateHeading(
|
| l10n_util::GetStringUTF16(IDS_APPLICATION_INFO_APP_PERMISSIONS_TITLE));
|
| - std::vector<base::string16> permission_strings =
|
| - GetActivePermissionMessages();
|
| - if (permission_strings.empty()) {
|
| + AddChildView(permissions_heading);
|
| +
|
| + if (!HasActivePermissionMessages() && GetRetainedDeviceCount() == 0 &&
|
| + GetRetainedFileCount() == 0) {
|
| views::Label* no_permissions_text =
|
| new views::Label(l10n_util::GetStringUTF16(
|
| app_->is_extension()
|
| ? IDS_APPLICATION_INFO_EXTENSION_NO_PERMISSIONS_TEXT
|
| : IDS_APPLICATION_INFO_APP_NO_PERMISSIONS_TEXT));
|
| no_permissions_text->SetHorizontalAlignment(gfx::ALIGN_LEFT);
|
| - permissions_list_ = no_permissions_text;
|
| - } else {
|
| - permissions_list_ =
|
| - CreateBulletedListView(permission_strings, true, gfx::NO_ELIDE);
|
| + AddChildView(no_permissions_text);
|
| + return;
|
| }
|
| -}
|
|
|
| -void AppInfoPermissionsPanel::CreateRetainedFilesControl() {
|
| - const std::vector<base::string16> retained_file_permission_messages =
|
| - GetRetainedFilePaths();
|
| -
|
| - if (!retained_file_permission_messages.empty()) {
|
| - revoke_file_permissions_button_ = new views::LabelButton(
|
| - this,
|
| - l10n_util::GetStringUTF16(
|
| - IDS_APPLICATION_INFO_REVOKE_RETAINED_FILE_PERMISSIONS_BUTTON_TEXT));
|
| - revoke_file_permissions_button_->SetStyle(views::Button::STYLE_BUTTON);
|
| -
|
| - retained_files_heading_ = CreateHeading(l10n_util::GetStringUTF16(
|
| - IDS_APPLICATION_INFO_RETAINED_FILE_PERMISSIONS_TEXT));
|
| - retained_files_list_ = CreateBulletedListView(
|
| - retained_file_permission_messages, false, gfx::ELIDE_MIDDLE);
|
| - }
|
| -}
|
| + BulletedPermissionsList* permissions_list = new BulletedPermissionsList();
|
|
|
| -void AppInfoPermissionsPanel::CreateRetainedDevicesControl() {
|
| - const std::vector<base::string16> retained_device_permission_messages =
|
| - GetRetainedDevices();
|
| -
|
| - if (!retained_device_permission_messages.empty()) {
|
| - revoke_device_permissions_button_ = new views::LabelButton(
|
| - this,
|
| - l10n_util::GetStringUTF16(
|
| - IDS_APPLICATION_INFO_REVOKE_DEVICE_PERMISSIONS_BUTTON_TEXT));
|
| - revoke_device_permissions_button_->SetStyle(views::Button::STYLE_BUTTON);
|
| -
|
| - retained_devices_heading_ = CreateHeading(l10n_util::GetStringUTF16(
|
| - IDS_APPLICATION_INFO_RETAINED_DEVICE_PERMISSIONS_TEXT));
|
| - retained_devices_list_ = CreateBulletedListView(
|
| - retained_device_permission_messages, false, gfx::ELIDE_TAIL);
|
| + // Add regular permission messages.
|
| + for (const auto& message : GetActivePermissionMessages()) {
|
| + permissions_list->AddPermissionBullets(
|
| + message, std::vector<base::string16>(), gfx::NO_ELIDE, base::Closure());
|
| }
|
| -}
|
|
|
| -void AppInfoPermissionsPanel::LayoutActivePermissionsControl() {
|
| - views::View* vertical_stack = CreateVerticalStack();
|
| - vertical_stack->AddChildView(permissions_heading_);
|
| - vertical_stack->AddChildView(permissions_list_);
|
| - AddChildView(vertical_stack);
|
| -}
|
| + // TODO(sashab): Add host permission messages, if the app has any.
|
|
|
| -void AppInfoPermissionsPanel::LayoutRetainedFilesControl() {
|
| - if (retained_files_list_) {
|
| - DCHECK(retained_files_heading_);
|
| - DCHECK(revoke_file_permissions_button_);
|
| -
|
| - // Add a sub-view so the revoke button is right-aligned.
|
| - views::View* right_aligned_button = new views::View();
|
| - views::BoxLayout* right_aligned_horizontal_layout =
|
| - new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 0);
|
| - right_aligned_horizontal_layout->set_main_axis_alignment(
|
| - views::BoxLayout::MAIN_AXIS_ALIGNMENT_END);
|
| - right_aligned_button->SetLayoutManager(right_aligned_horizontal_layout);
|
| - right_aligned_button->AddChildView(revoke_file_permissions_button_);
|
| -
|
| - views::View* vertical_stack = CreateVerticalStack();
|
| - vertical_stack->AddChildView(retained_files_heading_);
|
| - vertical_stack->AddChildView(retained_files_list_);
|
| - vertical_stack->AddChildView(right_aligned_button);
|
| -
|
| - AddChildView(vertical_stack);
|
| + // Add USB devices, if the app has any.
|
| + if (GetRetainedDeviceCount() > 0) {
|
| + permissions_list->AddPermissionBullets(
|
| + GetRetainedDeviceHeading(),
|
| + GetRetainedDevices(),
|
| + gfx::ELIDE_TAIL,
|
| + base::Bind(&AppInfoPermissionsPanel::RevokeDevicePermissions,
|
| + base::Unretained(this)));
|
| }
|
| -}
|
|
|
| -void AppInfoPermissionsPanel::LayoutRetainedDevicesControl() {
|
| - if (retained_devices_list_) {
|
| - DCHECK(retained_devices_heading_);
|
| - DCHECK(revoke_device_permissions_button_);
|
| -
|
| - // Add a sub-view so the revoke button is right-aligned.
|
| - views::View* right_aligned_button = new views::View();
|
| - views::BoxLayout* right_aligned_horizontal_layout =
|
| - new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 0);
|
| - right_aligned_horizontal_layout->set_main_axis_alignment(
|
| - views::BoxLayout::MAIN_AXIS_ALIGNMENT_END);
|
| - right_aligned_button->SetLayoutManager(right_aligned_horizontal_layout);
|
| - right_aligned_button->AddChildView(revoke_device_permissions_button_);
|
| -
|
| - views::View* vertical_stack = CreateVerticalStack();
|
| - vertical_stack->AddChildView(retained_devices_heading_);
|
| - vertical_stack->AddChildView(retained_devices_list_);
|
| - vertical_stack->AddChildView(right_aligned_button);
|
| -
|
| - AddChildView(vertical_stack);
|
| + // Add retained files, if the app has any.
|
| + if (GetRetainedFileCount() > 0) {
|
| + permissions_list->AddPermissionBullets(
|
| + GetRetainedFileHeading(),
|
| + GetRetainedFilePaths(),
|
| + gfx::ELIDE_MIDDLE,
|
| + base::Bind(&AppInfoPermissionsPanel::RevokeFilePermissions,
|
| + base::Unretained(this)));
|
| }
|
| +
|
| + AddChildView(permissions_list);
|
| }
|
|
|
| -void AppInfoPermissionsPanel::ButtonPressed(views::Button* sender,
|
| - const ui::Event& event) {
|
| - if (sender == revoke_file_permissions_button_) {
|
| - RevokeFilePermissions();
|
| - } else if (sender == revoke_device_permissions_button_) {
|
| - RevokeDevicePermissions();
|
| - } else {
|
| - NOTREACHED();
|
| - }
|
| +bool AppInfoPermissionsPanel::HasActivePermissionMessages() const {
|
| + return !GetActivePermissionMessages().empty();
|
| }
|
|
|
| const std::vector<base::string16>
|
| @@ -238,6 +258,32 @@ AppInfoPermissionsPanel::GetActivePermissionMessages() const {
|
| return app_->permissions_data()->GetPermissionMessageStrings();
|
| }
|
|
|
| +int AppInfoPermissionsPanel::GetRetainedFileCount() const {
|
| + if (app_->permissions_data()->HasAPIPermission(
|
| + extensions::APIPermission::kFileSystem)) {
|
| + return apps::SavedFilesService::Get(profile_)
|
| + ->GetAllFileEntries(app_->id())
|
| + .size();
|
| + }
|
| + return 0;
|
| +}
|
| +
|
| +base::string16 AppInfoPermissionsPanel::GetRetainedFileHeading() const {
|
| + const int kRetainedFilesMessageIDs[6] = {
|
| + IDS_APPLICATION_INFO_RETAINED_FILES_DEFAULT,
|
| + IDS_APPLICATION_INFO_RETAINED_FILE_SINGULAR,
|
| + IDS_APPLICATION_INFO_RETAINED_FILES_ZERO,
|
| + IDS_APPLICATION_INFO_RETAINED_FILES_TWO,
|
| + IDS_APPLICATION_INFO_RETAINED_FILES_FEW,
|
| + IDS_APPLICATION_INFO_RETAINED_FILES_MANY,
|
| + };
|
| + std::vector<int> message_ids(
|
| + kRetainedFilesMessageIDs,
|
| + kRetainedFilesMessageIDs + arraysize(kRetainedFilesMessageIDs));
|
| +
|
| + return l10n_util::GetPluralStringFUTF16(message_ids, GetRetainedFileCount());
|
| +}
|
| +
|
| const std::vector<base::string16>
|
| AppInfoPermissionsPanel::GetRetainedFilePaths() const {
|
| std::vector<base::string16> retained_file_paths;
|
| @@ -262,6 +308,29 @@ void AppInfoPermissionsPanel::RevokeFilePermissions() {
|
| Close();
|
| }
|
|
|
| +int AppInfoPermissionsPanel::GetRetainedDeviceCount() const {
|
| + return extensions::DevicePermissionsManager::Get(profile_)
|
| + ->GetPermissionMessageStrings(app_->id())
|
| + .size();
|
| +}
|
| +
|
| +base::string16 AppInfoPermissionsPanel::GetRetainedDeviceHeading() const {
|
| + const int kRetainedDevicesMessageIDs[6] = {
|
| + IDS_APPLICATION_INFO_RETAINED_DEVICES_DEFAULT,
|
| + IDS_APPLICATION_INFO_RETAINED_DEVICE_SINGULAR,
|
| + IDS_APPLICATION_INFO_RETAINED_DEVICES_ZERO,
|
| + IDS_APPLICATION_INFO_RETAINED_DEVICES_TWO,
|
| + IDS_APPLICATION_INFO_RETAINED_DEVICES_FEW,
|
| + IDS_APPLICATION_INFO_RETAINED_DEVICES_MANY,
|
| + };
|
| + std::vector<int> message_ids(
|
| + kRetainedDevicesMessageIDs,
|
| + kRetainedDevicesMessageIDs + arraysize(kRetainedDevicesMessageIDs));
|
| +
|
| + return l10n_util::GetPluralStringFUTF16(message_ids,
|
| + GetRetainedDeviceCount());
|
| +}
|
| +
|
| const std::vector<base::string16> AppInfoPermissionsPanel::GetRetainedDevices()
|
| const {
|
| return extensions::DevicePermissionsManager::Get(profile_)
|
|
|