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