| Index: chrome/browser/ui/views/apps/app_info_dialog/app_info_details_panel.cc
|
| diff --git a/chrome/browser/ui/views/apps/app_info_dialog/app_info_details_panel.cc b/chrome/browser/ui/views/apps/app_info_dialog/app_info_details_panel.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..2e3719c48c451e704c8200933bba28fd590a721b
|
| --- /dev/null
|
| +++ b/chrome/browser/ui/views/apps/app_info_dialog/app_info_details_panel.cc
|
| @@ -0,0 +1,457 @@
|
| +// Copyright 2014 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "chrome/browser/ui/views/apps/app_info_dialog/app_info_details_panel.h"
|
| +
|
| +#include <string>
|
| +#include <vector>
|
| +
|
| +#include "apps/app_load_service.h"
|
| +#include "apps/app_restore_service.h"
|
| +#include "apps/saved_files_service.h"
|
| +#include "base/callback.h"
|
| +#include "base/command_line.h"
|
| +#include "base/files/file_path.h"
|
| +#include "base/strings/utf_string_conversions.h"
|
| +#include "chrome/browser/extensions/extension_service.h"
|
| +#include "chrome/browser/extensions/launch_util.h"
|
| +#include "chrome/browser/profiles/profile.h"
|
| +#include "chrome/browser/ui/browser_dialogs.h"
|
| +#include "chrome/common/chrome_switches.h"
|
| +#include "chrome/common/extensions/extension_constants.h"
|
| +#include "extensions/browser/extension_prefs.h"
|
| +#include "extensions/browser/extension_system.h"
|
| +#include "extensions/common/extension.h"
|
| +#include "extensions/common/permissions/api_permission.h"
|
| +#include "extensions/common/permissions/permissions_data.h"
|
| +#include "grit/generated_resources.h"
|
| +#include "ui/base/l10n/l10n_util.h"
|
| +#include "ui/base/models/combobox_model.h"
|
| +#include "ui/base/resource/resource_bundle.h"
|
| +#include "ui/gfx/text_constants.h"
|
| +#include "ui/views/controls/button/button.h"
|
| +#include "ui/views/controls/button/label_button.h"
|
| +#include "ui/views/controls/combobox/combobox.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/widget/widget.h"
|
| +
|
| +namespace {
|
| +
|
| +// Create a heading label with the given text.
|
| +views::Label* CreateHeading(const base::string16& text) {
|
| + views::Label* label = new views::Label(text);
|
| + label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
|
| + label->SetFontList(ui::ResourceBundle::GetSharedInstance().GetFontList(
|
| + ui::ResourceBundle::MediumFont));
|
| + return label;
|
| +}
|
| +
|
| +// Create a view with a vertically-stacked box layout, which can have child
|
| +// views appended to it.
|
| +views::View* CreateVerticalStack() {
|
| + views::View* vertically_stacked_view = new views::View();
|
| + vertically_stacked_view->SetLayoutManager(
|
| + new views::BoxLayout(views::BoxLayout::kVertical,
|
| + 0,
|
| + 0,
|
| + views::kRelatedControlVerticalSpacing));
|
| + return vertically_stacked_view;
|
| +}
|
| +
|
| +// Given a list of strings, returns a view containing a list of these strings
|
| +// as bulleted items.
|
| +views::View* CreateBulletedListView(
|
| + const std::vector<base::string16>& messages) {
|
| + const int kSpacingBetweenBulletAndStartOfText = 5;
|
| + views::View* list_view = CreateVerticalStack();
|
| +
|
| + for (std::vector<base::string16>::const_iterator it = messages.begin();
|
| + it != messages.end();
|
| + ++it) {
|
| + views::Label* permission_label = new views::Label(*it);
|
| + permission_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
|
| + permission_label->SetMultiLine(true);
|
| +
|
| + // Extract only the bullet from the IDS_EXTENSION_PERMISSION_LINE text, and
|
| + // place it in it's own view so it doesn't align vertically with the
|
| + // multilined permissions text.
|
| + views::Label* bullet_label = new views::Label(l10n_util::GetStringFUTF16(
|
| + IDS_EXTENSION_PERMISSION_LINE, base::string16()));
|
| + views::View* bullet_label_top_aligned = CreateVerticalStack();
|
| + bullet_label_top_aligned->AddChildView(bullet_label);
|
| +
|
| + // Place the bullet and the text so all permissions line up at the bullet.
|
| + views::View* bulleted_list_item = new views::View();
|
| + bulleted_list_item->SetLayoutManager(
|
| + new views::BoxLayout(views::BoxLayout::kHorizontal,
|
| + 0,
|
| + 0,
|
| + kSpacingBetweenBulletAndStartOfText));
|
| + bulleted_list_item->AddChildView(bullet_label_top_aligned);
|
| + bulleted_list_item->AddChildView(permission_label);
|
| +
|
| + list_view->AddChildView(bulleted_list_item);
|
| + }
|
| +
|
| + return list_view;
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +// A model for a combobox selecting the launch options for a hosted app.
|
| +// Displays different options depending on the host OS.
|
| +class LaunchOptionsComboboxModel : public ui::ComboboxModel {
|
| + public:
|
| + LaunchOptionsComboboxModel();
|
| + virtual ~LaunchOptionsComboboxModel();
|
| +
|
| + extensions::LaunchType GetLaunchTypeAtIndex(int index) const;
|
| + int GetIndexForLaunchType(extensions::LaunchType launch_type) const;
|
| +
|
| + // Overridden from ui::ComboboxModel:
|
| + virtual int GetItemCount() const OVERRIDE;
|
| + virtual base::string16 GetItemAt(int index) OVERRIDE;
|
| +
|
| + private:
|
| + // A list of the launch types available in the combobox, in order.
|
| + std::vector<extensions::LaunchType> launch_types_;
|
| +
|
| + // A list of the messages to display in the combobox, in order. The indexes in
|
| + // this list correspond to the indexes in launch_types_.
|
| + std::vector<base::string16> launch_type_messages_;
|
| +};
|
| +
|
| +LaunchOptionsComboboxModel::LaunchOptionsComboboxModel() {
|
| + if (CommandLine::ForCurrentProcess()->HasSwitch(
|
| + switches::kEnableStreamlinedHostedApps)) {
|
| + // Streamlined hosted apps can only toggle between LAUNCH_TYPE_WINDOW and
|
| + // LAUNCH_TYPE_REGULAR.
|
| + // TODO(sashab): Use a checkbox for this choice instead of combobox.
|
| + launch_types_.push_back(extensions::LAUNCH_TYPE_REGULAR);
|
| + launch_type_messages_.push_back(
|
| + l10n_util::GetStringUTF16(IDS_APP_CONTEXT_MENU_OPEN_TAB));
|
| +
|
| + // Although LAUNCH_TYPE_WINDOW doesn't work on Mac, the streamlined hosted
|
| + // apps flag isn't available on Mac, so we must be on a non-Mac OS.
|
| + launch_types_.push_back(extensions::LAUNCH_TYPE_WINDOW);
|
| + launch_type_messages_.push_back(
|
| + l10n_util::GetStringUTF16(IDS_APP_CONTEXT_MENU_OPEN_WINDOW));
|
| + } else {
|
| + launch_types_.push_back(extensions::LAUNCH_TYPE_REGULAR);
|
| + launch_type_messages_.push_back(
|
| + l10n_util::GetStringUTF16(IDS_APP_CONTEXT_MENU_OPEN_REGULAR));
|
| +
|
| + launch_types_.push_back(extensions::LAUNCH_TYPE_PINNED);
|
| + launch_type_messages_.push_back(
|
| + l10n_util::GetStringUTF16(IDS_APP_CONTEXT_MENU_OPEN_PINNED));
|
| +
|
| +#if defined(OS_MACOSX)
|
| + // Mac does not support standalone web app browser windows or maximize.
|
| + launch_types_.push_back(extensions::LAUNCH_TYPE_FULLSCREEN);
|
| + launch_type_messages_.push_back(
|
| + l10n_util::GetStringUTF16(IDS_APP_CONTEXT_MENU_OPEN_FULLSCREEN));
|
| +#else
|
| + launch_types_.push_back(extensions::LAUNCH_TYPE_WINDOW);
|
| + launch_type_messages_.push_back(
|
| + l10n_util::GetStringUTF16(IDS_APP_CONTEXT_MENU_OPEN_WINDOW));
|
| +
|
| + // Even though the launch type is Full Screen, it is more accurately
|
| + // described as Maximized in non-Mac OSs.
|
| + launch_types_.push_back(extensions::LAUNCH_TYPE_FULLSCREEN);
|
| + launch_type_messages_.push_back(
|
| + l10n_util::GetStringUTF16(IDS_APP_CONTEXT_MENU_OPEN_MAXIMIZED));
|
| +#endif
|
| + }
|
| +}
|
| +
|
| +LaunchOptionsComboboxModel::~LaunchOptionsComboboxModel() {
|
| +}
|
| +
|
| +extensions::LaunchType LaunchOptionsComboboxModel::GetLaunchTypeAtIndex(
|
| + int index) const {
|
| + return launch_types_[index];
|
| +}
|
| +
|
| +int LaunchOptionsComboboxModel::GetIndexForLaunchType(
|
| + extensions::LaunchType launch_type) const {
|
| + for (size_t i = 0; i < launch_types_.size(); i++) {
|
| + if (launch_types_[i] == launch_type) {
|
| + return i;
|
| + }
|
| + }
|
| + // If the requested launch type is not available, just select the first one.
|
| + LOG(WARNING) << "Unavailable launch type " << launch_type << " selected.";
|
| + return 0;
|
| +}
|
| +
|
| +int LaunchOptionsComboboxModel::GetItemCount() const {
|
| + return launch_types_.size();
|
| +}
|
| +
|
| +base::string16 LaunchOptionsComboboxModel::GetItemAt(int index) {
|
| + return launch_type_messages_[index];
|
| +}
|
| +
|
| +AppInfoDetailsPanel::AppInfoDetailsPanel(gfx::NativeWindow parent_window,
|
| + Profile* profile,
|
| + const extensions::Extension* app)
|
| + : parent_window_(parent_window),
|
| + profile_(profile),
|
| + app_(app),
|
| + description_heading_(NULL),
|
| + description_label_(NULL),
|
| + active_permissions_heading_(NULL),
|
| + active_permissions_list_(NULL),
|
| + retained_files_heading_(NULL),
|
| + retained_files_list_(NULL),
|
| + revoke_file_permissions_button_(NULL),
|
| + launch_options_combobox_(NULL) {
|
| + // Create UI elements.
|
| + CreateDescriptionControl();
|
| + CreateActivePermissionsControl();
|
| + CreateRetainedFilesControl();
|
| + CreateLaunchOptionControl();
|
| + CreateShortcutsButton();
|
| +
|
| + // Layout elements.
|
| + SetLayoutManager(
|
| + new views::BoxLayout(views::BoxLayout::kVertical,
|
| + views::kButtonHEdgeMarginNew,
|
| + views::kPanelVertMargin,
|
| + views::kUnrelatedControlVerticalSpacing));
|
| +
|
| + LayoutDescriptionControl();
|
| +
|
| + if (launch_options_combobox_)
|
| + AddChildView(launch_options_combobox_);
|
| +
|
| + LayoutActivePermissionsControl();
|
| + LayoutRetainedFilesControl();
|
| + LayoutShortcutsButton();
|
| +}
|
| +
|
| +AppInfoDetailsPanel::~AppInfoDetailsPanel() {
|
| + // Destroy view children before their models.
|
| + RemoveAllChildViews(true);
|
| +}
|
| +
|
| +void AppInfoDetailsPanel::CreateDescriptionControl() {
|
| + if (!app_->description().empty()) {
|
| + const size_t kMaxLength = 400;
|
| +
|
| + base::string16 text = base::UTF8ToUTF16(app_->description());
|
| + if (text.length() > kMaxLength) {
|
| + text = text.substr(0, kMaxLength);
|
| + text += base::ASCIIToUTF16(" ... ");
|
| + }
|
| +
|
| + description_heading_ = CreateHeading(
|
| + l10n_util::GetStringUTF16(IDS_APPLICATION_INFO_DESCRIPTION_TITLE));
|
| +
|
| + description_label_ = new views::Label(text);
|
| + description_label_->SetMultiLine(true);
|
| + description_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
|
| + }
|
| +}
|
| +
|
| +void AppInfoDetailsPanel::CreateActivePermissionsControl() {
|
| + std::vector<base::string16> permission_strings =
|
| + GetActivePermissionMessages();
|
| + if (permission_strings.empty()) {
|
| + views::Label* no_permissions_text = new views::Label(
|
| + l10n_util::GetStringUTF16(IDS_APPLICATION_INFO_NO_PERMISSIONS_TEXT));
|
| + no_permissions_text->SetHorizontalAlignment(gfx::ALIGN_LEFT);
|
| + active_permissions_list_ = no_permissions_text;
|
| + } else {
|
| + active_permissions_heading_ = CreateHeading(l10n_util::GetStringUTF16(
|
| + IDS_APPLICATION_INFO_ACTIVE_PERMISSIONS_TEXT));
|
| + active_permissions_list_ = CreateBulletedListView(permission_strings);
|
| + }
|
| +}
|
| +
|
| +void AppInfoDetailsPanel::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);
|
| + }
|
| +}
|
| +
|
| +void AppInfoDetailsPanel::CreateLaunchOptionControl() {
|
| + if (CanSetLaunchType()) {
|
| + launch_options_combobox_model_.reset(new LaunchOptionsComboboxModel());
|
| + launch_options_combobox_ =
|
| + new views::Combobox(launch_options_combobox_model_.get());
|
| +
|
| + launch_options_combobox_->set_listener(this);
|
| + launch_options_combobox_->SetSelectedIndex(
|
| + launch_options_combobox_model_->GetIndexForLaunchType(GetLaunchType()));
|
| + }
|
| +}
|
| +
|
| +void AppInfoDetailsPanel::CreateShortcutsButton() {
|
| + if (CanCreateShortcuts()) {
|
| + create_shortcuts_button_ = new views::LabelButton(
|
| + this,
|
| + l10n_util::GetStringUTF16(
|
| + IDS_APPLICATION_INFO_CREATE_SHORTCUTS_BUTTON_TEXT));
|
| + create_shortcuts_button_->SetStyle(views::Button::STYLE_BUTTON);
|
| + }
|
| +}
|
| +
|
| +void AppInfoDetailsPanel::LayoutDescriptionControl() {
|
| + if (description_label_) {
|
| + DCHECK(description_heading_);
|
| + views::View* vertical_stack = CreateVerticalStack();
|
| + vertical_stack->AddChildView(description_heading_);
|
| + vertical_stack->AddChildView(description_label_);
|
| +
|
| + AddChildView(vertical_stack);
|
| + }
|
| +}
|
| +
|
| +void AppInfoDetailsPanel::LayoutActivePermissionsControl() {
|
| + if (active_permissions_list_) {
|
| + views::View* vertical_stack = CreateVerticalStack();
|
| + if (active_permissions_heading_)
|
| + vertical_stack->AddChildView(active_permissions_heading_);
|
| + vertical_stack->AddChildView(active_permissions_list_);
|
| +
|
| + AddChildView(vertical_stack);
|
| + }
|
| +}
|
| +
|
| +void AppInfoDetailsPanel::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);
|
| + }
|
| +}
|
| +
|
| +void AppInfoDetailsPanel::LayoutShortcutsButton() {
|
| + if (create_shortcuts_button_) {
|
| + // Add a sub-view so the shortcuts button is left-aligned.
|
| + views::View* left_aligned_button = new views::View();
|
| + left_aligned_button->SetLayoutManager(
|
| + new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 0));
|
| + left_aligned_button->AddChildView(create_shortcuts_button_);
|
| +
|
| + AddChildView(left_aligned_button);
|
| + }
|
| +}
|
| +
|
| +void AppInfoDetailsPanel::OnPerformAction(views::Combobox* combobox) {
|
| + if (combobox == launch_options_combobox_) {
|
| + SetLaunchType(launch_options_combobox_model_->GetLaunchTypeAtIndex(
|
| + launch_options_combobox_->selected_index()));
|
| + } else {
|
| + NOTREACHED();
|
| + }
|
| +}
|
| +
|
| +void AppInfoDetailsPanel::ButtonPressed(views::Button* sender,
|
| + const ui::Event& event) {
|
| + if (sender == revoke_file_permissions_button_) {
|
| + RevokeFilePermissions();
|
| + } else if (sender == create_shortcuts_button_) {
|
| + CreateShortcuts();
|
| + } else {
|
| + NOTREACHED();
|
| + }
|
| +}
|
| +
|
| +extensions::LaunchType AppInfoDetailsPanel::GetLaunchType() const {
|
| + return extensions::GetLaunchType(extensions::ExtensionPrefs::Get(profile_),
|
| + app_);
|
| +}
|
| +
|
| +void AppInfoDetailsPanel::SetLaunchType(
|
| + extensions::LaunchType launch_type) const {
|
| + DCHECK(CanSetLaunchType());
|
| + ExtensionService* service =
|
| + extensions::ExtensionSystem::Get(profile_)->extension_service();
|
| + extensions::SetLaunchType(service, app_->id(), launch_type);
|
| +}
|
| +
|
| +bool AppInfoDetailsPanel::CanSetLaunchType() const {
|
| + // V2 apps don't have a launch type.
|
| + return !app_->is_platform_app();
|
| +}
|
| +
|
| +void AppInfoDetailsPanel::CreateShortcuts() {
|
| + DCHECK(CanCreateShortcuts());
|
| + chrome::ShowCreateChromeAppShortcutsDialog(
|
| + parent_window_, profile_, app_, base::Callback<void(bool)>());
|
| +}
|
| +
|
| +bool AppInfoDetailsPanel::CanCreateShortcuts() const {
|
| +// ChromeOS can pin apps to the app launcher, but can't create shortcuts.
|
| +#if defined(OS_CHROMEOS)
|
| + return false;
|
| +#else
|
| + return true;
|
| +#endif
|
| +}
|
| +
|
| +const std::vector<base::string16>
|
| +AppInfoDetailsPanel::GetActivePermissionMessages() const {
|
| + return app_->permissions_data()->GetPermissionMessageStrings();
|
| +}
|
| +
|
| +const std::vector<base::string16> AppInfoDetailsPanel::GetRetainedFilePaths()
|
| + const {
|
| + std::vector<base::string16> retained_file_paths;
|
| + if (app_->permissions_data()->HasAPIPermission(
|
| + extensions::APIPermission::kFileSystem)) {
|
| + std::vector<apps::SavedFileEntry> retained_file_entries =
|
| + apps::SavedFilesService::Get(profile_)->GetAllFileEntries(app_->id());
|
| + for (std::vector<apps::SavedFileEntry>::const_iterator it =
|
| + retained_file_entries.begin();
|
| + it != retained_file_entries.end();
|
| + ++it) {
|
| + retained_file_paths.push_back(it->path.LossyDisplayName());
|
| + }
|
| + }
|
| + return retained_file_paths;
|
| +}
|
| +
|
| +void AppInfoDetailsPanel::RevokeFilePermissions() {
|
| + apps::SavedFilesService::Get(profile_)->ClearQueue(app_);
|
| +
|
| + // TODO(benwells): Fix this to call something like
|
| + // AppLoadService::RestartApplicationIfRunning.
|
| + if (apps::AppRestoreService::Get(profile_)->IsAppRestorable(app_->id()))
|
| + apps::AppLoadService::Get(profile_)->RestartApplication(app_->id());
|
| +
|
| + GetWidget()->Close();
|
| +}
|
|
|