| 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 | |
| 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 |