| 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_number_conversions.h" | 8 #include "base/strings/string_number_conversions.h" |
| 9 #include "base/strings/string_util.h" | 9 #include "base/strings/string_util.h" |
| 10 #include "base/strings/stringprintf.h" | 10 #include "base/strings/stringprintf.h" |
| 11 #include "base/strings/utf_string_conversions.h" | 11 #include "base/strings/utf_string_conversions.h" |
| 12 #include "content/app/strings/grit/content_strings.h" | 12 #include "content/app/strings/grit/content_strings.h" |
| 13 #include "content/browser/accessibility/browser_accessibility_manager_android.h" | 13 #include "content/browser/accessibility/browser_accessibility_manager_android.h" |
| 14 #include "content/common/accessibility_messages.h" | 14 #include "content/common/accessibility_messages.h" |
| 15 #include "content/public/common/content_client.h" | 15 #include "content/public/common/content_client.h" |
| 16 #include "third_party/skia/include/core/SkColor.h" | 16 #include "third_party/skia/include/core/SkColor.h" |
| 17 #include "ui/accessibility/platform/ax_android_constants.h" | 17 #include "ui/accessibility/platform/ax_android_constants.h" |
| 18 #include "ui/accessibility/platform/ax_snapshot_node_android_platform.h" |
| 18 | 19 |
| 19 namespace { | 20 namespace { |
| 20 | 21 |
| 21 const base::char16 kSecurePasswordBullet = 0x2022; | |
| 22 | |
| 23 // These are enums from android.text.InputType in Java: | 22 // These are enums from android.text.InputType in Java: |
| 24 enum { | 23 enum { |
| 25 ANDROID_TEXT_INPUTTYPE_TYPE_NULL = 0, | 24 ANDROID_TEXT_INPUTTYPE_TYPE_NULL = 0, |
| 26 ANDROID_TEXT_INPUTTYPE_TYPE_DATETIME = 0x4, | 25 ANDROID_TEXT_INPUTTYPE_TYPE_DATETIME = 0x4, |
| 27 ANDROID_TEXT_INPUTTYPE_TYPE_DATETIME_DATE = 0x14, | 26 ANDROID_TEXT_INPUTTYPE_TYPE_DATETIME_DATE = 0x14, |
| 28 ANDROID_TEXT_INPUTTYPE_TYPE_DATETIME_TIME = 0x24, | 27 ANDROID_TEXT_INPUTTYPE_TYPE_DATETIME_TIME = 0x24, |
| 29 ANDROID_TEXT_INPUTTYPE_TYPE_NUMBER = 0x2, | 28 ANDROID_TEXT_INPUTTYPE_TYPE_NUMBER = 0x2, |
| 30 ANDROID_TEXT_INPUTTYPE_TYPE_PHONE = 0x3, | 29 ANDROID_TEXT_INPUTTYPE_TYPE_PHONE = 0x3, |
| 31 ANDROID_TEXT_INPUTTYPE_TYPE_TEXT = 0x1, | 30 ANDROID_TEXT_INPUTTYPE_TYPE_TEXT = 0x1, |
| 32 ANDROID_TEXT_INPUTTYPE_TYPE_TEXT_URI = 0x11, | 31 ANDROID_TEXT_INPUTTYPE_TYPE_TEXT_URI = 0x11, |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 74 | 73 |
| 75 base::string16 BrowserAccessibilityAndroid::GetValue() const { | 74 base::string16 BrowserAccessibilityAndroid::GetValue() const { |
| 76 base::string16 value = BrowserAccessibility::GetValue(); | 75 base::string16 value = BrowserAccessibility::GetValue(); |
| 77 | 76 |
| 78 // Optionally replace entered password text with bullet characters | 77 // Optionally replace entered password text with bullet characters |
| 79 // based on a user preference. | 78 // based on a user preference. |
| 80 if (IsPassword()) { | 79 if (IsPassword()) { |
| 81 bool should_expose = static_cast<BrowserAccessibilityManagerAndroid*>( | 80 bool should_expose = static_cast<BrowserAccessibilityManagerAndroid*>( |
| 82 manager())->ShouldExposePasswordText(); | 81 manager())->ShouldExposePasswordText(); |
| 83 if (!should_expose) { | 82 if (!should_expose) { |
| 84 value = base::string16(value.size(), kSecurePasswordBullet); | 83 value = base::string16(value.size(), ui::kSecurePasswordBullet); |
| 85 } | 84 } |
| 86 } | 85 } |
| 87 | 86 |
| 88 return value; | 87 return value; |
| 89 } | 88 } |
| 90 | 89 |
| 91 bool BrowserAccessibilityAndroid::PlatformIsLeaf() const { | 90 bool BrowserAccessibilityAndroid::PlatformIsLeaf() const { |
| 92 if (BrowserAccessibility::PlatformIsLeaf()) | 91 if (BrowserAccessibility::PlatformIsLeaf()) |
| 93 return true; | 92 return true; |
| 94 | 93 |
| (...skipping 143 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 238 GetRole() == ui::AX_ROLE_ROW_HEADER); | 237 GetRole() == ui::AX_ROLE_ROW_HEADER); |
| 239 } | 238 } |
| 240 | 239 |
| 241 bool BrowserAccessibilityAndroid::IsHierarchical() const { | 240 bool BrowserAccessibilityAndroid::IsHierarchical() const { |
| 242 return (GetRole() == ui::AX_ROLE_LIST || | 241 return (GetRole() == ui::AX_ROLE_LIST || |
| 243 GetRole() == ui::AX_ROLE_DESCRIPTION_LIST || | 242 GetRole() == ui::AX_ROLE_DESCRIPTION_LIST || |
| 244 GetRole() == ui::AX_ROLE_TREE); | 243 GetRole() == ui::AX_ROLE_TREE); |
| 245 } | 244 } |
| 246 | 245 |
| 247 bool BrowserAccessibilityAndroid::IsLink() const { | 246 bool BrowserAccessibilityAndroid::IsLink() const { |
| 248 return (GetRole() == ui::AX_ROLE_LINK || | 247 return ui::AXSnapshotNodeAndroid::AXRoleIsLink(GetRole()); |
| 249 GetRole() == ui::AX_ROLE_IMAGE_MAP_LINK); | |
| 250 } | 248 } |
| 251 | 249 |
| 252 bool BrowserAccessibilityAndroid::IsMultiLine() const { | 250 bool BrowserAccessibilityAndroid::IsMultiLine() const { |
| 253 return HasState(ui::AX_STATE_MULTILINE); | 251 return HasState(ui::AX_STATE_MULTILINE); |
| 254 } | 252 } |
| 255 | 253 |
| 256 bool BrowserAccessibilityAndroid::IsPassword() const { | 254 bool BrowserAccessibilityAndroid::IsPassword() const { |
| 257 return HasState(ui::AX_STATE_PROTECTED); | 255 return HasState(ui::AX_STATE_PROTECTED); |
| 258 } | 256 } |
| 259 | 257 |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 321 } | 319 } |
| 322 | 320 |
| 323 return sole_interesting_node; | 321 return sole_interesting_node; |
| 324 } | 322 } |
| 325 | 323 |
| 326 bool BrowserAccessibilityAndroid::CanOpenPopup() const { | 324 bool BrowserAccessibilityAndroid::CanOpenPopup() const { |
| 327 return HasState(ui::AX_STATE_HASPOPUP); | 325 return HasState(ui::AX_STATE_HASPOPUP); |
| 328 } | 326 } |
| 329 | 327 |
| 330 const char* BrowserAccessibilityAndroid::GetClassName() const { | 328 const char* BrowserAccessibilityAndroid::GetClassName() const { |
| 331 const char* class_name = NULL; | 329 return ui::AXSnapshotNodeAndroid::AXRoleToAndroidClassName( |
| 332 | 330 GetRole(), PlatformGetParent() != nullptr); |
| 333 switch (GetRole()) { | |
| 334 case ui::AX_ROLE_SEARCH_BOX: | |
| 335 case ui::AX_ROLE_SPIN_BUTTON: | |
| 336 case ui::AX_ROLE_TEXT_FIELD: | |
| 337 class_name = ui::kAXEditTextClassname; | |
| 338 break; | |
| 339 case ui::AX_ROLE_SLIDER: | |
| 340 class_name = ui::kAXSeekBarClassname; | |
| 341 break; | |
| 342 case ui::AX_ROLE_COLOR_WELL: | |
| 343 case ui::AX_ROLE_COMBO_BOX: | |
| 344 case ui::AX_ROLE_DATE: | |
| 345 case ui::AX_ROLE_POP_UP_BUTTON: | |
| 346 case ui::AX_ROLE_INPUT_TIME: | |
| 347 class_name = ui::kAXSpinnerClassname; | |
| 348 break; | |
| 349 case ui::AX_ROLE_BUTTON: | |
| 350 case ui::AX_ROLE_MENU_BUTTON: | |
| 351 class_name = ui::kAXButtonClassname; | |
| 352 break; | |
| 353 case ui::AX_ROLE_CHECK_BOX: | |
| 354 case ui::AX_ROLE_SWITCH: | |
| 355 class_name = ui::kAXCheckBoxClassname; | |
| 356 break; | |
| 357 case ui::AX_ROLE_RADIO_BUTTON: | |
| 358 class_name = ui::kAXRadioButtonClassname; | |
| 359 break; | |
| 360 case ui::AX_ROLE_TOGGLE_BUTTON: | |
| 361 class_name = ui::kAXToggleButtonClassname; | |
| 362 break; | |
| 363 case ui::AX_ROLE_CANVAS: | |
| 364 case ui::AX_ROLE_IMAGE: | |
| 365 case ui::AX_ROLE_SVG_ROOT: | |
| 366 class_name = ui::kAXImageClassname; | |
| 367 break; | |
| 368 case ui::AX_ROLE_METER: | |
| 369 case ui::AX_ROLE_PROGRESS_INDICATOR: | |
| 370 class_name = ui::kAXProgressBarClassname; | |
| 371 break; | |
| 372 case ui::AX_ROLE_TAB_LIST: | |
| 373 class_name = ui::kAXTabWidgetClassname; | |
| 374 break; | |
| 375 case ui::AX_ROLE_GRID: | |
| 376 case ui::AX_ROLE_TREE_GRID: | |
| 377 case ui::AX_ROLE_TABLE: | |
| 378 class_name = ui::kAXGridViewClassname; | |
| 379 break; | |
| 380 case ui::AX_ROLE_LIST: | |
| 381 case ui::AX_ROLE_LIST_BOX: | |
| 382 case ui::AX_ROLE_DESCRIPTION_LIST: | |
| 383 class_name = ui::kAXListViewClassname; | |
| 384 break; | |
| 385 case ui::AX_ROLE_DIALOG: | |
| 386 class_name = ui::kAXDialogClassname; | |
| 387 break; | |
| 388 case ui::AX_ROLE_ROOT_WEB_AREA: | |
| 389 if (PlatformGetParent() == nullptr) | |
| 390 class_name = ui::kAXWebViewClassname; | |
| 391 else | |
| 392 class_name = ui::kAXViewClassname; | |
| 393 break; | |
| 394 case ui::AX_ROLE_MENU_ITEM: | |
| 395 case ui::AX_ROLE_MENU_ITEM_CHECK_BOX: | |
| 396 case ui::AX_ROLE_MENU_ITEM_RADIO: | |
| 397 class_name = ui::kAXMenuItemClassname; | |
| 398 break; | |
| 399 default: | |
| 400 class_name = ui::kAXViewClassname; | |
| 401 break; | |
| 402 } | |
| 403 | |
| 404 return class_name; | |
| 405 } | 331 } |
| 406 | 332 |
| 407 base::string16 BrowserAccessibilityAndroid::GetText() const { | 333 base::string16 BrowserAccessibilityAndroid::GetText() const { |
| 408 if (IsIframe() || | 334 if (IsIframe() || |
| 409 GetRole() == ui::AX_ROLE_WEB_AREA) { | 335 GetRole() == ui::AX_ROLE_WEB_AREA) { |
| 410 return base::string16(); | 336 return base::string16(); |
| 411 } | 337 } |
| 412 | 338 |
| 413 // In accordance with ARIA, some elements use their contents as an | 339 // In accordance with ARIA, some elements use their contents as an |
| 414 // accessibility name, so some elements will have the same name as their | 340 // accessibility name, so some elements will have the same name as their |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 477 if (text.empty() && (HasOnlyTextChildren() || | 403 if (text.empty() && (HasOnlyTextChildren() || |
| 478 (IsFocusable() && HasOnlyTextAndImageChildren()))) { | 404 (IsFocusable() && HasOnlyTextAndImageChildren()))) { |
| 479 for (uint32_t i = 0; i < InternalChildCount(); i++) { | 405 for (uint32_t i = 0; i < InternalChildCount(); i++) { |
| 480 BrowserAccessibility* child = InternalGetChild(i); | 406 BrowserAccessibility* child = InternalGetChild(i); |
| 481 text += static_cast<BrowserAccessibilityAndroid*>(child)->GetText(); | 407 text += static_cast<BrowserAccessibilityAndroid*>(child)->GetText(); |
| 482 } | 408 } |
| 483 } | 409 } |
| 484 | 410 |
| 485 if (text.empty() && (IsLink() || GetRole() == ui::AX_ROLE_IMAGE)) { | 411 if (text.empty() && (IsLink() || GetRole() == ui::AX_ROLE_IMAGE)) { |
| 486 base::string16 url = GetString16Attribute(ui::AX_ATTR_URL); | 412 base::string16 url = GetString16Attribute(ui::AX_ATTR_URL); |
| 487 // Given a url like http://foo.com/bar/baz.png, just return the | 413 text = ui::AXSnapshotNodeAndroid::AXUrlBaseText(url); |
| 488 // base text, e.g., "baz". | |
| 489 int trailing_slashes = 0; | |
| 490 while (url.size() - trailing_slashes > 0 && | |
| 491 url[url.size() - trailing_slashes - 1] == '/') { | |
| 492 trailing_slashes++; | |
| 493 } | |
| 494 if (trailing_slashes) | |
| 495 url = url.substr(0, url.size() - trailing_slashes); | |
| 496 size_t slash_index = url.rfind('/'); | |
| 497 if (slash_index != std::string::npos) | |
| 498 url = url.substr(slash_index + 1); | |
| 499 size_t dot_index = url.rfind('.'); | |
| 500 if (dot_index != std::string::npos) | |
| 501 url = url.substr(0, dot_index); | |
| 502 text = url; | |
| 503 } | 414 } |
| 504 | 415 |
| 505 return text; | 416 return text; |
| 506 } | 417 } |
| 507 | 418 |
| 508 base::string16 BrowserAccessibilityAndroid::GetRoleDescription() const { | 419 base::string16 BrowserAccessibilityAndroid::GetRoleDescription() const { |
| 509 content::ContentClient* content_client = content::GetContentClient(); | 420 content::ContentClient* content_client = content::GetContentClient(); |
| 510 | 421 |
| 511 // As a special case, if we have a heading level return a string like | 422 // As a special case, if we have a heading level return a string like |
| 512 // "heading level 1", etc. | 423 // "heading level 1", etc. |
| (...skipping 991 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1504 int BrowserAccessibilityAndroid::CountChildrenWithRole(ui::AXRole role) const { | 1415 int BrowserAccessibilityAndroid::CountChildrenWithRole(ui::AXRole role) const { |
| 1505 int count = 0; | 1416 int count = 0; |
| 1506 for (uint32_t i = 0; i < PlatformChildCount(); i++) { | 1417 for (uint32_t i = 0; i < PlatformChildCount(); i++) { |
| 1507 if (PlatformGetChild(i)->GetRole() == role) | 1418 if (PlatformGetChild(i)->GetRole() == role) |
| 1508 count++; | 1419 count++; |
| 1509 } | 1420 } |
| 1510 return count; | 1421 return count; |
| 1511 } | 1422 } |
| 1512 | 1423 |
| 1513 } // namespace content | 1424 } // namespace content |
| OLD | NEW |