ksv: Add a11y name for special chars

Certain punctuation isn't verbalized by ChromeVox, i.e. period, comma,
semicolon, hyphen or brackets. So, whenever one of these is used in a
keyboard shortcut, it just will speak "Ctrl plus [silence]" so you don't
actually hear what the correct command is.
This patch adds special labels mapping to those chars.

Bug: 974799
Test: manual
Change-Id: I548c9e0a179245c42e40ae63b1b457a0c9cba3cb
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1674634
Commit-Queue: Tao Wu <wutao@chromium.org>
Reviewed-by: James Cook <jamescook@chromium.org>
Cr-Commit-Position: refs/heads/master@{#672178}
diff --git a/ash/components/shortcut_viewer/keyboard_shortcut_viewer_metadata.cc b/ash/components/shortcut_viewer/keyboard_shortcut_viewer_metadata.cc
index 6411c6b..e5997d1 100644
--- a/ash/components/shortcut_viewer/keyboard_shortcut_viewer_metadata.cc
+++ b/ash/components/shortcut_viewer/keyboard_shortcut_viewer_metadata.cc
@@ -146,6 +146,30 @@
   return base::string16();
 }
 
+base::string16 GetAccessibleNameForKeyboardCode(ui::KeyboardCode key_code) {
+  int msg_id = 0;
+  switch (key_code) {
+    case ui::VKEY_OEM_PERIOD:
+      msg_id = IDS_KSV_KEY_PERIOD_ACCESSIBILITY_NAME;
+      break;
+    case ui::VKEY_OEM_COMMA:
+      msg_id = IDS_KSV_KEY_COMMA_ACCESSIBILITY_NAME;
+      break;
+    case ui::VKEY_OEM_MINUS:
+      msg_id = IDS_KSV_KEY_HYPHEN_ACCESSIBILITY_NAME;
+      break;
+    case ui::VKEY_OEM_4:
+      msg_id = IDS_KSV_KEY_BRACKET_LEFT_ACCESSIBILITY_NAME;
+      break;
+    case ui::VKEY_OEM_6:
+      msg_id = IDS_KSV_KEY_BRACKET_RIGHT_ACCESSIBILITY_NAME;
+      break;
+    default:
+      break;
+  }
+  return msg_id ? l10n_util::GetStringUTF16(msg_id) : base::string16();
+}
+
 const gfx::VectorIcon* GetVectorIconForKeyboardCode(ui::KeyboardCode key_code) {
   switch (key_code) {
     case ui::VKEY_BROWSER_BACK:
diff --git a/ash/components/shortcut_viewer/keyboard_shortcut_viewer_metadata.h b/ash/components/shortcut_viewer/keyboard_shortcut_viewer_metadata.h
index 492bae0..865e268 100644
--- a/ash/components/shortcut_viewer/keyboard_shortcut_viewer_metadata.h
+++ b/ash/components/shortcut_viewer/keyboard_shortcut_viewer_metadata.h
@@ -35,6 +35,13 @@
 // character.
 base::string16 GetStringForKeyboardCode(ui::KeyboardCode key_code);
 
+// Certain punctuation is not verbalized by ChromeVox, i.e. ".". So, whenever
+// one of these is used in a keyboard shortcut, need to set the accessible name
+// to explicitly specified text, such as "period".
+// Returns empty string if there is no special accessible name for the
+// |key_code|.
+base::string16 GetAccessibleNameForKeyboardCode(ui::KeyboardCode key_code);
+
 // Returns the VectorIcon if |key_code| need to be represented as an icon.
 // Returns nullptr if |key_code| should not be represented as an icon.
 const gfx::VectorIcon* GetVectorIconForKeyboardCode(ui::KeyboardCode key_code);
diff --git a/ash/components/shortcut_viewer/views/keyboard_shortcut_item_view.cc b/ash/components/shortcut_viewer/views/keyboard_shortcut_item_view.cc
index 0157255..40bfb3d 100644
--- a/ash/components/shortcut_viewer/views/keyboard_shortcut_item_view.cc
+++ b/ash/components/shortcut_viewer/views/keyboard_shortcut_item_view.cc
@@ -71,9 +71,11 @@
 
   std::vector<size_t> offsets;
   std::vector<base::string16> replacement_strings;
+  std::vector<base::string16> accessible_names;
   const size_t shortcut_key_codes_size = item.shortcut_key_codes.size();
   offsets.reserve(shortcut_key_codes_size);
   replacement_strings.reserve(shortcut_key_codes_size);
+  accessible_names.reserve(shortcut_key_codes_size);
   bool has_invalid_dom_key = false;
   for (ui::KeyboardCode key_code : item.shortcut_key_codes) {
     auto iter = GetKeycodeToString16Cache()->find(key_code);
@@ -88,20 +90,29 @@
     // layout.
     if (dom_key_string.empty()) {
       replacement_strings.clear();
+      accessible_names.clear();
       has_invalid_dom_key = true;
       break;
     }
     replacement_strings.emplace_back(dom_key_string);
+
+    base::string16 accessible_name = GetAccessibleNameForKeyboardCode(key_code);
+    accessible_names.emplace_back(accessible_name.empty() ? dom_key_string
+                                                          : accessible_name);
   }
 
   base::string16 shortcut_string;
+  base::string16 accessible_string;
   if (replacement_strings.empty()) {
     shortcut_string = l10n_util::GetStringUTF16(has_invalid_dom_key
                                                     ? IDS_KSV_KEY_NO_MAPPING
                                                     : item.shortcut_message_id);
+    accessible_string = shortcut_string;
   } else {
     shortcut_string = l10n_util::GetStringFUTF16(item.shortcut_message_id,
                                                  replacement_strings, &offsets);
+    accessible_string = l10n_util::GetStringFUTF16(
+        item.shortcut_message_id, accessible_names, /*offsets=*/nullptr);
   }
   shortcut_label_view_ = new views::StyledLabel(shortcut_string, nullptr);
   // StyledLabel will flip the alignment if UI layout is right-to-left.
@@ -138,7 +149,7 @@
   GetViewAccessibility().OverrideRole(ax::mojom::Role::kListItem);
   GetViewAccessibility().OverrideIsLeaf(true);
   accessible_name_ = description_label_view_->text() +
-                     base::ASCIIToUTF16(", ") + shortcut_label_view_->text();
+                     base::ASCIIToUTF16(", ") + accessible_string;
 }
 
 void KeyboardShortcutItemView::GetAccessibleNodeData(
diff --git a/ash/components/shortcut_viewer_strings.grdp b/ash/components/shortcut_viewer_strings.grdp
index a524a49..1ca004e 100644
--- a/ash/components/shortcut_viewer_strings.grdp
+++ b/ash/components/shortcut_viewer_strings.grdp
@@ -21,6 +21,21 @@
   <message name="IDS_KSV_SEARCH_BOX_ACCESSIBILITY_VALUE_WITHOUT_RESULTS" desc="Accessibility value for the search box without searched results.">
     No search result for <ph name="query">$1<ex>a</ex></ph>
   </message>
+  <message name="IDS_KSV_KEY_PERIOD_ACCESSIBILITY_NAME" desc="Accessibility Name of [.] key">
+    period
+  </message>
+  <message name="IDS_KSV_KEY_COMMA_ACCESSIBILITY_NAME" desc="Accessibility Name of [,] key">
+    comma
+  </message>
+  <message name="IDS_KSV_KEY_HYPHEN_ACCESSIBILITY_NAME" desc="Accessibility Name of [-] key">
+    hyphen
+  </message>
+  <message name="IDS_KSV_KEY_BRACKET_LEFT_ACCESSIBILITY_NAME" desc="Accessibility Name of [ key">
+    bracket left
+  </message>
+  <message name="IDS_KSV_KEY_BRACKET_RIGHT_ACCESSIBILITY_NAME" desc="Accessibility Name of ] key">
+    bracket right
+  </message>
 
   <!-- Search illustration -->
   <message name="IDS_KSV_SEARCH_NO_RESULT" desc="No search results.">