• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 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 "base/logging.h"
6 #include "chrome/browser/ui/view_ids.h"
7 #include "chrome/browser/ui/views/accessible_pane_view.h"
8 #include "chrome/browser/ui/views/frame/browser_view.h"
9 #include "chrome/browser/ui/views/location_bar/location_bar_view.h"
10 #include "ui/base/accessibility/accessible_view_state.h"
11 #include "views/controls/button/menu_button.h"
12 #include "views/controls/native/native_view_host.h"
13 #include "views/focus/focus_search.h"
14 #include "views/focus/view_storage.h"
15 #include "views/widget/tooltip_manager.h"
16 #include "views/widget/widget.h"
17 
AccessiblePaneView()18 AccessiblePaneView::AccessiblePaneView()
19     : pane_has_focus_(false),
20       ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)),
21       focus_manager_(NULL),
22       home_key_(ui::VKEY_HOME, false, false, false),
23       end_key_(ui::VKEY_END, false, false, false),
24       escape_key_(ui::VKEY_ESCAPE, false, false, false),
25       left_key_(ui::VKEY_LEFT, false, false, false),
26       right_key_(ui::VKEY_RIGHT, false, false, false),
27       last_focused_view_storage_id_(-1) {
28   focus_search_.reset(new views::FocusSearch(this, true, true));
29 }
30 
~AccessiblePaneView()31 AccessiblePaneView::~AccessiblePaneView() {
32   if (pane_has_focus_) {
33     focus_manager_->RemoveFocusChangeListener(this);
34   }
35 }
36 
SetPaneFocus(int view_storage_id,views::View * initial_focus)37 bool AccessiblePaneView::SetPaneFocus(int view_storage_id,
38                                       views::View* initial_focus) {
39   if (!IsVisible())
40     return false;
41 
42   // Save the storage id to the last focused view. This would be used to request
43   // focus to the view when the traversal is ended.
44   last_focused_view_storage_id_ = view_storage_id;
45 
46   if (!focus_manager_)
47     focus_manager_ = GetFocusManager();
48 
49   // Use the provided initial focus if it's visible and enabled, otherwise
50   // use the first focusable child.
51   if (!initial_focus ||
52       !Contains(initial_focus) ||
53       !initial_focus->IsVisible() ||
54       !initial_focus->IsEnabled()) {
55     initial_focus = GetFirstFocusableChild();
56   }
57 
58   // Return false if there are no focusable children.
59   if (!initial_focus)
60     return false;
61 
62   // Set focus to the initial view. If it's a location bar, use a special
63   // method that tells it to select all, also.
64   if (initial_focus->GetClassName() == LocationBarView::kViewClassName) {
65     static_cast<LocationBarView*>(initial_focus)->FocusLocation(true);
66   } else {
67     focus_manager_->SetFocusedView(initial_focus);
68   }
69 
70   // If we already have pane focus, we're done.
71   if (pane_has_focus_)
72     return true;
73 
74   // Otherwise, set accelerators and start listening for focus change events.
75   pane_has_focus_ = true;
76   focus_manager_->RegisterAccelerator(home_key_, this);
77   focus_manager_->RegisterAccelerator(end_key_, this);
78   focus_manager_->RegisterAccelerator(escape_key_, this);
79   focus_manager_->RegisterAccelerator(left_key_, this);
80   focus_manager_->RegisterAccelerator(right_key_, this);
81   focus_manager_->AddFocusChangeListener(this);
82 
83   return true;
84 }
85 
SetPaneFocusAndFocusDefault(int view_storage_id)86 bool AccessiblePaneView::SetPaneFocusAndFocusDefault(
87     int view_storage_id) {
88   return SetPaneFocus(view_storage_id, GetDefaultFocusableChild());
89 }
90 
GetDefaultFocusableChild()91 views::View* AccessiblePaneView::GetDefaultFocusableChild() {
92   return NULL;
93 }
94 
RemovePaneFocus()95 void AccessiblePaneView::RemovePaneFocus() {
96   focus_manager_->RemoveFocusChangeListener(this);
97   pane_has_focus_ = false;
98 
99   focus_manager_->UnregisterAccelerator(home_key_, this);
100   focus_manager_->UnregisterAccelerator(end_key_, this);
101   focus_manager_->UnregisterAccelerator(escape_key_, this);
102   focus_manager_->UnregisterAccelerator(left_key_, this);
103   focus_manager_->UnregisterAccelerator(right_key_, this);
104 }
105 
LocationBarSelectAll()106 void AccessiblePaneView::LocationBarSelectAll() {
107   views::View* focused_view = GetFocusManager()->GetFocusedView();
108   if (focused_view &&
109       focused_view->GetClassName() == LocationBarView::kViewClassName) {
110     static_cast<LocationBarView*>(focused_view)->SelectAll();
111   }
112 }
113 
RestoreLastFocusedView()114 void AccessiblePaneView::RestoreLastFocusedView() {
115   views::ViewStorage* view_storage = views::ViewStorage::GetInstance();
116   views::View* last_focused_view =
117       view_storage->RetrieveView(last_focused_view_storage_id_);
118   if (last_focused_view) {
119     focus_manager_->SetFocusedViewWithReason(
120         last_focused_view, views::FocusManager::kReasonFocusRestore);
121   } else {
122     // Focus the location bar
123     views::View* view = GetAncestorWithClassName(BrowserView::kViewClassName);
124     if (view) {
125       BrowserView* browser_view = static_cast<BrowserView*>(view);
126       browser_view->SetFocusToLocationBar(false);
127     }
128   }
129 }
130 
GetFirstFocusableChild()131 views::View* AccessiblePaneView::GetFirstFocusableChild() {
132   FocusTraversable* dummy_focus_traversable;
133   views::View* dummy_focus_traversable_view;
134   return focus_search_->FindNextFocusableView(
135       NULL, false, views::FocusSearch::DOWN, false,
136       &dummy_focus_traversable, &dummy_focus_traversable_view);
137 }
138 
GetLastFocusableChild()139 views::View* AccessiblePaneView::GetLastFocusableChild() {
140   FocusTraversable* dummy_focus_traversable;
141   views::View* dummy_focus_traversable_view;
142   return focus_search_->FindNextFocusableView(
143       this, true, views::FocusSearch::DOWN, false,
144       &dummy_focus_traversable, &dummy_focus_traversable_view);
145 }
146 
147 ////////////////////////////////////////////////////////////////////////////////
148 // View overrides:
149 
GetPaneFocusTraversable()150 views::FocusTraversable* AccessiblePaneView::GetPaneFocusTraversable() {
151   if (pane_has_focus_)
152     return this;
153   else
154     return NULL;
155 }
156 
AcceleratorPressed(const views::Accelerator & accelerator)157 bool AccessiblePaneView::AcceleratorPressed(
158     const views::Accelerator& accelerator) {
159   // Special case: don't handle any accelerators for the location bar,
160   // so that it behaves exactly the same whether you focus it with Ctrl+L
161   // or F6 or Alt+D or Alt+Shift+T.
162   views::View* focused_view = focus_manager_->GetFocusedView();
163   if ((focused_view->GetClassName() == LocationBarView::kViewClassName ||
164        focused_view->GetClassName() == views::NativeViewHost::kViewClassName)) {
165     return false;
166   }
167 
168   switch (accelerator.GetKeyCode()) {
169     case ui::VKEY_ESCAPE:
170       RemovePaneFocus();
171       RestoreLastFocusedView();
172       return true;
173     case ui::VKEY_LEFT:
174       focus_manager_->AdvanceFocus(true);
175       return true;
176     case ui::VKEY_RIGHT:
177       focus_manager_->AdvanceFocus(false);
178       return true;
179     case ui::VKEY_HOME:
180       focus_manager_->SetFocusedViewWithReason(
181           GetFirstFocusableChild(), views::FocusManager::kReasonFocusTraversal);
182       return true;
183     case ui::VKEY_END:
184       focus_manager_->SetFocusedViewWithReason(
185           GetLastFocusableChild(), views::FocusManager::kReasonFocusTraversal);
186       return true;
187     default:
188       return false;
189   }
190 }
191 
SetVisible(bool flag)192 void AccessiblePaneView::SetVisible(bool flag) {
193   if (IsVisible() && !flag && pane_has_focus_) {
194     RemovePaneFocus();
195     RestoreLastFocusedView();
196   }
197   View::SetVisible(flag);
198 }
199 
GetAccessibleState(ui::AccessibleViewState * state)200 void AccessiblePaneView::GetAccessibleState(ui::AccessibleViewState* state) {
201   state->role = ui::AccessibilityTypes::ROLE_PANE;
202 }
203 
204 ////////////////////////////////////////////////////////////////////////////////
205 // FocusChangeListener overrides:
206 
FocusWillChange(views::View * focused_before,views::View * focused_now)207 void AccessiblePaneView::FocusWillChange(views::View* focused_before,
208                                          views::View* focused_now) {
209   if (!focused_now)
210     return;
211 
212   views::FocusManager::FocusChangeReason reason =
213       focus_manager_->focus_change_reason();
214 
215   if (focused_now->GetClassName() == LocationBarView::kViewClassName &&
216       reason == views::FocusManager::kReasonFocusTraversal) {
217     // Tabbing to the location bar should select all. Defer so that it happens
218     // after the focus.
219     MessageLoop::current()->PostTask(
220         FROM_HERE, method_factory_.NewRunnableMethod(
221             &AccessiblePaneView::LocationBarSelectAll));
222   }
223 
224   if (!Contains(focused_now) ||
225       reason == views::FocusManager::kReasonDirectFocusChange) {
226     // We should remove pane focus (i.e. make most of the controls
227     // not focusable again) either because the focus is leaving the pane,
228     // or because the focus changed within the pane due to the user
229     // directly focusing to a specific view (e.g., clicking on it).
230     //
231     // Defer rather than calling RemovePaneFocus right away, because we can't
232     // remove |this| as a focus change listener while FocusManager is in the
233     // middle of iterating over the list of listeners.
234     MessageLoop::current()->PostTask(
235         FROM_HERE, method_factory_.NewRunnableMethod(
236             &AccessiblePaneView::RemovePaneFocus));
237   }
238 }
239 
240 ////////////////////////////////////////////////////////////////////////////////
241 // FocusTraversable overrides:
242 
GetFocusSearch()243 views::FocusSearch* AccessiblePaneView::GetFocusSearch() {
244   DCHECK(pane_has_focus_);
245   return focus_search_.get();
246 }
247 
GetFocusTraversableParent()248 views::FocusTraversable* AccessiblePaneView::GetFocusTraversableParent() {
249   DCHECK(pane_has_focus_);
250   return NULL;
251 }
252 
GetFocusTraversableParentView()253 views::View* AccessiblePaneView::GetFocusTraversableParentView() {
254   DCHECK(pane_has_focus_);
255   return NULL;
256 }
257