OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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_win.h" | 5 #include "content/browser/accessibility/browser_accessibility_win.h" |
6 | 6 |
7 #include <UIAutomationClient.h> | 7 #include <UIAutomationClient.h> |
8 #include <UIAutomationCoreApi.h> | 8 #include <UIAutomationCoreApi.h> |
9 | 9 |
10 #include <algorithm> | 10 #include <algorithm> |
(...skipping 438 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
449 return S_OK; | 449 return S_OK; |
450 } | 450 } |
451 | 451 |
452 STDMETHODIMP BrowserAccessibilityWin::get_accHelp(VARIANT var_id, BSTR* help) { | 452 STDMETHODIMP BrowserAccessibilityWin::get_accHelp(VARIANT var_id, BSTR* help) { |
453 if (!instance_active()) | 453 if (!instance_active()) |
454 return E_FAIL; | 454 return E_FAIL; |
455 | 455 |
456 if (!help) | 456 if (!help) |
457 return E_INVALIDARG; | 457 return E_INVALIDARG; |
458 | 458 |
459 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id); | 459 return S_FALSE; |
460 if (!target) | |
461 return E_INVALIDARG; | |
462 | |
463 base::string16 help_str = target->help(); | |
464 if (help_str.empty()) | |
465 return S_FALSE; | |
466 | |
467 *help = SysAllocString(help_str.c_str()); | |
468 | |
469 DCHECK(*help); | |
470 return S_OK; | |
471 } | 460 } |
472 | 461 |
473 STDMETHODIMP BrowserAccessibilityWin::get_accKeyboardShortcut(VARIANT var_id, | 462 STDMETHODIMP BrowserAccessibilityWin::get_accKeyboardShortcut(VARIANT var_id, |
474 BSTR* acc_key) { | 463 BSTR* acc_key) { |
475 if (!instance_active()) | 464 if (!instance_active()) |
476 return E_FAIL; | 465 return E_FAIL; |
477 | 466 |
478 if (!acc_key) | 467 if (!acc_key) |
479 return E_INVALIDARG; | 468 return E_INVALIDARG; |
480 | 469 |
(...skipping 10 matching lines...) Expand all Loading... | |
491 return E_FAIL; | 480 return E_FAIL; |
492 | 481 |
493 if (!name) | 482 if (!name) |
494 return E_INVALIDARG; | 483 return E_INVALIDARG; |
495 | 484 |
496 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id); | 485 BrowserAccessibilityWin* target = GetTargetFromChildID(var_id); |
497 if (!target) | 486 if (!target) |
498 return E_INVALIDARG; | 487 return E_INVALIDARG; |
499 | 488 |
500 base::string16 name_str = target->name(); | 489 base::string16 name_str = target->name(); |
501 | |
502 // If the name is empty, see if it's labeled by another element. | |
503 if (name_str.empty()) { | |
504 int title_elem_id; | |
505 if (target->GetIntAttribute(ui::AX_ATTR_TITLE_UI_ELEMENT, | |
506 &title_elem_id)) { | |
507 BrowserAccessibilityWin* title_elem = | |
508 manager()->GetFromID(title_elem_id)->ToBrowserAccessibilityWin(); | |
509 if (title_elem) | |
510 name_str = title_elem->GetNameRecursive(); | |
511 } | |
512 } | |
513 | |
514 if (name_str.empty()) | 490 if (name_str.empty()) |
515 return S_FALSE; | 491 return S_FALSE; |
516 | 492 |
517 *name = SysAllocString(name_str.c_str()); | 493 *name = SysAllocString(name_str.c_str()); |
518 | 494 |
519 DCHECK(*name); | 495 DCHECK(*name); |
520 return S_OK; | 496 return S_OK; |
521 } | 497 } |
522 | 498 |
523 STDMETHODIMP BrowserAccessibilityWin::get_accParent(IDispatch** disp_parent) { | 499 STDMETHODIMP BrowserAccessibilityWin::get_accParent(IDispatch** disp_parent) { |
(...skipping 1614 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2138 // The spec does not allow the start or end offsets to be out or range; | 2114 // The spec does not allow the start or end offsets to be out or range; |
2139 // we must return an error if so. | 2115 // we must return an error if so. |
2140 LONG len = text_str.length(); | 2116 LONG len = text_str.length(); |
2141 if (start_offset < 0) | 2117 if (start_offset < 0) |
2142 return E_INVALIDARG; | 2118 return E_INVALIDARG; |
2143 if (end_offset > len) | 2119 if (end_offset > len) |
2144 return E_INVALIDARG; | 2120 return E_INVALIDARG; |
2145 | 2121 |
2146 base::string16 substr = text_str.substr(start_offset, | 2122 base::string16 substr = text_str.substr(start_offset, |
2147 end_offset - start_offset); | 2123 end_offset - start_offset); |
2124 | |
2148 if (substr.empty()) | 2125 if (substr.empty()) |
2149 return S_FALSE; | 2126 return S_FALSE; |
2150 | 2127 |
2151 *text = SysAllocString(substr.c_str()); | 2128 *text = SysAllocString(substr.c_str()); |
2152 DCHECK(*text); | 2129 DCHECK(*text); |
2153 return S_OK; | 2130 return S_OK; |
2154 } | 2131 } |
2155 | 2132 |
2156 STDMETHODIMP BrowserAccessibilityWin::get_textAtOffset( | 2133 STDMETHODIMP BrowserAccessibilityWin::get_textAtOffset( |
2157 LONG offset, | 2134 LONG offset, |
(...skipping 1239 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
3397 win_attributes_->ia2_attributes.push_back(L"sort:descending"); | 3374 win_attributes_->ia2_attributes.push_back(L"sort:descending"); |
3398 break; | 3375 break; |
3399 case ui::AX_SORT_DIRECTION_OTHER: | 3376 case ui::AX_SORT_DIRECTION_OTHER: |
3400 win_attributes_->ia2_attributes.push_back(L"sort:other"); | 3377 win_attributes_->ia2_attributes.push_back(L"sort:other"); |
3401 break; | 3378 break; |
3402 default: | 3379 default: |
3403 NOTREACHED(); | 3380 NOTREACHED(); |
3404 } | 3381 } |
3405 } | 3382 } |
3406 | 3383 |
3407 // The calculation of the accessible name of an element has been | 3384 win_attributes_->name = GetString16Attribute(ui::AX_ATTR_NAME); |
3408 // standardized in the HTML to Platform Accessibility APIs Implementation | 3385 win_attributes_->description = GetString16Attribute(ui::AX_ATTR_DESCRIPTION); |
3409 // Guide (http://www.w3.org/TR/html-aapi/). In order to return the | |
3410 // appropriate accessible name on Windows, we need to apply some logic | |
3411 // to the fields we get from WebKit. | |
3412 // | |
3413 // TODO(dmazzoni): move most of this logic into WebKit. | |
3414 // | |
3415 // WebKit gives us: | |
3416 // | |
3417 // name: the default name, e.g. inner text | |
3418 // title ui element: a reference to a <label> element on the same | |
3419 // page that labels this node. | |
3420 // description: accessible labels that override the default name: | |
3421 // aria-label or aria-labelledby or aria-describedby | |
3422 // help: the value of the "title" attribute | |
3423 // | |
3424 // On Windows, the logic we apply lets some fields take precedence and | |
3425 // always returns the primary name in "name" and the secondary name, | |
3426 // if any, in "description". | |
3427 | 3386 |
3428 int title_elem_id = GetIntAttribute(ui::AX_ATTR_TITLE_UI_ELEMENT); | |
3429 base::string16 name = GetString16Attribute(ui::AX_ATTR_NAME); | |
3430 base::string16 description = GetString16Attribute(ui::AX_ATTR_DESCRIPTION); | |
3431 base::string16 help = GetString16Attribute(ui::AX_ATTR_HELP); | |
3432 base::string16 value = GetString16Attribute(ui::AX_ATTR_VALUE); | 3387 base::string16 value = GetString16Attribute(ui::AX_ATTR_VALUE); |
3433 | 3388 |
3434 // WebKit annoyingly puts the title in the description if there's no other | |
3435 // description, which just confuses the rest of the logic. Put it back. | |
3436 // Now "help" is always the value of the "title" attribute, if present. | |
3437 base::string16 title_attr; | |
3438 if (GetHtmlAttribute("title", &title_attr) && | |
3439 description == title_attr && | |
3440 help.empty()) { | |
3441 help = description; | |
3442 description.clear(); | |
3443 } | |
3444 | |
3445 // Now implement the main logic: the descripion should become the name if | |
aboxhall
2015/11/13 23:13:48
This is so great.
dmazzoni
2015/11/16 18:52:04
Yay for deleted code!
| |
3446 // it's nonempty, and the help should become the description if | |
3447 // there's no description - or the name if there's no name or description. | |
3448 if (!description.empty()) { | |
3449 name = description; | |
3450 description.clear(); | |
3451 } | |
3452 if (!help.empty() && description.empty()) { | |
3453 description = help; | |
3454 help.clear(); | |
3455 } | |
3456 if (!description.empty() && name.empty() && !title_elem_id) { | |
3457 name = description; | |
3458 description.clear(); | |
3459 } | |
3460 | |
3461 // If it's a text field, also consider the placeholder. | |
3462 base::string16 placeholder; | |
3463 if (GetRole() == ui::AX_ROLE_TEXT_FIELD && | |
3464 HasState(ui::AX_STATE_FOCUSABLE) && | |
3465 GetHtmlAttribute("placeholder", &placeholder)) { | |
3466 if (name.empty() && !title_elem_id) { | |
3467 name = placeholder; | |
3468 } else if (description.empty()) { | |
3469 description = placeholder; | |
3470 } | |
3471 } | |
3472 | |
3473 // On Windows, the value of a document should be its url. | 3389 // On Windows, the value of a document should be its url. |
3474 if (GetRole() == ui::AX_ROLE_ROOT_WEB_AREA || | 3390 if (GetRole() == ui::AX_ROLE_ROOT_WEB_AREA || |
3475 GetRole() == ui::AX_ROLE_WEB_AREA) { | 3391 GetRole() == ui::AX_ROLE_WEB_AREA) { |
3476 value = base::UTF8ToUTF16(manager()->GetTreeData().url); | 3392 value = base::UTF8ToUTF16(manager()->GetTreeData().url); |
3477 } | 3393 } |
3478 | 3394 |
3479 // For certain roles (listbox option, static text, and list marker) | |
3480 // WebKit stores the main accessible text in the "value" - swap it so | |
3481 // that it's the "name". | |
3482 if (name.empty() && | |
3483 (GetRole() == ui::AX_ROLE_STATIC_TEXT || | |
3484 GetRole() == ui::AX_ROLE_LIST_MARKER || | |
3485 IsListBoxOptionOrMenuListOption())) { | |
3486 base::string16 tmp = value; | |
3487 value = name; | |
3488 name = tmp; | |
3489 } | |
3490 | |
3491 // If this doesn't have a value and is linked then set its value to the url | 3395 // If this doesn't have a value and is linked then set its value to the url |
3492 // attribute. This allows screen readers to read an empty link's destination. | 3396 // attribute. This allows screen readers to read an empty link's destination. |
3493 if (value.empty() && (ia_state() & STATE_SYSTEM_LINKED)) | 3397 if (value.empty() && (ia_state() & STATE_SYSTEM_LINKED)) |
3494 value = GetString16Attribute(ui::AX_ATTR_URL); | 3398 value = GetString16Attribute(ui::AX_ATTR_URL); |
3495 | 3399 |
3496 win_attributes_->name = name; | |
3497 win_attributes_->description = description; | |
3498 win_attributes_->help = help; | |
3499 win_attributes_->value = value; | 3400 win_attributes_->value = value; |
3500 | 3401 |
3501 // Clear any old relationships between this node and other nodes. | 3402 // Clear any old relationships between this node and other nodes. |
3502 for (size_t i = 0; i < relations_.size(); ++i) | 3403 for (size_t i = 0; i < relations_.size(); ++i) |
3503 relations_[i]->Release(); | 3404 relations_[i]->Release(); |
3504 relations_.clear(); | 3405 relations_.clear(); |
3505 | 3406 |
3506 // Handle title UI element. | 3407 // Handle title UI element. |
3507 if (title_elem_id) { | 3408 AddRelations(ui::AX_ATTR_CONTROLS_IDS, IA2_RELATION_CONTROLLER_FOR); |
3508 // Add a labelled by relationship. | 3409 AddRelations(ui::AX_ATTR_DESCRIBEDBY_IDS, IA2_RELATION_DESCRIBED_BY); |
3509 CComObject<BrowserAccessibilityRelation>* relation; | 3410 AddRelations(ui::AX_ATTR_FLOWTO_IDS, IA2_RELATION_FLOWS_TO); |
3510 HRESULT hr = CComObject<BrowserAccessibilityRelation>::CreateInstance( | 3411 AddRelations(ui::AX_ATTR_LABELLEDBY_IDS, IA2_RELATION_LABELLED_BY); |
3511 &relation); | |
3512 DCHECK(SUCCEEDED(hr)); | |
3513 relation->AddRef(); | |
3514 relation->Initialize(this, IA2_RELATION_LABELLED_BY); | |
3515 relation->AddTarget(title_elem_id); | |
3516 relations_.push_back(relation); | |
3517 } | |
3518 | 3412 |
3519 UpdateRequiredAttributes(); | 3413 UpdateRequiredAttributes(); |
3520 // If this is a web area for a presentational iframe, give it a role of | 3414 // If this is a web area for a presentational iframe, give it a role of |
3521 // something other than DOCUMENT so that the fact that it's a separate doc | 3415 // something other than DOCUMENT so that the fact that it's a separate doc |
3522 // is not exposed to AT. | 3416 // is not exposed to AT. |
3523 if (IsWebAreaForPresentationalIframe()) { | 3417 if (IsWebAreaForPresentationalIframe()) { |
3524 win_attributes_->ia_role = ROLE_SYSTEM_GROUPING; | 3418 win_attributes_->ia_role = ROLE_SYSTEM_GROUPING; |
3525 win_attributes_->ia2_role = ROLE_SYSTEM_GROUPING; | 3419 win_attributes_->ia2_role = ROLE_SYSTEM_GROUPING; |
3526 } | 3420 } |
3527 } | 3421 } |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
3574 manager->MaybeCallNotifyWinEvent(EVENT_OBJECT_SHOW, this); | 3468 manager->MaybeCallNotifyWinEvent(EVENT_OBJECT_SHOW, this); |
3575 | 3469 |
3576 // The rest of the events only fire on changes, not on new objects. | 3470 // The rest of the events only fire on changes, not on new objects. |
3577 if (old_win_attributes_->ia_role != 0 || | 3471 if (old_win_attributes_->ia_role != 0 || |
3578 !old_win_attributes_->role_name.empty()) { | 3472 !old_win_attributes_->role_name.empty()) { |
3579 // Fire an event if the name, description, help, or value changes. | 3473 // Fire an event if the name, description, help, or value changes. |
3580 if (name() != old_win_attributes_->name) | 3474 if (name() != old_win_attributes_->name) |
3581 manager->MaybeCallNotifyWinEvent(EVENT_OBJECT_NAMECHANGE, this); | 3475 manager->MaybeCallNotifyWinEvent(EVENT_OBJECT_NAMECHANGE, this); |
3582 if (description() != old_win_attributes_->description) | 3476 if (description() != old_win_attributes_->description) |
3583 manager->MaybeCallNotifyWinEvent(EVENT_OBJECT_DESCRIPTIONCHANGE, this); | 3477 manager->MaybeCallNotifyWinEvent(EVENT_OBJECT_DESCRIPTIONCHANGE, this); |
3584 if (help() != old_win_attributes_->help) | |
3585 manager->MaybeCallNotifyWinEvent(EVENT_OBJECT_HELPCHANGE, this); | |
3586 if (value() != old_win_attributes_->value) | 3478 if (value() != old_win_attributes_->value) |
3587 manager->MaybeCallNotifyWinEvent(EVENT_OBJECT_VALUECHANGE, this); | 3479 manager->MaybeCallNotifyWinEvent(EVENT_OBJECT_VALUECHANGE, this); |
3588 if (ia_state() != old_win_attributes_->ia_state) | 3480 if (ia_state() != old_win_attributes_->ia_state) |
3589 manager->MaybeCallNotifyWinEvent(EVENT_OBJECT_STATECHANGE, this); | 3481 manager->MaybeCallNotifyWinEvent(EVENT_OBJECT_STATECHANGE, this); |
3590 | 3482 |
3591 // Normally focus events are handled elsewhere, however | 3483 // Normally focus events are handled elsewhere, however |
3592 // focus for managed descendants is platform-specific. | 3484 // focus for managed descendants is platform-specific. |
3593 // Fire a focus event if the focused descendant in a multi-select | 3485 // Fire a focus event if the focused descendant in a multi-select |
3594 // list box changes. | 3486 // list box changes. |
3595 if (GetRole() == ui::AX_ROLE_LIST_BOX_OPTION && | 3487 if (GetRole() == ui::AX_ROLE_LIST_BOX_OPTION && |
(...skipping 627 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
4223 // Expose input-text type attribute. | 4115 // Expose input-text type attribute. |
4224 base::string16 type; | 4116 base::string16 type; |
4225 base::string16 html_tag = GetString16Attribute(ui::AX_ATTR_HTML_TAG); | 4117 base::string16 html_tag = GetString16Attribute(ui::AX_ATTR_HTML_TAG); |
4226 if (GetRole() == ui::AX_ROLE_TEXT_FIELD && html_tag == L"input" && | 4118 if (GetRole() == ui::AX_ROLE_TEXT_FIELD && html_tag == L"input" && |
4227 GetHtmlAttribute("type", &type)) { | 4119 GetHtmlAttribute("type", &type)) { |
4228 SanitizeStringAttributeForIA2(type, &type); | 4120 SanitizeStringAttributeForIA2(type, &type); |
4229 win_attributes_->ia2_attributes.push_back(L"text-input-type:" + type); | 4121 win_attributes_->ia2_attributes.push_back(L"text-input-type:" + type); |
4230 } | 4122 } |
4231 } | 4123 } |
4232 | 4124 |
4125 void BrowserAccessibilityWin::AddRelations( | |
4126 ui::AXIntListAttribute src_attr, | |
4127 const base::string16& iaccessiblerelation_type) { | |
4128 if (!HasIntListAttribute(src_attr)) | |
4129 return; | |
4130 | |
4131 const std::vector<int32>& ids = GetIntListAttribute(src_attr); | |
4132 for (size_t i = 0; i < ids.size(); ++i) { | |
4133 CComObject<BrowserAccessibilityRelation>* relation; | |
4134 HRESULT hr = CComObject<BrowserAccessibilityRelation>::CreateInstance( | |
4135 &relation); | |
4136 DCHECK(SUCCEEDED(hr)); | |
4137 relation->AddRef(); | |
4138 relation->Initialize(this, iaccessiblerelation_type); | |
4139 relation->AddTarget(ids[i]); | |
4140 relations_.push_back(relation); | |
4141 } | |
4142 } | |
4143 | |
4233 void BrowserAccessibilityWin::InitRoleAndState() { | 4144 void BrowserAccessibilityWin::InitRoleAndState() { |
4234 int32 ia_role = 0; | 4145 int32 ia_role = 0; |
4235 int32 ia_state = 0; | 4146 int32 ia_state = 0; |
4236 base::string16 role_name; | 4147 base::string16 role_name; |
4237 int32 ia2_role = 0; | 4148 int32 ia2_role = 0; |
4238 int32 ia2_state = IA2_STATE_OPAQUE; | 4149 int32 ia2_state = IA2_STATE_OPAQUE; |
4239 | 4150 |
4240 if (HasState(ui::AX_STATE_BUSY)) | 4151 if (HasState(ui::AX_STATE_BUSY)) |
4241 ia_state |= STATE_SYSTEM_BUSY; | 4152 ia_state |= STATE_SYSTEM_BUSY; |
4242 if (HasState(ui::AX_STATE_CHECKED)) | 4153 if (HasState(ui::AX_STATE_CHECKED)) |
(...skipping 536 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
4779 ia2_role = ia_role; | 4690 ia2_role = ia_role; |
4780 | 4691 |
4781 win_attributes_->ia_role = ia_role; | 4692 win_attributes_->ia_role = ia_role; |
4782 win_attributes_->ia_state = ia_state; | 4693 win_attributes_->ia_state = ia_state; |
4783 win_attributes_->role_name = role_name; | 4694 win_attributes_->role_name = role_name; |
4784 win_attributes_->ia2_role = ia2_role; | 4695 win_attributes_->ia2_role = ia2_role; |
4785 win_attributes_->ia2_state = ia2_state; | 4696 win_attributes_->ia2_state = ia2_state; |
4786 } | 4697 } |
4787 | 4698 |
4788 } // namespace content | 4699 } // namespace content |
OLD | NEW |