
This CL is the second of a multi-CL set that will migrate how TrayBackgroundView and its derived classes set their accessible names. This CL migrates the accessible name setter for ash::AnnotationTray from being calculated only when requested in TrayBackgroundView::GetAccessibleNodeData ("pull" system) to updating the value in the ViewAccessibility cache whenever it changes ("push"). The accessible name of AnnotationTray is dependent on the return value of GetCurrentTool(), so this CL adds code to update the AnnotationTray's accessible name whenever the return value of GetCurrentTool() may change. This CL also adds a test. This CL sets up AnnotationTray in preparation for a future CL where TrayBackgroundView::GetAccessibleNodeData will be deleted. As part of the ViewsAX project, we are removing GetAccessibleNodeData from Views across chromium: https://docs.google.com/document/d/1Ku7HOyDsiZem1yaV6ccZ-tz3lO2XR2NEcm8HjR6d-VY/edit Future CLs will handle more complex cases where the accessible name for the derived class needs to be updated dynamically. The final CL will remove TrayBackgroundView::GetAccessibleNode data entirely. Bug: 325137417, 379818907 Change-Id: I9829d70c2454b3dbc6133ad6079543dd8d1c0e60 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6026695 Reviewed-by: Jacques Newman <janewman@microsoft.com> Reviewed-by: Xiaoqian Dai <xdai@chromium.org> Commit-Queue: Evelynn Kaplan <evelynn.kaplan@microsoft.com> Cr-Commit-Position: refs/heads/main@{#1390503}
429 lines
17 KiB
C++
429 lines
17 KiB
C++
// Copyright 2024 The Chromium Authors
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "ash/annotator/annotator_controller.h"
|
|
|
|
#include "ash/annotator/annotation_tray.h"
|
|
#include "ash/annotator/annotator_metrics.h"
|
|
#include "ash/constants/ash_pref_names.h"
|
|
#include "ash/projector/projector_metrics.h"
|
|
#include "ash/public/cpp/annotator/annotator_tool.h"
|
|
#include "ash/root_window_controller.h"
|
|
#include "ash/session/session_controller_impl.h"
|
|
#include "ash/shell.h"
|
|
#include "ash/strings/grit/ash_strings.h"
|
|
#include "ash/system/status_area_widget.h"
|
|
#include "ash/system/tray/tray_container.h"
|
|
#include "ash/test/ash_test_base.h"
|
|
#include "ash/webui/annotator/test/mock_annotator_client.h"
|
|
#include "base/task/single_thread_task_runner.h"
|
|
#include "base/test/metrics/histogram_tester.h"
|
|
#include "base/time/time.h"
|
|
#include "testing/gmock/include/gmock/gmock.h"
|
|
#include "testing/gtest/include/gtest/gtest.h"
|
|
#include "ui/aura/window.h"
|
|
#include "ui/base/l10n/l10n_util.h"
|
|
#include "ui/events/gesture_detection/gesture_configuration.h"
|
|
#include "ui/events/test/event_generator.h"
|
|
#include "ui/gfx/geometry/point.h"
|
|
#include "ui/views/accessibility/view_accessibility.h"
|
|
#include "ui/views/controls/image_view.h"
|
|
|
|
namespace ash {
|
|
|
|
namespace {
|
|
|
|
constexpr char kAnnotatorMarkerColorHistogramName[] =
|
|
"Ash.Projector.MarkerColor.ClamshellMode";
|
|
constexpr char kProjectorToolbarHistogramName[] =
|
|
"Ash.Projector.Toolbar.ClamshellMode";
|
|
|
|
} // namespace
|
|
|
|
class AnnotatorControllerTest : public AshTestBase {
|
|
public:
|
|
AnnotatorControllerTest() = default;
|
|
|
|
AnnotatorControllerTest(const AnnotatorControllerTest&) = delete;
|
|
AnnotatorControllerTest& operator=(const AnnotatorControllerTest&) = delete;
|
|
|
|
// AshTestBase:
|
|
void SetUp() override {
|
|
AshTestBase::SetUp();
|
|
|
|
auto* annotator_controller = Shell::Get()->annotator_controller();
|
|
annotator_controller->SetToolClient(&client_);
|
|
}
|
|
|
|
AnnotatorController* annotator_controller() {
|
|
return Shell::Get()->annotator_controller();
|
|
}
|
|
|
|
protected:
|
|
MockAnnotatorClient client_;
|
|
};
|
|
|
|
TEST_F(AnnotatorControllerTest, SetAnnotatorTool) {
|
|
base::HistogramTester histogram_tester;
|
|
AnnotatorTool tool;
|
|
tool.color = kAnnotatorDefaultPenColor;
|
|
EXPECT_CALL(client_, SetTool(tool));
|
|
|
|
annotator_controller()->SetAnnotatorTool(tool);
|
|
histogram_tester.ExpectUniqueSample(kAnnotatorMarkerColorHistogramName,
|
|
AnnotatorMarkerColor::kMagenta,
|
|
/*count=*/1);
|
|
}
|
|
|
|
TEST_F(AnnotatorControllerTest, RegisterAndUnregisterView) {
|
|
auto* annotation_tray = Shell::GetPrimaryRootWindowController()
|
|
->GetStatusAreaWidget()
|
|
->annotation_tray();
|
|
annotator_controller()->RegisterView(Shell::GetPrimaryRootWindow());
|
|
EXPECT_TRUE(annotation_tray->visible_preferred());
|
|
annotator_controller()->UnregisterView(Shell::GetPrimaryRootWindow());
|
|
EXPECT_FALSE(annotation_tray->visible_preferred());
|
|
}
|
|
|
|
TEST_F(AnnotatorControllerTest, RegisterAndUnregisterViewMultipleDisplays) {
|
|
UpdateDisplay("800x700,801+0-800x700");
|
|
aura::Window::Windows roots = Shell::GetAllRootWindows();
|
|
ASSERT_EQ(2u, roots.size());
|
|
auto* primary_display_tray = Shell::GetPrimaryRootWindowController()
|
|
->GetStatusAreaWidget()
|
|
->annotation_tray();
|
|
auto* external_display_tray = RootWindowController::ForWindow(roots[1])
|
|
->GetStatusAreaWidget()
|
|
->annotation_tray();
|
|
|
|
// Show tray on primary root window.
|
|
annotator_controller()->RegisterView(Shell::GetPrimaryRootWindow());
|
|
EXPECT_TRUE(primary_display_tray->visible_preferred());
|
|
EXPECT_FALSE(external_display_tray->visible_preferred());
|
|
|
|
// Change the root window.
|
|
annotator_controller()->UnregisterView(Shell::GetPrimaryRootWindow());
|
|
annotator_controller()->RegisterView(roots[1]);
|
|
EXPECT_FALSE(primary_display_tray->visible_preferred());
|
|
EXPECT_TRUE(external_display_tray->visible_preferred());
|
|
|
|
annotator_controller()->UnregisterView(roots[1]);
|
|
EXPECT_FALSE(primary_display_tray->visible_preferred());
|
|
EXPECT_FALSE(external_display_tray->visible_preferred());
|
|
}
|
|
|
|
TEST_F(AnnotatorControllerTest, UnregisterViewWhenAnnotatorIsEnabled) {
|
|
base::HistogramTester histogram_tester;
|
|
|
|
auto* annotation_tray = Shell::GetPrimaryRootWindowController()
|
|
->GetStatusAreaWidget()
|
|
->annotation_tray();
|
|
annotator_controller()->RegisterView(Shell::GetPrimaryRootWindow());
|
|
EXPECT_TRUE(annotation_tray->visible_preferred());
|
|
|
|
annotator_controller()->CreateAnnotationOverlayForWindow(
|
|
Shell::GetPrimaryRootWindow(), std::nullopt);
|
|
annotator_controller()->EnableAnnotatorTool();
|
|
EXPECT_TRUE(annotator_controller()->is_annotator_enabled());
|
|
|
|
annotator_controller()->UnregisterView(Shell::GetPrimaryRootWindow());
|
|
EXPECT_FALSE(annotation_tray->visible_preferred());
|
|
histogram_tester.ExpectUniqueSample(kProjectorToolbarHistogramName,
|
|
ProjectorToolbar::kMarkerTool,
|
|
/*count=*/1);
|
|
}
|
|
|
|
TEST_F(AnnotatorControllerTest, EnableAnnotator) {
|
|
// Does nothing as there is no view for annotating.
|
|
annotator_controller()->EnableAnnotatorTool();
|
|
EXPECT_FALSE(annotator_controller()->is_annotator_enabled());
|
|
|
|
// Enables the annotator after creating the view for annotating.
|
|
annotator_controller()->CreateAnnotationOverlayForWindow(
|
|
Shell::GetPrimaryRootWindow(), std::nullopt);
|
|
annotator_controller()->EnableAnnotatorTool();
|
|
EXPECT_TRUE(annotator_controller()->is_annotator_enabled());
|
|
}
|
|
|
|
TEST_F(AnnotatorControllerTest, DisableAnnotator) {
|
|
base::HistogramTester histogram_tester;
|
|
|
|
auto* annotation_tray = Shell::GetPrimaryRootWindowController()
|
|
->GetStatusAreaWidget()
|
|
->annotation_tray();
|
|
annotator_controller()->RegisterView(Shell::GetPrimaryRootWindow());
|
|
EXPECT_TRUE(annotation_tray->visible_preferred());
|
|
|
|
annotator_controller()->CreateAnnotationOverlayForWindow(
|
|
Shell::GetPrimaryRootWindow(), std::nullopt);
|
|
annotator_controller()->EnableAnnotatorTool();
|
|
EXPECT_TRUE(annotator_controller()->is_annotator_enabled());
|
|
|
|
annotator_controller()->DisableAnnotator();
|
|
EXPECT_FALSE(annotation_tray->visible_preferred());
|
|
EXPECT_FALSE(annotator_controller()->is_annotator_enabled());
|
|
}
|
|
|
|
// Verifies that toggling on the marker enables the annotator tool.
|
|
TEST_F(AnnotatorControllerTest, EnablingDisablingMarker) {
|
|
base::HistogramTester histogram_tester;
|
|
|
|
// Enable marker.
|
|
annotator_controller()->CreateAnnotationOverlayForWindow(
|
|
Shell::GetPrimaryRootWindow(), std::nullopt);
|
|
annotator_controller()->EnableAnnotatorTool();
|
|
EXPECT_TRUE(annotator_controller()->is_annotator_enabled());
|
|
|
|
EXPECT_CALL(client_, Clear());
|
|
annotator_controller()->ResetTools();
|
|
EXPECT_FALSE(annotator_controller()->is_annotator_enabled());
|
|
|
|
histogram_tester.ExpectUniqueSample(kProjectorToolbarHistogramName,
|
|
ProjectorToolbar::kMarkerTool,
|
|
/*count=*/1);
|
|
}
|
|
|
|
TEST_F(AnnotatorControllerTest, ToggleMarkerWithKeyboardShortcut) {
|
|
annotator_controller()->RegisterView(Shell::GetPrimaryRootWindow());
|
|
annotator_controller()->CreateAnnotationOverlayForWindow(
|
|
Shell::GetPrimaryRootWindow(), std::nullopt);
|
|
annotator_controller()->OnCanvasInitialized(true);
|
|
|
|
auto* event_generator = GetEventGenerator();
|
|
event_generator->PressAndReleaseKey(ui::VKEY_OEM_3, ui::EF_COMMAND_DOWN);
|
|
EXPECT_TRUE(annotator_controller()->is_annotator_enabled());
|
|
|
|
event_generator->PressAndReleaseKey(ui::VKEY_OEM_3, ui::EF_COMMAND_DOWN);
|
|
EXPECT_FALSE(annotator_controller()->is_annotator_enabled());
|
|
}
|
|
|
|
TEST_F(AnnotatorControllerTest, SetAnnotatorToolAndStorePref) {
|
|
auto* annotation_tray = Shell::GetPrimaryRootWindowController()
|
|
->GetStatusAreaWidget()
|
|
->annotation_tray();
|
|
// Assert that the initial pref is unset.
|
|
PrefService* pref_service =
|
|
Shell::Get()->session_controller()->GetActivePrefService();
|
|
EXPECT_EQ(
|
|
pref_service->GetUint64(prefs::kProjectorAnnotatorLastUsedMarkerColor),
|
|
0u);
|
|
|
|
annotator_controller()->RegisterView(Shell::GetPrimaryRootWindow());
|
|
annotator_controller()->CreateAnnotationOverlayForWindow(
|
|
Shell::GetPrimaryRootWindow(), std::nullopt);
|
|
annotator_controller()->OnCanvasInitialized(true);
|
|
// Assert that the icon image for the annotator is set.
|
|
ASSERT_EQ(annotation_tray->tray_container()->children().size(), 1u);
|
|
const views::ImageView* image_view = static_cast<views::ImageView*>(
|
|
annotation_tray->tray_container()->children()[0]);
|
|
EXPECT_FALSE(image_view->GetImageModel().IsEmpty());
|
|
|
|
LeftClickOn(annotation_tray);
|
|
EXPECT_TRUE(annotator_controller()->is_annotator_enabled());
|
|
|
|
annotator_controller()->DisableAnnotator();
|
|
EXPECT_FALSE(annotator_controller()->is_annotator_enabled());
|
|
// Check that the last used color is stored in the pref.
|
|
EXPECT_EQ(
|
|
pref_service->GetUint64(prefs::kProjectorAnnotatorLastUsedMarkerColor),
|
|
kAnnotatorDefaultPenColor);
|
|
}
|
|
|
|
// Tests that long pressing the AnnotationTray shows a bubble.
|
|
TEST_F(AnnotatorControllerTest, LongPressShowsBubble) {
|
|
annotator_controller()->RegisterView(Shell::GetPrimaryRootWindow());
|
|
annotator_controller()->CreateAnnotationOverlayForWindow(
|
|
Shell::GetPrimaryRootWindow(), std::nullopt);
|
|
annotator_controller()->OnCanvasInitialized(true);
|
|
|
|
auto* annotation_tray = Shell::GetPrimaryRootWindowController()
|
|
->GetStatusAreaWidget()
|
|
->annotation_tray();
|
|
|
|
// Long press the tray item, it should show a bubble.
|
|
gfx::Point location = annotation_tray->GetBoundsInScreen().CenterPoint();
|
|
|
|
// Temporarily reconfigure gestures so that the long press takes 2
|
|
// milliseconds.
|
|
ui::GestureConfiguration* gesture_config =
|
|
ui::GestureConfiguration::GetInstance();
|
|
const int old_long_press_time_in_ms = gesture_config->long_press_time_in_ms();
|
|
const base::TimeDelta old_short_press_time =
|
|
gesture_config->short_press_time();
|
|
const int old_show_press_delay_in_ms =
|
|
gesture_config->show_press_delay_in_ms();
|
|
gesture_config->set_long_press_time_in_ms(1);
|
|
gesture_config->set_short_press_time(base::Milliseconds(1));
|
|
gesture_config->set_show_press_delay_in_ms(1);
|
|
|
|
ui::test::EventGenerator* event_generator = GetEventGenerator();
|
|
event_generator->set_current_screen_location(location);
|
|
event_generator->PressTouch();
|
|
|
|
// Hold the press down for 2 ms, to trigger a long press.
|
|
base::RunLoop run_loop;
|
|
base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
|
|
FROM_HERE, run_loop.QuitClosure(), base::Milliseconds(2));
|
|
run_loop.Run();
|
|
|
|
gesture_config->set_long_press_time_in_ms(old_long_press_time_in_ms);
|
|
gesture_config->set_short_press_time(old_short_press_time);
|
|
gesture_config->set_show_press_delay_in_ms(old_show_press_delay_in_ms);
|
|
|
|
event_generator->ReleaseTouch();
|
|
|
|
EXPECT_TRUE(annotation_tray->GetBubbleWidget());
|
|
}
|
|
|
|
// Tests that tapping the AnnotationTray enables annotation.
|
|
TEST_F(AnnotatorControllerTest, TapEnabledAnnotation) {
|
|
annotator_controller()->RegisterView(Shell::GetPrimaryRootWindow());
|
|
annotator_controller()->CreateAnnotationOverlayForWindow(
|
|
Shell::GetPrimaryRootWindow(), std::nullopt);
|
|
annotator_controller()->OnCanvasInitialized(true);
|
|
|
|
GestureTapOn(Shell::GetPrimaryRootWindowController()
|
|
->GetStatusAreaWidget()
|
|
->annotation_tray());
|
|
|
|
EXPECT_TRUE(annotator_controller()->is_annotator_enabled());
|
|
}
|
|
|
|
TEST_F(AnnotatorControllerTest, OnCanvasInitialized) {
|
|
auto* annotation_tray = Shell::GetPrimaryRootWindowController()
|
|
->GetStatusAreaWidget()
|
|
->annotation_tray();
|
|
annotator_controller()->RegisterView(Shell::GetPrimaryRootWindow());
|
|
|
|
EXPECT_FALSE(annotation_tray->GetEnabled());
|
|
|
|
annotator_controller()->CreateAnnotationOverlayForWindow(
|
|
Shell::GetPrimaryRootWindow(), std::nullopt);
|
|
annotator_controller()->OnCanvasInitialized(/*success=*/true);
|
|
EXPECT_TRUE(annotator_controller()->GetAnnotatorAvailability());
|
|
EXPECT_TRUE(annotation_tray->GetEnabled());
|
|
|
|
annotator_controller()->OnCanvasInitialized(/*success=*/false);
|
|
EXPECT_FALSE(annotator_controller()->GetAnnotatorAvailability());
|
|
EXPECT_FALSE(annotation_tray->GetEnabled());
|
|
}
|
|
|
|
TEST_F(AnnotatorControllerTest, ToggleAnnotationTray) {
|
|
annotator_controller()->RegisterView(Shell::GetPrimaryRootWindow());
|
|
annotator_controller()->CreateAnnotationOverlayForWindow(
|
|
Shell::GetPrimaryRootWindow(), std::nullopt);
|
|
annotator_controller()->OnCanvasInitialized(true);
|
|
EXPECT_FALSE(annotator_controller()->is_annotator_enabled());
|
|
|
|
annotator_controller()->ToggleAnnotationTray();
|
|
EXPECT_TRUE(annotator_controller()->is_annotator_enabled());
|
|
}
|
|
|
|
TEST_F(AnnotatorControllerTest, CreateAnnotationsOverlayView) {
|
|
auto overlay = annotator_controller()->CreateAnnotationsOverlayView();
|
|
EXPECT_TRUE(overlay.get());
|
|
}
|
|
|
|
TEST_F(AnnotatorControllerTest, UpdateRootViewMultipleDisplays) {
|
|
UpdateDisplay("800x700,801+0-800x700");
|
|
aura::Window::Windows roots = Shell::GetAllRootWindows();
|
|
ASSERT_EQ(2u, roots.size());
|
|
auto* primary_display_tray = Shell::GetPrimaryRootWindowController()
|
|
->GetStatusAreaWidget()
|
|
->annotation_tray();
|
|
auto* external_display_tray = RootWindowController::ForWindow(roots[1])
|
|
->GetStatusAreaWidget()
|
|
->annotation_tray();
|
|
|
|
// Show tray on primary root window.
|
|
annotator_controller()->RegisterView(Shell::GetPrimaryRootWindow());
|
|
EXPECT_TRUE(primary_display_tray->visible_preferred());
|
|
EXPECT_FALSE(external_display_tray->visible_preferred());
|
|
|
|
// Change the root window of the recorded window.
|
|
annotator_controller()->UpdateRootView(roots[1]);
|
|
EXPECT_FALSE(primary_display_tray->visible_preferred());
|
|
EXPECT_TRUE(external_display_tray->visible_preferred());
|
|
|
|
annotator_controller()->DisableAnnotator();
|
|
EXPECT_FALSE(primary_display_tray->visible_preferred());
|
|
EXPECT_FALSE(external_display_tray->visible_preferred());
|
|
}
|
|
|
|
TEST_F(AnnotatorControllerTest, RegisterViewTwice) {
|
|
UpdateDisplay("800x700,801+0-800x700");
|
|
aura::Window::Windows roots = Shell::GetAllRootWindows();
|
|
ASSERT_EQ(2u, roots.size());
|
|
auto* primary_display_tray = Shell::GetPrimaryRootWindowController()
|
|
->GetStatusAreaWidget()
|
|
->annotation_tray();
|
|
auto* external_display_tray = RootWindowController::ForWindow(roots[1])
|
|
->GetStatusAreaWidget()
|
|
->annotation_tray();
|
|
|
|
// Register view twice on 2 different roots.
|
|
annotator_controller()->RegisterView(Shell::GetPrimaryRootWindow());
|
|
annotator_controller()->RegisterView(roots[1]);
|
|
// Expect the tray to be visible only on the second root window, as the first
|
|
// root window was unregistered. Annotator allows only one root window at a
|
|
// time.
|
|
EXPECT_FALSE(primary_display_tray->visible_preferred());
|
|
EXPECT_TRUE(external_display_tray->visible_preferred());
|
|
|
|
annotator_controller()->DisableAnnotator();
|
|
EXPECT_FALSE(primary_display_tray->visible_preferred());
|
|
EXPECT_FALSE(external_display_tray->visible_preferred());
|
|
}
|
|
|
|
TEST_F(AnnotatorControllerTest, AnnotatorTrayAccessibleName) {
|
|
auto* annotation_tray = Shell::GetPrimaryRootWindowController()
|
|
->GetStatusAreaWidget()
|
|
->annotation_tray();
|
|
annotator_controller()->RegisterView(Shell::GetPrimaryRootWindow());
|
|
annotator_controller()->CreateAnnotationOverlayForWindow(
|
|
Shell::GetPrimaryRootWindow(), std::nullopt);
|
|
EXPECT_TRUE(annotation_tray->visible_preferred());
|
|
|
|
{
|
|
ui::AXNodeData tray_data;
|
|
annotation_tray->GetViewAccessibility().GetAccessibleNodeData(&tray_data);
|
|
EXPECT_EQ(
|
|
tray_data.GetString16Attribute(ax::mojom::StringAttribute::kName),
|
|
l10n_util::GetStringFUTF16(
|
|
IDS_ASH_STATUS_AREA_PROJECTOR_ANNOTATION_TRAY_ACCESSIBLE_TITLE,
|
|
l10n_util::GetStringUTF16(
|
|
IDS_ASH_STATUS_AREA_PROJECTOR_ANNOTATION_TRAY_OFF_STATE)));
|
|
}
|
|
|
|
annotator_controller()->EnableAnnotatorTool();
|
|
EXPECT_TRUE(annotator_controller()->is_annotator_enabled());
|
|
|
|
{
|
|
ui::AXNodeData tray_data;
|
|
annotation_tray->GetViewAccessibility().GetAccessibleNodeData(&tray_data);
|
|
EXPECT_EQ(
|
|
tray_data.GetString16Attribute(ax::mojom::StringAttribute::kName),
|
|
l10n_util::GetStringFUTF16(
|
|
IDS_ASH_STATUS_AREA_PROJECTOR_ANNOTATION_TRAY_ACCESSIBLE_TITLE,
|
|
l10n_util::GetStringUTF16(
|
|
IDS_ASH_STATUS_AREA_PROJECTOR_ANNOTATION_TRAY_ON_STATE)));
|
|
}
|
|
|
|
annotator_controller()->ResetTools();
|
|
EXPECT_FALSE(annotator_controller()->is_annotator_enabled());
|
|
|
|
{
|
|
ui::AXNodeData tray_data;
|
|
annotation_tray->GetViewAccessibility().GetAccessibleNodeData(&tray_data);
|
|
EXPECT_EQ(
|
|
tray_data.GetString16Attribute(ax::mojom::StringAttribute::kName),
|
|
l10n_util::GetStringFUTF16(
|
|
IDS_ASH_STATUS_AREA_PROJECTOR_ANNOTATION_TRAY_ACCESSIBLE_TITLE,
|
|
l10n_util::GetStringUTF16(
|
|
IDS_ASH_STATUS_AREA_PROJECTOR_ANNOTATION_TRAY_OFF_STATE)));
|
|
}
|
|
}
|
|
|
|
} // namespace ash
|