0

FaceGaze: Add close button to bubble UI

This CL adds a close button to the FaceGaze bubble. When clicked, the
close button will show a confirmation dialog to turn off FaceGaze.

Screenshot: https://drive.google.com/file/d/16Koo1F4ZpGHUdOxOFpXMQMyYz3Y55ap0/view?usp=sharing&resourcekey=0-jlutAkohxfPWGwns61MMQQ

Bug: 404626573
Change-Id: I5d2a69a4c2b6e9c02f34c482b2fd7896e9f64595
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6361960
Commit-Queue: Akihiro Ota <akihiroota@chromium.org>
Reviewed-by: Amanda Lin Dietz <aldietz@google.com>
Cr-Commit-Position: refs/heads/main@{#1435478}
This commit is contained in:
Akihiro Ota 2025-03-20 09:22:36 -07:00 committed by Chromium LUCI CQ
parent def11e9193
commit 22dd106294
14 changed files with 276 additions and 43 deletions

@ -3958,7 +3958,9 @@ void AccessibilityController::UpdateFeatureFromPref(FeatureType feature) {
if (enabled && ::features::IsAccessibilityFaceGazeEnabled()) {
if (!facegaze_bubble_controller_) {
facegaze_bubble_controller_ =
std::make_unique<FaceGazeBubbleController>();
std::make_unique<FaceGazeBubbleController>(base::BindRepeating(
&AccessibilityController::RequestDisableFaceGaze,
GetWeakPtr()));
}
if (!drag_event_rewriter_) {
drag_event_rewriter_ = std::make_unique<DragEventRewriter>();
@ -4142,7 +4144,9 @@ void AccessibilityController::UpdateFaceGazeBubble(const std::u16string& text,
FaceGazeBubbleController*
AccessibilityController::GetFaceGazeBubbleControllerForTest() {
if (!facegaze_bubble_controller_) {
facegaze_bubble_controller_ = std::make_unique<FaceGazeBubbleController>();
facegaze_bubble_controller_ =
std::make_unique<FaceGazeBubbleController>(base::BindRepeating(
&AccessibilityController::RequestDisableFaceGaze, GetWeakPtr()));
}
return facegaze_bubble_controller_.get();
@ -4164,4 +4168,14 @@ void AccessibilityController::EnableDragEventRewriter(bool enabled) {
drag_event_rewriter_->SetEnabled(enabled);
}
void AccessibilityController::RequestDisableFaceGaze() {
if (!active_user_prefs_) {
return;
}
active_user_prefs_->SetBoolean(prefs::kAccessibilityFaceGazeEnabledSentinel,
false);
active_user_prefs_->CommitPendingWrite();
}
} // namespace ash

@ -832,6 +832,10 @@ class ASH_EXPORT AccessibilityController
const std::string& behavior_pref,
bool dialog_accepted);
// Will set kAccessibilityFaceGazeEnabledSentinel to false, which will show a
// confirmation dialog asking if the user wants to turn off FaceGaze.
void RequestDisableFaceGaze();
void RecordSelectToSpeakSpeechDuration(SelectToSpeakState old_state,
SelectToSpeakState new_state);

@ -1574,6 +1574,10 @@ You can also use the keyboard shortcut. First, highlight text, then press <ph na
Are you sure you want to turn off facial gestures?
</message>
<message name="IDS_ASH_FACEGAZE_CLOSE_BUTTON_TEXT" desc="The text for the close button that is shown in the FaceGaze top bar UI.">
Turn off Face control
</message>
<!-- Accessibility nudges -->
<message name="IDS_ASH_ACCESSIBILITY_NUDGE_DICTATION_NO_FOCUSED_TEXT_FIELD" desc="Displayed in a nudge when Dictation is stopped automatically because there is no focused text field.">
Go to a text field to use Dictation

@ -0,0 +1 @@
d58a7ed5af4fd9af8aaf1a13efc05b110d25ceff

@ -8,6 +8,7 @@
#include "ash/wm/collision_detection/collision_detection_utils.h"
#include "base/functional/bind.h"
#include "ui/display/screen.h"
#include "ui/events/event.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
#include "ui/views/widget/widget.h"
@ -19,7 +20,9 @@ constexpr base::TimeDelta kShowTimeout = base::Seconds(1);
constexpr int kMarginFromTopDip = 8;
} // namespace
FaceGazeBubbleController::FaceGazeBubbleController() = default;
FaceGazeBubbleController::FaceGazeBubbleController(
const base::RepeatingCallback<void()>& on_close_button_clicked)
: on_close_button_clicked_(std::move(on_close_button_clicked)) {}
FaceGazeBubbleController::~FaceGazeBubbleController() {
show_timer_.Stop();
@ -53,8 +56,11 @@ void FaceGazeBubbleController::MaybeInitialize() {
return;
}
facegaze_bubble_view_ = new FaceGazeBubbleView(base::BindRepeating(
&FaceGazeBubbleController::OnMouseEntered, GetWeakPtr()));
facegaze_bubble_view_ = new FaceGazeBubbleView(
base::BindRepeating(&FaceGazeBubbleController::OnMouseEntered,
GetWeakPtr()),
base::BindRepeating(&FaceGazeBubbleController::OnCloseButtonClicked,
GetWeakPtr()));
facegaze_bubble_view_->views::View::AddObserver(this);
widget_ =
@ -94,7 +100,24 @@ void FaceGazeBubbleController::OnMouseEntered() {
GetWeakPtr()));
}
void FaceGazeBubbleController::OnCloseButtonClicked(const ui::Event& event) {
on_close_button_clicked_.Run();
}
void FaceGazeBubbleController::OnShowTimer() {
gfx::Point cursor_location =
display::Screen::GetScreen()->GetCursorScreenPoint();
gfx::Rect bubble_bounds = facegaze_bubble_view_->GetBoundsInScreen();
if (bubble_bounds.Contains(cursor_location)) {
// Though we hide FaceGazeBubble view only if the main content is hovered,
// we continue to hide it if the mouse is contained by the entire bounds of
// the view. This is to allow users to click on elements occluded by
// FaceGazeBubbleView.
OnMouseEntered();
return;
}
// If the mouse cursor isn't contained by the bubble, then we can show it.
widget_->Show();
}

@ -13,6 +13,10 @@
#include "base/timer/timer.h"
#include "ui/views/view_observer.h"
namespace ui {
class Event;
} // namespace ui
namespace views {
class View;
class Widget;
@ -25,7 +29,8 @@ class FaceGazeBubbleView;
// Manages the FaceGaze bubble view.
class ASH_EXPORT FaceGazeBubbleController : public views::ViewObserver {
public:
FaceGazeBubbleController();
FaceGazeBubbleController(
const base::RepeatingCallback<void()>& on_close_button_clicked);
FaceGazeBubbleController(const FaceGazeBubbleController&) = delete;
FaceGazeBubbleController& operator=(const FaceGazeBubbleController&) = delete;
~FaceGazeBubbleController() override;
@ -46,9 +51,15 @@ class ASH_EXPORT FaceGazeBubbleController : public views::ViewObserver {
// Updates the view and widget.
void Update(const std::u16string& text, bool is_warning);
// Called whenever the mouse enters the `FaceGazeBubbleView`.
// Called whenever the mouse enters the the main content of
// FaceGazeBubbleView; this method doesn't get called if the mouse hovers the
// close button.
void OnMouseEntered();
// Called whenever the close button in the FaceGazeBubbleView is
// clicked.
void OnCloseButtonClicked(const ui::Event& event);
// Shows the `FaceGazeBubbleView`.
void OnShowTimer();
@ -56,6 +67,10 @@ class ASH_EXPORT FaceGazeBubbleController : public views::ViewObserver {
return weak_ptr_factory_.GetWeakPtr();
}
// A callback that is run when the close button in the bubble UI is clicked.
// This is passed in from AccessibilityController, which owns this object.
const base::RepeatingCallback<void()> on_close_button_clicked_;
// Owned by views hierarchy.
raw_ptr<FaceGazeBubbleView> facegaze_bubble_view_ = nullptr;
raw_ptr<views::Widget> widget_ = nullptr;

@ -50,6 +50,10 @@ class FaceGazeBubbleControllerTest : public AshTestBase {
return GetController()->facegaze_bubble_view_;
}
const raw_ptr<FaceGazeBubbleCloseView> GetCloseView() {
return GetView()->GetCloseViewForTesting();
}
bool IsVisible() { return GetController()->widget_->IsVisible(); }
std::u16string_view GetBubbleText() { return GetView()->GetTextForTesting(); }
@ -179,4 +183,21 @@ TEST_F(FaceGazeBubbleControllerTest, UpdateWhileHidden) {
EXPECT_FALSE(IsVisible());
}
TEST_F(FaceGazeBubbleControllerTest, CloseButton) {
Update(u"Face control active", /*is_warning=*/false);
EXPECT_TRUE(GetCloseView());
}
TEST_F(FaceGazeBubbleControllerTest, HoverCloseButton) {
Update(u"Testing", /*is_warning=*/false);
EXPECT_TRUE(GetView());
EXPECT_TRUE(IsVisible());
// Ensure that the bubble remains visible if the close button is hovered.
GetEventGenerator()->MoveMouseTo(
GetCloseView()->GetBoundsInScreen().CenterPoint());
EXPECT_TRUE(GetView());
EXPECT_TRUE(IsVisible());
}
} // namespace ash

@ -11,8 +11,10 @@
#include "ash/public/cpp/shell_window_ids.h"
#include "ash/resources/vector_icons/vector_icons.h"
#include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/style/ash_color_id.h"
#include "ui/accessibility/ax_enums.mojom.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/base/mojom/dialog_button.mojom.h"
#include "ui/base/resource/resource_bundle.h"
@ -22,6 +24,7 @@
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/background.h"
#include "ui/views/bubble/bubble_frame_view.h"
#include "ui/views/controls/button/image_button.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/controls/label.h"
#include "ui/views/layout/box_layout.h"
@ -74,13 +77,15 @@ std::unique_ptr<views::ImageView> CreateImageView(
} // namespace
FaceGazeBubbleView::FaceGazeBubbleView(
const base::RepeatingCallback<void()>& on_mouse_entered)
: on_mouse_entered_(std::move(on_mouse_entered)) {
const base::RepeatingCallback<void()>& on_mouse_entered,
const base::RepeatingCallback<void(const ui::Event& event)>&
on_close_button_clicked) {
set_background_color(kBackgroundColorId);
set_parent_window(
Shell::GetContainer(Shell::GetPrimaryRootWindow(),
kShellWindowId_AccessibilityBubbleContainer));
// Create layout and specify other properties.
auto layout = std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kHorizontal);
layout->set_between_child_spacing(kSpaceBetweenIconAndTextDip);
@ -98,30 +103,26 @@ FaceGazeBubbleView::FaceGazeBubbleView(
SetButtons(static_cast<int>(ui::mojom::DialogButton::kNone));
SetProperty(views::kElementIdentifierKey, kFaceGazeBubbleElementId);
AddChildView(CreateImageView(&image_, kFacegazeIcon));
AddChildView(
CreateLabelView(&label_, std::u16string(), kColorAshTextColorPrimary));
// Add main content and close button to this view.
main_content_view_ =
AddChildView(std::make_unique<FaceGazeBubbleMainContentView>(
std::move(on_mouse_entered)));
close_view_ = AddChildView(std::make_unique<FaceGazeBubbleCloseView>(
std::move(on_close_button_clicked)));
}
FaceGazeBubbleView::~FaceGazeBubbleView() = default;
void FaceGazeBubbleView::Update(const std::u16string& text, bool is_warning) {
main_content_view_->Update(text, is_warning);
UpdateColor(is_warning);
label_->SetVisible(text != u"");
label_->SetText(text);
SizeToContents();
}
void FaceGazeBubbleView::OnMouseEntered(const ui::MouseEvent& event) {
on_mouse_entered_.Run();
}
void FaceGazeBubbleView::UpdateColor(bool is_warning) {
ui::ColorId background_color_id =
is_warning ? kWarningBackgroundColor : kBackgroundColorId;
SkColor background_color = GetColorProvider()->GetColor(background_color_id);
ui::ColorId foreground_color =
is_warning ? kWarningForegroundColor : kColorAshTextColorPrimary;
set_background_color(background_color_id);
View* const contents_view = GetContentsView();
@ -132,16 +133,80 @@ void FaceGazeBubbleView::UpdateColor(bool is_warning) {
if (frame_view) {
frame_view->SetBackgroundColor(background_color);
}
image_->SetImage(ui::ImageModel::FromVectorIcon(
kFacegazeIcon, foreground_color, kIconSizeDip));
label_->SetEnabledColor(GetColorProvider()->GetColor(foreground_color));
}
std::u16string_view FaceGazeBubbleView::GetTextForTesting() const {
return label_->GetText();
return main_content_view_->label()->GetText();
}
BEGIN_METADATA(FaceGazeBubbleView)
END_METADATA
FaceGazeBubbleMainContentView::FaceGazeBubbleMainContentView(
const base::RepeatingCallback<void()>& on_mouse_entered)
: on_mouse_entered_(std::move(on_mouse_entered)) {
// Create layout and ensure we receive mouse events.
auto layout = std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kHorizontal);
layout->set_between_child_spacing(kSpaceBetweenIconAndTextDip);
SetLayoutManager(std::move(layout));
SetNotifyEnterExitOnChild(true);
GetViewAccessibility().SetRole(ax::mojom::Role::kGenericContainer);
// Add child views to the view structure.
AddChildView(CreateImageView(&image_, kFacegazeIcon));
AddChildView(
CreateLabelView(&label_, std::u16string(), kColorAshTextColorPrimary));
}
FaceGazeBubbleMainContentView::~FaceGazeBubbleMainContentView() = default;
void FaceGazeBubbleMainContentView::OnMouseEntered(
const ui::MouseEvent& event) {
on_mouse_entered_.Run();
}
void FaceGazeBubbleMainContentView::Update(const std::u16string& text,
bool is_warning) {
UpdateColor(is_warning);
label_->SetVisible(text != u"");
label_->SetText(text);
}
void FaceGazeBubbleMainContentView::UpdateColor(bool is_warning) {
ui::ColorId foreground_color =
is_warning ? kWarningForegroundColor : kColorAshTextColorPrimary;
image_->SetImage(ui::ImageModel::FromVectorIcon(
kFacegazeIcon, foreground_color, kIconSizeDip));
label_->SetEnabledColor(GetColorProvider()->GetColor(foreground_color));
}
BEGIN_METADATA(FaceGazeBubbleMainContentView)
END_METADATA
FaceGazeBubbleCloseView::FaceGazeBubbleCloseView(
const base::RepeatingCallback<void(const ui::Event& event)>&
on_close_button_clicked)
: on_close_button_clicked_(std::move(on_close_button_clicked)) {
// Create layout.
auto layout = std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kHorizontal);
SetLayoutManager(std::move(layout));
// Add close button to view structure.
AddChildView(CreateImageView(&close_button_, kMediumOrLargeCloseButtonIcon));
close_button_->SetTooltipText(
l10n_util::GetStringUTF16(IDS_ASH_FACEGAZE_CLOSE_BUTTON_TEXT));
}
FaceGazeBubbleCloseView::~FaceGazeBubbleCloseView() = default;
bool FaceGazeBubbleCloseView::OnMousePressed(const ui::MouseEvent& event) {
on_close_button_clicked_.Run(event);
return false;
}
BEGIN_METADATA(FaceGazeBubbleCloseView)
END_METADATA
} // namespace ash

@ -27,16 +27,22 @@ class Label;
namespace ash {
class FaceGazeBubbleCloseView;
class FaceGazeBubbleMainContentView;
// The FaceGaze bubble view. This is a UI that appears at the top of the screen,
// which tells the user the most recently recognized facial gesture and the
// corresponding action that was taken. This view is only visible when the
// FaceGaze feature is enabled.
// corresponding action that was taken. It also exposes a close button so that
// the feature can be conveniently turned off, if necessary. This view is only
// visible when the FaceGaze feature is enabled.
class ASH_EXPORT FaceGazeBubbleView : public views::BubbleDialogDelegateView {
METADATA_HEADER(FaceGazeBubbleView, views::BubbleDialogDelegateView)
public:
explicit FaceGazeBubbleView(
const base::RepeatingCallback<void()>& on_mouse_entered);
const base::RepeatingCallback<void()>& on_mouse_entered,
const base::RepeatingCallback<void(const ui::Event& event)>&
on_close_button_clicked);
FaceGazeBubbleView(const FaceGazeBubbleView&) = delete;
FaceGazeBubbleView& operator=(const FaceGazeBubbleView&) = delete;
~FaceGazeBubbleView() override;
@ -44,17 +50,53 @@ class ASH_EXPORT FaceGazeBubbleView : public views::BubbleDialogDelegateView {
// Updates text content of this view.
void Update(const std::u16string& text, bool is_warning);
// views::View:
void OnMouseEntered(const ui::MouseEvent& event) override;
std::u16string_view GetTextForTesting() const;
const raw_ptr<FaceGazeBubbleCloseView> GetCloseViewForTesting() const {
return close_view_;
}
private:
friend class FaceGazeBubbleControllerTest;
// Updates color of this view.
void UpdateColor(bool is_warning);
// The view containing the main content, such as the FaceGaze icon and the
// informational text. Owned by the views hierarchy.
raw_ptr<FaceGazeBubbleMainContentView> main_content_view_ = nullptr;
// The view containing the close button, which can be used to quickly turn
// FaceGaze off. Owned by the views hierarchy.
raw_ptr<FaceGazeBubbleCloseView> close_view_ = nullptr;
};
// The main content view. This is the part of the bubble UI that tells the user
// the most recently recognized facial gesture and the corresponding action that
// was taken.
class ASH_EXPORT FaceGazeBubbleMainContentView : public views::View {
METADATA_HEADER(FaceGazeBubbleMainContentView, views::View)
public:
FaceGazeBubbleMainContentView(
const base::RepeatingCallback<void()>& on_mouse_entered);
FaceGazeBubbleMainContentView(const FaceGazeBubbleMainContentView&) = delete;
FaceGazeBubbleMainContentView& operator=(
const FaceGazeBubbleMainContentView&) = delete;
~FaceGazeBubbleMainContentView() override;
// views::View:
void OnMouseEntered(const ui::MouseEvent& event) override;
// Updates the text content, visibility, and color of this view.
void Update(const std::u16string& text, bool is_warning);
views::Label* label() { return label_; }
private:
// Updates color of this view.
void UpdateColor(bool is_warning);
// Custom callback that is called whenever the mouse enters or exits this
// view.
const base::RepeatingCallback<void()> on_mouse_entered_;
@ -67,6 +109,31 @@ class ASH_EXPORT FaceGazeBubbleView : public views::BubbleDialogDelegateView {
raw_ptr<views::Label> label_ = nullptr;
};
// The close button view. It contains the button used to quickly toggle
// FaceGaze off.
class ASH_EXPORT FaceGazeBubbleCloseView : public views::View {
METADATA_HEADER(FaceGazeBubbleCloseView, views::View)
public:
FaceGazeBubbleCloseView(
const base::RepeatingCallback<void(const ui::Event& event)>&
on_close_button_clicked);
FaceGazeBubbleCloseView(const FaceGazeBubbleCloseView&) = delete;
FaceGazeBubbleCloseView& operator=(const FaceGazeBubbleCloseView&) = delete;
~FaceGazeBubbleCloseView() override;
// views::View:
bool OnMousePressed(const ui::MouseEvent& event) override;
private:
// Custom callback that is called whenever the close button is clicked.
const base::RepeatingCallback<void(const ui::Event& event)>
on_close_button_clicked_;
// The close 'X' image.
raw_ptr<views::ImageView> close_button_ = nullptr;
};
} // namespace ash
#endif // ASH_SYSTEM_ACCESSIBILITY_FACEGAZE_BUBBLE_VIEW_H_

@ -89,8 +89,18 @@ export class BubbleController {
BubbleController.getDisplayTextForGesture_(dictation)));
}
const isWarning = this.baseText_.length > 0;
if (this.baseText_.length === 0) {
// If there is no other text to show in the bubble, then show the
// 'Face control active' message. This is gives users, who may be
// unfamiliar with FaceGaze, an understanding of why their computer
// can be controlled without touching the device.
this.baseText_.push(chrome.i18n.getMessage('facegaze_active'));
}
chrome.accessibilityPrivate.updateFaceGazeBubble(
this.baseText_.join(', '), /*isWarning=*/ this.baseText_.length > 0);
this.baseText_.join(', '), isWarning);
}
static getDisplayText(gesture: FacialGesture, macro: Macro): string

