Allows webview to access extension resources.
[chromium-blink-merge.git] / ash / system / tray_accessibility.cc
blob143c94e28360a4057780498b8c1ed1f7014cce69
1 // Copyright (c) 2012 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 "ash/system/tray_accessibility.h"
7 #include "ash/accessibility_delegate.h"
8 #include "ash/metrics/user_metrics_recorder.h"
9 #include "ash/session/session_state_delegate.h"
10 #include "ash/shell.h"
11 #include "ash/system/tray/hover_highlight_view.h"
12 #include "ash/system/tray/system_tray.h"
13 #include "ash/system/tray/system_tray_delegate.h"
14 #include "ash/system/tray/system_tray_notifier.h"
15 #include "ash/system/tray/tray_constants.h"
16 #include "ash/system/tray/tray_details_view.h"
17 #include "ash/system/tray/tray_item_more.h"
18 #include "ash/system/tray/tray_popup_label_button.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "grit/ash_resources.h"
21 #include "grit/ash_strings.h"
22 #include "ui/base/l10n/l10n_util.h"
23 #include "ui/base/resource/resource_bundle.h"
24 #include "ui/gfx/image/image.h"
25 #include "ui/views/controls/image_view.h"
26 #include "ui/views/controls/label.h"
27 #include "ui/views/layout/box_layout.h"
28 #include "ui/views/widget/widget.h"
30 namespace ash {
31 namespace {
33 enum AccessibilityState {
34 A11Y_NONE = 0,
35 A11Y_SPOKEN_FEEDBACK = 1 << 0,
36 A11Y_HIGH_CONTRAST = 1 << 1,
37 A11Y_SCREEN_MAGNIFIER = 1 << 2,
38 A11Y_LARGE_CURSOR = 1 << 3,
39 A11Y_AUTOCLICK = 1 << 4,
40 A11Y_VIRTUAL_KEYBOARD = 1 << 5,
41 A11Y_BRAILLE_DISPLAY_CONNECTED = 1 << 6,
44 uint32 GetAccessibilityState() {
45 AccessibilityDelegate* delegate =
46 Shell::GetInstance()->accessibility_delegate();
47 uint32 state = A11Y_NONE;
48 if (delegate->IsSpokenFeedbackEnabled())
49 state |= A11Y_SPOKEN_FEEDBACK;
50 if (delegate->IsHighContrastEnabled())
51 state |= A11Y_HIGH_CONTRAST;
52 if (delegate->IsMagnifierEnabled())
53 state |= A11Y_SCREEN_MAGNIFIER;
54 if (delegate->IsLargeCursorEnabled())
55 state |= A11Y_LARGE_CURSOR;
56 if (delegate->IsAutoclickEnabled())
57 state |= A11Y_AUTOCLICK;
58 if (delegate->IsVirtualKeyboardEnabled())
59 state |= A11Y_VIRTUAL_KEYBOARD;
60 if (delegate->IsBrailleDisplayConnected())
61 state |= A11Y_BRAILLE_DISPLAY_CONNECTED;
62 return state;
65 user::LoginStatus GetCurrentLoginStatus() {
66 return Shell::GetInstance()->system_tray_delegate()->GetUserLoginStatus();
69 } // namespace
71 namespace tray {
73 class DefaultAccessibilityView : public TrayItemMore {
74 public:
75 explicit DefaultAccessibilityView(SystemTrayItem* owner)
76 : TrayItemMore(owner, true) {
77 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
78 SetImage(bundle.GetImageNamed(IDR_AURA_UBER_TRAY_ACCESSIBILITY_DARK).
79 ToImageSkia());
80 base::string16 label = bundle.GetLocalizedString(
81 IDS_ASH_STATUS_TRAY_ACCESSIBILITY);
82 SetLabel(label);
83 SetAccessibleName(label);
84 set_id(test::kAccessibilityTrayItemViewId);
87 virtual ~DefaultAccessibilityView() {
90 private:
91 DISALLOW_COPY_AND_ASSIGN(DefaultAccessibilityView);
94 ////////////////////////////////////////////////////////////////////////////////
95 // ash::tray::AccessibilityPopupView
97 AccessibilityPopupView::AccessibilityPopupView(SystemTrayItem* owner,
98 uint32 enabled_state_bits)
99 : TrayNotificationView(owner, IDR_AURA_UBER_TRAY_ACCESSIBILITY_DARK),
100 label_(CreateLabel(enabled_state_bits)) {
101 InitView(label_);
104 views::Label* AccessibilityPopupView::CreateLabel(uint32 enabled_state_bits) {
105 DCHECK((enabled_state_bits &
106 (A11Y_SPOKEN_FEEDBACK | A11Y_BRAILLE_DISPLAY_CONNECTED)) != 0);
107 base::string16 text;
108 if (enabled_state_bits & A11Y_BRAILLE_DISPLAY_CONNECTED) {
109 text.append(l10n_util::GetStringUTF16(
110 IDS_ASH_STATUS_TRAY_BRAILLE_DISPLAY_CONNECTED_BUBBLE));
112 if (enabled_state_bits & A11Y_SPOKEN_FEEDBACK) {
113 if (!text.empty())
114 text.append(base::ASCIIToUTF16(" "));
115 text.append(l10n_util::GetStringUTF16(
116 IDS_ASH_STATUS_TRAY_SPOKEN_FEEDBACK_ENABLED_BUBBLE));
118 views::Label* label = new views::Label(text);
119 label->SetMultiLine(true);
120 label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
121 return label;
124 ////////////////////////////////////////////////////////////////////////////////
125 // ash::tray::AccessibilityDetailedView
127 AccessibilityDetailedView::AccessibilityDetailedView(
128 SystemTrayItem* owner, user::LoginStatus login) :
129 TrayDetailsView(owner),
130 spoken_feedback_view_(NULL),
131 high_contrast_view_(NULL),
132 screen_magnifier_view_(NULL),
133 large_cursor_view_(NULL),
134 help_view_(NULL),
135 settings_view_(NULL),
136 autoclick_view_(NULL),
137 virtual_keyboard_view_(NULL),
138 spoken_feedback_enabled_(false),
139 high_contrast_enabled_(false),
140 screen_magnifier_enabled_(false),
141 large_cursor_enabled_(false),
142 autoclick_enabled_(false),
143 virtual_keyboard_enabled_(false),
144 login_(login) {
146 Reset();
148 AppendAccessibilityList();
149 AppendHelpEntries();
150 CreateSpecialRow(IDS_ASH_STATUS_TRAY_ACCESSIBILITY_TITLE, this);
152 Layout();
155 void AccessibilityDetailedView::AppendAccessibilityList() {
156 CreateScrollableList();
157 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
159 AccessibilityDelegate* delegate =
160 Shell::GetInstance()->accessibility_delegate();
161 spoken_feedback_enabled_ = delegate->IsSpokenFeedbackEnabled();
162 spoken_feedback_view_ = AddScrollListItem(
163 bundle.GetLocalizedString(
164 IDS_ASH_STATUS_TRAY_ACCESSIBILITY_SPOKEN_FEEDBACK),
165 spoken_feedback_enabled_ ? gfx::Font::BOLD : gfx::Font::NORMAL,
166 spoken_feedback_enabled_);
168 // Large Cursor item is shown only in Login screen.
169 if (login_ == user::LOGGED_IN_NONE) {
170 large_cursor_enabled_ = delegate->IsLargeCursorEnabled();
171 large_cursor_view_ = AddScrollListItem(
172 bundle.GetLocalizedString(
173 IDS_ASH_STATUS_TRAY_ACCESSIBILITY_LARGE_CURSOR),
174 large_cursor_enabled_ ? gfx::Font::BOLD : gfx::Font::NORMAL,
175 large_cursor_enabled_);
178 high_contrast_enabled_ = delegate->IsHighContrastEnabled();
179 high_contrast_view_ = AddScrollListItem(
180 bundle.GetLocalizedString(
181 IDS_ASH_STATUS_TRAY_ACCESSIBILITY_HIGH_CONTRAST_MODE),
182 high_contrast_enabled_ ? gfx::Font::BOLD : gfx::Font::NORMAL,
183 high_contrast_enabled_);
184 screen_magnifier_enabled_ = delegate->IsMagnifierEnabled();
185 screen_magnifier_view_ = AddScrollListItem(
186 bundle.GetLocalizedString(
187 IDS_ASH_STATUS_TRAY_ACCESSIBILITY_SCREEN_MAGNIFIER),
188 screen_magnifier_enabled_ ? gfx::Font::BOLD : gfx::Font::NORMAL,
189 screen_magnifier_enabled_);
191 // Don't show autoclick option at login screen.
192 if (login_ != user::LOGGED_IN_NONE) {
193 autoclick_enabled_ = delegate->IsAutoclickEnabled();
194 autoclick_view_ = AddScrollListItem(
195 bundle.GetLocalizedString(
196 IDS_ASH_STATUS_TRAY_ACCESSIBILITY_AUTOCLICK),
197 autoclick_enabled_ ? gfx::Font::BOLD : gfx::Font::NORMAL,
198 autoclick_enabled_);
201 virtual_keyboard_enabled_ = delegate->IsVirtualKeyboardEnabled();
202 virtual_keyboard_view_ = AddScrollListItem(
203 bundle.GetLocalizedString(
204 IDS_ASH_STATUS_TRAY_ACCESSIBILITY_VIRTUAL_KEYBOARD),
205 virtual_keyboard_enabled_ ? gfx::Font::BOLD : gfx::Font::NORMAL,
206 virtual_keyboard_enabled_);
209 void AccessibilityDetailedView::AppendHelpEntries() {
210 // Currently the help page requires a browser window.
211 // TODO(yoshiki): show this even on login/lock screen. crbug.com/158286
212 bool userAddingRunning = ash::Shell::GetInstance()
213 ->session_state_delegate()
214 ->IsInSecondaryLoginScreen();
216 if (login_ == user::LOGGED_IN_NONE ||
217 login_ == user::LOGGED_IN_LOCKED || userAddingRunning)
218 return;
220 views::View* bottom_row = new View();
221 views::BoxLayout* layout = new
222 views::BoxLayout(views::BoxLayout::kHorizontal,
223 kTrayMenuBottomRowPadding,
224 kTrayMenuBottomRowPadding,
225 kTrayMenuBottomRowPaddingBetweenItems);
226 layout->SetDefaultFlex(1);
227 bottom_row->SetLayoutManager(layout);
229 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
231 TrayPopupLabelButton* help = new TrayPopupLabelButton(
232 this,
233 bundle.GetLocalizedString(
234 IDS_ASH_STATUS_TRAY_ACCESSIBILITY_LEARN_MORE));
235 bottom_row->AddChildView(help);
236 help_view_ = help;
238 TrayPopupLabelButton* settings = new TrayPopupLabelButton(
239 this,
240 bundle.GetLocalizedString(
241 IDS_ASH_STATUS_TRAY_ACCESSIBILITY_SETTINGS));
242 bottom_row->AddChildView(settings);
243 settings_view_ = settings;
245 AddChildView(bottom_row);
248 HoverHighlightView* AccessibilityDetailedView::AddScrollListItem(
249 const base::string16& text,
250 gfx::Font::FontStyle style,
251 bool checked) {
252 HoverHighlightView* container = new HoverHighlightView(this);
253 container->AddCheckableLabel(text, style, checked);
254 scroll_content()->AddChildView(container);
255 return container;
258 void AccessibilityDetailedView::OnViewClicked(views::View* sender) {
259 AccessibilityDelegate* delegate =
260 Shell::GetInstance()->accessibility_delegate();
261 if (sender == footer()->content()) {
262 TransitionToDefaultView();
263 } else if (sender == spoken_feedback_view_) {
264 Shell::GetInstance()->metrics()->RecordUserMetricsAction(
265 delegate->IsSpokenFeedbackEnabled() ?
266 ash::UMA_STATUS_AREA_DISABLE_SPOKEN_FEEDBACK :
267 ash::UMA_STATUS_AREA_ENABLE_SPOKEN_FEEDBACK);
268 delegate->ToggleSpokenFeedback(ash::A11Y_NOTIFICATION_NONE);
269 } else if (sender == high_contrast_view_) {
270 Shell::GetInstance()->metrics()->RecordUserMetricsAction(
271 delegate->IsHighContrastEnabled() ?
272 ash::UMA_STATUS_AREA_DISABLE_HIGH_CONTRAST :
273 ash::UMA_STATUS_AREA_ENABLE_HIGH_CONTRAST);
274 delegate->ToggleHighContrast();
275 } else if (sender == screen_magnifier_view_) {
276 Shell::GetInstance()->metrics()->RecordUserMetricsAction(
277 delegate->IsMagnifierEnabled() ?
278 ash::UMA_STATUS_AREA_DISABLE_MAGNIFIER :
279 ash::UMA_STATUS_AREA_ENABLE_MAGNIFIER);
280 delegate->SetMagnifierEnabled(!delegate->IsMagnifierEnabled());
281 } else if (large_cursor_view_ && sender == large_cursor_view_) {
282 Shell::GetInstance()->metrics()->RecordUserMetricsAction(
283 delegate->IsLargeCursorEnabled() ?
284 ash::UMA_STATUS_AREA_DISABLE_LARGE_CURSOR :
285 ash::UMA_STATUS_AREA_ENABLE_LARGE_CURSOR);
286 delegate->SetLargeCursorEnabled(!delegate->IsLargeCursorEnabled());
287 } else if (autoclick_view_ && sender == autoclick_view_) {
288 Shell::GetInstance()->metrics()->RecordUserMetricsAction(
289 delegate->IsAutoclickEnabled() ?
290 ash::UMA_STATUS_AREA_DISABLE_AUTO_CLICK :
291 ash::UMA_STATUS_AREA_ENABLE_AUTO_CLICK);
292 delegate->SetAutoclickEnabled(!delegate->IsAutoclickEnabled());
293 } else if (virtual_keyboard_view_ && sender == virtual_keyboard_view_) {
294 Shell::GetInstance()->metrics()->RecordUserMetricsAction(
295 delegate->IsVirtualKeyboardEnabled() ?
296 ash::UMA_STATUS_AREA_DISABLE_VIRTUAL_KEYBOARD :
297 ash::UMA_STATUS_AREA_ENABLE_VIRTUAL_KEYBOARD);
298 delegate->SetVirtualKeyboardEnabled(!delegate->IsVirtualKeyboardEnabled());
302 void AccessibilityDetailedView::ButtonPressed(views::Button* sender,
303 const ui::Event& event) {
304 SystemTrayDelegate* tray_delegate =
305 Shell::GetInstance()->system_tray_delegate();
306 if (sender == help_view_)
307 tray_delegate->ShowAccessibilityHelp();
308 else if (sender == settings_view_)
309 tray_delegate->ShowAccessibilitySettings();
312 } // namespace tray
314 ////////////////////////////////////////////////////////////////////////////////
315 // ash::TrayAccessibility
317 TrayAccessibility::TrayAccessibility(SystemTray* system_tray)
318 : TrayImageItem(system_tray, IDR_AURA_UBER_TRAY_ACCESSIBILITY),
319 default_(NULL),
320 detailed_popup_(NULL),
321 detailed_menu_(NULL),
322 request_popup_view_state_(A11Y_NONE),
323 tray_icon_visible_(false),
324 login_(GetCurrentLoginStatus()),
325 previous_accessibility_state_(GetAccessibilityState()),
326 show_a11y_menu_on_lock_screen_(true) {
327 DCHECK(Shell::GetInstance()->delegate());
328 DCHECK(system_tray);
329 Shell::GetInstance()->system_tray_notifier()->AddAccessibilityObserver(this);
332 TrayAccessibility::~TrayAccessibility() {
333 Shell::GetInstance()->system_tray_notifier()->
334 RemoveAccessibilityObserver(this);
337 void TrayAccessibility::SetTrayIconVisible(bool visible) {
338 if (tray_view())
339 tray_view()->SetVisible(visible);
340 tray_icon_visible_ = visible;
343 tray::AccessibilityDetailedView* TrayAccessibility::CreateDetailedMenu() {
344 return new tray::AccessibilityDetailedView(this, login_);
347 bool TrayAccessibility::GetInitialVisibility() {
348 // Shows accessibility icon if any accessibility feature is enabled.
349 // Otherwise, doen't show it.
350 return GetAccessibilityState() != A11Y_NONE;
353 views::View* TrayAccessibility::CreateDefaultView(user::LoginStatus status) {
354 CHECK(default_ == NULL);
356 // Shows accessibility menu if:
357 // - on login screen (not logged in);
358 // - "Enable accessibility menu" on chrome://settings is checked;
359 // - or any of accessibility features is enabled
360 // Otherwise, not shows it.
361 AccessibilityDelegate* delegate =
362 Shell::GetInstance()->accessibility_delegate();
363 if (login_ != user::LOGGED_IN_NONE &&
364 !delegate->ShouldShowAccessibilityMenu() &&
365 // On login screen, keeps the initial visibility of the menu.
366 (status != user::LOGGED_IN_LOCKED || !show_a11y_menu_on_lock_screen_))
367 return NULL;
369 CHECK(default_ == NULL);
370 default_ = new tray::DefaultAccessibilityView(this);
372 return default_;
375 views::View* TrayAccessibility::CreateDetailedView(user::LoginStatus status) {
376 CHECK(detailed_popup_ == NULL);
377 CHECK(detailed_menu_ == NULL);
379 if (request_popup_view_state_) {
380 detailed_popup_ =
381 new tray::AccessibilityPopupView(this, request_popup_view_state_);
382 request_popup_view_state_ = A11Y_NONE;
383 return detailed_popup_;
384 } else {
385 Shell::GetInstance()->metrics()->RecordUserMetricsAction(
386 ash::UMA_STATUS_AREA_DETAILED_ACCESSABILITY);
387 detailed_menu_ = CreateDetailedMenu();
388 return detailed_menu_;
392 void TrayAccessibility::DestroyDefaultView() {
393 default_ = NULL;
396 void TrayAccessibility::DestroyDetailedView() {
397 detailed_popup_ = NULL;
398 detailed_menu_ = NULL;
401 void TrayAccessibility::UpdateAfterLoginStatusChange(user::LoginStatus status) {
402 // Stores the a11y feature status on just entering the lock screen.
403 if (login_ != user::LOGGED_IN_LOCKED && status == user::LOGGED_IN_LOCKED)
404 show_a11y_menu_on_lock_screen_ = (GetAccessibilityState() != A11Y_NONE);
406 login_ = status;
407 SetTrayIconVisible(GetInitialVisibility());
410 void TrayAccessibility::OnAccessibilityModeChanged(
411 AccessibilityNotificationVisibility notify) {
412 SetTrayIconVisible(GetInitialVisibility());
414 uint32 accessibility_state = GetAccessibilityState();
415 // We'll get an extra notification if a braille display is connected when
416 // spoken feedback wasn't already enabled. This is because the braille
417 // connection state is already updated when spoken feedback is enabled so
418 // that the notifications can be consolidated into one. Therefore, we
419 // return early if there's no change in the state that we keep track of.
420 if (accessibility_state == previous_accessibility_state_)
421 return;
422 // Contains bits for spoken feedback and braille display connected currently
423 // being enabled.
424 uint32 being_enabled =
425 (accessibility_state & ~previous_accessibility_state_) &
426 (A11Y_SPOKEN_FEEDBACK | A11Y_BRAILLE_DISPLAY_CONNECTED);
427 if ((notify == ash::A11Y_NOTIFICATION_SHOW) && being_enabled != A11Y_NONE) {
428 // Shows popup if |notify| is true and the spoken feedback is being enabled.
429 request_popup_view_state_ = being_enabled;
430 PopupDetailedView(kTrayPopupAutoCloseDelayForTextInSeconds, false);
431 } else {
432 if (detailed_popup_)
433 detailed_popup_->GetWidget()->Close();
434 if (detailed_menu_)
435 detailed_menu_->GetWidget()->Close();
438 previous_accessibility_state_ = accessibility_state;
441 } // namespace ash