#include "ui/views/corewm/tooltip_controller.h"
#include <memory>
#include <utility>
#include "base/at_exit.h"
#include "base/memory/raw_ptr.h"
#include "base/ranges/algorithm.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "ui/accessibility/ax_enums.mojom.h"
#include "ui/accessibility/ax_node_data.h"
#include "ui/aura/client/cursor_client.h"
#include "ui/aura/client/window_types.h"
#include "ui/aura/test/aura_test_base.h"
#include "ui/aura/test/test_window_delegate.h"
#include "ui/aura/window.h"
#include "ui/aura/window_event_dispatcher.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"
#include "ui/events/test/event_generator.h"
#include "ui/gfx/font.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/render_text.h"
#include "ui/gfx/text_elider.h"
#include "ui/views/buildflags.h"
#include "ui/views/corewm/test/tooltip_aura_test_api.h"
#include "ui/views/corewm/tooltip_aura.h"
#include "ui/views/corewm/tooltip_controller_test_helper.h"
#include "ui/views/corewm/tooltip_state_manager.h"
#include "ui/views/test/desktop_test_views_delegate.h"
#include "ui/views/test/native_widget_factory.h"
#include "ui/views/test/test_views_delegate.h"
#include "ui/views/test/views_test_base.h"
#include "ui/views/view.h"
#include "ui/views/widget/tooltip_manager.h"
#include "ui/views/widget/widget.h"
#include "ui/wm/public/activation_client.h"
#include "ui/wm/public/tooltip_observer.h"
#if BUILDFLAG(IS_WIN)
#include "ui/base/win/scoped_ole_initializer.h"
#endif
#if BUILDFLAG(ENABLE_DESKTOP_AURA)
#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
#endif
namespace views::corewm::test {
namespace {
#if BUILDFLAG(IS_CHROMEOS_LACROS)
class TestTooltipLacros : public Tooltip {
public:
TestTooltipLacros() = default;
TestTooltipLacros(const TestTooltipLacros&) = delete;
TestTooltipLacros& operator=(const TestTooltipLacros&) = delete;
~TestTooltipLacros() override {
state_manager_ = nullptr;
}
void AddObserver(wm::TooltipObserver* observer) override {}
void RemoveObserver(wm::TooltipObserver* observer) override {}
const std::u16string& tooltip_text() const { return tooltip_text_; }
int GetMaxWidth(const gfx::Point& location) const override { return 100; }
void Update(aura::Window* window,
const std::u16string& tooltip_text,
const gfx::Point& position,
const TooltipTrigger trigger) override {
tooltip_parent_ = window;
tooltip_text_ = tooltip_text;
anchor_point_ = position + window->GetBoundsInScreen().OffsetFromOrigin();
trigger_ = trigger;
}
void Show() override {
is_visible_ = true;
DCHECK(state_manager_);
state_manager_->OnTooltipShownOnServer(tooltip_parent_, tooltip_text_,
gfx::Rect());
}
void Hide() override {
is_visible_ = false;
tooltip_parent_ = nullptr;
DCHECK(state_manager_);
state_manager_->OnTooltipHiddenOnServer();
}
bool IsVisible() override { return is_visible_; }
void SetStateManager(TooltipStateManager* state_manager) {
state_manager_ = state_manager;
}
const gfx::Point& anchor_point() { return anchor_point_; }
TooltipTrigger trigger() { return trigger_; }
private:
bool is_visible_ = false;
raw_ptr<aura::Window> tooltip_parent_ = nullptr;
raw_ptr<TooltipStateManager> state_manager_ = nullptr;
std::u16string tooltip_text_;
gfx::Point anchor_point_;
TooltipTrigger trigger_;
};
#endif
std::unique_ptr<views::Widget> CreateWidget(aura::Window* root) { … }
}
class TooltipControllerTest : public ViewsTestBase { … };
TEST_F(TooltipControllerTest, ViewTooltip) { … }
TEST_F(TooltipControllerTest, HideEmptyTooltip) { … }
TEST_F(TooltipControllerTest, DontShowTooltipOnTouch) { … }
#if !BUILDFLAG(ENABLE_DESKTOP_AURA) || BUILDFLAG(IS_WIN)
TEST_F(TooltipControllerTest, MaxWidth) {
std::u16string text =
u"Really, really, really, really, really, really long tooltip that "
u"exceeds max width";
view_->set_tooltip_text(text);
gfx::Point center = GetWindow()->bounds().CenterPoint();
generator_->MoveMouseTo(center);
EXPECT_TRUE(helper_->IsTooltipVisible());
EXPECT_EQ(helper_->state_manager()->tooltip_trigger(),
TooltipTrigger::kCursor);
const gfx::RenderText* render_text =
test::TooltipAuraTestApi(tooltip_).GetRenderText();
int max = helper_->controller()->GetMaxWidth(center);
EXPECT_EQ(max, render_text->display_rect().width());
}
TEST_F(TooltipControllerTest, AccessibleNodeData) {
std::u16string text = u"Tooltip Text";
view_->set_tooltip_text(text);
gfx::Point center = GetWindow()->bounds().CenterPoint();
generator_->MoveMouseTo(center);
EXPECT_TRUE(helper_->IsTooltipVisible());
EXPECT_EQ(helper_->state_manager()->tooltip_trigger(),
TooltipTrigger::kCursor);
ui::AXNodeData node_data;
test::TooltipAuraTestApi(tooltip_).GetAccessibleNodeData(&node_data);
EXPECT_EQ(ax::mojom::Role::kTooltip, node_data.role);
EXPECT_EQ(text, base::ASCIIToUTF16(node_data.GetStringAttribute(
ax::mojom::StringAttribute::kName)));
}
TEST_F(TooltipControllerTest, TooltipBounds) {
gfx::Size tooltip_size(100, 40);
gfx::Rect display_bounds(display::Screen::GetScreen()
->GetDisplayNearestPoint(gfx::Point(0, 0))
.bounds());
gfx::Point anchor_point = display_bounds.CenterPoint();
int a_expected_y(anchor_point.y() + TooltipAura::kCursorOffsetY);
int b_expected_y(anchor_point.y());
{
gfx::Rect bounds = test::TooltipAuraTestApi(tooltip_).GetTooltipBounds(
tooltip_size, anchor_point, TooltipTrigger::kCursor);
gfx::Point expected_position(anchor_point.x() + TooltipAura::kCursorOffsetX,
a_expected_y);
EXPECT_EQ(bounds, gfx::Rect(expected_position, tooltip_size));
bounds = test::TooltipAuraTestApi(tooltip_).GetTooltipBounds(
tooltip_size, anchor_point, TooltipTrigger::kKeyboard);
expected_position =
gfx::Point(anchor_point.x() - tooltip_size.width() / 2, b_expected_y);
EXPECT_EQ(bounds, gfx::Rect(expected_position, tooltip_size));
}
{
anchor_point = display_bounds.left_center();
anchor_point.Offset(-TooltipAura::kCursorOffsetX - 10, 0);
gfx::Rect bounds = test::TooltipAuraTestApi(tooltip_).GetTooltipBounds(
tooltip_size, anchor_point, TooltipTrigger::kCursor);
gfx::Point expected_position(0, a_expected_y);
EXPECT_EQ(bounds, gfx::Rect(expected_position, tooltip_size));
bounds = test::TooltipAuraTestApi(tooltip_).GetTooltipBounds(
tooltip_size, anchor_point, TooltipTrigger::kKeyboard);
expected_position = gfx::Point(0, b_expected_y);
EXPECT_EQ(bounds, gfx::Rect(expected_position, tooltip_size));
}
{
anchor_point = display_bounds.right_center();
anchor_point.Offset(10, 0);
gfx::Rect bounds = test::TooltipAuraTestApi(tooltip_).GetTooltipBounds(
tooltip_size, anchor_point, TooltipTrigger::kCursor);
gfx::Point expected_position(display_bounds.right() - tooltip_size.width(),
a_expected_y);
EXPECT_EQ(bounds, gfx::Rect(expected_position, tooltip_size));
bounds = test::TooltipAuraTestApi(tooltip_).GetTooltipBounds(
tooltip_size, anchor_point, TooltipTrigger::kKeyboard);
expected_position =
gfx::Point(display_bounds.right() - tooltip_size.width(), b_expected_y);
EXPECT_EQ(bounds, gfx::Rect(expected_position, tooltip_size));
}
{
anchor_point = display_bounds.bottom_center();
gfx::Rect bounds = test::TooltipAuraTestApi(tooltip_).GetTooltipBounds(
tooltip_size, anchor_point, TooltipTrigger::kCursor);
gfx::Point expected_position(anchor_point.x() + TooltipAura::kCursorOffsetX,
anchor_point.y() - tooltip_size.height());
EXPECT_EQ(bounds, gfx::Rect(expected_position, tooltip_size));
bounds = test::TooltipAuraTestApi(tooltip_).GetTooltipBounds(
tooltip_size, anchor_point, TooltipTrigger::kKeyboard);
expected_position = gfx::Point(anchor_point.x() - tooltip_size.width() / 2,
anchor_point.y() - tooltip_size.height());
EXPECT_EQ(bounds, gfx::Rect(expected_position, tooltip_size));
}
}
#endif
TEST_F(TooltipControllerTest, TooltipsInMultipleViews) { … }
TEST_F(TooltipControllerTest, EnableOrDisableTooltips) { … }
TEST_F(TooltipControllerTest, DontShowEmptyTooltips) { … }
#if BUILDFLAG(IS_CHROMEOS_LACROS)
#define MAYBE_TooltipUpdateWhenTooltipDeferTimerIsRunning …
#else
#define MAYBE_TooltipUpdateWhenTooltipDeferTimerIsRunning …
#endif
TEST_F(TooltipControllerTest,
MAYBE_TooltipUpdateWhenTooltipDeferTimerIsRunning) { … }
TEST_F(TooltipControllerTest, TooltipHidesOnKeyPressAndStaysHiddenUntilChange) { … }
TEST_F(TooltipControllerTest, TooltipStaysVisibleOnKeyRelease) { … }
TEST_F(TooltipControllerTest, TooltipHidesOnTimeoutAndStaysHiddenUntilChange) { … }
TEST_F(TooltipControllerTest, HideOnExit) { … }
TEST_F(TooltipControllerTest, ReshowOnClickAfterEnterExit) { … }
TEST_F(TooltipControllerTest, ShowAndHideTooltipTriggeredFromKeyboard) { … }
TEST_F(TooltipControllerTest,
KeyboardTriggeredTooltipStaysVisibleOnMouseExitedEvent) { … }
namespace {
int IndexInParent(const aura::Window* window) { … }
}
TEST_F(TooltipControllerTest, DISABLED_CloseOnCaptureLost) { … }
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_CHROMEOS_LACROS)
#define MAYBE_Capture …
#else
#define MAYBE_Capture …
#endif
TEST_F(TooltipControllerTest, MAYBE_Capture) { … }
TEST_F(TooltipControllerTest, ShowTooltipOnTooltipTextUpdate) { … }
#if BUILDFLAG(IS_CHROMEOS_LACROS)
#define MAYBE_TooltipPositionUpdatedWhenTimerRunning …
#else
#define MAYBE_TooltipPositionUpdatedWhenTimerRunning …
#endif
TEST_F(TooltipControllerTest, MAYBE_TooltipPositionUpdatedWhenTimerRunning) { … }
TEST_F(TooltipControllerTest, TooltipHiddenWhenWindowDeactivated) { … }
namespace {
#if BUILDFLAG(IS_CHROMEOS_LACROS)
using TestTooltip = TestTooltipLacros;
#else
class TestTooltip : public Tooltip { … };
#endif
}
class TooltipControllerTest2 : public aura::test::AuraTestBase { … };
TEST_F(TooltipControllerTest2, VerifyLeadingTrailingWhitespaceStripped) { … }
TEST_F(TooltipControllerTest2, CloseOnCancelMode) { … }
class TooltipControllerTest3 : public ViewsTestBase { … };
TEST_F(TooltipControllerTest3, TooltipPositionChangesOnTwoViewsWithSameLabel) { … }
class TooltipStateManagerTest : public TooltipControllerTest { … };
TEST_F(TooltipStateManagerTest, ShowAndHideTooltip) { … }
#if BUILDFLAG(IS_CHROMEOS_LACROS)
#define MAYBE_ShowTooltipWithDelay …
#else
#define MAYBE_ShowTooltipWithDelay …
#endif
TEST_F(TooltipStateManagerTest, MAYBE_ShowTooltipWithDelay) { … }
#if BUILDFLAG(IS_CHROMEOS_LACROS)
#define MAYBE_UpdatePositionIfNeeded …
#else
#define MAYBE_UpdatePositionIfNeeded …
#endif
TEST_F(TooltipStateManagerTest, MAYBE_UpdatePositionIfNeeded) { … }
}