chromium/ui/views/focus/focus_manager_unittest.cc

// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/views/focus/focus_manager.h"

#include <stddef.h>

#include <string>
#include <utility>
#include <vector>

#include "base/command_line.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/icu_test_util.h"
#include "build/build_config.h"
#include "ui/base/accelerators/accelerator.h"
#include "ui/base/accelerators/test_accelerator_target.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/base/mojom/dialog_button.mojom.h"
#include "ui/events/keycodes/keyboard_codes.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/accessible_pane_view.h"
#include "ui/views/bubble/bubble_dialog_delegate_view.h"
#include "ui/views/buildflags.h"
#include "ui/views/controls/focus_ring.h"
#include "ui/views/focus/focus_manager_delegate.h"
#include "ui/views/focus/focus_manager_factory.h"
#include "ui/views/focus/widget_focus_manager.h"
#include "ui/views/layout/flex_layout.h"
#include "ui/views/test/focus_manager_test.h"
#include "ui/views/test/native_widget_factory.h"
#include "ui/views/test/test_platform_native_widget.h"
#include "ui/views/test/test_views.h"
#include "ui/views/test/views_test_utils.h"
#include "ui/views/test/widget_test.h"
#include "ui/views/view_class_properties.h"
#include "ui/views/widget/widget.h"

#if defined(USE_AURA)
#include "ui/aura/client/focus_client.h"
#include "ui/views/widget/native_widget_aura.h"
#endif  // USE_AURA

