Add an accessible describedby relation to bubble dialogs.
The JAWS screen reader is not announcing the text of a
bubble dialog when it appears unless the user presses
a hot key to read the whole dialog. This can be mitigated
by adding a "describedby" relation between the dialog and
the main label text.
Bug: 953325
Change-Id: I8f9a68a80d7d7e91ea51f2816a3d85e312e93b39
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1582844
Reviewed-by: Elly Fong-Jones <ellyjones@chromium.org>
Reviewed-by: Aaron Leventhal <aleventhal@chromium.org>
Commit-Queue: Dominic Mazzoni <dmazzoni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#657371}
diff --git a/chrome/browser/ui/views/confirm_bubble_views.cc b/chrome/browser/ui/views/confirm_bubble_views.cc
index 8a28dfa..c14aa70 100644
--- a/chrome/browser/ui/views/confirm_bubble_views.cc
+++ b/chrome/browser/ui/views/confirm_bubble_views.cc
@@ -13,8 +13,10 @@
#include "components/constrained_window/constrained_window_views.h"
#include "components/strings/grit/components_strings.h"
#include "components/vector_icons/vector_icons.h"
+#include "ui/accessibility/ax_node_data.h"
#include "ui/base/buildflags.h"
#include "ui/base/l10n/l10n_util.h"
+#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/controls/button/image_button.h"
#include "ui/views/controls/button/image_button_factory.h"
#include "ui/views/controls/label.h"
@@ -37,13 +39,14 @@
kMaxMessageWidth, false);
// Add the message label.
- views::Label* label = new views::Label(model_->GetMessageText());
+ auto label = std::make_unique<views::Label>(model_->GetMessageText());
+ label_ = label.get();
DCHECK(!label->text().empty());
label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
label->SetMultiLine(true);
label->SizeToFit(kMaxMessageWidth);
layout->StartRow(views::GridLayout::kFixedSize, 0);
- layout->AddView(label);
+ layout->AddView(label.release());
chrome::RecordDialogCreation(chrome::DialogIdentifier::CONFIRM_BUBBLE);
}
@@ -115,6 +118,15 @@
}
}
+void ConfirmBubbleViews::ViewHierarchyChanged(
+ const views::ViewHierarchyChangedDetails& details) {
+ if (details.is_add && details.child == this && GetWidget()) {
+ GetWidget()->GetRootView()->GetViewAccessibility().OverrideDescribedBy(
+ label_);
+ }
+ DialogDelegateView::ViewHierarchyChanged(details);
+}
+
namespace chrome {
void ShowConfirmBubble(gfx::NativeWindow window,
diff --git a/chrome/browser/ui/views/confirm_bubble_views.h b/chrome/browser/ui/views/confirm_bubble_views.h
index 4b160b6..decea20 100644
--- a/chrome/browser/ui/views/confirm_bubble_views.h
+++ b/chrome/browser/ui/views/confirm_bubble_views.h
@@ -16,7 +16,8 @@
namespace views {
class ImageButton;
-}
+class Label;
+} // namespace views
// A dialog (with the standard Title/[OK]/[Cancel] UI elements), as well as
// a message Label and help (?) button. The dialog ultimately appears like this:
@@ -50,10 +51,15 @@
// views::ButtonListener implementation.
void ButtonPressed(views::Button* sender, const ui::Event& event) override;
+ // views::View implementation.
+ void ViewHierarchyChanged(
+ const views::ViewHierarchyChangedDetails& details) override;
+
private:
// The model to customize this bubble view.
std::unique_ptr<ConfirmBubbleModel> model_;
+ views::Label* label_;
views::ImageButton* help_button_;
DISALLOW_COPY_AND_ASSIGN(ConfirmBubbleViews);
diff --git a/ui/views/accessibility/view_accessibility.cc b/ui/views/accessibility/view_accessibility.cc
index 2f2ea9c..943d59e 100644
--- a/ui/views/accessibility/view_accessibility.cc
+++ b/ui/views/accessibility/view_accessibility.cc
@@ -166,12 +166,20 @@
ax::mojom::IntAttribute::kPosInSet,
ax::mojom::IntAttribute::kSetSize,
};
-
for (auto attribute : kOverridableIntAttributes) {
if (custom_data_.HasIntAttribute(attribute))
data->AddIntAttribute(attribute, custom_data_.GetIntAttribute(attribute));
}
+ static const ax::mojom::IntListAttribute kOverridableIntListAttributes[]{
+ ax::mojom::IntListAttribute::kDescribedbyIds,
+ };
+ for (auto attribute : kOverridableIntListAttributes) {
+ if (custom_data_.HasIntListAttribute(attribute))
+ data->AddIntListAttribute(attribute,
+ custom_data_.GetIntListAttribute(attribute));
+ }
+
if (!data->HasStringAttribute(ax::mojom::StringAttribute::kDescription)) {
base::string16 tooltip = view_->GetTooltipText(gfx::Point());
// Some screen readers announce the accessible description right after the
@@ -244,6 +252,13 @@
custom_data_.relative_bounds.bounds = bounds;
}
+void ViewAccessibility::OverrideDescribedBy(View* described_by_view) {
+ int described_by_id =
+ described_by_view->GetViewAccessibility().GetUniqueId().Get();
+ custom_data_.AddIntListAttribute(ax::mojom::IntListAttribute::kDescribedbyIds,
+ {described_by_id});
+}
+
void ViewAccessibility::OverridePosInSet(int pos_in_set, int set_size) {
custom_data_.AddIntAttribute(ax::mojom::IntAttribute::kPosInSet, pos_in_set);
custom_data_.AddIntAttribute(ax::mojom::IntAttribute::kSetSize, set_size);
diff --git a/ui/views/accessibility/view_accessibility.h b/ui/views/accessibility/view_accessibility.h
index 35f9970..866305b 100644
--- a/ui/views/accessibility/view_accessibility.h
+++ b/ui/views/accessibility/view_accessibility.h
@@ -66,6 +66,7 @@
void OverrideIsLeaf(bool value);
void OverrideIsIgnored(bool value);
void OverrideBounds(const gfx::RectF& bounds);
+ void OverrideDescribedBy(View* described_by_view);
// Override indexes used by some screen readers when describing elements in a
// menu, list, etc. If not specified, a view's index in its parent and its