Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / views / website_settings / permissions_bubble_view.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/website_settings/permissions_bubble_view.h"
6
7 #include "base/strings/string16.h"
8 #include "chrome/browser/ui/views/website_settings/permission_selector_view.h"
9 #include "chrome/browser/ui/views/website_settings/permission_selector_view_observer.h"
10 #include "chrome/browser/ui/website_settings/permission_bubble_request.h"
11 #include "chrome/grit/generated_resources.h"
12 #include "net/base/net_util.h"
13 #include "ui/accessibility/ax_view_state.h"
14 #include "ui/base/l10n/l10n_util.h"
15 #include "ui/base/models/combobox_model.h"
16 #include "ui/base/resource/resource_bundle.h"
17 #include "ui/gfx/text_constants.h"
18 #include "ui/views/bubble/bubble_delegate.h"
19 #include "ui/views/controls/button/checkbox.h"
20 #include "ui/views/controls/button/label_button.h"
21 #include "ui/views/controls/button/label_button_border.h"
22 #include "ui/views/controls/button/menu_button.h"
23 #include "ui/views/controls/button/menu_button_listener.h"
24 #include "ui/views/controls/combobox/combobox.h"
25 #include "ui/views/controls/combobox/combobox_listener.h"
26 #include "ui/views/controls/label.h"
27 #include "ui/views/controls/menu/menu_runner.h"
28 #include "ui/views/layout/box_layout.h"
29 #include "ui/views/layout/grid_layout.h"
30
31 namespace {
32
33 // Spacing constant for outer margin. This is added to the
34 // bubble margin itself to equalize the margins at 13px.
35 const int kBubbleOuterMargin = 5;
36
37 // Spacing between major items should be 9px.
38 const int kItemMajorSpacing = 9;
39
40 // Button border size, draws inside the spacing distance.
41 const int kButtonBorderSize = 2;
42
43 // (Square) pixel size of icon.
44 const int kIconSize = 18;
45
46 // Number of pixels to indent the permission request labels.
47 const int kPermissionIndentSpacing = 12;
48
49 }  // namespace
50
51 // This class is a MenuButton which is given a PermissionMenuModel. It
52 // shows the current checked item in the menu model, and notifies its listener
53 // about any updates to the state of the selection.
54 // TODO: refactor PermissionMenuButton to work like this and re-use?
55 class PermissionCombobox : public views::MenuButton,
56                            public views::MenuButtonListener {
57  public:
58   // Get notifications when the selection changes.
59   class Listener {
60    public:
61     virtual void PermissionSelectionChanged(int index, bool allowed) = 0;
62   };
63
64   PermissionCombobox(Listener* listener,
65                      int index,
66                      const GURL& url,
67                      ContentSetting setting);
68   virtual ~PermissionCombobox();
69
70   int index() const { return index_; }
71
72   virtual void GetAccessibleState(ui::AXViewState* state) OVERRIDE;
73
74   // MenuButtonListener:
75   virtual void OnMenuButtonClicked(View* source,
76                                    const gfx::Point& point) OVERRIDE;
77
78   // Callback when a permission's setting is changed.
79   void PermissionChanged(const WebsiteSettingsUI::PermissionInfo& permission);
80
81  private:
82   int index_;
83   Listener* listener_;
84   scoped_ptr<PermissionMenuModel> model_;
85   scoped_ptr<views::MenuRunner> menu_runner_;
86 };
87
88 PermissionCombobox::PermissionCombobox(Listener* listener,
89                                        int index,
90                                        const GURL& url,
91                                        ContentSetting setting)
92     : MenuButton(NULL, base::string16(), this, true),
93       index_(index),
94       listener_(listener),
95       model_(new PermissionMenuModel(
96           url,
97           setting,
98           base::Bind(&PermissionCombobox::PermissionChanged,
99                      base::Unretained(this)))) {
100   SetText(model_->GetLabelAt(model_->GetIndexOfCommandId(setting)));
101   SizeToPreferredSize();
102 }
103
104 PermissionCombobox::~PermissionCombobox() {}
105
106 void PermissionCombobox::GetAccessibleState(ui::AXViewState* state) {
107   MenuButton::GetAccessibleState(state);
108   state->value = GetText();
109 }
110
111 void PermissionCombobox::OnMenuButtonClicked(View* source,
112                                              const gfx::Point& point) {
113   menu_runner_.reset(
114       new views::MenuRunner(model_.get(), views::MenuRunner::HAS_MNEMONICS));
115
116   gfx::Point p(point);
117   p.Offset(-source->width(), 0);
118   if (menu_runner_->RunMenuAt(source->GetWidget()->GetTopLevelWidget(),
119                               this,
120                               gfx::Rect(p, gfx::Size()),
121                               views::MENU_ANCHOR_TOPLEFT,
122                               ui::MENU_SOURCE_NONE) ==
123       views::MenuRunner::MENU_DELETED) {
124     return;
125   }
126 }
127
128 void PermissionCombobox::PermissionChanged(
129     const WebsiteSettingsUI::PermissionInfo& permission) {
130   SetText(model_->GetLabelAt(model_->GetIndexOfCommandId(permission.setting)));
131   SizeToPreferredSize();
132
133   listener_->PermissionSelectionChanged(
134       index_, permission.setting == CONTENT_SETTING_ALLOW);
135 }
136
137 // A combobox originating on the Allow button allowing for customization
138 // of permissions.
139 class CustomizeAllowComboboxModel : public ui::ComboboxModel {
140  public:
141   enum Item {
142     INDEX_ALLOW = 0,
143     INDEX_CUSTOMIZE = 1
144   };
145
146   CustomizeAllowComboboxModel() {}
147   virtual ~CustomizeAllowComboboxModel() {}
148
149   virtual int GetItemCount() const OVERRIDE;
150   virtual base::string16 GetItemAt(int index) OVERRIDE;
151   virtual int GetDefaultIndex() const OVERRIDE;
152 };
153
154 int CustomizeAllowComboboxModel::GetItemCount() const {
155   return 2;
156 }
157
158 base::string16 CustomizeAllowComboboxModel::GetItemAt(int index) {
159   if (index == INDEX_ALLOW)
160     return l10n_util::GetStringUTF16(IDS_PERMISSION_ALLOW);
161   else
162     return l10n_util::GetStringUTF16(IDS_PERMISSION_CUSTOMIZE);
163 }
164
165 int CustomizeAllowComboboxModel::GetDefaultIndex() const {
166   return INDEX_ALLOW;
167 }
168
169 ///////////////////////////////////////////////////////////////////////////////
170 // View implementation for the permissions bubble.
171 class PermissionsBubbleDelegateView : public views::BubbleDelegateView,
172                                       public views::ButtonListener,
173                                       public views::ComboboxListener,
174                                       public PermissionCombobox::Listener {
175  public:
176   PermissionsBubbleDelegateView(
177       views::View* anchor,
178       PermissionBubbleViewViews* owner,
179       const std::vector<PermissionBubbleRequest*>& requests,
180       const std::vector<bool>& accept_state,
181       bool customization_mode);
182   virtual ~PermissionsBubbleDelegateView();
183
184   void Close();
185   void SizeToContents();
186
187   // BubbleDelegateView:
188   virtual bool ShouldShowCloseButton() const OVERRIDE;
189   virtual bool ShouldShowWindowTitle() const OVERRIDE;
190   virtual const gfx::FontList& GetTitleFontList() const OVERRIDE;
191   virtual base::string16 GetWindowTitle() const OVERRIDE;
192   virtual void OnWidgetDestroying(views::Widget* widget) OVERRIDE;
193
194   // ButtonListener:
195   virtual void ButtonPressed(views::Button* button,
196                              const ui::Event& event) OVERRIDE;
197
198   // ComboboxListener:
199   virtual void OnPerformAction(views::Combobox* combobox) OVERRIDE;
200
201   // PermissionCombobox::Listener:
202   virtual void PermissionSelectionChanged(int index, bool allowed) OVERRIDE;
203
204  private:
205   PermissionBubbleViewViews* owner_;
206   views::Button* allow_;
207   views::Button* deny_;
208   views::Combobox* allow_combobox_;
209   base::string16 hostname_;
210   scoped_ptr<PermissionMenuModel> menu_button_model_;
211   std::vector<PermissionCombobox*> customize_comboboxes_;
212
213   DISALLOW_COPY_AND_ASSIGN(PermissionsBubbleDelegateView);
214 };
215
216 PermissionsBubbleDelegateView::PermissionsBubbleDelegateView(
217     views::View* anchor,
218     PermissionBubbleViewViews* owner,
219     const std::vector<PermissionBubbleRequest*>& requests,
220     const std::vector<bool>& accept_state,
221     bool customization_mode)
222     : views::BubbleDelegateView(anchor, views::BubbleBorder::TOP_LEFT),
223       owner_(owner),
224       allow_(NULL),
225       deny_(NULL),
226       allow_combobox_(NULL) {
227   DCHECK(!requests.empty());
228
229   RemoveAllChildViews(true);
230   customize_comboboxes_.clear();
231   set_close_on_esc(false);
232   set_close_on_deactivate(false);
233
234   SetLayoutManager(new views::BoxLayout(
235       views::BoxLayout::kVertical, kBubbleOuterMargin, 0, kItemMajorSpacing));
236
237   // TODO(gbillock): support other languages than English.
238   hostname_ = net::FormatUrl(requests[0]->GetRequestingHostname(),
239                              "en",
240                              net::kFormatUrlOmitUsernamePassword |
241                              net::kFormatUrlOmitTrailingSlashOnBareHostname,
242                              net::UnescapeRule::SPACES, NULL, NULL, NULL);
243
244   ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
245   for (size_t index = 0; index < requests.size(); index++) {
246     DCHECK(index < accept_state.size());
247     // The row is laid out containing a leading-aligned label area and a
248     // trailing column which will be filled during customization with a
249     // combobox.
250     views::View* row = new views::View();
251     views::GridLayout* row_layout = new views::GridLayout(row);
252     row->SetLayoutManager(row_layout);
253     views::ColumnSet* columns = row_layout->AddColumnSet(0);
254     columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::FILL,
255                        0, views::GridLayout::USE_PREF, 0, 0);
256     columns->AddColumn(views::GridLayout::TRAILING, views::GridLayout::FILL,
257                        100, views::GridLayout::USE_PREF, 0, 0);
258     row_layout->StartRow(0, 0);
259
260     views::View* label_container = new views::View();
261     label_container->SetLayoutManager(
262         new views::BoxLayout(views::BoxLayout::kHorizontal,
263                              kPermissionIndentSpacing,
264                              0, kBubbleOuterMargin));
265     views::ImageView* icon = new views::ImageView();
266     icon->SetImage(bundle.GetImageSkiaNamed(requests.at(index)->GetIconID()));
267     icon->SetImageSize(gfx::Size(kIconSize, kIconSize));
268     label_container->AddChildView(icon);
269     views::Label* label =
270         new views::Label(requests.at(index)->GetMessageTextFragment());
271     label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
272     label_container->AddChildView(label);
273     row_layout->AddView(label_container);
274
275     if (customization_mode) {
276       PermissionCombobox* combobox = new PermissionCombobox(
277           this,
278           index,
279           requests[index]->GetRequestingHostname(),
280           accept_state[index] ? CONTENT_SETTING_ALLOW : CONTENT_SETTING_BLOCK);
281       row_layout->AddView(combobox);
282       customize_comboboxes_.push_back(combobox);
283     } else {
284       row_layout->AddView(new views::View());
285     }
286
287     AddChildView(row);
288   }
289
290   views::View* button_row = new views::View();
291   views::GridLayout* button_layout = new views::GridLayout(button_row);
292   views::ColumnSet* columns = button_layout->AddColumnSet(0);
293   button_row->SetLayoutManager(button_layout);
294   AddChildView(button_row);
295
296   // Customization case: just an "OK" button
297   if (customization_mode) {
298     columns->AddColumn(views::GridLayout::TRAILING, views::GridLayout::FILL,
299                        100, views::GridLayout::USE_PREF, 0, 0);
300     button_layout->StartRow(0, 0);
301     views::LabelButton* ok_button =
302         new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_OK));
303     ok_button->SetStyle(views::Button::STYLE_BUTTON);
304     button_layout->AddView(ok_button);
305     allow_ = ok_button;
306
307     button_layout->AddPaddingRow(0, kBubbleOuterMargin);
308     return;
309   }
310
311   // No customization: lay out the Deny/Allow buttons.
312
313   columns->AddColumn(views::GridLayout::TRAILING, views::GridLayout::FILL,
314                      100, views::GridLayout::USE_PREF, 0, 0);
315   columns->AddPaddingColumn(0, kItemMajorSpacing - (2*kButtonBorderSize));
316   columns->AddColumn(views::GridLayout::TRAILING, views::GridLayout::FILL,
317                      0, views::GridLayout::USE_PREF, 0, 0);
318   button_layout->StartRow(0, 0);
319
320   // Allow button is a regular button when there's only one option, and a
321   // STYLE_ACTION Combobox when there are more than one option and
322   // customization is an option.
323
324   base::string16 allow_text = l10n_util::GetStringUTF16(IDS_PERMISSION_ALLOW);
325   if (requests.size() == 1) {
326     views::LabelButton* allow_button = new views::LabelButton(this, allow_text);
327     allow_button->SetStyle(views::Button::STYLE_BUTTON);
328     button_layout->AddView(allow_button);
329     allow_ = allow_button;
330   } else {
331     views::Combobox* allow_combobox = new views::Combobox(
332         new CustomizeAllowComboboxModel());
333     allow_combobox->set_listener(this);
334     allow_combobox->SetStyle(views::Combobox::STYLE_ACTION);
335     button_layout->AddView(allow_combobox);
336     allow_combobox_ = allow_combobox;
337   }
338
339   base::string16 deny_text = l10n_util::GetStringUTF16(IDS_PERMISSION_DENY);
340   views::LabelButton* deny_button = new views::LabelButton(this, deny_text);
341   deny_button->SetStyle(views::Button::STYLE_BUTTON);
342   button_layout->AddView(deny_button);
343   deny_ = deny_button;
344
345   button_layout->AddPaddingRow(0, kBubbleOuterMargin);
346 }
347
348 PermissionsBubbleDelegateView::~PermissionsBubbleDelegateView() {
349   if (owner_)
350     owner_->Closing();
351 }
352
353 void PermissionsBubbleDelegateView::Close() {
354   owner_ = NULL;
355   GetWidget()->Close();
356 }
357
358 bool PermissionsBubbleDelegateView::ShouldShowCloseButton() const {
359   return true;
360 }
361
362 bool PermissionsBubbleDelegateView::ShouldShowWindowTitle() const {
363   return true;
364 }
365
366 const gfx::FontList& PermissionsBubbleDelegateView::GetTitleFontList() const {
367   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
368   return rb.GetFontList(ui::ResourceBundle::BaseFont);
369 }
370
371 base::string16 PermissionsBubbleDelegateView::GetWindowTitle() const {
372   return l10n_util::GetStringFUTF16(IDS_PERMISSIONS_BUBBLE_PROMPT,
373                                     hostname_);
374 }
375
376 void PermissionsBubbleDelegateView::SizeToContents() {
377   BubbleDelegateView::SizeToContents();
378 }
379
380 void PermissionsBubbleDelegateView::OnWidgetDestroying(views::Widget* widget) {
381   views::BubbleDelegateView::OnWidgetDestroying(widget);
382   if (owner_) {
383     owner_->Closing();
384     owner_ = NULL;
385   }
386 }
387
388 void PermissionsBubbleDelegateView::ButtonPressed(views::Button* button,
389                                                   const ui::Event& event) {
390   if (!owner_)
391     return;
392
393   if (button == allow_)
394     owner_->Accept();
395   else if (button == deny_)
396     owner_->Deny();
397 }
398
399 void PermissionsBubbleDelegateView::PermissionSelectionChanged(
400     int index, bool allowed) {
401   owner_->Toggle(index, allowed);
402 }
403
404 void PermissionsBubbleDelegateView::OnPerformAction(
405     views::Combobox* combobox) {
406   if (combobox == allow_combobox_) {
407     if (combobox->selected_index() ==
408         CustomizeAllowComboboxModel::INDEX_CUSTOMIZE)
409       owner_->SetCustomizationMode();
410     else if (combobox->selected_index() ==
411              CustomizeAllowComboboxModel::INDEX_ALLOW)
412       owner_->Accept();
413   }
414 }
415
416 //////////////////////////////////////////////////////////////////////////////
417 // PermissionBubbleViewViews
418
419 PermissionBubbleViewViews::PermissionBubbleViewViews(views::View* anchor_view)
420     : anchor_view_(anchor_view),
421       delegate_(NULL),
422       bubble_delegate_(NULL) {}
423
424 PermissionBubbleViewViews::~PermissionBubbleViewViews() {
425   if (delegate_)
426     delegate_->SetView(NULL);
427 }
428
429 void PermissionBubbleViewViews::SetDelegate(Delegate* delegate) {
430   delegate_ = delegate;
431 }
432
433 void PermissionBubbleViewViews::Show(
434     const std::vector<PermissionBubbleRequest*>& requests,
435     const std::vector<bool>& values,
436     bool customization_mode) {
437   if (bubble_delegate_ != NULL)
438     bubble_delegate_->Close();
439
440   bubble_delegate_ =
441       new PermissionsBubbleDelegateView(anchor_view_, this,
442                                         requests, values, customization_mode);
443   views::BubbleDelegateView::CreateBubble(bubble_delegate_)->Show();
444   bubble_delegate_->SizeToContents();
445 }
446
447 bool PermissionBubbleViewViews::CanAcceptRequestUpdate() {
448   return !(bubble_delegate_ && bubble_delegate_->IsMouseHovered());
449 }
450
451 void PermissionBubbleViewViews::Hide() {
452   if (bubble_delegate_) {
453     bubble_delegate_->Close();
454     bubble_delegate_ = NULL;
455   }
456 }
457
458 bool PermissionBubbleViewViews::IsVisible() {
459   return bubble_delegate_ != NULL;
460 }
461
462 void PermissionBubbleViewViews::Closing() {
463   if (bubble_delegate_)
464     bubble_delegate_ = NULL;
465   if (delegate_)
466     delegate_->Closing();
467 }
468
469 void PermissionBubbleViewViews::Toggle(int index, bool value) {
470   if (delegate_)
471     delegate_->ToggleAccept(index, value);
472 }
473
474 void PermissionBubbleViewViews::Accept() {
475   if (delegate_)
476     delegate_->Accept();
477 }
478
479 void PermissionBubbleViewViews::Deny() {
480   if (delegate_)
481     delegate_->Deny();
482 }
483
484 void PermissionBubbleViewViews::SetCustomizationMode() {
485   if (delegate_)
486     delegate_->SetCustomizationMode();
487 }