namespace views {

enum FocusTestEventType {};

struct FocusTestEvent {};

class FocusTestEventList : public base::RefCounted<FocusTestEventList> {};

class SimpleTestView : public View {};

BEGIN_METADATA()

// Tests that the appropriate Focus related methods are called when a View
// gets/loses focus.
TEST_F(FocusManagerTest, ViewFocusCallbacks) {}

TEST_F(FocusManagerTest, FocusChangeListener) {}

TEST_F(FocusManagerTest, WidgetFocusChangeListener) {}

TEST_F(FocusManagerTest, CallsNormalAcceleratorTarget) {}

TEST_F(FocusManagerTest, HighPriorityHandlers) {}

TEST_F(FocusManagerTest, CallsEnabledAcceleratorTargetsOnly) {}

// Unregisters itself when its accelerator is invoked.
class SelfUnregisteringAcceleratorTarget : public ui::TestAcceleratorTarget {};

TEST_F(FocusManagerTest, CallsSelfDeletingAcceleratorTarget) {}

TEST_F(FocusManagerTest, SuspendAccelerators) {}

namespace {

class FocusInAboutToRequestFocusFromTabTraversalView : public View {};

BEGIN_METADATA()

}  // namespace

// Verifies a focus change done during a call to
// AboutToRequestFocusFromTabTraversal() is honored.
TEST_F(FocusManagerTest, FocusInAboutToRequestFocusFromTabTraversal) {}

TEST_F(FocusManagerTest, RotateFocus) {}

// Verifies the stored focus view tracks the focused view.
TEST_F(FocusManagerTest, ImplicitlyStoresFocus) {}

namespace {

class FocusManagerArrowKeyTraversalTest
    : public FocusManagerTest,
      public testing::WithParamInterface<bool> {};

// Instantiate the Boolean which is used to toggle RTL in
// the parameterized tests.
INSTANTIATE_TEST_SUITE_P();

}  // namespace

TEST_P(FocusManagerArrowKeyTraversalTest, ArrowKeyTraversal) {}

TEST_F(FocusManagerTest, SkipViewsInArrowKeyTraversal) {}

TEST_F(FocusManagerTest, StoreFocusedView) {}

#if BUILDFLAG(IS_MAC)
// Test that the correct view is restored if full keyboard access is changed.
TEST_F(FocusManagerTest, StoreFocusedViewFullKeyboardAccess) {
  View* view1 = new View;
  View* view2 = new View;
  View* view3 = new View;

  // Make view1 focusable in accessibility mode, view2 not focusable and view3
  // always focusable.
  view1->SetFocusBehavior(View::FocusBehavior::ACCESSIBLE_ONLY);
  view2->SetFocusBehavior(View::FocusBehavior::NEVER);
  view3->SetFocusBehavior(View::FocusBehavior::ALWAYS);

  // Add views to the view hierarchy
  GetWidget()->GetRootView()->AddChildView(view1);
  GetWidget()->GetRootView()->AddChildView(view2);
  GetWidget()->GetRootView()->AddChildView(view3);

  view1->RequestFocus();
  EXPECT_EQ(view1, GetFocusManager()->GetFocusedView());
  GetFocusManager()->StoreFocusedView(true);
  EXPECT_EQ(nullptr, GetFocusManager()->GetFocusedView());

  // Turn off full keyboard access mode and restore focused view. Since view1 is
  // no longer focusable, view3 should have focus.
  GetFocusManager()->SetKeyboardAccessible(false);
  EXPECT_FALSE(GetFocusManager()->RestoreFocusedView());
  EXPECT_EQ(view3, GetFocusManager()->GetFocusedView());

  GetFocusManager()->StoreFocusedView(false);
  EXPECT_EQ(nullptr, GetFocusManager()->GetFocusedView());

  // Turn on full keyboard access mode and restore focused view. Since view3 is
  // still focusable, view3 should have focus.
  GetFocusManager()->SetKeyboardAccessible(true);
  EXPECT_TRUE(GetFocusManager()->RestoreFocusedView());
  EXPECT_EQ(view3, GetFocusManager()->GetFocusedView());
}

// Test that View::RequestFocus() respects full keyboard access mode.
TEST_F(FocusManagerTest, RequestFocus) {
  View* view1 = new View();
  View* view2 = new View();

  // Make view1 always focusable, view2 only focusable in accessibility mode.
  view1->SetFocusBehavior(View::FocusBehavior::ALWAYS);
  view2->SetFocusBehavior(View::FocusBehavior::ACCESSIBLE_ONLY);

  // Adds views to the view hierarchy.
  GetWidget()->GetRootView()->AddChildView(view1);
  GetWidget()->GetRootView()->AddChildView(view2);

  // Verify view1 can always get focus via View::RequestFocus, while view2 can
  // only get focus in full keyboard accessibility mode.
  EXPECT_TRUE(GetFocusManager()->keyboard_accessible());
  view1->RequestFocus();
  EXPECT_EQ(view1, GetFocusManager()->GetFocusedView());
  view2->RequestFocus();
  EXPECT_EQ(view2, GetFocusManager()->GetFocusedView());

  // Toggle full keyboard accessibility.
  GetFocusManager()->SetKeyboardAccessible(false);

  GetFocusManager()->ClearFocus();
  EXPECT_NE(view1, GetFocusManager()->GetFocusedView());
  view1->RequestFocus();
  EXPECT_EQ(view1, GetFocusManager()->GetFocusedView());
  view2->RequestFocus();
  EXPECT_EQ(view1, GetFocusManager()->GetFocusedView());
}

#endif

namespace {

// Trivial WidgetDelegate implementation that allows setting return value of
// ShouldAdvanceFocusToTopLevelWidget().
class AdvanceFocusWidgetDelegate : public WidgetDelegate {};

class TestBubbleDialogDelegateView : public BubbleDialogDelegateView {};

}  // namespace

// Verifies focus wrapping happens in the same widget.
TEST_F(FocusManagerTest, AdvanceFocusStaysInWidget) {}

TEST_F(FocusManagerTest, NavigateIntoAnchoredDialog) {}

TEST_F(FocusManagerTest, AnchoredDialogOnContainerView) {}

// Checks that focus traverses from a View to a bubble anchored at that View
// when in a pane.
TEST_F(FocusManagerTest, AnchoredDialogInPane) {}

// Test that a focused view has a visible focus ring.
// This test uses FlexLayout intentionally because it had issues showing focus
// rings.
TEST_F(FocusManagerTest, FocusRing) {}

#if BUILDFLAG(ENABLE_DESKTOP_AURA)
// This test is specifically for the permutation where the main widget is a
// DesktopNativeWidgetAura and the bubble is a NativeWidgetAura. When focus
// moves back from the bubble to the parent widget, ensure that the DNWA's aura
// window is focused.
class DesktopWidgetFocusManagerTest : public FocusManagerTest {};

TEST_F(DesktopWidgetFocusManagerTest, AnchoredDialogInDesktopNativeWidgetAura) {}
#endif

#if defined(USE_AURA)
class RedirectToParentFocusManagerTest : public FocusManagerTest {};

// Test that when an accelerator is sent to a bubble that isn't registered,
// the bubble's parent handles it instead.
TEST_F(RedirectToParentFocusManagerTest, ParentHandlesAcceleratorFromBubble) {}

// Test that when an accelerator is sent to a bubble that is registered on both
// it and its parent, the bubble handles it.
TEST_F(RedirectToParentFocusManagerTest, BubbleHandlesRegisteredAccelerators) {}

// Test that when an accelerator is sent to a bubble that isn't registered
// for either the bubble or the bubble's parent, the bubble isn't closed.
TEST_F(RedirectToParentFocusManagerTest, NotProcessedAccelerator) {}

#endif

}  // namespace views