Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(180)

Side by Side Diff: chrome/browser/ui/views/website_settings/permission_prompt_impl.cc

Issue 2748443005: Rename website_settings UI folders to permission_bubble. (Closed)
Patch Set: Rebase. Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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/permission_prompt_impl.h"
6
7 #include <stddef.h>
8
9 #include "base/macros.h"
10 #include "base/memory/ptr_util.h"
11 #include "base/strings/string16.h"
12 #include "chrome/browser/permissions/permission_request.h"
13 #include "chrome/browser/platform_util.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/ui/browser.h"
16 #include "chrome/browser/ui/browser_window.h"
17 #include "chrome/browser/ui/layout_constants.h"
18 #include "chrome/browser/ui/views/exclusive_access_bubble_views.h"
19 #include "chrome/browser/ui/views/harmony/layout_delegate.h"
20 #include "chrome/browser/ui/views/page_info/permission_selector_row.h"
21 #include "chrome/browser/ui/views/page_info/permission_selector_row_observer.h"
22 #include "chrome/grit/generated_resources.h"
23 #include "components/strings/grit/components_strings.h"
24 #include "components/url_formatter/elide_url.h"
25 #include "ui/accessibility/ax_node_data.h"
26 #include "ui/base/l10n/l10n_util.h"
27 #include "ui/base/models/combobox_model.h"
28 #include "ui/base/resource/resource_bundle.h"
29 #include "ui/gfx/color_palette.h"
30 #include "ui/gfx/paint_vector_icon.h"
31 #include "ui/gfx/text_constants.h"
32 #include "ui/views/background.h"
33 #include "ui/views/bubble/bubble_dialog_delegate.h"
34 #include "ui/views/bubble/bubble_frame_view.h"
35 #include "ui/views/controls/button/checkbox.h"
36 #include "ui/views/controls/button/menu_button.h"
37 #include "ui/views/controls/button/menu_button_listener.h"
38 #include "ui/views/controls/combobox/combobox.h"
39 #include "ui/views/controls/combobox/combobox_listener.h"
40 #include "ui/views/controls/label.h"
41 #include "ui/views/controls/menu/menu_runner.h"
42 #include "ui/views/layout/box_layout.h"
43 #include "ui/views/layout/grid_layout.h"
44 #include "ui/views/layout/layout_constants.h"
45
46 namespace {
47
48 // (Square) pixel size of icon.
49 const int kIconSize = 18;
50
51 } // namespace
52
53 // This class is a MenuButton which is given a PermissionMenuModel. It
54 // shows the current checked item in the menu model, and notifies its listener
55 // about any updates to the state of the selection.
56 // TODO(gbillock): refactor PermissionMenuButton to work like this and re-use?
57 class PermissionCombobox : public views::MenuButton,
58 public views::MenuButtonListener {
59 public:
60 // Get notifications when the selection changes.
61 class Listener {
62 public:
63 virtual void PermissionSelectionChanged(int index, bool allowed) = 0;
64 };
65
66 PermissionCombobox(Profile* profile,
67 Listener* listener,
68 int index,
69 const GURL& url,
70 ContentSetting setting);
71 ~PermissionCombobox() override;
72
73 int index() const { return index_; }
74
75 void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
76
77 // MenuButtonListener:
78 void OnMenuButtonClicked(views::MenuButton* source,
79 const gfx::Point& point,
80 const ui::Event* event) override;
81
82 // Callback when a permission's setting is changed.
83 void PermissionChanged(const WebsiteSettingsUI::PermissionInfo& permission);
84
85 private:
86 int index_;
87 Listener* listener_;
88 std::unique_ptr<PermissionMenuModel> model_;
89 std::unique_ptr<views::MenuRunner> menu_runner_;
90 };
91
92 PermissionCombobox::PermissionCombobox(Profile* profile,
93 Listener* listener,
94 int index,
95 const GURL& url,
96 ContentSetting setting)
97 : MenuButton(base::string16(), this, true),
98 index_(index),
99 listener_(listener),
100 model_(new PermissionMenuModel(
101 profile,
102 url,
103 setting,
104 base::Bind(&PermissionCombobox::PermissionChanged,
105 base::Unretained(this)))) {
106 SetText(model_->GetLabelAt(model_->GetIndexOfCommandId(setting)));
107 SizeToPreferredSize();
108 }
109
110 PermissionCombobox::~PermissionCombobox() {}
111
112 void PermissionCombobox::GetAccessibleNodeData(ui::AXNodeData* node_data) {
113 MenuButton::GetAccessibleNodeData(node_data);
114 node_data->SetValue(GetText());
115 }
116
117 void PermissionCombobox::OnMenuButtonClicked(views::MenuButton* source,
118 const gfx::Point& point,
119 const ui::Event* event) {
120 menu_runner_.reset(new views::MenuRunner(
121 model_.get(),
122 views::MenuRunner::HAS_MNEMONICS | views::MenuRunner::ASYNC));
123
124 gfx::Point p(point);
125 p.Offset(-source->width(), 0);
126 menu_runner_->RunMenuAt(source->GetWidget()->GetTopLevelWidget(), this,
127 gfx::Rect(p, gfx::Size()), views::MENU_ANCHOR_TOPLEFT,
128 ui::MENU_SOURCE_NONE);
129 }
130
131 void PermissionCombobox::PermissionChanged(
132 const WebsiteSettingsUI::PermissionInfo& permission) {
133 SetText(model_->GetLabelAt(model_->GetIndexOfCommandId(permission.setting)));
134 SizeToPreferredSize();
135
136 listener_->PermissionSelectionChanged(
137 index_, permission.setting == CONTENT_SETTING_ALLOW);
138 }
139
140 ///////////////////////////////////////////////////////////////////////////////
141 // View implementation for the permissions bubble.
142 class PermissionsBubbleDialogDelegateView
143 : public views::BubbleDialogDelegateView,
144 public PermissionCombobox::Listener {
145 public:
146 PermissionsBubbleDialogDelegateView(
147 PermissionPromptImpl* owner,
148 const std::vector<PermissionRequest*>& requests,
149 const std::vector<bool>& accept_state);
150 ~PermissionsBubbleDialogDelegateView() override;
151
152 void CloseBubble();
153 void SizeToContents();
154
155 // BubbleDialogDelegateView:
156 bool ShouldShowCloseButton() const override;
157 const gfx::FontList& GetTitleFontList() const override;
158 base::string16 GetWindowTitle() const override;
159 void OnWidgetDestroying(views::Widget* widget) override;
160 gfx::Size GetPreferredSize() const override;
161 void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
162 bool Cancel() override;
163 bool Accept() override;
164 bool Close() override;
165 int GetDefaultDialogButton() const override;
166 int GetDialogButtons() const override;
167 base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
168
169 // PermissionCombobox::Listener:
170 void PermissionSelectionChanged(int index, bool allowed) override;
171
172 // Updates the anchor's arrow and view. Also repositions the bubble so it's
173 // displayed in the correct location.
174 void UpdateAnchor(views::View* anchor_view,
175 const gfx::Point& anchor_point,
176 views::BubbleBorder::Arrow anchor_arrow);
177
178 private:
179 PermissionPromptImpl* owner_;
180 bool multiple_requests_;
181 base::string16 display_origin_;
182 std::unique_ptr<PermissionMenuModel> menu_button_model_;
183 std::vector<PermissionCombobox*> customize_comboboxes_;
184 views::Checkbox* persist_checkbox_;
185
186 DISALLOW_COPY_AND_ASSIGN(PermissionsBubbleDialogDelegateView);
187 };
188
189 PermissionsBubbleDialogDelegateView::PermissionsBubbleDialogDelegateView(
190 PermissionPromptImpl* owner,
191 const std::vector<PermissionRequest*>& requests,
192 const std::vector<bool>& accept_state)
193 : owner_(owner),
194 multiple_requests_(requests.size() > 1),
195 persist_checkbox_(nullptr) {
196 DCHECK(!requests.empty());
197
198 set_close_on_deactivate(false);
199
200 LayoutDelegate* layout_delegate = LayoutDelegate::Get();
201 SetLayoutManager(
202 new views::BoxLayout(views::BoxLayout::kVertical, 0, 0,
203 layout_delegate->GetMetric(
204 LayoutDelegate::Metric::
205 RELATED_CONTROL_VERTICAL_SPACING)));
206
207 display_origin_ = url_formatter::FormatUrlForSecurityDisplay(
208 requests[0]->GetOrigin(),
209 url_formatter::SchemeDisplay::OMIT_CRYPTOGRAPHIC);
210
211 bool show_persistence_toggle = true;
212 for (size_t index = 0; index < requests.size(); index++) {
213 DCHECK(index < accept_state.size());
214 // The row is laid out containing a leading-aligned label area and a
215 // trailing column which will be filled if there are multiple permission
216 // requests.
217 views::View* row = new views::View();
218 views::GridLayout* row_layout = new views::GridLayout(row);
219 row->SetLayoutManager(row_layout);
220 views::ColumnSet* columns = row_layout->AddColumnSet(0);
221 columns->AddColumn(views::GridLayout::LEADING, views::GridLayout::FILL,
222 0, views::GridLayout::USE_PREF, 0, 0);
223 columns->AddColumn(views::GridLayout::TRAILING, views::GridLayout::FILL,
224 100, views::GridLayout::USE_PREF, 0, 0);
225 row_layout->StartRow(0, 0);
226
227 views::View* label_container = new views::View();
228 int indent = layout_delegate->GetMetric(
229 LayoutDelegate::Metric::SUBSECTION_HORIZONTAL_INDENT);
230 label_container->SetLayoutManager(new views::BoxLayout(
231 views::BoxLayout::kHorizontal, indent, 0,
232 layout_delegate->GetMetric(
233 LayoutDelegate::Metric::RELATED_LABEL_HORIZONTAL_SPACING)));
234 views::ImageView* icon = new views::ImageView();
235 const gfx::VectorIcon& vector_id = requests[index]->GetIconId();
236 icon->SetImage(
237 gfx::CreateVectorIcon(vector_id, kIconSize, gfx::kChromeIconGrey));
238 icon->SetTooltipText(base::string16()); // Redundant with the text fragment
239 label_container->AddChildView(icon);
240 views::Label* label =
241 new views::Label(requests.at(index)->GetMessageTextFragment());
242 label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
243 label_container->AddChildView(label);
244 row_layout->AddView(label_container);
245
246 // Only show the toggle if every request wants to show it.
247 show_persistence_toggle = show_persistence_toggle &&
248 requests[index]->ShouldShowPersistenceToggle();
249 if (requests.size() > 1) {
250 PermissionCombobox* combobox = new PermissionCombobox(
251 owner->GetProfile(), this, index, requests[index]->GetOrigin(),
252 accept_state[index] ? CONTENT_SETTING_ALLOW : CONTENT_SETTING_BLOCK);
253 row_layout->AddView(combobox);
254 customize_comboboxes_.push_back(combobox);
255 } else {
256 row_layout->AddView(new views::View());
257 }
258
259 AddChildView(row);
260 }
261
262 if (show_persistence_toggle) {
263 persist_checkbox_ = new views::Checkbox(
264 l10n_util::GetStringUTF16(IDS_PERMISSIONS_BUBBLE_PERSIST_TEXT));
265 persist_checkbox_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
266 persist_checkbox_->SetChecked(true);
267 AddChildView(persist_checkbox_);
268 }
269 }
270
271 PermissionsBubbleDialogDelegateView::~PermissionsBubbleDialogDelegateView() {
272 if (owner_)
273 owner_->Closing();
274 }
275
276 void PermissionsBubbleDialogDelegateView::CloseBubble() {
277 owner_ = nullptr;
278 GetWidget()->Close();
279 }
280
281 bool PermissionsBubbleDialogDelegateView::ShouldShowCloseButton() const {
282 return true;
283 }
284
285 const gfx::FontList& PermissionsBubbleDialogDelegateView::GetTitleFontList()
286 const {
287 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
288 return rb.GetFontList(ui::ResourceBundle::BaseFont);
289 }
290
291 base::string16 PermissionsBubbleDialogDelegateView::GetWindowTitle() const {
292 return l10n_util::GetStringFUTF16(IDS_PERMISSIONS_BUBBLE_PROMPT,
293 display_origin_);
294 }
295
296 void PermissionsBubbleDialogDelegateView::SizeToContents() {
297 BubbleDialogDelegateView::SizeToContents();
298 }
299
300 void PermissionsBubbleDialogDelegateView::OnWidgetDestroying(
301 views::Widget* widget) {
302 views::BubbleDialogDelegateView::OnWidgetDestroying(widget);
303 if (owner_) {
304 owner_->Closing();
305 owner_ = nullptr;
306 }
307 }
308
309 gfx::Size PermissionsBubbleDialogDelegateView::GetPreferredSize() const {
310 // TODO(estade): bubbles should default to this width.
311 const int kWidth = 320 - GetInsets().width();
312 return gfx::Size(kWidth, GetHeightForWidth(kWidth));
313 }
314
315 void PermissionsBubbleDialogDelegateView::GetAccessibleNodeData(
316 ui::AXNodeData* node_data) {
317 views::BubbleDialogDelegateView::GetAccessibleNodeData(node_data);
318 node_data->role = ui::AX_ROLE_ALERT_DIALOG;
319 }
320
321 int PermissionsBubbleDialogDelegateView::GetDefaultDialogButton() const {
322 // To prevent permissions being accepted accidentally, and as a security
323 // measure against crbug.com/619429, permission prompts should not be accepted
324 // as the default action.
325 return ui::DIALOG_BUTTON_NONE;
326 }
327
328 int PermissionsBubbleDialogDelegateView::GetDialogButtons() const {
329 int buttons = ui::DIALOG_BUTTON_OK;
330 if (!multiple_requests_)
331 buttons |= ui::DIALOG_BUTTON_CANCEL;
332 return buttons;
333 }
334
335 base::string16 PermissionsBubbleDialogDelegateView::GetDialogButtonLabel(
336 ui::DialogButton button) const {
337 if (button == ui::DIALOG_BUTTON_CANCEL)
338 return l10n_util::GetStringUTF16(IDS_PERMISSION_DENY);
339
340 // The text differs based on whether OK is the only visible button.
341 return l10n_util::GetStringUTF16(GetDialogButtons() == ui::DIALOG_BUTTON_OK
342 ? IDS_OK
343 : IDS_PERMISSION_ALLOW);
344 }
345
346 bool PermissionsBubbleDialogDelegateView::Cancel() {
347 if (owner_) {
348 owner_->TogglePersist(!persist_checkbox_ || persist_checkbox_->checked());
349 owner_->Deny();
350 }
351 return true;
352 }
353
354 bool PermissionsBubbleDialogDelegateView::Accept() {
355 if (owner_) {
356 owner_->TogglePersist(!persist_checkbox_ || persist_checkbox_->checked());
357 owner_->Accept();
358 }
359 return true;
360 }
361
362 bool PermissionsBubbleDialogDelegateView::Close() {
363 // Neither explicit accept nor explicit deny.
364 return true;
365 }
366
367 void PermissionsBubbleDialogDelegateView::PermissionSelectionChanged(
368 int index,
369 bool allowed) {
370 owner_->ToggleAccept(index, allowed);
371 }
372
373 void PermissionsBubbleDialogDelegateView::UpdateAnchor(
374 views::View* anchor_view,
375 const gfx::Point& anchor_point,
376 views::BubbleBorder::Arrow anchor_arrow) {
377 set_arrow(anchor_arrow);
378
379 // Update the border in the bubble: will either add or remove the arrow.
380 views::BubbleFrameView* frame =
381 views::BubbleDialogDelegateView::GetBubbleFrameView();
382 views::BubbleBorder::Arrow adjusted_arrow = anchor_arrow;
383 if (base::i18n::IsRTL())
384 adjusted_arrow = views::BubbleBorder::horizontal_mirror(adjusted_arrow);
385 frame->SetBubbleBorder(std::unique_ptr<views::BubbleBorder>(
386 new views::BubbleBorder(adjusted_arrow, shadow(), color())));
387
388 // Reposition the bubble based on the updated arrow and view.
389 SetAnchorView(anchor_view);
390 // The anchor rect is ignored unless |anchor_view| is nullptr.
391 SetAnchorRect(gfx::Rect(anchor_point, gfx::Size()));
392 }
393
394 //////////////////////////////////////////////////////////////////////////////
395 // PermissionPromptImpl
396
397 PermissionPromptImpl::PermissionPromptImpl(Browser* browser)
398 : browser_(browser),
399 delegate_(nullptr),
400 bubble_delegate_(nullptr) {}
401
402 PermissionPromptImpl::~PermissionPromptImpl() {
403 }
404
405 void PermissionPromptImpl::SetDelegate(Delegate* delegate) {
406 delegate_ = delegate;
407 }
408
409 void PermissionPromptImpl::Show(const std::vector<PermissionRequest*>& requests,
410 const std::vector<bool>& values) {
411 DCHECK(browser_);
412 DCHECK(browser_->window());
413
414 if (bubble_delegate_)
415 bubble_delegate_->CloseBubble();
416
417 bubble_delegate_ =
418 new PermissionsBubbleDialogDelegateView(this, requests, values);
419
420 // Set |parent_window| because some valid anchors can become hidden.
421 bubble_delegate_->set_parent_window(
422 platform_util::GetViewForWindow(browser_->window()->GetNativeWindow()));
423
424 // Compensate for vertical padding in the anchor view's image. Note this is
425 // ignored whenever the anchor view is null.
426 bubble_delegate_->set_anchor_view_insets(gfx::Insets(
427 GetLayoutConstant(LOCATION_BAR_BUBBLE_ANCHOR_VERTICAL_INSET), 0));
428
429 views::BubbleDialogDelegateView::CreateBubble(bubble_delegate_)->Show();
430 bubble_delegate_->SizeToContents();
431
432 bubble_delegate_->UpdateAnchor(GetAnchorView(),
433 GetAnchorPoint(),
434 GetAnchorArrow());
435 }
436
437 bool PermissionPromptImpl::CanAcceptRequestUpdate() {
438 return !(bubble_delegate_ && bubble_delegate_->IsMouseHovered());
439 }
440
441 void PermissionPromptImpl::Hide() {
442 if (bubble_delegate_) {
443 bubble_delegate_->CloseBubble();
444 bubble_delegate_ = nullptr;
445 }
446 }
447
448 bool PermissionPromptImpl::IsVisible() {
449 return bubble_delegate_ != nullptr;
450 }
451
452 void PermissionPromptImpl::UpdateAnchorPosition() {
453 DCHECK(browser_);
454 DCHECK(browser_->window());
455
456 if (IsVisible()) {
457 bubble_delegate_->set_parent_window(
458 platform_util::GetViewForWindow(browser_->window()->GetNativeWindow()));
459 bubble_delegate_->UpdateAnchor(GetAnchorView(),
460 GetAnchorPoint(),
461 GetAnchorArrow());
462 }
463 }
464
465 gfx::NativeWindow PermissionPromptImpl::GetNativeWindow() {
466 if (bubble_delegate_ && bubble_delegate_->GetWidget())
467 return bubble_delegate_->GetWidget()->GetNativeWindow();
468 return nullptr;
469 }
470
471 void PermissionPromptImpl::Closing() {
472 if (bubble_delegate_)
473 bubble_delegate_ = nullptr;
474 if (delegate_)
475 delegate_->Closing();
476 }
477
478 void PermissionPromptImpl::ToggleAccept(int index, bool value) {
479 if (delegate_)
480 delegate_->ToggleAccept(index, value);
481 }
482
483 void PermissionPromptImpl::TogglePersist(bool value) {
484 if (delegate_)
485 delegate_->TogglePersist(value);
486 }
487
488 void PermissionPromptImpl::Accept() {
489 if (delegate_)
490 delegate_->Accept();
491 }
492
493 void PermissionPromptImpl::Deny() {
494 if (delegate_)
495 delegate_->Deny();
496 }
497
498 Profile* PermissionPromptImpl::GetProfile() {
499 return browser_->profile();
500 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698