[Metrics] Make MetricsStateManager take a callback param to check if UMA is enabled.
[chromium-blink-merge.git] / chrome / browser / ui / views / website_settings / permissions_bubble_view.cc
bloba9c064aed27c1e2f1e66a54c44b7c22b0e005341
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.
5 #include "chrome/browser/ui/views/website_settings/permissions_bubble_view.h"
7 #include "base/strings/utf_string_conversions.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 "grit/generated_resources.h"
12 #include "grit/ui_resources.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"
31 namespace {
33 // Spacing constant for outer margin. This is added to the
34 // bubble margin itself to equalize the margins at 20px.
35 const int kBubbleOuterMargin = 12;
37 // Spacing between major items should be 10px.
38 const int kItemMajorSpacing = 10;
40 // Button border size, draws inside the spacing distance.
41 const int kButtonBorderSize = 2;
43 } // namespace
45 // This class is a MenuButton which is given a PermissionMenuModel. It
46 // shows the current checked item in the menu model, and notifies its listener
47 // about any updates to the state of the selection.
48 // TODO: refactor PermissionMenuButton to work like this and re-use?
49 class PermissionCombobox : public views::MenuButton,
50 public views::MenuButtonListener {
51 public:
52 // Get notifications when the selection changes.
53 class Listener {
54 public:
55 virtual void PermissionSelectionChanged(int index, bool allowed) = 0;
58 PermissionCombobox(Listener* listener,
59 int index,
60 const GURL& url,
61 ContentSetting setting);
62 virtual ~PermissionCombobox();
64 int index() const { return index_; }
66 virtual void GetAccessibleState(ui::AXViewState* state) OVERRIDE;
68 // MenuButtonListener:
69 virtual void OnMenuButtonClicked(View* source,
70 const gfx::Point& point) OVERRIDE;
72 // Callback when a permission's setting is changed.
73 void PermissionChanged(const WebsiteSettingsUI::PermissionInfo& permission);
75 private:
76 int index_;
77 Listener* listener_;
78 scoped_ptr<PermissionMenuModel> model_;
79 scoped_ptr<views::MenuRunner> menu_runner_;
82 PermissionCombobox::PermissionCombobox(Listener* listener,
83 int index,
84 const GURL& url,
85 ContentSetting setting)
86 : MenuButton(NULL, base::string16(), this, true),
87 index_(index),
88 listener_(listener),
89 model_(new PermissionMenuModel(
90 url,
91 setting,
92 base::Bind(&PermissionCombobox::PermissionChanged,
93 base::Unretained(this)))) {
94 SetText(model_->GetLabelAt(model_->GetIndexOfCommandId(setting)));
95 SizeToPreferredSize();
98 PermissionCombobox::~PermissionCombobox() {}
100 void PermissionCombobox::GetAccessibleState(ui::AXViewState* state) {
101 MenuButton::GetAccessibleState(state);
102 state->value = text();
105 void PermissionCombobox::OnMenuButtonClicked(View* source,
106 const gfx::Point& point) {
107 menu_runner_.reset(new views::MenuRunner(model_.get()));
109 gfx::Point p(point);
110 p.Offset(-source->width(), 0);
111 if (menu_runner_->RunMenuAt(source->GetWidget()->GetTopLevelWidget(),
112 this,
113 gfx::Rect(p, gfx::Size()),
114 views::MENU_ANCHOR_TOPLEFT,
115 ui::MENU_SOURCE_NONE,
116 views::MenuRunner::HAS_MNEMONICS) ==
117 views::MenuRunner::MENU_DELETED) {
118 return;
122 void PermissionCombobox::PermissionChanged(
123 const WebsiteSettingsUI::PermissionInfo& permission) {
124 SetText(model_->GetLabelAt(model_->GetIndexOfCommandId(permission.setting)));
125 SizeToPreferredSize();
127 listener_->PermissionSelectionChanged(
128 index_, permission.setting == CONTENT_SETTING_ALLOW);
131 // A combobox originating on the Allow button allowing for customization
132 // of permissions.
133 class CustomizeAllowComboboxModel : public ui::ComboboxModel {
134 public:
135 enum Item {
136 INDEX_ALLOW = 0,
137 INDEX_CUSTOMIZE = 1
140 CustomizeAllowComboboxModel() {}
141 virtual ~CustomizeAllowComboboxModel() {}
143 virtual int GetItemCount() const OVERRIDE;
144 virtual base::string16 GetItemAt(int index) OVERRIDE;
145 virtual int GetDefaultIndex() const OVERRIDE;
148 int CustomizeAllowComboboxModel::GetItemCount() const {
149 return 2;
152 base::string16 CustomizeAllowComboboxModel::GetItemAt(int index) {
153 if (index == INDEX_ALLOW)
154 return l10n_util::GetStringUTF16(IDS_PERMISSION_ALLOW);
155 else
156 return l10n_util::GetStringUTF16(IDS_PERMISSION_CUSTOMIZE);
159 int CustomizeAllowComboboxModel::GetDefaultIndex() const {
160 return INDEX_ALLOW;
163 ///////////////////////////////////////////////////////////////////////////////
164 // View implementation for the permissions bubble.
166 class PermissionsBubbleDelegateView : public views::BubbleDelegateView,
167 public views::ButtonListener,
168 public views::ComboboxListener,
169 public PermissionCombobox::Listener {
170 public:
171 PermissionsBubbleDelegateView(
172 views::View* anchor,
173 PermissionBubbleViewViews* owner,
174 const std::vector<PermissionBubbleRequest*>& requests,
175 const std::vector<bool>& accept_state,
176 bool customization_mode);
177 virtual ~PermissionsBubbleDelegateView();
179 void Close();
180 void SizeToContents();
182 // BubbleDelegateView:
183 virtual bool ShouldShowCloseButton() const OVERRIDE;
184 virtual bool ShouldShowWindowTitle() const OVERRIDE;
185 virtual base::string16 GetWindowTitle() const OVERRIDE;
186 virtual void OnWidgetDestroying(views::Widget* widget) OVERRIDE;
188 // ButtonListener:
189 virtual void ButtonPressed(views::Button* button,
190 const ui::Event& event) OVERRIDE;
192 // ComboboxListener:
193 virtual void OnPerformAction(views::Combobox* combobox) OVERRIDE;
195 // PermissionCombobox::Listener:
196 virtual void PermissionSelectionChanged(int index, bool allowed) OVERRIDE;
198 private:
199 PermissionBubbleViewViews* owner_;
200 views::Button* allow_;
201 views::Button* deny_;
202 views::Combobox* allow_combobox_;
203 base::string16 title_;
204 std::string hostname_;
205 scoped_ptr<PermissionMenuModel> menu_button_model_;
206 std::vector<PermissionCombobox*> customize_comboboxes_;
208 DISALLOW_COPY_AND_ASSIGN(PermissionsBubbleDelegateView);
211 PermissionsBubbleDelegateView::PermissionsBubbleDelegateView(
212 views::View* anchor,
213 PermissionBubbleViewViews* owner,
214 const std::vector<PermissionBubbleRequest*>& requests,
215 const std::vector<bool>& accept_state,
216 bool customization_mode)
217 : views::BubbleDelegateView(anchor, views::BubbleBorder::TOP_LEFT),
218 owner_(owner),
219 allow_(NULL),
220 deny_(NULL),
221 allow_combobox_(NULL) {
222 DCHECK(!requests.empty());
224 RemoveAllChildViews(true);
225 customize_comboboxes_.clear();
226 set_close_on_esc(false);
227 set_close_on_deactivate(false);
229 SetLayoutManager(new views::BoxLayout(
230 views::BoxLayout::kVertical, kBubbleOuterMargin, 0, kItemMajorSpacing));
232 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
234 // TODO(gbillock): account for different requests from different hosts.
235 hostname_ = requests[0]->GetRequestingHostname().host();
237 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
238 for (size_t index = 0; index < requests.size(); index++) {
239 DCHECK(index < accept_state.size());
240 // The row is laid out containing a leading-aligned label area and a
241 // trailing column which will be filled during customization with a
242 // combobox.
243 views::View* row = new views::View();
244 views::GridLayout* row_layout = new views::GridLayout(row);
245 row->SetLayoutManager(row_layout);
246 views::ColumnSet* columns = row_layout->AddColumnSet(0);
247 columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::FILL,
248 0, views::GridLayout::USE_PREF, 0, 0);
249 columns->AddColumn(views::GridLayout::TRAILING, views::GridLayout::FILL,
250 100, views::GridLayout::USE_PREF, 0, 0);
251 row_layout->StartRow(0, 0);
253 views::View* label_container = new views::View();
254 label_container->SetLayoutManager(
255 new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 5));
256 views::ImageView* icon = new views::ImageView();
257 icon->SetImage(bundle.GetImageSkiaNamed(requests.at(index)->GetIconID()));
258 label_container->AddChildView(icon);
259 views::Label* label =
260 new views::Label(requests.at(index)->GetMessageTextFragment());
261 label->SetFontList(rb.GetFontList(ui::ResourceBundle::MediumFont));
262 label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
263 label_container->AddChildView(label);
264 row_layout->AddView(label_container);
266 if (customization_mode) {
267 PermissionCombobox* combobox = new PermissionCombobox(
268 this,
269 index,
270 requests[index]->GetRequestingHostname(),
271 accept_state[index] ? CONTENT_SETTING_ALLOW : CONTENT_SETTING_BLOCK);
272 row_layout->AddView(combobox);
273 customize_comboboxes_.push_back(combobox);
274 } else {
275 row_layout->AddView(new views::View());
278 AddChildView(row);
281 views::View* button_row = new views::View();
282 views::GridLayout* button_layout = new views::GridLayout(button_row);
283 views::ColumnSet* columns = button_layout->AddColumnSet(0);
284 button_row->SetLayoutManager(button_layout);
285 AddChildView(button_row);
287 // Customization case: just an "OK" button
288 if (customization_mode) {
289 columns->AddColumn(views::GridLayout::TRAILING, views::GridLayout::FILL,
290 100, views::GridLayout::USE_PREF, 0, 0);
291 button_layout->StartRow(0, 0);
292 views::LabelButton* ok_button =
293 new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_OK));
294 ok_button->SetStyle(views::Button::STYLE_BUTTON);
295 button_layout->AddView(ok_button);
296 allow_ = ok_button;
298 button_layout->AddPaddingRow(0, kBubbleOuterMargin);
299 return;
302 // No customization: lay out the Deny/Allow buttons.
304 columns->AddColumn(views::GridLayout::TRAILING, views::GridLayout::FILL,
305 100, views::GridLayout::USE_PREF, 0, 0);
306 columns->AddPaddingColumn(0, kItemMajorSpacing - (2*kButtonBorderSize));
307 columns->AddColumn(views::GridLayout::TRAILING, views::GridLayout::FILL,
308 0, views::GridLayout::USE_PREF, 0, 0);
309 button_layout->StartRow(0, 0);
311 // Allow button is a regular button when there's only one option, and a
312 // STYLE_ACTION Combobox when there are more than one option and
313 // customization is an option.
315 base::string16 allow_text = l10n_util::GetStringUTF16(IDS_PERMISSION_ALLOW);
316 if (requests.size() == 1) {
317 views::LabelButton* allow_button = new views::LabelButton(this, allow_text);
318 allow_button->SetStyle(views::Button::STYLE_BUTTON);
319 allow_button->SetFontList(rb.GetFontList(ui::ResourceBundle::MediumFont));
320 button_layout->AddView(allow_button);
321 allow_ = allow_button;
322 } else {
323 views::Combobox* allow_combobox = new views::Combobox(
324 new CustomizeAllowComboboxModel());
325 allow_combobox->set_listener(this);
326 allow_combobox->SetStyle(views::Combobox::STYLE_ACTION);
327 button_layout->AddView(allow_combobox);
328 allow_combobox_ = allow_combobox;
331 base::string16 deny_text = l10n_util::GetStringUTF16(IDS_PERMISSION_DENY);
332 views::LabelButton* deny_button = new views::LabelButton(this, deny_text);
333 deny_button->SetStyle(views::Button::STYLE_BUTTON);
334 deny_button->SetFontList(rb.GetFontList(ui::ResourceBundle::MediumFont));
335 button_layout->AddView(deny_button);
336 deny_ = deny_button;
338 button_layout->AddPaddingRow(0, kBubbleOuterMargin);
341 PermissionsBubbleDelegateView::~PermissionsBubbleDelegateView() {
342 if (owner_)
343 owner_->Closing();
346 void PermissionsBubbleDelegateView::Close() {
347 owner_ = NULL;
348 GetWidget()->Close();
351 bool PermissionsBubbleDelegateView::ShouldShowCloseButton() const {
352 return true;
355 bool PermissionsBubbleDelegateView::ShouldShowWindowTitle() const {
356 return true;
359 base::string16 PermissionsBubbleDelegateView::GetWindowTitle() const {
360 if (!title_.empty()) {
361 return title_;
364 return l10n_util::GetStringFUTF16(IDS_PERMISSIONS_BUBBLE_PROMPT,
365 base::UTF8ToUTF16(hostname_));
368 void PermissionsBubbleDelegateView::SizeToContents() {
369 BubbleDelegateView::SizeToContents();
372 void PermissionsBubbleDelegateView::OnWidgetDestroying(views::Widget* widget) {
373 views::BubbleDelegateView::OnWidgetDestroying(widget);
374 if (owner_) {
375 owner_->Closing();
376 owner_ = NULL;
380 void PermissionsBubbleDelegateView::ButtonPressed(views::Button* button,
381 const ui::Event& event) {
382 if (!owner_)
383 return;
385 if (button == allow_)
386 owner_->Accept();
387 else if (button == deny_)
388 owner_->Deny();
391 void PermissionsBubbleDelegateView::PermissionSelectionChanged(
392 int index, bool allowed) {
393 owner_->Toggle(index, allowed);
396 void PermissionsBubbleDelegateView::OnPerformAction(
397 views::Combobox* combobox) {
398 if (combobox == allow_combobox_) {
399 if (combobox->selected_index() ==
400 CustomizeAllowComboboxModel::INDEX_CUSTOMIZE)
401 owner_->SetCustomizationMode();
402 else if (combobox->selected_index() ==
403 CustomizeAllowComboboxModel::INDEX_ALLOW)
404 owner_->Accept();
408 //////////////////////////////////////////////////////////////////////////////
409 // PermissionBubbleViewViews
411 PermissionBubbleViewViews::PermissionBubbleViewViews(views::View* anchor_view)
412 : anchor_view_(anchor_view),
413 delegate_(NULL),
414 bubble_delegate_(NULL) {}
416 PermissionBubbleViewViews::~PermissionBubbleViewViews() {
417 if (delegate_)
418 delegate_->SetView(NULL);
421 void PermissionBubbleViewViews::SetDelegate(Delegate* delegate) {
422 delegate_ = delegate;
425 void PermissionBubbleViewViews::Show(
426 const std::vector<PermissionBubbleRequest*>& requests,
427 const std::vector<bool>& values,
428 bool customization_mode) {
429 if (bubble_delegate_ != NULL)
430 bubble_delegate_->Close();
432 bubble_delegate_ =
433 new PermissionsBubbleDelegateView(anchor_view_, this,
434 requests, values, customization_mode);
435 views::BubbleDelegateView::CreateBubble(bubble_delegate_)->Show();
436 bubble_delegate_->SizeToContents();
439 bool PermissionBubbleViewViews::CanAcceptRequestUpdate() {
440 return !(bubble_delegate_ && bubble_delegate_->IsMouseHovered());
443 void PermissionBubbleViewViews::Hide() {
444 if (bubble_delegate_) {
445 bubble_delegate_->Close();
446 bubble_delegate_ = NULL;
450 void PermissionBubbleViewViews::Closing() {
451 if (bubble_delegate_)
452 bubble_delegate_ = NULL;
453 if (delegate_)
454 delegate_->Closing();
457 void PermissionBubbleViewViews::Toggle(int index, bool value) {
458 if (delegate_)
459 delegate_->ToggleAccept(index, value);
462 void PermissionBubbleViewViews::Accept() {
463 if (delegate_)
464 delegate_->Accept();
467 void PermissionBubbleViewViews::Deny() {
468 if (delegate_)
469 delegate_->Deny();
472 void PermissionBubbleViewViews::SetCustomizationMode() {
473 if (delegate_)
474 delegate_->SetCustomizationMode();