| OLD | NEW |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/browser/accessibility/browser_accessibility_android.h" | 5 #include "content/browser/accessibility/browser_accessibility_android.h" |
| 6 | 6 |
| 7 #include "base/i18n/break_iterator.h" | 7 #include "base/i18n/break_iterator.h" |
| 8 #include "base/strings/string_util.h" | 8 #include "base/strings/string_util.h" |
| 9 #include "base/strings/stringprintf.h" |
| 9 #include "base/strings/utf_string_conversions.h" | 10 #include "base/strings/utf_string_conversions.h" |
| 10 #include "content/browser/accessibility/browser_accessibility_manager_android.h" | 11 #include "content/browser/accessibility/browser_accessibility_manager_android.h" |
| 11 #include "content/common/accessibility_messages.h" | 12 #include "content/common/accessibility_messages.h" |
| 12 | 13 |
| 13 namespace { | 14 namespace { |
| 14 | 15 |
| 15 // These are enums from android.text.InputType in Java: | 16 // These are enums from android.text.InputType in Java: |
| 16 enum { | 17 enum { |
| 17 ANDROID_TEXT_INPUTTYPE_TYPE_NULL = 0, | 18 ANDROID_TEXT_INPUTTYPE_TYPE_NULL = 0, |
| 18 ANDROID_TEXT_INPUTTYPE_TYPE_DATETIME = 0x4, | 19 ANDROID_TEXT_INPUTTYPE_TYPE_DATETIME = 0x4, |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 69 if (IsIframe() || | 70 if (IsIframe() || |
| 70 GetRole() == ui::AX_ROLE_ROOT_WEB_AREA || | 71 GetRole() == ui::AX_ROLE_ROOT_WEB_AREA || |
| 71 GetRole() == ui::AX_ROLE_WEB_AREA) { | 72 GetRole() == ui::AX_ROLE_WEB_AREA) { |
| 72 return false; | 73 return false; |
| 73 } | 74 } |
| 74 | 75 |
| 75 // If it has a focusable child, we definitely can't leave out children. | 76 // If it has a focusable child, we definitely can't leave out children. |
| 76 if (HasFocusableChild()) | 77 if (HasFocusableChild()) |
| 77 return false; | 78 return false; |
| 78 | 79 |
| 80 // Date and time controls should drop their children. |
| 81 if (GetRole() == ui::AX_ROLE_DATE || GetRole() == ui::AX_ROLE_TIME) |
| 82 return true; |
| 83 |
| 79 // Headings with text can drop their children. | 84 // Headings with text can drop their children. |
| 80 base::string16 name = GetText(); | 85 base::string16 name = GetText(); |
| 81 if (GetRole() == ui::AX_ROLE_HEADING && !name.empty()) | 86 if (GetRole() == ui::AX_ROLE_HEADING && !name.empty()) |
| 82 return true; | 87 return true; |
| 83 | 88 |
| 84 // Focusable nodes with text can drop their children. | 89 // Focusable nodes with text can drop their children. |
| 85 if (HasState(ui::AX_STATE_FOCUSABLE) && !name.empty()) | 90 if (HasState(ui::AX_STATE_FOCUSABLE) && !name.empty()) |
| 86 return true; | 91 return true; |
| 87 | 92 |
| 88 // Nodes with only static text as children can drop their children. | 93 // Nodes with only static text as children can drop their children. |
| (...skipping 155 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 244 switch(GetRole()) { | 249 switch(GetRole()) { |
| 245 case ui::AX_ROLE_EDITABLE_TEXT: | 250 case ui::AX_ROLE_EDITABLE_TEXT: |
| 246 case ui::AX_ROLE_SPIN_BUTTON: | 251 case ui::AX_ROLE_SPIN_BUTTON: |
| 247 case ui::AX_ROLE_TEXT_AREA: | 252 case ui::AX_ROLE_TEXT_AREA: |
| 248 case ui::AX_ROLE_TEXT_FIELD: | 253 case ui::AX_ROLE_TEXT_FIELD: |
| 249 class_name = "android.widget.EditText"; | 254 class_name = "android.widget.EditText"; |
| 250 break; | 255 break; |
| 251 case ui::AX_ROLE_SLIDER: | 256 case ui::AX_ROLE_SLIDER: |
| 252 class_name = "android.widget.SeekBar"; | 257 class_name = "android.widget.SeekBar"; |
| 253 break; | 258 break; |
| 259 case ui::AX_ROLE_COLOR_WELL: |
| 254 case ui::AX_ROLE_COMBO_BOX: | 260 case ui::AX_ROLE_COMBO_BOX: |
| 261 case ui::AX_ROLE_DATE: |
| 262 case ui::AX_ROLE_POP_UP_BUTTON: |
| 263 case ui::AX_ROLE_TIME: |
| 255 class_name = "android.widget.Spinner"; | 264 class_name = "android.widget.Spinner"; |
| 256 break; | 265 break; |
| 257 case ui::AX_ROLE_BUTTON: | 266 case ui::AX_ROLE_BUTTON: |
| 258 case ui::AX_ROLE_MENU_BUTTON: | 267 case ui::AX_ROLE_MENU_BUTTON: |
| 259 case ui::AX_ROLE_POP_UP_BUTTON: | |
| 260 class_name = "android.widget.Button"; | 268 class_name = "android.widget.Button"; |
| 261 break; | 269 break; |
| 262 case ui::AX_ROLE_CHECK_BOX: | 270 case ui::AX_ROLE_CHECK_BOX: |
| 263 class_name = "android.widget.CheckBox"; | 271 class_name = "android.widget.CheckBox"; |
| 264 break; | 272 break; |
| 265 case ui::AX_ROLE_RADIO_BUTTON: | 273 case ui::AX_ROLE_RADIO_BUTTON: |
| 266 class_name = "android.widget.RadioButton"; | 274 class_name = "android.widget.RadioButton"; |
| 267 break; | 275 break; |
| 268 case ui::AX_ROLE_TOGGLE_BUTTON: | 276 case ui::AX_ROLE_TOGGLE_BUTTON: |
| 269 class_name = "android.widget.ToggleButton"; | 277 class_name = "android.widget.ToggleButton"; |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 312 if (IsIframe() || | 320 if (IsIframe() || |
| 313 GetRole() == ui::AX_ROLE_WEB_AREA) { | 321 GetRole() == ui::AX_ROLE_WEB_AREA) { |
| 314 return base::string16(); | 322 return base::string16(); |
| 315 } | 323 } |
| 316 | 324 |
| 317 // See comment in browser_accessibility_win.cc for details. | 325 // See comment in browser_accessibility_win.cc for details. |
| 318 // The difference here is that we can only expose one accessible | 326 // The difference here is that we can only expose one accessible |
| 319 // name on Android, not 2 or 3 like on Windows or Mac. | 327 // name on Android, not 2 or 3 like on Windows or Mac. |
| 320 | 328 |
| 321 // First, always return the |value| attribute if this is an | 329 // First, always return the |value| attribute if this is an |
| 322 // editable text field. | 330 // input field. |
| 323 if (!value().empty() && | 331 if (!value().empty()) { |
| 324 (GetRole() == ui::AX_ROLE_EDITABLE_TEXT || | 332 if (HasState(ui::AX_STATE_EDITABLE)) |
| 325 GetRole() == ui::AX_ROLE_TEXT_AREA || | 333 return base::UTF8ToUTF16(value()); |
| 326 GetRole() == ui::AX_ROLE_TEXT_FIELD || | 334 |
| 327 HasState(ui::AX_STATE_EDITABLE))) { | 335 switch (GetRole()) { |
| 328 return base::UTF8ToUTF16(value()); | 336 case ui::AX_ROLE_COMBO_BOX: |
| 337 case ui::AX_ROLE_EDITABLE_TEXT: |
| 338 case ui::AX_ROLE_POP_UP_BUTTON: |
| 339 case ui::AX_ROLE_TEXT_AREA: |
| 340 case ui::AX_ROLE_TEXT_FIELD: |
| 341 return base::UTF8ToUTF16(value()); |
| 342 } |
| 343 } |
| 344 |
| 345 // For color wells, the color is stored in separate attributes. |
| 346 // Perhaps we could return color names in the future? |
| 347 if (GetRole() == ui::AX_ROLE_COLOR_WELL) { |
| 348 int red = GetIntAttribute(ui::AX_ATTR_COLOR_VALUE_RED); |
| 349 int green = GetIntAttribute(ui::AX_ATTR_COLOR_VALUE_GREEN); |
| 350 int blue = GetIntAttribute(ui::AX_ATTR_COLOR_VALUE_BLUE); |
| 351 return base::UTF8ToUTF16( |
| 352 base::StringPrintf("#%02X%02X%02X", red, green, blue)); |
| 329 } | 353 } |
| 330 | 354 |
| 331 // Always prefer visible text if this is a link. Sites sometimes add | 355 // Always prefer visible text if this is a link. Sites sometimes add |
| 332 // a "title" attribute to a link with more information, but we can't | 356 // a "title" attribute to a link with more information, but we can't |
| 333 // lose the link text. | 357 // lose the link text. |
| 334 if (!name().empty() && GetRole() == ui::AX_ROLE_LINK) | 358 if (!name().empty() && GetRole() == ui::AX_ROLE_LINK) |
| 335 return base::UTF8ToUTF16(name()); | 359 return base::UTF8ToUTF16(name()); |
| 336 | 360 |
| 337 // If there's no text value, the basic rule is: prefer description | 361 // If there's no text value, the basic rule is: prefer description |
| 338 // (aria-labelledby or aria-label), then help (title), then name | 362 // (aria-labelledby or aria-label), then help (title), then name |
| 339 // (inner text), then value (control value). However, if | 363 // (inner text), then value (control value). However, if |
| 340 // title_elem_id is set, that means there's a label element | 364 // title_elem_id is set, that means there's a label element |
| 341 // supplying the name and then name takes precedence over help. | 365 // supplying the name and then name takes precedence over help. |
| 342 // TODO(dmazzoni): clean this up by providing more granular labels in | 366 // TODO(dmazzoni): clean this up by providing more granular labels in |
| 343 // Blink, making the platform-specific mapping to accessible text simpler. | 367 // Blink, making the platform-specific mapping to accessible text simpler. |
| 344 base::string16 description = GetString16Attribute(ui::AX_ATTR_DESCRIPTION); | 368 base::string16 description = GetString16Attribute(ui::AX_ATTR_DESCRIPTION); |
| 345 base::string16 help = GetString16Attribute(ui::AX_ATTR_HELP); | 369 base::string16 help = GetString16Attribute(ui::AX_ATTR_HELP); |
| 370 |
| 346 base::string16 placeholder; | 371 base::string16 placeholder; |
| 347 GetHtmlAttribute("placeholder", &placeholder); | 372 switch (GetRole()) { |
| 373 case ui::AX_ROLE_DATE: |
| 374 case ui::AX_ROLE_EDITABLE_TEXT: |
| 375 case ui::AX_ROLE_TEXT_AREA: |
| 376 case ui::AX_ROLE_TEXT_FIELD: |
| 377 case ui::AX_ROLE_TIME: |
| 378 GetHtmlAttribute("placeholder", &placeholder); |
| 379 } |
| 380 |
| 348 int title_elem_id = GetIntAttribute( | 381 int title_elem_id = GetIntAttribute( |
| 349 ui::AX_ATTR_TITLE_UI_ELEMENT); | 382 ui::AX_ATTR_TITLE_UI_ELEMENT); |
| 350 base::string16 text; | 383 base::string16 text; |
| 351 if (!description.empty()) | 384 if (!description.empty()) |
| 352 text = description; | 385 text = description; |
| 353 else if (title_elem_id && !name().empty()) | 386 else if (title_elem_id && !name().empty()) |
| 354 text = base::UTF8ToUTF16(name()); | 387 text = base::UTF8ToUTF16(name()); |
| 355 else if (!help.empty()) | 388 else if (!help.empty()) |
| 356 text = help; | 389 text = help; |
| 357 else if (!name().empty()) | 390 else if (!name().empty()) |
| 358 text = base::UTF8ToUTF16(name()); | 391 text = base::UTF8ToUTF16(name()); |
| 359 else if (GetRole() == ui::AX_ROLE_TEXT_FIELD && !placeholder.empty()) | 392 else if (!placeholder.empty()) |
| 360 text = placeholder; | 393 text = placeholder; |
| 361 else if (!value().empty()) | 394 else if (!value().empty()) |
| 362 text = base::UTF8ToUTF16(value()); | 395 text = base::UTF8ToUTF16(value()); |
| 363 | 396 |
| 364 // This is called from PlatformIsLeaf, so don't call PlatformChildCount | 397 // This is called from PlatformIsLeaf, so don't call PlatformChildCount |
| 365 // from within this! | 398 // from within this! |
| 366 if (text.empty() && | 399 if (text.empty() && |
| 367 (HasOnlyStaticTextChildren() || | 400 (HasOnlyStaticTextChildren() || |
| 368 (IsFocusable() && HasOnlyTextAndImageChildren()))) { | 401 (IsFocusable() && HasOnlyTextAndImageChildren()))) { |
| 369 for (uint32 i = 0; i < InternalChildCount(); i++) { | 402 for (uint32 i = 0; i < InternalChildCount(); i++) { |
| (...skipping 459 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 829 int BrowserAccessibilityAndroid::CountChildrenWithRole(ui::AXRole role) const { | 862 int BrowserAccessibilityAndroid::CountChildrenWithRole(ui::AXRole role) const { |
| 830 int count = 0; | 863 int count = 0; |
| 831 for (uint32 i = 0; i < PlatformChildCount(); i++) { | 864 for (uint32 i = 0; i < PlatformChildCount(); i++) { |
| 832 if (PlatformGetChild(i)->GetRole() == role) | 865 if (PlatformGetChild(i)->GetRole() == role) |
| 833 count++; | 866 count++; |
| 834 } | 867 } |
| 835 return count; | 868 return count; |
| 836 } | 869 } |
| 837 | 870 |
| 838 } // namespace content | 871 } // namespace content |
| OLD | NEW |