172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen// Use of this source code is governed by a BSD-style license that can be
3bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen// found in the LICENSE file.
4bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen
5bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen#include "chrome/browser/chromeos/login/wizard_accessibility_handler.h"
6bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen
7731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick#include <algorithm>
8731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
9731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick#include "base/i18n/char_iterator.h"
10731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick#include "base/logging.h"
11ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "base/memory/scoped_ptr.h"
12731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick#include "base/string_number_conversions.h"
13bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen#include "chrome/browser/accessibility_events.h"
14bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen#include "chrome/browser/chromeos/cros/cros_library.h"
15bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen#include "chrome/browser/chromeos/cros/speech_synthesis_library.h"
16731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick#include "chrome/browser/extensions/extension_accessibility_api.h"
17731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick#include "chrome/browser/extensions/extension_accessibility_api_constants.h"
1821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "chrome/browser/profiles/profile_manager.h"
19dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/common/notification_details.h"
20dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/common/notification_source.h"
21731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick#include "grit/generated_resources.h"
2272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/base/l10n/l10n_util.h"
23731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
24731df977c0511bca2206b5f333555b1205ff1f43Iain Merricknamespace keys = extension_accessibility_api_constants;
25731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
26731df977c0511bca2206b5f333555b1205ff1f43Iain Merricknamespace {
27731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
28731df977c0511bca2206b5f333555b1205ff1f43Iain Merrickstatic std::string SubstringUTF8(std::string str, int start, int len) {
2972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  base::i18n::UTF8CharIterator iter(&str);
30731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  for (int i = 0; i < start; i++) {
31731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    if (!iter.Advance())
32731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      return std::string();
33731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  }
34731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
35731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  int byte_start = iter.array_pos();
36731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  for (int i = 0; i < len; i++) {
37731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    if (!iter.Advance())
38731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      break;
39731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  }
40731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  int byte_len = iter.array_pos() - byte_start;
41731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
42731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  return str.substr(byte_start, byte_len);
43731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick}
44731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
45731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick// If the string consists of a single character and that character is
46731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick// punctuation that is not normally spoken by TTS, replace the string
47731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick// with a description of that character (like "period" for ".").
48731df977c0511bca2206b5f333555b1205ff1f43Iain Merrickstd::string DescribePunctuation(const std::string& str) {
49731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  if (str == "!") {
50731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    return l10n_util::GetStringUTF8(IDS_CHROMEOS_ACC_EXCLAMATION_POINT);
51731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  } else if (str == "(") {
52731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    return l10n_util::GetStringUTF8(IDS_CHROMEOS_ACC_LEFT_PAREN);
53731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  } else if (str == ")") {
54731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    return l10n_util::GetStringUTF8(IDS_CHROMEOS_ACC_RIGHT_PAREN);
55731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  } else if (str == ";") {
56731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    return l10n_util::GetStringUTF8(IDS_CHROMEOS_ACC_SEMICOLON);
57731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  } else if (str == ":") {
58731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    return l10n_util::GetStringUTF8(IDS_CHROMEOS_ACC_COLON);
59731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  } else if (str == "\"") {
60731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    return l10n_util::GetStringUTF8(IDS_CHROMEOS_ACC_QUOTE);
61731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  } else if (str == ",") {
62731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    return l10n_util::GetStringUTF8(IDS_CHROMEOS_ACC_COMMA);
63731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  } else if (str == ".") {
64731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    return l10n_util::GetStringUTF8(IDS_CHROMEOS_ACC_PERIOD);
65731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  } else if (str == " ") {
66731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    return l10n_util::GetStringUTF8(IDS_CHROMEOS_ACC_SPACE);
67731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  } else {
68731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    return str;
69731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  }
70731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick}
71731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
72731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick// Append words and separate adding a space if needed.  Call
73731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick// DescribePunctuation on to_append so that single punctuation
74731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick// characters are expanded ('.' -> 'period') but punctuation
75731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick// in the middle of a larger phrase are handled by the speech
76731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick// engine.
77731df977c0511bca2206b5f333555b1205ff1f43Iain Merrickvoid AppendUtterance(std::string to_append, std::string* str) {
78731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  if ((*str).size())
79731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    *str += " ";
80731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
81731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  *str += DescribePunctuation(to_append);
82731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick}
83731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
84731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick// Append a localized string from its message ID, adding a space if needed.
85731df977c0511bca2206b5f333555b1205ff1f43Iain Merrickvoid AppendUtterance(int message_id, std::string* str) {
86731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  AppendUtterance(l10n_util::GetStringUTF8(message_id), str);
87731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick}
88731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
89731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick// Append a phrase of the form "3 of 5", adding a space if needed.
90731df977c0511bca2206b5f333555b1205ff1f43Iain Merrickvoid AppendIndexOfCount(int index, int count, std::string* str) {
91731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  string16 index_str = base::IntToString16(index);
92731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  string16 count_str = base::IntToString16(count);
93731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  AppendUtterance(l10n_util::GetStringFUTF8(IDS_CHROMEOS_ACC_INDEX_OF_COUNT,
94731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick                                            index_str,
95731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick                                            count_str), str);
96731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick}
97731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
98731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick}  // anonymous namespace
99731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
100731df977c0511bca2206b5f333555b1205ff1f43Iain Merricknamespace chromeos {
101bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen
102bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsenvoid WizardAccessibilityHandler::Observe(
103bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen    NotificationType type,
104bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen    const NotificationSource& source,
105bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen    const NotificationDetails& details) {
106731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  const AccessibilityControlInfo *control_info =
107731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      Details<const AccessibilityControlInfo>(details).ptr();
108731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  std::string description;
109731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  EarconType earcon = NO_EARCON;
110731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  DescribeAccessibilityEvent(type, control_info, &description, &earcon);
111731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  Speak(description.c_str(), false, true);
112bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen}
113bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen
114bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsenvoid WizardAccessibilityHandler::Speak(const char* speak_str,
115bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen                                       bool queue,
116bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen                                       bool interruptible) {
117bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  if (chromeos::CrosLibrary::Get()->EnsureLoaded()) {
118bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen    if (queue || !interruptible) {
119bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen      std::string props = "";
120bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen      props.append("enqueue=");
121bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen      props.append(queue ? "1;" : "0;");
122bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen      props.append("interruptible=");
123bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen      props.append(interruptible ? "1;" : "0;");
124bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen      chromeos::CrosLibrary::Get()->GetSpeechSynthesisLibrary()->
125bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen          SetSpeakProperties(props.c_str());
126bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen    }
127bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen    chromeos::CrosLibrary::Get()->GetSpeechSynthesisLibrary()->
128bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen        Speak(speak_str);
129bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen  }
130bda42a81ee5f9b20d2bebedcf0bbef1e30e5b293Kristian Monsen}
131731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
132731df977c0511bca2206b5f333555b1205ff1f43Iain Merrickvoid WizardAccessibilityHandler::DescribeAccessibilityEvent(
133731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    NotificationType event_type,
134731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    const AccessibilityControlInfo* control_info,
135731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    std::string* out_spoken_description,
136731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    EarconType* out_earcon) {
137731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  *out_spoken_description = std::string();
138731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  *out_earcon = NO_EARCON;
139731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
140731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  switch (event_type.value) {
141731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    case NotificationType::ACCESSIBILITY_CONTROL_FOCUSED:
142731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      DescribeControl(control_info, false, out_spoken_description, out_earcon);
143731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      break;
144731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    case NotificationType::ACCESSIBILITY_CONTROL_ACTION:
145731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      DescribeControl(control_info, true, out_spoken_description, out_earcon);
146731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      break;
147731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    case NotificationType::ACCESSIBILITY_TEXT_CHANGED:
148731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      DescribeTextChanged(control_info, out_spoken_description, out_earcon);
149731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      break;
150731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    case NotificationType::ACCESSIBILITY_MENU_OPENED:
151731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      *out_earcon = EARCON_OBJECT_OPENED;
152731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      break;
153731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    case NotificationType::ACCESSIBILITY_MENU_CLOSED:
154731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      *out_earcon = EARCON_OBJECT_CLOSED;
155731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      break;
156731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    default:
157731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      NOTREACHED();
158731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      return;
159731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  }
160731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
161731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  if (control_info->type() == keys::kTypeTextBox) {
162731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    const AccessibilityTextBoxInfo* text_box =
163731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick        static_cast<const AccessibilityTextBoxInfo*>(control_info);
164731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    previous_text_value_ = GetTextBoxValue(text_box);
165731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    previous_text_selection_start_ = text_box->selection_start();
166731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    previous_text_selection_end_ = text_box->selection_end();
167731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  }
168731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick}
169731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
170731df977c0511bca2206b5f333555b1205ff1f43Iain Merrickvoid WizardAccessibilityHandler::DescribeControl(
171731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    const AccessibilityControlInfo* control_info,
172731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    bool is_action,
173731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    std::string* out_spoken_description,
174731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    EarconType* out_earcon) {
175731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  if (control_info->type() == keys::kTypeButton) {
176731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    *out_earcon = EARCON_BUTTON;
177731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    AppendUtterance(control_info->name(), out_spoken_description);
178731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    AppendUtterance(IDS_CHROMEOS_ACC_BUTTON, out_spoken_description);
179731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  } else if (control_info->type() == keys::kTypeCheckbox) {
180731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    AppendUtterance(control_info->name(), out_spoken_description);
181731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    const AccessibilityCheckboxInfo* checkbox_info =
182731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick        static_cast<const AccessibilityCheckboxInfo*>(control_info);
183731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    if (checkbox_info->checked()) {
184731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      *out_earcon = EARCON_CHECK_ON;
185731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      AppendUtterance(IDS_CHROMEOS_ACC_CHECKBOX_CHECKED,
186731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick                      out_spoken_description);
187731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    } else {
188731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      *out_earcon = EARCON_CHECK_OFF;
189731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      AppendUtterance(IDS_CHROMEOS_ACC_CHECKBOX_UNCHECKED,
190731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick                      out_spoken_description);
191731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    }
192731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  } else if (control_info->type() == keys::kTypeComboBox) {
193731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    *out_earcon = EARCON_LISTBOX;
194731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    const AccessibilityComboBoxInfo* combobox_info =
195731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick        static_cast<const AccessibilityComboBoxInfo*>(control_info);
196731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    AppendUtterance(combobox_info->value(), out_spoken_description);
197731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    AppendUtterance(combobox_info->name(), out_spoken_description);
198731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    AppendUtterance(IDS_CHROMEOS_ACC_COMBOBOX, out_spoken_description);
199731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    AppendIndexOfCount(combobox_info->item_index() + 1,
200731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick                       combobox_info->item_count(),
201731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick                       out_spoken_description);
202731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  } else if (control_info->type() == keys::kTypeLink) {
203731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    *out_earcon = EARCON_LINK;
204731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    AppendUtterance(control_info->name(), out_spoken_description);
205731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    AppendUtterance(IDS_CHROMEOS_ACC_LINK, out_spoken_description);
206731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  } else if (control_info->type() == keys::kTypeListBox) {
207731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    *out_earcon = EARCON_LISTBOX;
208731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    const AccessibilityListBoxInfo* listbox_info =
209731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick        static_cast<const AccessibilityListBoxInfo*>(control_info);
210731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    AppendUtterance(listbox_info->value(), out_spoken_description);
211731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    AppendUtterance(listbox_info->name(), out_spoken_description);
212731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    AppendUtterance(IDS_CHROMEOS_ACC_LISTBOX, out_spoken_description);
213731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    AppendIndexOfCount(listbox_info->item_index() + 1,
214731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick                       listbox_info->item_count(),
215731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick                       out_spoken_description);
216731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  } else if (control_info->type() == keys::kTypeMenu) {
217731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    *out_earcon = EARCON_MENU;
218731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    AppendUtterance(control_info->name(), out_spoken_description);
219731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    AppendUtterance(IDS_CHROMEOS_ACC_MENU, out_spoken_description);
220731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  } else if (control_info->type() == keys::kTypeMenuItem) {
221731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    const AccessibilityMenuItemInfo* menu_item_info =
222731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick        static_cast<const AccessibilityMenuItemInfo*>(control_info);
223731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    AppendUtterance(menu_item_info->name(), out_spoken_description);
224731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    if (menu_item_info->has_submenu())
225731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      AppendUtterance(IDS_CHROMEOS_ACC_HAS_SUBMENU, out_spoken_description);
226731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    AppendIndexOfCount(menu_item_info->item_index() + 1,
227731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick                       menu_item_info->item_count(),
228731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick                       out_spoken_description);
229731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  } else if (control_info->type() == keys::kTypeRadioButton) {
230731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    AppendUtterance(control_info->name(), out_spoken_description);
231731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    const AccessibilityRadioButtonInfo* radio_info =
232731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick        static_cast<const AccessibilityRadioButtonInfo*>(control_info);
233731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    if (radio_info->checked()) {
234731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      *out_earcon = EARCON_CHECK_ON;
235731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      AppendUtterance(IDS_CHROMEOS_ACC_RADIO_SELECTED, out_spoken_description);
236731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    } else {
237731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      *out_earcon = EARCON_CHECK_OFF;
238731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      AppendUtterance(IDS_CHROMEOS_ACC_RADIO_UNSELECTED,
239731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick                      out_spoken_description);
240731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    }
241731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    AppendIndexOfCount(radio_info->item_index() + 1,
242731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick                       radio_info->item_count(),
243731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick                       out_spoken_description);
244731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  } else if (control_info->type() == keys::kTypeTab) {
245731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    *out_earcon = EARCON_TAB;
246731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    AppendUtterance(control_info->name(), out_spoken_description);
247731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    const AccessibilityTabInfo* tab_info =
248731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick        static_cast<const AccessibilityTabInfo*>(control_info);
249731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    AppendUtterance(IDS_CHROMEOS_ACC_TAB, out_spoken_description);
250731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    AppendIndexOfCount(tab_info->tab_index() + 1,
251731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick                       tab_info->tab_count(),
252731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick                       out_spoken_description);
253731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  } else if (control_info->type() == keys::kTypeTextBox) {
254731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    *out_earcon = EARCON_TEXTBOX;
255731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    const AccessibilityTextBoxInfo* textbox_info =
256731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick        static_cast<const AccessibilityTextBoxInfo*>(control_info);
257731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    AppendUtterance(GetTextBoxValue(textbox_info), out_spoken_description);
258731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    AppendUtterance(textbox_info->name(), out_spoken_description);
259731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    if (textbox_info->password()) {
260731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      AppendUtterance(IDS_CHROMEOS_ACC_PASSWORDBOX, out_spoken_description);
261731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    } else {
262731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      AppendUtterance(IDS_CHROMEOS_ACC_TEXTBOX, out_spoken_description);
263731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    }
264731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  } else if (control_info->type() == keys::kTypeWindow) {
265731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    // No feedback when a window gets focus
266731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  }
267731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
268731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  if (is_action)
269731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    AppendUtterance(IDS_CHROMEOS_ACC_SELECTED, out_spoken_description);
270731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick}
271731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
272731df977c0511bca2206b5f333555b1205ff1f43Iain Merrickvoid WizardAccessibilityHandler::DescribeTextChanged(
273731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    const AccessibilityControlInfo* control_info,
274731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    std::string* out_spoken_description,
275731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    EarconType* out_earcon) {
276731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  DCHECK_EQ(control_info->type(), keys::kTypeTextBox);
277731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  const AccessibilityTextBoxInfo* text_box =
278731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      static_cast<const AccessibilityTextBoxInfo*>(control_info);
279731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
280731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  std::string old_value = previous_text_value_;
281731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  int old_start = previous_text_selection_start_;
282731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  int old_end = previous_text_selection_end_;
283731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  std::string new_value = GetTextBoxValue(text_box);
284731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  int new_start = text_box->selection_start();
285731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  int new_end = text_box->selection_end();
286731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
287731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  if (new_value == old_value) {
288731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    DescribeTextSelectionChanged(new_value,
289731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick                                 old_start, old_end,
290731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick                                 new_start, new_end,
291731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick                                 out_spoken_description);
292731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  } else {
293731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    DescribeTextContentsChanged(old_value, new_value,
294731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick                                out_spoken_description);
295731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  }
296731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick}
297731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
298731df977c0511bca2206b5f333555b1205ff1f43Iain Merrickstd::string WizardAccessibilityHandler::GetTextBoxValue(
299731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    const AccessibilityTextBoxInfo* textbox_info) {
300731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  std::string value = textbox_info->value();
301731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  if (textbox_info->password()) {
30272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    base::i18n::UTF8CharIterator iter(&value);
303731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    std::string obscured;
304731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    while (!iter.end()) {
305731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      obscured += "*";
306731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      iter.Advance();
307731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    }
308731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    return obscured;
309731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  } else {
310731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    return value;
311731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  }
312731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick}
313731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
314731df977c0511bca2206b5f333555b1205ff1f43Iain Merrickvoid WizardAccessibilityHandler::DescribeTextSelectionChanged(
315731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    const std::string& value,
316731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    int old_start,
317731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    int old_end,
318731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    int new_start,
319731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    int new_end,
320731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    std::string* out_spoken_description) {
321731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  if (new_start == new_end) {
322731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    // It's currently a cursor.
323731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    if (old_start != old_end) {
324731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      // It was previously a selection, so just announce 'unselected'.
325731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      AppendUtterance(IDS_CHROMEOS_ACC_TEXT_UNSELECTED, out_spoken_description);
326731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    } else if (old_start == new_start + 1 || old_start == new_start - 1) {
327731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      // Moved by one character; read it.
328731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      AppendUtterance(SubstringUTF8(value, std::min(old_start, new_start), 1),
329731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick                      out_spoken_description);
330731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    } else {
331731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      // Moved by more than one character. Read all characters crossed.
332731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      AppendUtterance(SubstringUTF8(value,
333731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick                                    std::min(old_start, new_start),
334731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick                                    abs(old_start - new_start)),
335731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick                      out_spoken_description);
336731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    }
337731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  } else {
338731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    // It's currently a selection.
339731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    if (old_start == old_end) {
340731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      // It was previously a cursor.
341731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      AppendUtterance(SubstringUTF8(value, new_start, new_end - new_start),
342731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick                      out_spoken_description);
343731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    } else if (old_start == new_start && old_end < new_end) {
344731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      // Added to end of selection.
345731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      AppendUtterance(SubstringUTF8(value, old_end, new_end - old_end),
346731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick                      out_spoken_description);
347731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    } else if (old_start == new_start && old_end > new_end) {
348731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      // Removed from end of selection.
349731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      AppendUtterance(SubstringUTF8(value, new_end, old_end - new_end),
350731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick                      out_spoken_description);
351731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    } else if (old_end == new_end && old_start > new_start) {
352731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      // Added to beginning of selection.
353731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      AppendUtterance(SubstringUTF8(value, new_start, old_start - new_start),
354731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick                      out_spoken_description);
355731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    } else if (old_end == new_end && old_start < new_start) {
356731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      // Removed from beginning of selection.
357731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      AppendUtterance(SubstringUTF8(value, old_start, new_start - old_start),
358731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick                      out_spoken_description);
359731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    } else {
360731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      // The selection changed but it wasn't an obvious extension of
361731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      // a previous selection. Just read the new selection.
362731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      AppendUtterance(SubstringUTF8(value, new_start, new_end - new_start),
363731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick                      out_spoken_description);
364731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    }
365731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  }
366731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick}
367731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
368731df977c0511bca2206b5f333555b1205ff1f43Iain Merrickvoid WizardAccessibilityHandler::DescribeTextContentsChanged(
369731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    const std::string& old_value,
370731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    const std::string& new_value,
371731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    std::string* out_spoken_description) {
372731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  int old_array_len = old_value.size();
373731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  int new_array_len = new_value.size();
374731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
375731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  // Get the unicode characters and indices of the start of each
376731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  // character's UTF8-encoded representation.
377731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  scoped_array<int32> old_chars(new int32[old_array_len]);
378731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  scoped_array<int> old_indices(new int[old_array_len + 1]);
37972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  base::i18n::UTF8CharIterator old_iter(&old_value);
380731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  while (!old_iter.end()) {
381731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    old_chars[old_iter.char_pos()] = old_iter.get();
382731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    old_indices[old_iter.char_pos()] = old_iter.array_pos();
383731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    old_iter.Advance();
384731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  }
385731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  int old_char_len = old_iter.char_pos();
386731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  old_indices[old_char_len] = old_iter.array_pos();
387731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
388731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  scoped_array<int32> new_chars(new int32[new_array_len]);
389731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  scoped_array<int> new_indices(new int[new_array_len + 1]);
39072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  base::i18n::UTF8CharIterator new_iter(&new_value);
391731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  while (!new_iter.end()) {
392731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    new_chars[new_iter.char_pos()] = new_iter.get();
393731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    new_indices[new_iter.char_pos()] = new_iter.array_pos();
394731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    new_iter.Advance();
395731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  }
396731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  int new_char_len = new_iter.char_pos();
397731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  new_indices[new_char_len] = new_iter.array_pos();
398731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
399731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  // Find the common prefix of the two strings.
400731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  int prefix_char_len = 0;
401731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  while (prefix_char_len < old_char_len &&
402731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick         prefix_char_len < new_char_len &&
403731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick         old_chars[prefix_char_len] == new_chars[prefix_char_len]) {
404731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    prefix_char_len++;
405731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  }
406731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
407731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  // Find the common suffix of the two stirngs.
408731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  int suffix_char_len = 0;
409731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  while (suffix_char_len < old_char_len - prefix_char_len &&
410731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick         suffix_char_len < new_char_len - prefix_char_len &&
411731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick         (old_chars[old_char_len - suffix_char_len - 1] ==
412731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick          new_chars[new_char_len - suffix_char_len - 1])) {
413731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    suffix_char_len++;
414731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  }
415731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
416731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  int old_suffix_char_start = old_char_len - suffix_char_len;
417731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  int new_suffix_char_start = new_char_len - suffix_char_len;
418731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
419731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  // Find the substring that was deleted (if any) to get the new string
420731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  // from the old - it's the part in the middle of the old string if you
421731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  // remove the common prefix and suffix.
422731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  std::string deleted = old_value.substr(
423731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      old_indices[prefix_char_len],
424731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      old_indices[old_suffix_char_start] - old_indices[prefix_char_len]);
425731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
426731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  // Find the substring that was inserted (if any) to get the new string
427731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  // from the old - it's the part in the middle of the new string if you
428731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  // remove the common prefix and suffix.
429731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  std::string inserted = new_value.substr(
430731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      new_indices[prefix_char_len],
431731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      new_indices[new_suffix_char_start] - new_indices[prefix_char_len]);
432731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
433dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  if (!inserted.empty() && !deleted.empty()) {
434731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    // Replace one substring with another, speak inserted text.
435731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    AppendUtterance(inserted, out_spoken_description);
436dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  } else if (!inserted.empty()) {
437731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    // Speak inserted text.
438731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    AppendUtterance(inserted, out_spoken_description);
439dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  } else if (!deleted.empty()) {
440731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    // Speak deleted text.
441731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    AppendUtterance(deleted, out_spoken_description);
442731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  }
443731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick}
444731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
445731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick}  // namespace chromeos
446