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