• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "ui/views/focus/focus_manager.h"
6 
7 #include <algorithm>
8 #include <vector>
9 
10 #include "base/auto_reset.h"
11 #include "base/logging.h"
12 #include "build/build_config.h"
13 #include "ui/base/accelerators/accelerator.h"
14 #include "ui/base/ime/input_method.h"
15 #include "ui/base/ime/text_input_client.h"
16 #include "ui/base/ime/text_input_focus_manager.h"
17 #include "ui/base/ui_base_switches_util.h"
18 #include "ui/events/event.h"
19 #include "ui/events/keycodes/keyboard_codes.h"
20 #include "ui/views/focus/focus_manager_delegate.h"
21 #include "ui/views/focus/focus_search.h"
22 #include "ui/views/focus/view_storage.h"
23 #include "ui/views/focus/widget_focus_manager.h"
24 #include "ui/views/view.h"
25 #include "ui/views/widget/root_view.h"
26 #include "ui/views/widget/widget.h"
27 #include "ui/views/widget/widget_delegate.h"
28 
29 namespace views {
30 
31 namespace {
32 
33 }  // namespace
34 
35 bool FocusManager::shortcut_handling_suspended_ = false;
36 bool FocusManager::arrow_key_traversal_enabled_ = false;
37 
FocusManager(Widget * widget,FocusManagerDelegate * delegate)38 FocusManager::FocusManager(Widget* widget, FocusManagerDelegate* delegate)
39     : widget_(widget),
40       delegate_(delegate),
41       focused_view_(NULL),
42       accelerator_manager_(new ui::AcceleratorManager),
43       focus_change_reason_(kReasonDirectFocusChange),
44       is_changing_focus_(false) {
45   DCHECK(widget_);
46   stored_focused_view_storage_id_ =
47       ViewStorage::GetInstance()->CreateStorageID();
48 }
49 
~FocusManager()50 FocusManager::~FocusManager() {
51 }
52 
OnKeyEvent(const ui::KeyEvent & event)53 bool FocusManager::OnKeyEvent(const ui::KeyEvent& event) {
54   const int key_code = event.key_code();
55 
56   if (event.type() != ui::ET_KEY_PRESSED && event.type() != ui::ET_KEY_RELEASED)
57     return false;
58 
59   if (shortcut_handling_suspended())
60     return true;
61 
62   int modifiers = ui::EF_NONE;
63   if (event.IsShiftDown())
64     modifiers |= ui::EF_SHIFT_DOWN;
65   if (event.IsControlDown())
66     modifiers |= ui::EF_CONTROL_DOWN;
67   if (event.IsAltDown())
68     modifiers |= ui::EF_ALT_DOWN;
69 #if defined(OS_MACOSX) || defined(OS_CHROMEOS)
70   if (event.IsCommandDown())
71     modifiers |= ui::EF_COMMAND_DOWN;
72 #endif
73   ui::Accelerator accelerator(event.key_code(), modifiers);
74   accelerator.set_type(event.type());
75   accelerator.set_is_repeat(event.IsRepeat());
76 
77   if (event.type() == ui::ET_KEY_PRESSED) {
78     // If the focused view wants to process the key event as is, let it be.
79     if (focused_view_ && focused_view_->SkipDefaultKeyEventProcessing(event) &&
80         !accelerator_manager_->HasPriorityHandler(accelerator))
81       return true;
82 
83     // Intercept Tab related messages for focus traversal.
84     // Note that we don't do focus traversal if the root window is not part of
85     // the active window hierarchy as this would mean we have no focused view
86     // and would focus the first focusable view.
87     if (IsTabTraversalKeyEvent(event)) {
88       AdvanceFocus(event.IsShiftDown());
89       return false;
90     }
91 
92     if (arrow_key_traversal_enabled_ && ProcessArrowKeyTraversal(event))
93       return false;
94 
95     // Intercept arrow key messages to switch between grouped views.
96     if (focused_view_ && focused_view_->GetGroup() != -1 &&
97         (key_code == ui::VKEY_UP || key_code == ui::VKEY_DOWN ||
98          key_code == ui::VKEY_LEFT || key_code == ui::VKEY_RIGHT)) {
99       bool next = (key_code == ui::VKEY_RIGHT || key_code == ui::VKEY_DOWN);
100       View::Views views;
101       focused_view_->parent()->GetViewsInGroup(focused_view_->GetGroup(),
102                                                &views);
103       View::Views::const_iterator i(
104           std::find(views.begin(), views.end(), focused_view_));
105       DCHECK(i != views.end());
106       int index = static_cast<int>(i - views.begin());
107       index += next ? 1 : -1;
108       if (index < 0) {
109         index = static_cast<int>(views.size()) - 1;
110       } else if (index >= static_cast<int>(views.size())) {
111         index = 0;
112       }
113       SetFocusedViewWithReason(views[index], kReasonFocusTraversal);
114       return false;
115     }
116   }
117 
118   // Process keyboard accelerators.
119   // If the key combination matches an accelerator, the accelerator is
120   // triggered, otherwise the key event is processed as usual.
121   if (ProcessAccelerator(accelerator)) {
122     // If a shortcut was activated for this keydown message, do not propagate
123     // the event further.
124     return false;
125   }
126   return true;
127 }
128 
ValidateFocusedView()129 void FocusManager::ValidateFocusedView() {
130   if (focused_view_ && !ContainsView(focused_view_))
131     ClearFocus();
132 }
133 
134 // Tests whether a view is valid, whether it still belongs to the window
135 // hierarchy of the FocusManager.
ContainsView(View * view)136 bool FocusManager::ContainsView(View* view) {
137   Widget* widget = view->GetWidget();
138   return widget ? widget->GetFocusManager() == this : false;
139 }
140 
AdvanceFocus(bool reverse)141 void FocusManager::AdvanceFocus(bool reverse) {
142   View* v = GetNextFocusableView(focused_view_, NULL, reverse, false);
143   // Note: Do not skip this next block when v == focused_view_.  If the user
144   // tabs past the last focusable element in a webpage, we'll get here, and if
145   // the TabContentsContainerView is the only focusable view (possible in
146   // fullscreen mode), we need to run this block in order to cycle around to the
147   // first element on the page.
148   if (v) {
149     views::View* focused_view = focused_view_;
150     v->AboutToRequestFocusFromTabTraversal(reverse);
151     // AboutToRequestFocusFromTabTraversal() may have changed focus. If it did,
152     // don't change focus again.
153     if (focused_view == focused_view_)
154       SetFocusedViewWithReason(v, kReasonFocusTraversal);
155   }
156 }
157 
ClearNativeFocus()158 void FocusManager::ClearNativeFocus() {
159   // Keep the top root window focused so we get keyboard events.
160   widget_->ClearNativeFocus();
161 }
162 
RotatePaneFocus(Direction direction,FocusCycleWrappingBehavior wrap)163 bool FocusManager::RotatePaneFocus(Direction direction,
164                                    FocusCycleWrappingBehavior wrap) {
165   // Get the list of all accessible panes.
166   std::vector<View*> panes;
167   widget_->widget_delegate()->GetAccessiblePanes(&panes);
168 
169   // Count the number of panes and set the default index if no pane
170   // is initially focused.
171   int count = static_cast<int>(panes.size());
172   if (count == 0)
173     return false;
174 
175   // Initialize |index| to an appropriate starting index if nothing is
176   // focused initially.
177   int index = direction == kBackward ? 0 : count - 1;
178 
179   // Check to see if a pane already has focus and update the index accordingly.
180   const views::View* focused_view = GetFocusedView();
181   if (focused_view) {
182     for (int i = 0; i < count; i++) {
183       if (panes[i] && panes[i]->Contains(focused_view)) {
184         index = i;
185         break;
186       }
187     }
188   }
189 
190   // Rotate focus.
191   int start_index = index;
192   for (;;) {
193     if (direction == kBackward)
194       index--;
195     else
196       index++;
197 
198     if (wrap == kNoWrap && (index >= count || index < 0))
199       return false;
200     index = (index + count) % count;
201 
202     // Ensure that we don't loop more than once.
203     if (index == start_index)
204       break;
205 
206     views::View* pane = panes[index];
207     DCHECK(pane);
208 
209     if (!pane->visible())
210       continue;
211 
212     pane->RequestFocus();
213     focused_view = GetFocusedView();
214     if (pane == focused_view || pane->Contains(focused_view))
215       return true;
216   }
217 
218   return false;
219 }
220 
GetNextFocusableView(View * original_starting_view,Widget * starting_widget,bool reverse,bool dont_loop)221 View* FocusManager::GetNextFocusableView(View* original_starting_view,
222                                          Widget* starting_widget,
223                                          bool reverse,
224                                          bool dont_loop) {
225   FocusTraversable* focus_traversable = NULL;
226 
227   // Let's revalidate the focused view.
228   ValidateFocusedView();
229 
230   View* starting_view = NULL;
231   if (original_starting_view) {
232     // Search up the containment hierarchy to see if a view is acting as
233     // a pane, and wants to implement its own focus traversable to keep
234     // the focus trapped within that pane.
235     View* pane_search = original_starting_view;
236     while (pane_search) {
237       focus_traversable = pane_search->GetPaneFocusTraversable();
238       if (focus_traversable) {
239         starting_view = original_starting_view;
240         break;
241       }
242       pane_search = pane_search->parent();
243     }
244 
245     if (!focus_traversable) {
246       if (!reverse) {
247         // If the starting view has a focus traversable, use it.
248         // This is the case with NativeWidgetWins for example.
249         focus_traversable = original_starting_view->GetFocusTraversable();
250 
251         // Otherwise default to the root view.
252         if (!focus_traversable) {
253           focus_traversable =
254               original_starting_view->GetWidget()->GetFocusTraversable();
255           starting_view = original_starting_view;
256         }
257       } else {
258         // When you are going back, starting view's FocusTraversable
259         // should not be used.
260         focus_traversable =
261             original_starting_view->GetWidget()->GetFocusTraversable();
262         starting_view = original_starting_view;
263       }
264     }
265   } else {
266     Widget* widget = starting_widget ? starting_widget : widget_;
267     focus_traversable = widget->GetFocusTraversable();
268   }
269 
270   // Traverse the FocusTraversable tree down to find the focusable view.
271   View* v = FindFocusableView(focus_traversable, starting_view, reverse);
272   if (v) {
273     return v;
274   } else {
275     // Let's go up in the FocusTraversable tree.
276     FocusTraversable* parent_focus_traversable =
277         focus_traversable->GetFocusTraversableParent();
278     starting_view = focus_traversable->GetFocusTraversableParentView();
279     while (parent_focus_traversable) {
280       FocusTraversable* new_focus_traversable = NULL;
281       View* new_starting_view = NULL;
282       // When we are going backward, the parent view might gain the next focus.
283       bool check_starting_view = reverse;
284       v = parent_focus_traversable->GetFocusSearch()->FindNextFocusableView(
285           starting_view, reverse, FocusSearch::UP,
286           check_starting_view, &new_focus_traversable, &new_starting_view);
287 
288       if (new_focus_traversable) {
289         DCHECK(!v);
290 
291         // There is a FocusTraversable, traverse it down.
292         v = FindFocusableView(new_focus_traversable, NULL, reverse);
293       }
294 
295       if (v)
296         return v;
297 
298       starting_view = focus_traversable->GetFocusTraversableParentView();
299       parent_focus_traversable =
300           parent_focus_traversable->GetFocusTraversableParent();
301     }
302 
303     // If we get here, we have reached the end of the focus hierarchy, let's
304     // loop. Make sure there was at least a view to start with, to prevent
305     // infinitely looping in empty windows.
306     if (!dont_loop && original_starting_view) {
307       // Easy, just clear the selection and press tab again.
308       // By calling with NULL as the starting view, we'll start from either
309       // the starting views widget or |widget_|.
310       Widget* widget = original_starting_view->GetWidget();
311       if (widget->widget_delegate()->ShouldAdvanceFocusToTopLevelWidget())
312         widget = widget_;
313       return GetNextFocusableView(NULL, widget, reverse, true);
314     }
315   }
316   return NULL;
317 }
318 
SetFocusedViewWithReason(View * view,FocusChangeReason reason)319 void FocusManager::SetFocusedViewWithReason(
320     View* view, FocusChangeReason reason) {
321   if (focused_view_ == view) {
322     // In the case that the widget lost the focus and gained it back without
323     // changing the focused view, we have to make the text input client focused.
324     // TODO(yukishiino): Remove this hack once we fix http://crbug.com/383236
325     FocusTextInputClient(focused_view_);
326     return;
327   }
328 
329   base::AutoReset<bool> auto_changing_focus(&is_changing_focus_, true);
330   // Update the reason for the focus change (since this is checked by
331   // some listeners), then notify all listeners.
332   focus_change_reason_ = reason;
333   FOR_EACH_OBSERVER(FocusChangeListener, focus_change_listeners_,
334                     OnWillChangeFocus(focused_view_, view));
335 
336   View* old_focused_view = focused_view_;
337   focused_view_ = view;
338   if (old_focused_view) {
339     old_focused_view->Blur();
340     BlurTextInputClient(old_focused_view);
341   }
342   // Also make |focused_view_| the stored focus view. This way the stored focus
343   // view is remembered if focus changes are requested prior to a show or while
344   // hidden.
345   SetStoredFocusView(focused_view_);
346   if (focused_view_) {
347     FocusTextInputClient(focused_view_);
348     focused_view_->Focus();
349   }
350 
351   FOR_EACH_OBSERVER(FocusChangeListener, focus_change_listeners_,
352                     OnDidChangeFocus(old_focused_view, focused_view_));
353 }
354 
ClearFocus()355 void FocusManager::ClearFocus() {
356   // SetFocusedView(NULL) is going to clear out the stored view to. We need to
357   // persist it in this case.
358   views::View* focused_view = GetStoredFocusView();
359   SetFocusedView(NULL);
360   ClearNativeFocus();
361   SetStoredFocusView(focused_view);
362 }
363 
AdvanceFocusIfNecessary()364 void FocusManager::AdvanceFocusIfNecessary() {
365   // If widget is inactive, there is no focused view to check. The stored view
366   // will also be checked for focusability when it is being restored.
367   if (!widget_->IsActive())
368     return;
369 
370   // If widget is active and focused view is not focusable, advance focus or,
371   // if not possible, clear focus.
372   if (focused_view_ && !focused_view_->IsAccessibilityFocusable()) {
373     AdvanceFocus(false);
374     if (focused_view_ && !focused_view_->IsAccessibilityFocusable())
375       ClearFocus();
376   }
377 }
378 
StoreFocusedView(bool clear_native_focus)379 void FocusManager::StoreFocusedView(bool clear_native_focus) {
380   View* focused_view = focused_view_;
381   // Don't do anything if no focused view. Storing the view (which is NULL), in
382   // this case, would clobber the view that was previously saved.
383   if (!focused_view_)
384     return;
385 
386   View* v = focused_view_;
387 
388   if (clear_native_focus) {
389     // Temporarily disable notification.  ClearFocus() will set the focus to the
390     // main browser window.  This extra focus bounce which happens during
391     // deactivation can confuse registered WidgetFocusListeners, as the focus
392     // is not changing due to a user-initiated event.
393     AutoNativeNotificationDisabler local_notification_disabler;
394     // ClearFocus() also stores the focused view.
395     ClearFocus();
396   } else {
397     SetFocusedView(NULL);
398     SetStoredFocusView(focused_view);
399   }
400 
401   if (v)
402     v->SchedulePaint();  // Remove focus border.
403 }
404 
RestoreFocusedView()405 bool FocusManager::RestoreFocusedView() {
406   View* view = GetStoredFocusView();
407   if (view) {
408     if (ContainsView(view)) {
409       if (!view->IsFocusable() && view->IsAccessibilityFocusable()) {
410         // RequestFocus would fail, but we want to restore focus to controls
411         // that had focus in accessibility mode.
412         SetFocusedViewWithReason(view, kReasonFocusRestore);
413       } else {
414         // This usually just sets the focus if this view is focusable, but
415         // let the view override RequestFocus if necessary.
416         view->RequestFocus();
417 
418         // If it succeeded, the reason would be incorrect; set it to
419         // focus restore.
420         if (focused_view_ == view)
421           focus_change_reason_ = kReasonFocusRestore;
422       }
423     }
424     return true;
425   }
426   return false;
427 }
428 
SetStoredFocusView(View * focus_view)429 void FocusManager::SetStoredFocusView(View* focus_view) {
430   ViewStorage* view_storage = ViewStorage::GetInstance();
431   if (!view_storage) {
432     // This should never happen but bug 981648 seems to indicate it could.
433     NOTREACHED();
434     return;
435   }
436 
437   // TODO(jcivelli): when a TabContents containing a popup is closed, the focus
438   // is stored twice causing an assert. We should find a better alternative than
439   // removing the view from the storage explicitly.
440   view_storage->RemoveView(stored_focused_view_storage_id_);
441 
442   if (!focus_view)
443     return;
444 
445   view_storage->StoreView(stored_focused_view_storage_id_, focus_view);
446 }
447 
GetStoredFocusView()448 View* FocusManager::GetStoredFocusView() {
449   ViewStorage* view_storage = ViewStorage::GetInstance();
450   if (!view_storage) {
451     // This should never happen but bug 981648 seems to indicate it could.
452     NOTREACHED();
453     return NULL;
454   }
455 
456   return view_storage->RetrieveView(stored_focused_view_storage_id_);
457 }
458 
ClearStoredFocusedView()459 void FocusManager::ClearStoredFocusedView() {
460   SetStoredFocusView(NULL);
461 }
462 
OnTextInputClientChanged(View * view)463 void FocusManager::OnTextInputClientChanged(View* view) {
464   if (view == focused_view_)
465     FocusTextInputClient(view);
466 }
467 
FocusTextInputClient(View * view)468 void FocusManager::FocusTextInputClient(View* view) {
469   if (!switches::IsTextInputFocusManagerEnabled())
470     return;
471 
472   // If the widget is not active, do not steal the text input focus.
473   if (!widget_->IsActive())
474     return;
475 
476   ui::TextInputClient* text_input_client =
477       view ? view->GetTextInputClient() : NULL;
478   ui::TextInputFocusManager::GetInstance()->
479       FocusTextInputClient(text_input_client);
480   ui::InputMethod* input_method = widget_->GetHostInputMethod();
481   if (input_method) {
482     input_method->OnTextInputTypeChanged(text_input_client);
483     input_method->OnCaretBoundsChanged(text_input_client);
484   }
485 }
486 
BlurTextInputClient(View * view)487 void FocusManager::BlurTextInputClient(View* view) {
488   if (!switches::IsTextInputFocusManagerEnabled())
489     return;
490 
491   ui::TextInputClient* text_input_client =
492       view ? view->GetTextInputClient() : NULL;
493   if (text_input_client && text_input_client->HasCompositionText()) {
494     text_input_client->ConfirmCompositionText();
495     ui::InputMethod* input_method = widget_->GetHostInputMethod();
496     if (input_method && input_method->GetTextInputClient() == text_input_client)
497       input_method->CancelComposition(text_input_client);
498   }
499   ui::TextInputFocusManager::GetInstance()->
500       BlurTextInputClient(text_input_client);
501 }
502 
503 // Find the next (previous if reverse is true) focusable view for the specified
504 // FocusTraversable, starting at the specified view, traversing down the
505 // FocusTraversable hierarchy.
FindFocusableView(FocusTraversable * focus_traversable,View * starting_view,bool reverse)506 View* FocusManager::FindFocusableView(FocusTraversable* focus_traversable,
507                                       View* starting_view,
508                                       bool reverse) {
509   FocusTraversable* new_focus_traversable = NULL;
510   View* new_starting_view = NULL;
511   View* v = focus_traversable->GetFocusSearch()->FindNextFocusableView(
512       starting_view,
513       reverse,
514       FocusSearch::DOWN,
515       false,
516       &new_focus_traversable,
517       &new_starting_view);
518 
519   // Let's go down the FocusTraversable tree as much as we can.
520   while (new_focus_traversable) {
521     DCHECK(!v);
522     focus_traversable = new_focus_traversable;
523     new_focus_traversable = NULL;
524     starting_view = NULL;
525     v = focus_traversable->GetFocusSearch()->FindNextFocusableView(
526         starting_view,
527         reverse,
528         FocusSearch::DOWN,
529         false,
530         &new_focus_traversable,
531         &new_starting_view);
532   }
533   return v;
534 }
535 
RegisterAccelerator(const ui::Accelerator & accelerator,ui::AcceleratorManager::HandlerPriority priority,ui::AcceleratorTarget * target)536 void FocusManager::RegisterAccelerator(
537     const ui::Accelerator& accelerator,
538     ui::AcceleratorManager::HandlerPriority priority,
539     ui::AcceleratorTarget* target) {
540   accelerator_manager_->Register(accelerator, priority, target);
541 }
542 
UnregisterAccelerator(const ui::Accelerator & accelerator,ui::AcceleratorTarget * target)543 void FocusManager::UnregisterAccelerator(const ui::Accelerator& accelerator,
544                                          ui::AcceleratorTarget* target) {
545   accelerator_manager_->Unregister(accelerator, target);
546 }
547 
UnregisterAccelerators(ui::AcceleratorTarget * target)548 void FocusManager::UnregisterAccelerators(ui::AcceleratorTarget* target) {
549   accelerator_manager_->UnregisterAll(target);
550 }
551 
ProcessAccelerator(const ui::Accelerator & accelerator)552 bool FocusManager::ProcessAccelerator(const ui::Accelerator& accelerator) {
553   if (accelerator_manager_->Process(accelerator))
554     return true;
555   if (delegate_.get())
556     return delegate_->ProcessAccelerator(accelerator);
557   return false;
558 }
559 
GetCurrentTargetForAccelerator(const ui::Accelerator & accelerator) const560 ui::AcceleratorTarget* FocusManager::GetCurrentTargetForAccelerator(
561     const ui::Accelerator& accelerator) const {
562   ui::AcceleratorTarget* target =
563       accelerator_manager_->GetCurrentTarget(accelerator);
564   if (!target && delegate_.get())
565     target = delegate_->GetCurrentTargetForAccelerator(accelerator);
566   return target;
567 }
568 
HasPriorityHandler(const ui::Accelerator & accelerator) const569 bool FocusManager::HasPriorityHandler(
570     const ui::Accelerator& accelerator) const {
571   return accelerator_manager_->HasPriorityHandler(accelerator);
572 }
573 
574 // static
IsTabTraversalKeyEvent(const ui::KeyEvent & key_event)575 bool FocusManager::IsTabTraversalKeyEvent(const ui::KeyEvent& key_event) {
576   return key_event.key_code() == ui::VKEY_TAB && !key_event.IsControlDown();
577 }
578 
ViewRemoved(View * removed)579 void FocusManager::ViewRemoved(View* removed) {
580   // If the view being removed contains (or is) the focused view,
581   // clear the focus.  However, it's not safe to call ClearFocus()
582   // (and in turn ClearNativeFocus()) here because ViewRemoved() can
583   // be called while the top level widget is being destroyed.
584   if (focused_view_ && removed->Contains(focused_view_))
585     SetFocusedView(NULL);
586 }
587 
AddFocusChangeListener(FocusChangeListener * listener)588 void FocusManager::AddFocusChangeListener(FocusChangeListener* listener) {
589   focus_change_listeners_.AddObserver(listener);
590 }
591 
RemoveFocusChangeListener(FocusChangeListener * listener)592 void FocusManager::RemoveFocusChangeListener(FocusChangeListener* listener) {
593   focus_change_listeners_.RemoveObserver(listener);
594 }
595 
ProcessArrowKeyTraversal(const ui::KeyEvent & event)596 bool FocusManager::ProcessArrowKeyTraversal(const ui::KeyEvent& event) {
597   if (event.IsShiftDown() || event.IsControlDown() || event.IsAltDown())
598     return false;
599 
600   const int key_code = event.key_code();
601   if (key_code == ui::VKEY_LEFT || key_code == ui::VKEY_UP) {
602     AdvanceFocus(true);
603     return true;
604   }
605   if (key_code == ui::VKEY_RIGHT || key_code == ui::VKEY_DOWN) {
606     AdvanceFocus(false);
607     return true;
608   }
609 
610   return false;
611 }
612 
613 }  // namespace views
614