Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / views / apps / app_info_dialog / app_info_permissions_tab.cc
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/ui/views/apps/app_info_dialog/app_info_permissions_tab.h"
6
7 #include "apps/saved_files_service.h"
8 #include "base/files/file_path.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "extensions/common/extension.h"
12 #include "extensions/common/permissions/api_permission.h"
13 #include "extensions/common/permissions/permission_message_provider.h"
14 #include "extensions/common/permissions/permissions_data.h"
15 #include "grit/generated_resources.h"
16 #include "grit/theme_resources.h"
17 #include "ui/base/l10n/l10n_util.h"
18 #include "ui/base/resource/resource_bundle.h"
19 #include "ui/gfx/animation/animation.h"
20 #include "ui/gfx/animation/animation_delegate.h"
21 #include "ui/gfx/animation/slide_animation.h"
22 #include "ui/gfx/text_constants.h"
23 #include "ui/views/controls/button/button.h"
24 #include "ui/views/controls/button/image_button.h"
25 #include "ui/views/controls/label.h"
26 #include "ui/views/controls/scroll_view.h"
27 #include "ui/views/layout/box_layout.h"
28 #include "ui/views/layout/fill_layout.h"
29 #include "ui/views/layout/grid_layout.h"
30 #include "ui/views/layout/layout_constants.h"
31
32 namespace {
33
34 // A view to display a title with an expandable permissions list section.
35 class ExpandableContainerView : public views::View,
36                                 public views::ButtonListener,
37                                 public gfx::AnimationDelegate {
38  public:
39   ExpandableContainerView(
40       views::View* owner,
41       const base::string16& title,
42       const std::vector<base::string16>& permission_messages);
43   virtual ~ExpandableContainerView();
44
45   // views::View:
46   virtual void ChildPreferredSizeChanged(views::View* child) OVERRIDE;
47
48   // views::ButtonListener:
49   virtual void ButtonPressed(views::Button* sender,
50                              const ui::Event& event) OVERRIDE;
51
52   // gfx::AnimationDelegate:
53   virtual void AnimationProgressed(const gfx::Animation* animation) OVERRIDE;
54   virtual void AnimationEnded(const gfx::Animation* animation) OVERRIDE;
55
56   // Expand/Collapse the detail section for this ExpandableContainerView.
57   void ToggleDetailLevel();
58
59  private:
60   // A view which displays the permission messages as a bulleted list.
61   class DetailsView : public views::View {
62    public:
63     explicit DetailsView(std::vector<base::string16> messages);
64     virtual ~DetailsView() {}
65
66     // views::View:
67     virtual gfx::Size GetPreferredSize() OVERRIDE;
68
69     // Animates this to be a height proportional to |ratio|.
70     void AnimateToRatio(double ratio);
71
72    private:
73     // The current state of the animation, as a decimal from 0 to 1 (0 is fully
74     // collapsed, 1 is fully expanded).
75     double visible_ratio_;
76
77     DISALLOW_COPY_AND_ASSIGN(DetailsView);
78   };
79
80   // The dialog that owns |this|. It's also an ancestor in the View hierarchy.
81   views::View* owner_;
82
83   // A view for showing |permission_messages|.
84   DetailsView* details_view_;
85
86   gfx::SlideAnimation slide_animation_;
87
88   // The up/down arrow next to the heading (points up/down depending on whether
89   // the details section is expanded).
90   views::ImageButton* arrow_toggle_;
91
92   // Whether the details section is expanded.
93   bool expanded_;
94
95   DISALLOW_COPY_AND_ASSIGN(ExpandableContainerView);
96 };
97
98 ExpandableContainerView::DetailsView::DetailsView(
99     std::vector<base::string16> messages)
100     : visible_ratio_(0) {
101   views::GridLayout* layout = views::GridLayout::CreatePanel(this);
102   SetLayoutManager(layout);
103
104   // Create 2 columns: one for the bullet, one for the bullet text.
105   static const int kColumnSet = 1;
106   views::ColumnSet* column_set = layout->AddColumnSet(kColumnSet);
107   column_set->AddPaddingColumn(0, 10);
108   column_set->AddColumn(views::GridLayout::LEADING,
109                         views::GridLayout::LEADING,
110                         1,
111                         views::GridLayout::USE_PREF,
112                         0,
113                         0);
114   column_set->AddPaddingColumn(0, 5);
115   column_set->AddColumn(views::GridLayout::LEADING,
116                         views::GridLayout::LEADING,
117                         1,
118                         views::GridLayout::USE_PREF,
119                         0,
120                         0);
121
122   // Add permissions to scrollable view.
123   for (std::vector<base::string16>::const_iterator it = messages.begin();
124        it != messages.end();
125        ++it) {
126     views::Label* permission_label = new views::Label(*it);
127
128     permission_label->SetMultiLine(true);
129     permission_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
130
131     layout->StartRow(0, kColumnSet);
132     // Extract only the bullet from the IDS_EXTENSION_PERMISSION_LINE text.
133     layout->AddView(new views::Label(l10n_util::GetStringFUTF16(
134         IDS_EXTENSION_PERMISSION_LINE, base::string16())));
135     // Place the text second, so multi-lined permissions line up below the
136     // bullet.
137     layout->AddView(permission_label);
138
139     layout->AddPaddingRow(0, views::kRelatedControlSmallVerticalSpacing);
140   }
141 }
142
143 gfx::Size ExpandableContainerView::DetailsView::GetPreferredSize() {
144   gfx::Size size = views::View::GetPreferredSize();
145   return gfx::Size(size.width(), size.height() * visible_ratio_);
146 }
147
148 void ExpandableContainerView::DetailsView::AnimateToRatio(double ratio) {
149   visible_ratio_ = ratio;
150   PreferredSizeChanged();
151   SchedulePaint();
152 }
153
154 ExpandableContainerView::ExpandableContainerView(
155     views::View* owner,
156     const base::string16& title,
157     const std::vector<base::string16>& permission_messages)
158     : owner_(owner),
159       details_view_(NULL),
160       slide_animation_(this),
161       arrow_toggle_(NULL),
162       expanded_(false) {
163   views::GridLayout* layout = new views::GridLayout(this);
164   SetLayoutManager(layout);
165   const int kMainColumnSetId = 0;
166   views::ColumnSet* column_set = layout->AddColumnSet(kMainColumnSetId);
167   column_set->AddColumn(views::GridLayout::LEADING,
168                         views::GridLayout::LEADING,
169                         1,
170                         views::GridLayout::USE_PREF,
171                         0,
172                         0);
173
174   // A column set that is split in half, to allow for the expand/collapse button
175   // image to be aligned to the right of the view.
176   const int kSplitColumnSetId = 1;
177   views::ColumnSet* split_column_set = layout->AddColumnSet(kSplitColumnSetId);
178   split_column_set->AddColumn(views::GridLayout::LEADING,
179                               views::GridLayout::LEADING,
180                               1,
181                               views::GridLayout::USE_PREF,
182                               0,
183                               0);
184   split_column_set->AddPaddingColumn(0,
185                                      views::kRelatedControlHorizontalSpacing);
186   split_column_set->AddColumn(views::GridLayout::TRAILING,
187                               views::GridLayout::LEADING,
188                               1,
189                               views::GridLayout::USE_PREF,
190                               0,
191                               0);
192
193   // To display the heading and count next to each other, create a sub-view
194   // with a box layout that stacks them horizontally.
195   views::View* title_view = new views::View();
196   title_view->SetLayoutManager(
197       new views::BoxLayout(views::BoxLayout::kHorizontal,
198                            0,
199                            0,
200                            views::kRelatedControlSmallHorizontalSpacing));
201
202   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
203
204   // Format the title as 'Title - number'. Unfortunately, this needs to be in
205   // separate views because labels only support a single font per view.
206   title_view->AddChildView(
207       new views::Label(title, rb.GetFontList(ui::ResourceBundle::BoldFont)));
208   title_view->AddChildView(
209       new views::Label(base::UTF8ToUTF16("\xe2\x80\x93")));  // En-dash.
210   title_view->AddChildView(
211       new views::Label(base::IntToString16(permission_messages.size())));
212
213   arrow_toggle_ = new views::ImageButton(this);
214   arrow_toggle_->SetImage(views::Button::STATE_NORMAL,
215                           rb.GetImageSkiaNamed(IDR_DOWN_ARROW));
216
217   layout->StartRow(0, kSplitColumnSetId);
218   layout->AddView(title_view);
219   layout->AddView(arrow_toggle_);
220
221   details_view_ = new DetailsView(permission_messages);
222   layout->StartRow(0, kMainColumnSetId);
223   layout->AddView(details_view_);
224 }
225
226 ExpandableContainerView::~ExpandableContainerView() {
227 }
228
229 void ExpandableContainerView::ButtonPressed(views::Button* sender,
230                                             const ui::Event& event) {
231   ToggleDetailLevel();
232 }
233
234 void ExpandableContainerView::AnimationProgressed(
235     const gfx::Animation* animation) {
236   DCHECK_EQ(&slide_animation_, animation);
237   if (details_view_) {
238     details_view_->AnimateToRatio(animation->GetCurrentValue());
239   }
240 }
241
242 void ExpandableContainerView::AnimationEnded(const gfx::Animation* animation) {
243   if (arrow_toggle_) {
244     if (animation->GetCurrentValue() != 0.0) {
245       arrow_toggle_->SetImage(
246           views::Button::STATE_NORMAL,
247           ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
248               IDR_UP_ARROW));
249     } else {
250       arrow_toggle_->SetImage(
251           views::Button::STATE_NORMAL,
252           ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
253               IDR_DOWN_ARROW));
254     }
255   }
256 }
257
258 void ExpandableContainerView::ChildPreferredSizeChanged(views::View* child) {
259   owner_->Layout();
260 }
261
262 void ExpandableContainerView::ToggleDetailLevel() {
263   expanded_ = !expanded_;
264
265   if (slide_animation_.IsShowing())
266     slide_animation_.Hide();
267   else
268     slide_animation_.Show();
269 }
270
271 }  // namespace
272
273 AppInfoPermissionsTab::AppInfoPermissionsTab(
274     gfx::NativeWindow parent_window,
275     Profile* profile,
276     const extensions::Extension* app,
277     const base::Closure& close_callback)
278     : AppInfoTab(parent_window, profile, app, close_callback) {
279   this->SetLayoutManager(new views::FillLayout);
280
281   // Create a scrollview and add it to the tab.
282   views::View* scrollable_content = new views::View();
283   scroll_view_ = new views::ScrollView();
284   scroll_view_->SetContents(scrollable_content);
285   AddChildView(scroll_view_);
286
287   // Give the inner scrollview (the 'scrollable' part) a layout.
288   views::GridLayout* layout =
289       views::GridLayout::CreatePanel(scrollable_content);
290   scrollable_content->SetLayoutManager(layout);
291
292   // Main column that stretches to the width of the view.
293   static const int kMainColumnSetId = 0;
294   views::ColumnSet* main_column_set = layout->AddColumnSet(kMainColumnSetId);
295   main_column_set->AddColumn(
296       views::GridLayout::FILL,
297       views::GridLayout::FILL,
298       1,  // This column resizes to the width of the dialog.
299       views::GridLayout::USE_PREF,
300       0,
301       0);
302
303   const std::vector<base::string16> required_permission_messages =
304       GetRequiredPermissionMessages();
305   const std::vector<base::string16> optional_permission_messages =
306       GetOptionalPermissionMessages();
307   const std::vector<base::string16> retained_file_permission_messages =
308       GetRetainedFilePermissionMessages();
309
310   if (required_permission_messages.empty() &&
311       optional_permission_messages.empty() &&
312       retained_file_permission_messages.empty()) {
313     // If there are no permissions at all, display an appropriate message.
314     views::Label* no_permissions_text = new views::Label(
315         l10n_util::GetStringUTF16(IDS_APPLICATION_INFO_NO_PERMISSIONS_TEXT));
316     no_permissions_text->SetHorizontalAlignment(gfx::ALIGN_LEFT);
317
318     layout->StartRow(0, kMainColumnSetId);
319     layout->AddView(no_permissions_text);
320   } else {
321     if (!required_permission_messages.empty()) {
322       ExpandableContainerView* details_container = new ExpandableContainerView(
323           this,
324           l10n_util::GetStringUTF16(
325               IDS_APPLICATION_INFO_REQUIRED_PERMISSIONS_TEXT),
326           required_permission_messages);
327       // Required permissions are visible by default.
328       details_container->ToggleDetailLevel();
329
330       layout->StartRow(0, kMainColumnSetId);
331       layout->AddView(details_container);
332       layout->AddPaddingRow(0, views::kRelatedControlHorizontalSpacing);
333     }
334
335     if (!optional_permission_messages.empty()) {
336       ExpandableContainerView* details_container = new ExpandableContainerView(
337           this,
338           l10n_util::GetStringUTF16(
339               IDS_APPLICATION_INFO_OPTIONAL_PERMISSIONS_TEXT),
340           optional_permission_messages);
341
342       layout->StartRow(0, kMainColumnSetId);
343       layout->AddView(details_container);
344       layout->AddPaddingRow(0, views::kRelatedControlHorizontalSpacing);
345     }
346
347     if (!retained_file_permission_messages.empty()) {
348       ExpandableContainerView* details_container = new ExpandableContainerView(
349           this,
350           l10n_util::GetStringUTF16(
351               IDS_APPLICATION_INFO_RETAINED_FILE_PERMISSIONS_TEXT),
352           retained_file_permission_messages);
353
354       layout->StartRow(0, kMainColumnSetId);
355       layout->AddView(details_container);
356       layout->AddPaddingRow(0, views::kRelatedControlHorizontalSpacing);
357     }
358   }
359 }
360
361 AppInfoPermissionsTab::~AppInfoPermissionsTab() {
362 }
363
364 void AppInfoPermissionsTab::Layout() {
365   // To avoid 'jumping' issues when the scrollbar becomes visible, size the
366   // scrollable area as though it always has a visible scrollbar.
367   views::View* contents_view = scroll_view_->contents();
368   int content_width = width() - scroll_view_->GetScrollBarWidth();
369   int content_height = contents_view->GetHeightForWidth(content_width);
370   contents_view->SetBounds(0, 0, content_width, content_height);
371   scroll_view_->SetBounds(0, 0, width(), height());
372 }
373
374 const extensions::PermissionSet* AppInfoPermissionsTab::GetRequiredPermissions()
375     const {
376   return extensions::PermissionsData::GetRequiredPermissions(app_);
377 }
378
379 const std::vector<base::string16>
380 AppInfoPermissionsTab::GetRequiredPermissionMessages() const {
381   return extensions::PermissionMessageProvider::Get()->GetWarningMessages(
382       GetRequiredPermissions(), app_->GetType());
383 }
384
385 const extensions::PermissionSet* AppInfoPermissionsTab::GetOptionalPermissions()
386     const {
387   return extensions::PermissionsData::GetOptionalPermissions(app_);
388 }
389
390 const std::vector<base::string16>
391 AppInfoPermissionsTab::GetOptionalPermissionMessages() const {
392   return extensions::PermissionMessageProvider::Get()->GetWarningMessages(
393       GetOptionalPermissions(), app_->GetType());
394 }
395
396 const std::vector<base::FilePath>
397 AppInfoPermissionsTab::GetRetainedFilePermissions() const {
398   std::vector<base::FilePath> retained_file_paths;
399   if (app_->HasAPIPermission(extensions::APIPermission::kFileSystem)) {
400     std::vector<apps::SavedFileEntry> retained_file_entries =
401         apps::SavedFilesService::Get(profile_)->GetAllFileEntries(app_->id());
402     for (std::vector<apps::SavedFileEntry>::const_iterator it =
403              retained_file_entries.begin();
404          it != retained_file_entries.end();
405          ++it) {
406       retained_file_paths.push_back(it->path);
407     }
408   }
409   return retained_file_paths;
410 }
411
412 const std::vector<base::string16>
413 AppInfoPermissionsTab::GetRetainedFilePermissionMessages() const {
414   const std::vector<base::FilePath> permissions = GetRetainedFilePermissions();
415   std::vector<base::string16> file_permission_messages;
416   for (std::vector<base::FilePath>::const_iterator it = permissions.begin();
417        it != permissions.end();
418        ++it) {
419     file_permission_messages.push_back(it->LossyDisplayName());
420   }
421   return file_permission_messages;
422 }