@ -1711,7 +1711,7 @@ AX_TEST_F('FaceGazeTest', 'BubbleTextSimple', async function() {
assertFalse(this.getBubbleIsWarning());
this.triggerBubbleControllerTimeout();
assertEquals('', this.getBubbleText());
assertEquals(this.getDefaultBubbleText(), this.getBubbleText());
});
AX_TEST_F('FaceGazeTest', 'BubbleTextMultiple', async function() {
@ -1742,7 +1742,7 @@ AX_TEST_F('FaceGazeTest', 'BubbleTextMultiple', async function() {
assertFalse(this.getBubbleIsWarning());
this.triggerBubbleControllerTimeout();
assertEquals('', this.getBubbleText());
assertEquals(this.getDefaultBubbleText(), this.getBubbleText());
});
AX_TEST_F('FaceGazeTest', 'BubbleTextKeyCombination', async function() {
@ -1791,7 +1791,7 @@ AX_TEST_F('FaceGazeTest', 'BubbleTextKeyCombination', async function() {
this.processFaceLandmarkerResult(result);
this.triggerBubbleControllerTimeout();
assertEquals('', this.getBubbleText());
assertEquals(this.getDefaultBubbleText(), this.getBubbleText());
});
AX_TEST_F(
@ -1858,7 +1858,7 @@ AX_TEST_F(
this.processFaceLandmarkerResult(result);
this.triggerBubbleControllerTimeout();
assertEquals('', this.getBubbleText());
assertEquals(this.getDefaultBubbleText(), this.getBubbleText());
});
AX_TEST_F(
@ -2038,7 +2038,7 @@ AX_TEST_F('FaceGazeTest', 'BubbleTextLongClickStateMessage', async function() {
assertFalse(this.getBubbleIsWarning());
this.triggerBubbleControllerTimeout();
assertEquals('', this.getBubbleText());
assertEquals(this.getDefaultBubbleText(), this.getBubbleText());
assertFalse(this.getBubbleIsWarning());
});
@ -2089,7 +2089,7 @@ AX_TEST_F('FaceGazeTest', 'BubbleTextDictationStateMessage', async function() {
assertEquals('Stop dictation (Open your mouth wide)', this.getBubbleText());
assertFalse(this.getBubbleIsWarning());
this.triggerBubbleControllerTimeout();
assertEquals('', this.getBubbleText());
assertEquals(this.getDefaultBubbleText(), this.getBubbleText());
assertFalse(this.getBubbleIsWarning());
});
@ -2175,7 +2175,7 @@ AX_TEST_F('FaceGazeTest', 'TurnOffActionsWhileInScrollMode', async function() {
// Ensure scroll mode automatically toggled off.
assertFalse(this.getScrollModeController().active());
assertEquals('', this.getBubbleText());
assertEquals(this.getDefaultBubbleText(), this.getBubbleText());
});
AX_TEST_F(
@ -2207,7 +2207,7 @@ AX_TEST_F(
// Ensure scroll mode automatically toggled off.
assertFalse(this.getScrollModeController().active());
assertEquals('', this.getBubbleText());
assertEquals(this.getDefaultBubbleText(), this.getBubbleText());
});
AX_TEST_F('FaceGazeTest', 'GesturesDisabledInScrollMode', async function() {
@ -2763,7 +2763,7 @@ AX_TEST_F('FaceGazeTest', 'PrecisionClickBubbleText', async function() {
'Left-click the mouse (Open your mouth wide)', this.getBubbleText());
this.triggerBubbleControllerTimeout();
assertEquals('', this.getBubbleText());
assertEquals(this.getDefaultBubbleText(), this.getBubbleText());
});
AX_TEST_F('FaceGazeTest', 'PrecisionClickMouseMovement', async function() {
@ -2826,7 +2826,7 @@ AX_TEST_F(
// Ensure precision click is automatically toggled off.
assertFalse(this.getMouseController().isPrecisionActive());
assertEquals('', this.getBubbleText());
assertEquals(this.getDefaultBubbleText(), this.getBubbleText());
});
AX_TEST_F(
@ -2856,7 +2856,7 @@ AX_TEST_F(
// Ensure precision click is automatically toggled off.
assertFalse(this.getMouseController().isPrecisionActive());
assertEquals('', this.getBubbleText());
assertEquals(this.getDefaultBubbleText(), this.getBubbleText());
});
AX_TEST_F('FaceGazeTest', 'PrecisionClickAndScrollMode', async function() {
@ -2967,7 +2967,7 @@ AX_TEST_F('FaceGazeTest', 'PrecisionRightClickBubbleText', async function() {
'Right-click the mouse (Open your mouth wide)', this.getBubbleText());
this.triggerBubbleControllerTimeout();
assertEquals('', this.getBubbleText());
assertEquals(this.getDefaultBubbleText(), this.getBubbleText());
});
AX_TEST_F('FaceGazeTest', 'PrecisionRightClickMouseEvents', async function() {

@ -557,6 +557,11 @@ FaceGazeTestBase = class extends E2ETestBase {
});
}
/** @return {string} */
getDefaultBubbleText() {
return 'Face control active';
}
/** @return {string|undefined} */
getBubbleText() {
return this.mockAccessibilityPrivate.getFaceGazeBubbleText();

@ -174,4 +174,7 @@
<message name="IDS_FACEGAZE_STATE_PRECISION_ACTIVE" desc="A message displayed in the FaceGaze bubble UI informing the user that precision click is currently active.">
Mouse speed reduced, <ph name="gesture">$1<ex>Smile</ex></ph> again to click
</message>
<message name="IDS_FACEGAZE_ACTIVE" desc="A message displayed in the FaceGaze bubble UI informing the user that FaceGaze is active. This is meant to be informational text that is shown when nothing else populates the bubble UI.">
Face control active
</message>
</grit-part>