Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1066)

Side by Side Diff: content/browser/accessibility/browser_accessibility_win.cc

Issue 8588036: Improve support for multiselect list box accessibility on Windows. (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 9 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 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 "base/string_number_conversions.h" 7 #include "base/string_number_conversions.h"
8 #include "base/string_util.h" 8 #include "base/string_util.h"
9 #include "base/utf_string_conversions.h" 9 #include "base/utf_string_conversions.h"
10 #include "base/win/enum_variant.h"
10 #include "base/win/scoped_comptr.h" 11 #include "base/win/scoped_comptr.h"
11 #include "content/browser/accessibility/browser_accessibility_manager_win.h" 12 #include "content/browser/accessibility/browser_accessibility_manager_win.h"
12 #include "content/common/view_messages.h" 13 #include "content/common/view_messages.h"
13 #include "net/base/escape.h" 14 #include "net/base/escape.h"
14 #include "ui/base/accessibility/accessible_text_utils.h" 15 #include "ui/base/accessibility/accessible_text_utils.h"
15 16
16 using webkit_glue::WebAccessibility; 17 using webkit_glue::WebAccessibility;
17 18
18 // The GUID for the ISimpleDOM service is not defined in the IDL files. 19 // The GUID for the ISimpleDOM service is not defined in the IDL files.
19 // This is taken directly from the Mozilla sources 20 // This is taken directly from the Mozilla sources
(...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after
161 162
162 BrowserAccessibilityWin* BrowserAccessibility::toBrowserAccessibilityWin() { 163 BrowserAccessibilityWin* BrowserAccessibility::toBrowserAccessibilityWin() {
163 return static_cast<BrowserAccessibilityWin*>(this); 164 return static_cast<BrowserAccessibilityWin*>(this);
164 } 165 }
165 166
166 BrowserAccessibilityWin::BrowserAccessibilityWin() 167 BrowserAccessibilityWin::BrowserAccessibilityWin()
167 : ia_role_(0), 168 : ia_role_(0),
168 ia_state_(0), 169 ia_state_(0),
169 ia2_role_(0), 170 ia2_role_(0),
170 ia2_state_(0), 171 ia2_state_(0),
171 first_time_(true) { 172 first_time_(true),
173 old_ia_state_(0) {
172 } 174 }
173 175
174 BrowserAccessibilityWin::~BrowserAccessibilityWin() { 176 BrowserAccessibilityWin::~BrowserAccessibilityWin() {
175 for (size_t i = 0; i < relations_.size(); ++i) 177 for (size_t i = 0; i < relations_.size(); ++i)
176 relations_[i]->Release(); 178 relations_[i]->Release();
177 } 179 }
178 180
179 // 181 //
180 // IAccessible methods. 182 // IAccessible methods.
181 // 183 //
(...skipping 340 matching lines...) Expand 10 before | Expand all | Expand 10 after
522 524
523 STDMETHODIMP BrowserAccessibilityWin::get_accHelpTopic( 525 STDMETHODIMP BrowserAccessibilityWin::get_accHelpTopic(
524 BSTR* help_file, VARIANT var_id, LONG* topic_id) { 526 BSTR* help_file, VARIANT var_id, LONG* topic_id) {
525 return E_NOTIMPL; 527 return E_NOTIMPL;
526 } 528 }
527 529
528 STDMETHODIMP BrowserAccessibilityWin::get_accSelection(VARIANT* selected) { 530 STDMETHODIMP BrowserAccessibilityWin::get_accSelection(VARIANT* selected) {
529 if (!instance_active_) 531 if (!instance_active_)
530 return E_FAIL; 532 return E_FAIL;
531 533
534 if (role_ == WebAccessibility::ROLE_LISTBOX && children_.size() > 0) {
535 unsigned long selected_count = 0;
536 for (size_t i = 0; i < children_.size(); i++) {
537 if ((children_[i]->state() >> WebAccessibility::STATE_SELECTED) & 1)
darin (slow to review) 2011/11/22 01:16:52 nit: A HasState method might be nice here. if (
dmazzoni 2011/11/22 08:08:14 Good idea, done.
538 selected_count++;
539 }
540
541 if (selected_count == 0) {
542 selected->vt = VT_EMPTY;
543 return S_OK;
544 }
545
546 if (selected_count == 1) {
547 for (size_t i = 0; i < children_.size(); i++) {
548 if ((children_[i]->state() >> WebAccessibility::STATE_SELECTED) & 1) {
549 selected->vt = VT_DISPATCH;
550 selected->pdispVal =
551 children_[i]->toBrowserAccessibilityWin()->NewReference();
552 return S_OK;
553 }
554 }
555 }
556
557 // Multiple items are selected.
558 base::win::EnumVariant* enum_variant =
559 new base::win::EnumVariant(selected_count);
560 unsigned long index = 0;
561 for (size_t i = 0; i < children_.size(); i++) {
562 if ((children_[i]->state() >> WebAccessibility::STATE_SELECTED) & 1) {
563 enum_variant->get(index)->vt = VT_DISPATCH;
564 enum_variant->get(index)->pdispVal =
565 children_[i]->toBrowserAccessibilityWin()->NewReference();
darin (slow to review) 2011/11/22 01:16:52 NewReference appears to take a reference count. I
dmazzoni 2011/11/22 08:08:14 It's the responsibility of the client who calls th
566 index++;
567 }
568 }
569 selected->vt = VT_UNKNOWN;
570 selected->punkVal = enum_variant;
571 }
572
532 return E_NOTIMPL; 573 return E_NOTIMPL;
533 } 574 }
534 575
535 STDMETHODIMP BrowserAccessibilityWin::accSelect( 576 STDMETHODIMP BrowserAccessibilityWin::accSelect(
536 LONG flags_sel, VARIANT var_id) { 577 LONG flags_sel, VARIANT var_id) {
537 if (!instance_active_) 578 if (!instance_active_)
538 return E_FAIL; 579 return E_FAIL;
539 580
540 if (flags_sel & SELFLAG_TAKEFOCUS) { 581 if (flags_sel & SELFLAG_TAKEFOCUS) {
541 manager_->SetFocus(this, true); 582 manager_->SetFocus(this, true);
(...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after
676 return S_FALSE; 717 return S_FALSE;
677 718
678 for (long i = 0; i < count; ++i) { 719 for (long i = 0; i < count; ++i) {
679 relations_[i]->AddRef(); 720 relations_[i]->AddRef();
680 relations[i] = relations_[i]; 721 relations[i] = relations_[i];
681 } 722 }
682 723
683 return S_OK; 724 return S_OK;
684 } 725 }
685 726
727 STDMETHODIMP BrowserAccessibilityWin::get_groupPosition(
728 LONG* group_level,
729 LONG* similar_items_in_group,
730 LONG* position_in_group) {
731 if (role_ == WebAccessibility::ROLE_LISTBOX_OPTION &&
732 parent_ &&
733 parent_->role() == WebAccessibility::ROLE_LISTBOX) {
734 *group_level = 0;
735 *similar_items_in_group = parent_->child_count();
736 *position_in_group = index_in_parent_ + 1;
737 return S_OK;
738 }
739
740 return E_NOTIMPL;
741 }
742
686 // 743 //
687 // IAccessibleImage methods. 744 // IAccessibleImage methods.
688 // 745 //
689 746
690 STDMETHODIMP BrowserAccessibilityWin::get_description(BSTR* desc) { 747 STDMETHODIMP BrowserAccessibilityWin::get_description(BSTR* desc) {
691 if (!instance_active_) 748 if (!instance_active_)
692 return E_FAIL; 749 return E_FAIL;
693 750
694 if (!desc) 751 if (!desc)
695 return E_INVALIDARG; 752 return E_INVALIDARG;
(...skipping 1632 matching lines...) Expand 10 before | Expand all | Expand 10 after
2328 } 2385 }
2329 2386
2330 // Expose the "display" and "tag" attributes. 2387 // Expose the "display" and "tag" attributes.
2331 StringAttributeToIA2(WebAccessibility::ATTR_DISPLAY, "display"); 2388 StringAttributeToIA2(WebAccessibility::ATTR_DISPLAY, "display");
2332 StringAttributeToIA2(WebAccessibility::ATTR_HTML_TAG, "tag"); 2389 StringAttributeToIA2(WebAccessibility::ATTR_HTML_TAG, "tag");
2333 StringAttributeToIA2(WebAccessibility::ATTR_ROLE, "xml-roles"); 2390 StringAttributeToIA2(WebAccessibility::ATTR_ROLE, "xml-roles");
2334 2391
2335 // Expose "level" attribute for tree nodes. 2392 // Expose "level" attribute for tree nodes.
2336 IntAttributeToIA2(WebAccessibility::ATTR_HIERARCHICAL_LEVEL, "level"); 2393 IntAttributeToIA2(WebAccessibility::ATTR_HIERARCHICAL_LEVEL, "level");
2337 2394
2395 // Expose the set size and position in set for listbox options.
2396 if (role_ == WebAccessibility::ROLE_LISTBOX_OPTION &&
2397 parent_ &&
2398 parent_->role() == WebAccessibility::ROLE_LISTBOX) {
2399 ia2_attributes_.push_back(
2400 string16(L"setsize:") + base::IntToString16(parent_->child_count()));
darin (slow to review) 2011/11/22 01:16:52 nit: is it necessary to wrap those constants with
dmazzoni 2011/11/22 08:08:14 You're right, thanks.
2401 ia2_attributes_.push_back(
2402 string16(L"setsize:") + base::IntToString16(index_in_parent_ + 1));
2403 }
2404
2338 // Expose live region attributes. 2405 // Expose live region attributes.
2339 StringAttributeToIA2(WebAccessibility::ATTR_LIVE_STATUS, "live"); 2406 StringAttributeToIA2(WebAccessibility::ATTR_LIVE_STATUS, "live");
2340 StringAttributeToIA2(WebAccessibility::ATTR_LIVE_RELEVANT, "relevant"); 2407 StringAttributeToIA2(WebAccessibility::ATTR_LIVE_RELEVANT, "relevant");
2341 BoolAttributeToIA2(WebAccessibility::ATTR_LIVE_ATOMIC, "atomic"); 2408 BoolAttributeToIA2(WebAccessibility::ATTR_LIVE_ATOMIC, "atomic");
2342 BoolAttributeToIA2(WebAccessibility::ATTR_LIVE_BUSY, "busy"); 2409 BoolAttributeToIA2(WebAccessibility::ATTR_LIVE_BUSY, "busy");
2343 2410
2344 // Expose container live region attributes. 2411 // Expose container live region attributes.
2345 StringAttributeToIA2(WebAccessibility::ATTR_CONTAINER_LIVE_STATUS, 2412 StringAttributeToIA2(WebAccessibility::ATTR_CONTAINER_LIVE_STATUS,
2346 "container-live"); 2413 "container-live");
2347 StringAttributeToIA2(WebAccessibility::ATTR_CONTAINER_LIVE_RELEVANT, 2414 StringAttributeToIA2(WebAccessibility::ATTR_CONTAINER_LIVE_RELEVANT,
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
2380 } 2447 }
2381 if (index >= 0) { 2448 if (index >= 0) {
2382 ia2_attributes_.push_back(string16(L"table-cell-index:") + 2449 ia2_attributes_.push_back(string16(L"table-cell-index:") +
2383 base::IntToString16(index)); 2450 base::IntToString16(index));
2384 } 2451 }
2385 } else { 2452 } else {
2386 NOTREACHED(); 2453 NOTREACHED();
2387 } 2454 }
2388 } 2455 }
2389 2456
2390 // If this is static text, put the text in the name rather than the value. 2457 if (name_.empty() &&
2391 if (role_ == WebAccessibility::ROLE_STATIC_TEXT && name_.empty()) 2458 (role_ == WebAccessibility::ROLE_LISTBOX_OPTION ||
2459 role_ == WebAccessibility::ROLE_STATIC_TEXT)) {
2392 name_.swap(value_); 2460 name_.swap(value_);
2461 }
2393 2462
2394 // If this object doesn't have a name but it does have a description, 2463 // If this object doesn't have a name but it does have a description,
2395 // use the description as its name - because some screen readers only 2464 // use the description as its name - because some screen readers only
2396 // announce the name. 2465 // announce the name.
2397 if (name_.empty()) 2466 if (name_.empty())
2398 GetStringAttribute(WebAccessibility::ATTR_DESCRIPTION, &name_); 2467 GetStringAttribute(WebAccessibility::ATTR_DESCRIPTION, &name_);
2399 2468
2400 // If this doesn't have a value and is linked then set its value to the url 2469 // If this doesn't have a value and is linked then set its value to the url
2401 // attribute. This allows screen readers to read an empty link's destination. 2470 // attribute. This allows screen readers to read an empty link's destination.
2402 string16 url; 2471 string16 url;
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
2436 manager_->NotifyAccessibilityEvent( 2505 manager_->NotifyAccessibilityEvent(
2437 ViewHostMsg_AccEvent::OBJECT_SHOW, this); 2506 ViewHostMsg_AccEvent::OBJECT_SHOW, this);
2438 } 2507 }
2439 2508
2440 // TODO(dmazzoni): Look into HIDE events, too. 2509 // TODO(dmazzoni): Look into HIDE events, too.
2441 2510
2442 old_text_ = previous_text_; 2511 old_text_ = previous_text_;
2443 previous_text_ = text; 2512 previous_text_ = text;
2444 } 2513 }
2445 2514
2515 // Fire events if the state has changed.
2516 if (!first_time_ && ia_state_ != old_ia_state_) {
2517 // Normally focus events are handled elsewhere, however
2518 // focus for managed descendants is platform-specific.
2519 // Fire a focus event if the focused descendant in a multi-select
2520 // list box changes.
2521 if (role_ == WebAccessibility::ROLE_LISTBOX_OPTION &&
2522 (ia_state_ & STATE_SYSTEM_FOCUSABLE) &&
2523 (ia_state_ & STATE_SYSTEM_SELECTABLE) &&
2524 (ia_state_ & STATE_SYSTEM_FOCUSED) &&
2525 !(old_ia_state_ & STATE_SYSTEM_FOCUSED)) {
2526 ::NotifyWinEvent(EVENT_OBJECT_FOCUS,
2527 manager_->GetParentView(),
2528 OBJID_CLIENT,
2529 child_id());
2530 }
2531
2532 if ((ia_state_ & STATE_SYSTEM_SELECTED) &&
2533 !(old_ia_state_ & STATE_SYSTEM_SELECTED)) {
2534 ::NotifyWinEvent(EVENT_OBJECT_SELECTIONADD,
2535 manager_->GetParentView(),
2536 OBJID_CLIENT,
2537 child_id());
2538 } else if (!(ia_state_ & STATE_SYSTEM_SELECTED) &&
2539 (old_ia_state_ & STATE_SYSTEM_SELECTED)) {
2540 ::NotifyWinEvent(EVENT_OBJECT_SELECTIONREMOVE,
2541 manager_->GetParentView(),
2542 OBJID_CLIENT,
2543 child_id());
2544 }
2545
2546 old_ia_state_ = ia_state_;
darin (slow to review) 2011/11/22 01:16:52 do we have to worry about |this| being deleted upo
dmazzoni 2011/11/22 08:08:14 I don't think we have to worry about that. It'd do
2547 }
2548
2446 first_time_ = false; 2549 first_time_ = false;
2447 } 2550 }
2448 2551
2449 void BrowserAccessibilityWin::NativeAddReference() { 2552 void BrowserAccessibilityWin::NativeAddReference() {
2450 AddRef(); 2553 AddRef();
2451 } 2554 }
2452 2555
2453 void BrowserAccessibilityWin::NativeReleaseReference() { 2556 void BrowserAccessibilityWin::NativeReleaseReference() {
2454 Release(); 2557 Release();
2455 } 2558 }
(...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after
2584 if ((state_ >> WebAccessibility::STATE_HASPOPUP) & 1) 2687 if ((state_ >> WebAccessibility::STATE_HASPOPUP) & 1)
2585 ia_state_|= STATE_SYSTEM_HASPOPUP; 2688 ia_state_|= STATE_SYSTEM_HASPOPUP;
2586 if ((state_ >> WebAccessibility::STATE_HOTTRACKED) & 1) 2689 if ((state_ >> WebAccessibility::STATE_HOTTRACKED) & 1)
2587 ia_state_|= STATE_SYSTEM_HOTTRACKED; 2690 ia_state_|= STATE_SYSTEM_HOTTRACKED;
2588 if ((state_ >> WebAccessibility::STATE_INDETERMINATE) & 1) 2691 if ((state_ >> WebAccessibility::STATE_INDETERMINATE) & 1)
2589 ia_state_|= STATE_SYSTEM_INDETERMINATE; 2692 ia_state_|= STATE_SYSTEM_INDETERMINATE;
2590 if ((state_ >> WebAccessibility::STATE_INVISIBLE) & 1) 2693 if ((state_ >> WebAccessibility::STATE_INVISIBLE) & 1)
2591 ia_state_|= STATE_SYSTEM_INVISIBLE; 2694 ia_state_|= STATE_SYSTEM_INVISIBLE;
2592 if ((state_ >> WebAccessibility::STATE_LINKED) & 1) 2695 if ((state_ >> WebAccessibility::STATE_LINKED) & 1)
2593 ia_state_|= STATE_SYSTEM_LINKED; 2696 ia_state_|= STATE_SYSTEM_LINKED;
2594 if ((state_ >> WebAccessibility::STATE_MULTISELECTABLE) & 1) 2697 if ((state_ >> WebAccessibility::STATE_MULTISELECTABLE) & 1) {
2698 ia_state_|= STATE_SYSTEM_EXTSELECTABLE;
2595 ia_state_|= STATE_SYSTEM_MULTISELECTABLE; 2699 ia_state_|= STATE_SYSTEM_MULTISELECTABLE;
2700 }
2596 // TODO(ctguil): Support STATE_SYSTEM_EXTSELECTABLE/accSelect. 2701 // TODO(ctguil): Support STATE_SYSTEM_EXTSELECTABLE/accSelect.
2597 if ((state_ >> WebAccessibility::STATE_OFFSCREEN) & 1) 2702 if ((state_ >> WebAccessibility::STATE_OFFSCREEN) & 1)
2598 ia_state_|= STATE_SYSTEM_OFFSCREEN; 2703 ia_state_|= STATE_SYSTEM_OFFSCREEN;
2599 if ((state_ >> WebAccessibility::STATE_PRESSED) & 1) 2704 if ((state_ >> WebAccessibility::STATE_PRESSED) & 1)
2600 ia_state_|= STATE_SYSTEM_PRESSED; 2705 ia_state_|= STATE_SYSTEM_PRESSED;
2601 if ((state_ >> WebAccessibility::STATE_PROTECTED) & 1) 2706 if ((state_ >> WebAccessibility::STATE_PROTECTED) & 1)
2602 ia_state_|= STATE_SYSTEM_PROTECTED; 2707 ia_state_|= STATE_SYSTEM_PROTECTED;
2603 if ((state_ >> WebAccessibility::STATE_REQUIRED) & 1) 2708 if ((state_ >> WebAccessibility::STATE_REQUIRED) & 1)
2604 ia2_state_|= IA2_STATE_REQUIRED; 2709 ia2_state_|= IA2_STATE_REQUIRED;
2605 if ((state_ >> WebAccessibility::STATE_SELECTABLE) & 1) 2710 if ((state_ >> WebAccessibility::STATE_SELECTABLE) & 1)
(...skipping 175 matching lines...) Expand 10 before | Expand all | Expand 10 after
2781 break; 2886 break;
2782 case WebAccessibility::ROLE_LIST: 2887 case WebAccessibility::ROLE_LIST:
2783 ia_role_ = ROLE_SYSTEM_LIST; 2888 ia_role_ = ROLE_SYSTEM_LIST;
2784 ia_state_|= STATE_SYSTEM_READONLY; 2889 ia_state_|= STATE_SYSTEM_READONLY;
2785 break; 2890 break;
2786 case WebAccessibility::ROLE_LISTBOX: 2891 case WebAccessibility::ROLE_LISTBOX:
2787 ia_role_ = ROLE_SYSTEM_LIST; 2892 ia_role_ = ROLE_SYSTEM_LIST;
2788 break; 2893 break;
2789 case WebAccessibility::ROLE_LISTBOX_OPTION: 2894 case WebAccessibility::ROLE_LISTBOX_OPTION:
2790 ia_role_ = ROLE_SYSTEM_LISTITEM; 2895 ia_role_ = ROLE_SYSTEM_LISTITEM;
2896 if (ia_state_ & STATE_SYSTEM_SELECTABLE) {
2897 ia_state_ |= STATE_SYSTEM_FOCUSABLE;
2898 if ((state_ >> WebAccessibility::STATE_FOCUSED) & 1)
darin (slow to review) 2011/11/22 01:16:52 again, it feels like a HasState type function migh
dmazzoni 2011/11/22 08:08:14 Done.
2899 ia_state_|= STATE_SYSTEM_FOCUSED;
2900 }
2791 break; 2901 break;
2792 case WebAccessibility::ROLE_LIST_ITEM: 2902 case WebAccessibility::ROLE_LIST_ITEM:
2793 ia_role_ = ROLE_SYSTEM_LISTITEM; 2903 ia_role_ = ROLE_SYSTEM_LISTITEM;
2794 ia_state_|= STATE_SYSTEM_READONLY; 2904 ia_state_|= STATE_SYSTEM_READONLY;
2795 break; 2905 break;
2796 case WebAccessibility::ROLE_LIST_MARKER: 2906 case WebAccessibility::ROLE_LIST_MARKER:
2797 ia_role_ = ROLE_SYSTEM_TEXT; 2907 ia_role_ = ROLE_SYSTEM_TEXT;
2798 ia_state_|= STATE_SYSTEM_READONLY; 2908 ia_state_|= STATE_SYSTEM_READONLY;
2799 break; 2909 break;
2800 case WebAccessibility::ROLE_MATH: 2910 case WebAccessibility::ROLE_MATH:
(...skipping 165 matching lines...) Expand 10 before | Expand all | Expand 10 after
2966 } 3076 }
2967 3077
2968 // The role should always be set. 3078 // The role should always be set.
2969 DCHECK(!role_name_.empty() || ia_role_); 3079 DCHECK(!role_name_.empty() || ia_role_);
2970 3080
2971 // If we didn't explicitly set the IAccessible2 role, make it the same 3081 // If we didn't explicitly set the IAccessible2 role, make it the same
2972 // as the MSAA role. 3082 // as the MSAA role.
2973 if (!ia2_role_) 3083 if (!ia2_role_)
2974 ia2_role_ = ia_role_; 3084 ia2_role_ = ia_role_;
2975 } 3085 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698