Commit 6e57ab4e authored by Dominic Mazzoni's avatar Dominic Mazzoni Committed by Commit Bot

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/+/1582844Reviewed-by: default avatarElly Fong-Jones <ellyjones@chromium.org>
Reviewed-by: default avatarAaron Leventhal <aleventhal@chromium.org>
Commit-Queue: Dominic Mazzoni <dmazzoni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#657371}
parent b252e7b4
...@@ -13,8 +13,10 @@ ...@@ -13,8 +13,10 @@
#include "components/constrained_window/constrained_window_views.h" #include "components/constrained_window/constrained_window_views.h"
#include "components/strings/grit/components_strings.h" #include "components/strings/grit/components_strings.h"
#include "components/vector_icons/vector_icons.h" #include "components/vector_icons/vector_icons.h"
#include "ui/accessibility/ax_node_data.h"
#include "ui/base/buildflags.h" #include "ui/base/buildflags.h"
#include "ui/base/l10n/l10n_util.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.h"
#include "ui/views/controls/button/image_button_factory.h" #include "ui/views/controls/button/image_button_factory.h"
#include "ui/views/controls/label.h" #include "ui/views/controls/label.h"
...@@ -37,13 +39,14 @@ ConfirmBubbleViews::ConfirmBubbleViews( ...@@ -37,13 +39,14 @@ ConfirmBubbleViews::ConfirmBubbleViews(
kMaxMessageWidth, false); kMaxMessageWidth, false);
// Add the message label. // 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()); DCHECK(!label->text().empty());
label->SetHorizontalAlignment(gfx::ALIGN_LEFT); label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
label->SetMultiLine(true); label->SetMultiLine(true);
label->SizeToFit(kMaxMessageWidth); label->SizeToFit(kMaxMessageWidth);
layout->StartRow(views::GridLayout::kFixedSize, 0); layout->StartRow(views::GridLayout::kFixedSize, 0);
layout->AddView(label); layout->AddView(label.release());
chrome::RecordDialogCreation(chrome::DialogIdentifier::CONFIRM_BUBBLE); chrome::RecordDialogCreation(chrome::DialogIdentifier::CONFIRM_BUBBLE);
} }
...@@ -115,6 +118,15 @@ void ConfirmBubbleViews::ButtonPressed(views::Button* sender, ...@@ -115,6 +118,15 @@ void ConfirmBubbleViews::ButtonPressed(views::Button* sender,
} }
} }
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 { namespace chrome {
void ShowConfirmBubble(gfx::NativeWindow window, void ShowConfirmBubble(gfx::NativeWindow window,
......
...@@ -16,7 +16,8 @@ class ConfirmBubbleModel; ...@@ -16,7 +16,8 @@ class ConfirmBubbleModel;
namespace views { namespace views {
class ImageButton; class ImageButton;
} class Label;
} // namespace views
// A dialog (with the standard Title/[OK]/[Cancel] UI elements), as well as // 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: // a message Label and help (?) button. The dialog ultimately appears like this:
...@@ -50,10 +51,15 @@ class ConfirmBubbleViews : public views::DialogDelegateView, ...@@ -50,10 +51,15 @@ class ConfirmBubbleViews : public views::DialogDelegateView,
// views::ButtonListener implementation. // views::ButtonListener implementation.
void ButtonPressed(views::Button* sender, const ui::Event& event) override; void ButtonPressed(views::Button* sender, const ui::Event& event) override;
// views::View implementation.
void ViewHierarchyChanged(
const views::ViewHierarchyChangedDetails& details) override;
private: private:
// The model to customize this bubble view. // The model to customize this bubble view.
std::unique_ptr<ConfirmBubbleModel> model_; std::unique_ptr<ConfirmBubbleModel> model_;
views::Label* label_;
views::ImageButton* help_button_; views::ImageButton* help_button_;
DISALLOW_COPY_AND_ASSIGN(ConfirmBubbleViews); DISALLOW_COPY_AND_ASSIGN(ConfirmBubbleViews);
......
...@@ -166,12 +166,20 @@ void ViewAccessibility::GetAccessibleNodeData(ui::AXNodeData* data) const { ...@@ -166,12 +166,20 @@ void ViewAccessibility::GetAccessibleNodeData(ui::AXNodeData* data) const {
ax::mojom::IntAttribute::kPosInSet, ax::mojom::IntAttribute::kPosInSet,
ax::mojom::IntAttribute::kSetSize, ax::mojom::IntAttribute::kSetSize,
}; };
for (auto attribute : kOverridableIntAttributes) { for (auto attribute : kOverridableIntAttributes) {
if (custom_data_.HasIntAttribute(attribute)) if (custom_data_.HasIntAttribute(attribute))
data->AddIntAttribute(attribute, custom_data_.GetIntAttribute(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)) { if (!data->HasStringAttribute(ax::mojom::StringAttribute::kDescription)) {
base::string16 tooltip = view_->GetTooltipText(gfx::Point()); base::string16 tooltip = view_->GetTooltipText(gfx::Point());
// Some screen readers announce the accessible description right after the // Some screen readers announce the accessible description right after the
...@@ -244,6 +252,13 @@ void ViewAccessibility::OverrideBounds(const gfx::RectF& bounds) { ...@@ -244,6 +252,13 @@ void ViewAccessibility::OverrideBounds(const gfx::RectF& bounds) {
custom_data_.relative_bounds.bounds = bounds; 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) { 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::kPosInSet, pos_in_set);
custom_data_.AddIntAttribute(ax::mojom::IntAttribute::kSetSize, set_size); custom_data_.AddIntAttribute(ax::mojom::IntAttribute::kSetSize, set_size);
......
...@@ -66,6 +66,7 @@ class VIEWS_EXPORT ViewAccessibility { ...@@ -66,6 +66,7 @@ class VIEWS_EXPORT ViewAccessibility {
void OverrideIsLeaf(bool value); void OverrideIsLeaf(bool value);
void OverrideIsIgnored(bool value); void OverrideIsIgnored(bool value);
void OverrideBounds(const gfx::RectF& bounds); void OverrideBounds(const gfx::RectF& bounds);
void OverrideDescribedBy(View* described_by_view);
// Override indexes used by some screen readers when describing elements in a // 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 // menu, list, etc. If not specified, a view's index in its parent and its
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment