From ee84512d1534bdf10fc11850665f2f6f5a16ba50 Mon Sep 17 00:00:00 2001 From: "dmazzoni@chromium.org" Date: Thu, 1 Sep 2011 08:44:16 +0000 Subject: Add a big grab bag of missing web accessibility functionality on Windows. (Much of this will benefit Mac in a future changelist.) Improvements include dozens of corrected roles and states for various elements, improved support for tables with rowspan and colspan, range control support, and live region support. Also adds a new command-line flag to turn on logging of accessibility events, to help making this type of bug fixing much easier in the future. BUG=89181,89185,89187,89188,89202,89205,89210,89212,89213,89223 TEST=Manual testing with JAWS, NVDA, AViewer, and accProbe. Review URL: http://codereview.chromium.org/7745035 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@99161 0039d316-1c4b-4281-b951-d872f2087c98 --- .../browser/accessibility/browser_accessibility.cc | 78 ++- .../browser/accessibility/browser_accessibility.h | 62 ++- .../accessibility/browser_accessibility_manager.cc | 139 +++-- .../accessibility/browser_accessibility_manager.h | 25 +- .../browser_accessibility_manager_mac.mm | 68 ++- .../browser_accessibility_manager_win.cc | 46 +- .../accessibility/browser_accessibility_win.cc | 564 +++++++++++++++++---- .../accessibility/browser_accessibility_win.h | 88 +++- .../renderer_host/browser_render_process_host.cc | 1 + content/browser/renderer_host/render_view_host.cc | 8 +- content/common/content_switches.cc | 3 + content/common/content_switches.h | 1 + content/common/view_messages.h | 68 ++- content/renderer/render_view.cc | 65 ++- content/renderer/render_view.h | 3 + 15 files changed, 932 insertions(+), 287 deletions(-) (limited to 'content') diff --git a/content/browser/accessibility/browser_accessibility.cc b/content/browser/accessibility/browser_accessibility.cc index f8a4d51..f9ed6a2 100644 --- a/content/browser/accessibility/browser_accessibility.cc +++ b/content/browser/accessibility/browser_accessibility.cc @@ -6,8 +6,12 @@ #include "base/logging.h" #include "base/string_number_conversions.h" +#include "base/string_util.h" #include "content/browser/accessibility/browser_accessibility_manager.h" +#include "content/common/view_messages.h" +typedef WebAccessibility::BoolAttribute BoolAttribute; +typedef WebAccessibility::FloatAttribute FloatAttribute; typedef WebAccessibility::IntAttribute IntAttribute; typedef WebAccessibility::StringAttribute StringAttribute; @@ -55,18 +59,22 @@ void BrowserAccessibility::Initialize( child_id_ = child_id; index_in_parent_ = index_in_parent; - renderer_id_ = src.id; + // Update all of the rest of the attributes. name_ = src.name; value_ = src.value; + role_ = src.role; + state_ = src.state; + renderer_id_ = src.id; string_attributes_ = src.string_attributes; int_attributes_ = src.int_attributes; + float_attributes_ = src.float_attributes; + bool_attributes_ = src.bool_attributes; html_attributes_ = src.html_attributes; location_ = src.location; - role_ = src.role; - state_ = src.state; indirect_child_ids_ = src.indirect_child_ids; line_breaks_ = src.line_breaks; cell_ids_ = src.cell_ids; + unique_cell_ids_ = src.unique_cell_ids; Initialize(); } @@ -166,6 +174,14 @@ void BrowserAccessibility::InternalReleaseReference(bool recursive) { ref_count_--; if (ref_count_ == 0) { + // Allow the object to fire a TEXT_REMOVED notification. + name_.clear(); + value_.clear(); + SendNodeUpdateEvents(); + + manager_->NotifyAccessibilityEvent( + ViewHostMsg_AccEvent::OBJECT_HIDE, this); + instance_active_ = false; children_.clear(); manager_->Remove(child_id_, renderer_id_); @@ -177,12 +193,21 @@ void BrowserAccessibility::NativeReleaseReference() { delete this; } -bool BrowserAccessibility::GetStringAttribute( - StringAttribute attribute, - string16* value) { - std::map::iterator iter = - string_attributes_.find(attribute); - if (iter != string_attributes_.end()) { +bool BrowserAccessibility::GetBoolAttribute( + BoolAttribute attribute, bool* value) const { + BoolAttrMap::const_iterator iter = bool_attributes_.find(attribute); + if (iter != bool_attributes_.end()) { + *value = iter->second; + return true; + } + + return false; +} + +bool BrowserAccessibility::GetFloatAttribute( + FloatAttribute attribute, float* value) const { + FloatAttrMap::const_iterator iter = float_attributes_.find(attribute); + if (iter != float_attributes_.end()) { *value = iter->second; return true; } @@ -191,9 +216,8 @@ bool BrowserAccessibility::GetStringAttribute( } bool BrowserAccessibility::GetIntAttribute( - IntAttribute attribute, int* value) { - std::map::iterator iter = - int_attributes_.find(attribute); + IntAttribute attribute, int* value) const { + IntAttrMap::const_iterator iter = int_attributes_.find(attribute); if (iter != int_attributes_.end()) { *value = iter->second; return true; @@ -201,3 +225,33 @@ bool BrowserAccessibility::GetIntAttribute( return false; } + +bool BrowserAccessibility::GetStringAttribute( + StringAttribute attribute, + string16* value) const { + StringAttrMap::const_iterator iter = string_attributes_.find(attribute); + if (iter != string_attributes_.end()) { + *value = iter->second; + return true; + } + + return false; +} + +bool BrowserAccessibility::GetHtmlAttribute( + const char* html_attr, string16* value) const { + for (size_t i = 0; i < html_attributes_.size(); i++) { + const string16& attr = html_attributes_[i].first; + if (LowerCaseEqualsASCII(attr, html_attr)) { + *value = html_attributes_[i].second; + return true; + } + } + + return false; +} + +bool BrowserAccessibility::IsEditableText() const { + return (role_ == WebAccessibility::ROLE_TEXT_FIELD || + role_ == WebAccessibility::ROLE_TEXTAREA); +} diff --git a/content/browser/accessibility/browser_accessibility.h b/content/browser/accessibility/browser_accessibility.h index 785fb23..1426e5c 100644 --- a/content/browser/accessibility/browser_accessibility.h +++ b/content/browser/accessibility/browser_accessibility.h @@ -22,6 +22,10 @@ class BrowserAccessibilityWin; #endif using webkit_glue::WebAccessibility; +typedef std::map BoolAttrMap; +typedef std::map FloatAttrMap; +typedef std::map IntAttrMap; +typedef std::map StringAttrMap; //////////////////////////////////////////////////////////////////////////////// // @@ -52,6 +56,12 @@ class BrowserAccessibility { // have been reset with new values from the renderer process. virtual void Initialize(); + // Optionally send events triggered simply by the fact that this node + // has been created or modified (and has been attached to the tree). + // This can include "show" events, "text changed" events in live regions, + // or "alert" events. + virtual void SendNodeUpdateEvents() {} + // Initialize this object, reading attributes from |src|. Does not // recurse into children of |src| and build the whole subtree. void Initialize(BrowserAccessibilityManager* manager, @@ -129,16 +139,22 @@ class BrowserAccessibility { // Accessors // - const std::map& - string_attributes() const { - return string_attributes_; + const BoolAttrMap& bool_attributes() const { + return bool_attributes_; } - const std::map& - int_attributes() const { + const FloatAttrMap& float_attributes() const { + return float_attributes_; + } + + const IntAttrMap& int_attributes() const { return int_attributes_; } + const StringAttrMap& string_attributes() const { + return string_attributes_; + } + int32 child_id() const { return child_id_; } const std::vector& children() const { return children_; @@ -156,6 +172,9 @@ class BrowserAccessibility { const std::vector& cell_ids() const { return cell_ids_; } + const std::vector& unique_cell_ids() const { + return unique_cell_ids_; + } gfx::Rect location() const { return location_; } BrowserAccessibilityManager* manager() const { return manager_; } const string16& name() const { return name_; } @@ -173,14 +192,32 @@ class BrowserAccessibility { BrowserAccessibilityWin* toBrowserAccessibilityWin(); #endif + // Retrieve the value of a bool attribute from the bool attribute + // map and returns true if found. + bool GetBoolAttribute(WebAccessibility::BoolAttribute attr, bool* value) + const; + + // Retrieve the value of a float attribute from the float attribute + // map and returns true if found. + bool GetFloatAttribute(WebAccessibility::FloatAttribute attr, float* value) + const; + + // Retrieve the value of an integer attribute from the integer attribute + // map and returns true if found. + bool GetIntAttribute(WebAccessibility::IntAttribute attribute, int* value) + const; + // Retrieve the value of a string attribute from the attribute map and // returns true if found. bool GetStringAttribute(WebAccessibility::StringAttribute attribute, - string16* value); + string16* value) const; - // Retrieve the value of an integer attribute from the integer attribute - // map and returns true if found. - bool GetIntAttribute(WebAccessibility::IntAttribute attribute, int* value); + // Retrieve the value of a html attribute from the attribute map and + // returns true if found. + bool GetHtmlAttribute(const char* attr, string16* value) const; + + // Returns true if this node is an editable text field of any kind. + bool IsEditableText() const; protected: BrowserAccessibility(); @@ -210,8 +247,10 @@ class BrowserAccessibility { // Accessibility metadata from the renderer string16 name_; string16 value_; - std::map string_attributes_; - std::map int_attributes_; + BoolAttrMap bool_attributes_; + IntAttrMap int_attributes_; + FloatAttrMap float_attributes_; + StringAttrMap string_attributes_; std::vector > html_attributes_; int32 role_; int32 state_; @@ -220,6 +259,7 @@ class BrowserAccessibility { std::vector indirect_child_ids_; std::vector line_breaks_; std::vector cell_ids_; + std::vector unique_cell_ids_; // BrowserAccessibility objects are reference-counted on some platforms. // When we're done with this object and it's removed from our accessibility diff --git a/content/browser/accessibility/browser_accessibility_manager.cc b/content/browser/accessibility/browser_accessibility_manager.cc index d097819..6102461 100644 --- a/content/browser/accessibility/browser_accessibility_manager.cc +++ b/content/browser/accessibility/browser_accessibility_manager.cc @@ -61,7 +61,7 @@ BrowserAccessibilityManager::BrowserAccessibilityManager( delegate_(delegate), factory_(factory), focus_(NULL) { - root_ = CreateAccessibilityTree(NULL, src, 0); + root_ = CreateAccessibilityTree(NULL, src, 0, false); if (!focus_) SetFocus(root_, false); } @@ -131,59 +131,59 @@ void BrowserAccessibilityManager::OnAccessibilityNotifications( const ViewHostMsg_AccessibilityNotification_Params& param = params[index]; switch (param.notification_type) { - case ViewHostMsg_AccessibilityNotification_Type:: - NOTIFICATION_TYPE_CHECK_STATE_CHANGED: - OnAccessibilityObjectStateChange(param.acc_obj); + // Notifications where children are included. + case ViewHostMsg_AccEvent::CHILDREN_CHANGED: + case ViewHostMsg_AccEvent::LIVE_REGION_CHANGED: + OnSimpleAccessibilityNotification( + param.acc_obj, param.notification_type, true); break; - case ViewHostMsg_AccessibilityNotification_Type:: - NOTIFICATION_TYPE_CHILDREN_CHANGED: - OnAccessibilityObjectChildrenChange(param.acc_obj); - break; - case ViewHostMsg_AccessibilityNotification_Type:: - NOTIFICATION_TYPE_FOCUS_CHANGED: + + case ViewHostMsg_AccEvent::FOCUS_CHANGED: OnAccessibilityObjectFocusChange(param.acc_obj); break; - case ViewHostMsg_AccessibilityNotification_Type:: - NOTIFICATION_TYPE_LOAD_COMPLETE: + + case ViewHostMsg_AccEvent::LOAD_COMPLETE: OnAccessibilityObjectLoadComplete(param.acc_obj); break; - case ViewHostMsg_AccessibilityNotification_Type:: - NOTIFICATION_TYPE_VALUE_CHANGED: - OnAccessibilityObjectValueChange(param.acc_obj); - break; - case ViewHostMsg_AccessibilityNotification_Type:: - NOTIFICATION_TYPE_SELECTED_TEXT_CHANGED: - OnAccessibilityObjectTextChange(param.acc_obj); + + // All other notifications: the node is updated, but + // children are not included. + case ViewHostMsg_AccEvent::ACTIVE_DESCENDANT_CHANGED: + case ViewHostMsg_AccEvent::ALERT: + case ViewHostMsg_AccEvent::CHECK_STATE_CHANGED: + case ViewHostMsg_AccEvent::LAYOUT_COMPLETE: + case ViewHostMsg_AccEvent::MENU_LIST_VALUE_CHANGED: + case ViewHostMsg_AccEvent::OBJECT_HIDE: + case ViewHostMsg_AccEvent::OBJECT_SHOW: + case ViewHostMsg_AccEvent::ROW_COLLAPSED: + case ViewHostMsg_AccEvent::ROW_COUNT_CHANGED: + case ViewHostMsg_AccEvent::ROW_EXPANDED: + case ViewHostMsg_AccEvent::SCROLLED_TO_ANCHOR: + case ViewHostMsg_AccEvent::SELECTED_CHILDREN_CHANGED: + case ViewHostMsg_AccEvent::SELECTED_TEXT_CHANGED: + case ViewHostMsg_AccEvent::TEXT_INSERTED: + case ViewHostMsg_AccEvent::TEXT_REMOVED: + case ViewHostMsg_AccEvent::VALUE_CHANGED: + OnSimpleAccessibilityNotification( + param.acc_obj, param.notification_type, false); break; + default: DCHECK(0); - break; } } } -void BrowserAccessibilityManager::OnAccessibilityObjectStateChange( - const WebAccessibility& acc_obj) { - BrowserAccessibility* new_browser_acc = UpdateNode(acc_obj, false); - if (!new_browser_acc) - return; - - NotifyAccessibilityEvent( - ViewHostMsg_AccessibilityNotification_Type:: - NOTIFICATION_TYPE_CHECK_STATE_CHANGED, - new_browser_acc); -} - -void BrowserAccessibilityManager::OnAccessibilityObjectChildrenChange( - const WebAccessibility& acc_obj) { - BrowserAccessibility* new_browser_acc = UpdateNode(acc_obj, true); +void BrowserAccessibilityManager::OnSimpleAccessibilityNotification( + const WebAccessibility& acc_obj, + int notification_type, + bool include_children) { + BrowserAccessibility* new_browser_acc = + UpdateNode(acc_obj, include_children); if (!new_browser_acc) return; - NotifyAccessibilityEvent( - ViewHostMsg_AccessibilityNotification_Type:: - NOTIFICATION_TYPE_CHILDREN_CHANGED, - new_browser_acc); + NotifyAccessibilityEvent(notification_type, new_browser_acc); } void BrowserAccessibilityManager::OnAccessibilityObjectFocusChange( @@ -197,10 +197,7 @@ void BrowserAccessibilityManager::OnAccessibilityObjectFocusChange( GotFocus(); } else if (!delegate_) { // Mac currently does not have a BrowserAccessibilityDelegate. - NotifyAccessibilityEvent( - ViewHostMsg_AccessibilityNotification_Type:: - NOTIFICATION_TYPE_FOCUS_CHANGED, - focus_); + NotifyAccessibilityEvent(ViewHostMsg_AccEvent::FOCUS_CHANGED, focus_); } } @@ -212,47 +209,17 @@ void BrowserAccessibilityManager::OnAccessibilityObjectLoadComplete( if (!focus_) SetFocus(root_, false); - NotifyAccessibilityEvent( - ViewHostMsg_AccessibilityNotification_Type:: - NOTIFICATION_TYPE_LOAD_COMPLETE, - root_); + NotifyAccessibilityEvent(ViewHostMsg_AccEvent::LOAD_COMPLETE, root_); if (delegate_ && delegate_->HasFocus()) GotFocus(); } -void BrowserAccessibilityManager::OnAccessibilityObjectValueChange( - const WebAccessibility& acc_obj) { - BrowserAccessibility* new_browser_acc = UpdateNode(acc_obj, false); - if (!new_browser_acc) - return; - - NotifyAccessibilityEvent( - ViewHostMsg_AccessibilityNotification_Type:: - NOTIFICATION_TYPE_VALUE_CHANGED, - new_browser_acc); -} - -void BrowserAccessibilityManager::OnAccessibilityObjectTextChange( - const WebAccessibility& acc_obj) { - BrowserAccessibility* new_browser_acc = UpdateNode(acc_obj, false); - if (!new_browser_acc) - return; - - NotifyAccessibilityEvent( - ViewHostMsg_AccessibilityNotification_Type:: - NOTIFICATION_TYPE_SELECTED_TEXT_CHANGED, - new_browser_acc); -} - void BrowserAccessibilityManager::GotFocus() { // TODO(ctguil): Remove when tree update logic handles focus changes. if (!focus_) return; - NotifyAccessibilityEvent( - ViewHostMsg_AccessibilityNotification_Type:: - NOTIFICATION_TYPE_FOCUS_CHANGED, - focus_); + NotifyAccessibilityEvent(ViewHostMsg_AccEvent::FOCUS_CHANGED, focus_); } gfx::NativeView BrowserAccessibilityManager::GetParentView() { @@ -314,6 +281,7 @@ BrowserAccessibility* BrowserAccessibilityManager::UpdateNode( current->child_id(), current->index_in_parent(), src); + current->SendNodeUpdateEvents(); return current; } @@ -327,8 +295,8 @@ BrowserAccessibility* BrowserAccessibilityManager::UpdateNode( // Build a new tree, reusing old nodes if possible. Each node that's // reused will have its reference count incremented by one. - current = - CreateAccessibilityTree(current_parent, src, current_index_in_parent); + current = CreateAccessibilityTree( + current_parent, src, current_index_in_parent, true); // Decrement the reference count of all nodes in the old tree, which will // delete any nodes no longer needed. @@ -344,9 +312,11 @@ BrowserAccessibility* BrowserAccessibilityManager::UpdateNode( BrowserAccessibility* BrowserAccessibilityManager::CreateAccessibilityTree( BrowserAccessibility* parent, const WebAccessibility& src, - int index_in_parent) { + int index_in_parent, + bool send_show_events) { BrowserAccessibility* instance = NULL; int32 child_id = 0; + bool children_can_send_show_events = send_show_events; base::hash_map::iterator iter = renderer_id_to_child_id_map_.find(src.id); @@ -375,10 +345,12 @@ BrowserAccessibility* BrowserAccessibilityManager::CreateAccessibilityTree( // reference count. instance->UpdateParent(parent, index_in_parent); instance->InternalAddReference(); + send_show_events = false; } else { // Otherwise, create a new instance. instance = factory_->Create(); child_id = GetNextChildID(); + children_can_send_show_events = false; } instance->Initialize(this, parent, child_id, index_in_parent, src); @@ -388,9 +360,18 @@ BrowserAccessibility* BrowserAccessibilityManager::CreateAccessibilityTree( SetFocus(instance, false); for (int i = 0; i < static_cast(src.children.size()); ++i) { BrowserAccessibility* child = CreateAccessibilityTree( - instance, src.children[i], i); + instance, src.children[i], i, children_can_send_show_events); instance->AddChild(child); } + // Note: the purpose of send_show_events and children_can_send_show_events + // is so that we send a single OBJECT_SHOW event for the root of a subtree + // that just appeared for the first time, but not on any descendant of + // that subtree. + if (send_show_events) + NotifyAccessibilityEvent(ViewHostMsg_AccEvent::OBJECT_SHOW, instance); + + instance->SendNodeUpdateEvents(); + return instance; } diff --git a/content/browser/accessibility/browser_accessibility_manager.h b/content/browser/accessibility/browser_accessibility_manager.h index f65109c..62f4a4a 100644 --- a/content/browser/accessibility/browser_accessibility_manager.h +++ b/content/browser/accessibility/browser_accessibility_manager.h @@ -19,10 +19,10 @@ class BrowserAccessibility; class BrowserAccessibilityManagerWin; #endif -struct ViewHostMsg_AccessibilityNotification_Params; - using webkit_glue::WebAccessibility; +struct ViewHostMsg_AccessibilityNotification_Params; + // Class that can perform actions on behalf of the BrowserAccessibilityManager. class BrowserAccessibilityDelegate { public: @@ -63,7 +63,7 @@ class BrowserAccessibilityManager { virtual ~BrowserAccessibilityManager(); - // Type is a ViewHostMsg_AccessibilityNotification_Params::int. + // Type is a ViewHostMsg_AccessibilityNotification_Type::int. // We pass it as int so that we don't include the render message declaration // header here. virtual void NotifyAccessibilityEvent( @@ -125,18 +125,18 @@ class BrowserAccessibilityManager { BrowserAccessibilityFactory* factory); private: - void OnAccessibilityObjectStateChange( - const WebAccessibility& acc_obj); - void OnAccessibilityObjectChildrenChange( - const WebAccessibility& acc_obj); + // Type is a ViewHostMsg_AccessibilityNotification_Type::int. + // We pass it as int so that we don't include the render message declaration + // header here. + void OnSimpleAccessibilityNotification( + const WebAccessibility& acc_obj, + int type, + bool include_children); + void OnAccessibilityObjectFocusChange( const WebAccessibility& acc_obj); void OnAccessibilityObjectLoadComplete( const WebAccessibility& acc_obj); - void OnAccessibilityObjectValueChange( - const WebAccessibility& acc_obj); - void OnAccessibilityObjectTextChange( - const WebAccessibility& acc_obj); // Update an accessibility node with an updated WebAccessibility node // received from the renderer process. When |include_children| is true @@ -152,7 +152,8 @@ class BrowserAccessibilityManager { BrowserAccessibility* CreateAccessibilityTree( BrowserAccessibility* parent, const WebAccessibility& src, - int index_in_parent); + int index_in_parent, + bool send_show_events); protected: // The next unique id for a BrowserAccessibility instance. diff --git a/content/browser/accessibility/browser_accessibility_manager_mac.mm b/content/browser/accessibility/browser_accessibility_manager_mac.mm index b5618fe3..8d2a22e 100644 --- a/content/browser/accessibility/browser_accessibility_manager_mac.mm +++ b/content/browser/accessibility/browser_accessibility_manager_mac.mm @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -32,30 +32,68 @@ void BrowserAccessibilityManagerMac::NotifyAccessibilityEvent( // Refer to AXObjectCache.mm (webkit). NSString* event_id = @""; switch (type) { - case ViewHostMsg_AccessibilityNotification_Type:: - NOTIFICATION_TYPE_CHECK_STATE_CHANGED: - // Does not exist on Mac. + case ViewHostMsg_AccEvent::ACTIVE_DESCENDANT_CHANGED: + if (node->role() == WebAccessibility::ROLE_TREE) + event_id = NSAccessibilitySelectedRowsChangedNotification; + else + event_id = NSAccessibilityFocusedUIElementChangedNotification; + case ViewHostMsg_AccEvent::ALERT: + // Not used on Mac. return; - case ViewHostMsg_AccessibilityNotification_Type:: - NOTIFICATION_TYPE_CHILDREN_CHANGED: + case ViewHostMsg_AccEvent::CHECK_STATE_CHANGED: + // Not used on Mac. + return; + case ViewHostMsg_AccEvent::CHILDREN_CHANGED: // TODO(dtseng): no clear equivalent on Mac. return; - case ViewHostMsg_AccessibilityNotification_Type:: - NOTIFICATION_TYPE_FOCUS_CHANGED: + case ViewHostMsg_AccEvent::FOCUS_CHANGED: event_id = NSAccessibilityFocusedUIElementChangedNotification; break; - case ViewHostMsg_AccessibilityNotification_Type:: - NOTIFICATION_TYPE_LOAD_COMPLETE: + case ViewHostMsg_AccEvent::LAYOUT_COMPLETE: + event_id = @"AXLayoutComplete"; + break; + case ViewHostMsg_AccEvent::LIVE_REGION_CHANGED: + event_id = @"AXLiveRegionChanged"; + break; + case ViewHostMsg_AccEvent::LOAD_COMPLETE: event_id = @"AXLoadComplete"; break; - case ViewHostMsg_AccessibilityNotification_Type:: - NOTIFICATION_TYPE_VALUE_CHANGED: - event_id = NSAccessibilityValueChangedNotification; + case ViewHostMsg_AccEvent::MENU_LIST_VALUE_CHANGED: + // Not used on Mac. + return; + case ViewHostMsg_AccEvent::OBJECT_SHOW: + // Not used on Mac. + return; + case ViewHostMsg_AccEvent::OBJECT_HIDE: + // Not used on Mac. + return; + case ViewHostMsg_AccEvent::ROW_COUNT_CHANGED: + event_id = NSAccessibilityRowCountChangedNotification; + break; + case ViewHostMsg_AccEvent::ROW_COLLAPSED: + event_id = @"AXRowCollapsed"; break; - case ViewHostMsg_AccessibilityNotification_Type:: - NOTIFICATION_TYPE_SELECTED_TEXT_CHANGED: + case ViewHostMsg_AccEvent::ROW_EXPANDED: + event_id = @"AXRowExpanded"; + break; + case ViewHostMsg_AccEvent::SCROLLED_TO_ANCHOR: + // Not used on Mac. + return; + case ViewHostMsg_AccEvent::SELECTED_CHILDREN_CHANGED: + event_id = NSAccessibilitySelectedChildrenChangedNotification; + break; + case ViewHostMsg_AccEvent::SELECTED_TEXT_CHANGED: event_id = NSAccessibilitySelectedTextChangedNotification; break; + case ViewHostMsg_AccEvent::TEXT_INSERTED: + // Not used on Mac. + return; + case ViewHostMsg_AccEvent::TEXT_REMOVED: + // Not used on Mac. + return; + case ViewHostMsg_AccEvent::VALUE_CHANGED: + event_id = NSAccessibilityValueChangedNotification; + break; } BrowserAccessibilityCocoa* native_node = node->toBrowserAccessibilityCocoa(); DCHECK(native_node); diff --git a/content/browser/accessibility/browser_accessibility_manager_win.cc b/content/browser/accessibility/browser_accessibility_manager_win.cc index 7b992e7..943831e 100644 --- a/content/browser/accessibility/browser_accessibility_manager_win.cc +++ b/content/browser/accessibility/browser_accessibility_manager_win.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -57,33 +57,49 @@ void BrowserAccessibilityManagerWin::NotifyAccessibilityEvent( BrowserAccessibility* node) { LONG event_id = EVENT_MIN; switch (type) { - case ViewHostMsg_AccessibilityNotification_Type:: - NOTIFICATION_TYPE_CHECK_STATE_CHANGED: + case ViewHostMsg_AccEvent::ACTIVE_DESCENDANT_CHANGED: + event_id = IA2_EVENT_ACTIVE_DESCENDANT_CHANGED; + break; + case ViewHostMsg_AccEvent::CHECK_STATE_CHANGED: event_id = EVENT_OBJECT_STATECHANGE; break; - case ViewHostMsg_AccessibilityNotification_Type:: - NOTIFICATION_TYPE_CHILDREN_CHANGED: + case ViewHostMsg_AccEvent::CHILDREN_CHANGED: event_id = EVENT_OBJECT_REORDER; break; - case ViewHostMsg_AccessibilityNotification_Type:: - NOTIFICATION_TYPE_FOCUS_CHANGED: + case ViewHostMsg_AccEvent::FOCUS_CHANGED: event_id = EVENT_OBJECT_FOCUS; break; - case ViewHostMsg_AccessibilityNotification_Type:: - NOTIFICATION_TYPE_LOAD_COMPLETE: + case ViewHostMsg_AccEvent::LOAD_COMPLETE: event_id = IA2_EVENT_DOCUMENT_LOAD_COMPLETE; break; - case ViewHostMsg_AccessibilityNotification_Type:: - NOTIFICATION_TYPE_VALUE_CHANGED: + case ViewHostMsg_AccEvent::VALUE_CHANGED: event_id = EVENT_OBJECT_VALUECHANGE; break; - case ViewHostMsg_AccessibilityNotification_Type:: - NOTIFICATION_TYPE_SELECTED_TEXT_CHANGED: + case ViewHostMsg_AccEvent::SELECTED_TEXT_CHANGED: event_id = IA2_EVENT_TEXT_CARET_MOVED; break; - default: - NOTREACHED(); + case ViewHostMsg_AccEvent::LIVE_REGION_CHANGED: + event_id = EVENT_OBJECT_REORDER; + break; + case ViewHostMsg_AccEvent::TEXT_INSERTED: + event_id = IA2_EVENT_TEXT_INSERTED; + break; + case ViewHostMsg_AccEvent::TEXT_REMOVED: + event_id = IA2_EVENT_TEXT_REMOVED; break; + case ViewHostMsg_AccEvent::OBJECT_SHOW: + event_id = EVENT_OBJECT_SHOW; + break; + case ViewHostMsg_AccEvent::OBJECT_HIDE: + event_id = EVENT_OBJECT_HIDE; + break; + case ViewHostMsg_AccEvent::ALERT: + event_id = EVENT_SYSTEM_ALERT; + break; + default: + // Not all WebKit accessibility events result in a Windows + // accessibility notification. + return; } NotifyWinEvent(event_id, GetParentView(), OBJID_CLIENT, node->child_id()); diff --git a/content/browser/accessibility/browser_accessibility_win.cc b/content/browser/accessibility/browser_accessibility_win.cc index 9201866..d426a6b2 100644 --- a/content/browser/accessibility/browser_accessibility_win.cc +++ b/content/browser/accessibility/browser_accessibility_win.cc @@ -8,6 +8,7 @@ #include "base/string_util.h" #include "base/utf_string_conversions.h" #include "content/browser/accessibility/browser_accessibility_manager_win.h" +#include "content/common/view_messages.h" #include "net/base/escape.h" using webkit_glue::WebAccessibility; @@ -37,7 +38,8 @@ BrowserAccessibilityWin::BrowserAccessibilityWin() : ia_role_(0), ia_state_(0), ia2_role_(0), - ia2_state_(0) { + ia2_state_(0), + first_time_(true) { } BrowserAccessibilityWin::~BrowserAccessibilityWin() { @@ -421,15 +423,13 @@ STDMETHODIMP BrowserAccessibilityWin::get_attributes(BSTR* attributes) { if (!attributes) return E_INVALIDARG; - // Follow Firefox's convention, which is to return a set of key-value pairs + // The iaccessible2 attributes are a set of key-value pairs // separated by semicolons, with a colon between the key and the value. string16 str; - for (unsigned int i = 0; i < html_attributes_.size(); i++) { + for (unsigned int i = 0; i < ia2_attributes_.size(); ++i) { if (i != 0) str += L';'; - str += Escape(html_attributes_[i].first); - str += L':'; - str += Escape(html_attributes_[i].second); + str += ia2_attributes_[i]; } if (str.empty()) @@ -574,7 +574,8 @@ STDMETHODIMP BrowserAccessibilityWin::get_accessibleAt( return S_OK; } - return S_FALSE; + *accessible = NULL; + return E_INVALIDARG; } STDMETHODIMP BrowserAccessibilityWin::get_caption(IUnknown** accessible) { @@ -610,9 +611,16 @@ STDMETHODIMP BrowserAccessibilityWin::get_childIndex( if (row < 0 || row >= rows || column < 0 || column >= columns) return E_INVALIDARG; - *cell_index = row * columns + column; + DCHECK_EQ(columns * rows, static_cast(cell_ids_.size())); + int cell_id = cell_ids_[row * columns + column]; + for (size_t i = 0; i < unique_cell_ids_.size(); ++i) { + if (unique_cell_ids_[i] == cell_id) { + *cell_index = (long)i; + return S_OK; + } + } - return S_OK; + return S_FALSE; } STDMETHODIMP BrowserAccessibilityWin::get_columnDescription( @@ -636,7 +644,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_columnDescription( if (column < 0 || column >= columns) return E_INVALIDARG; - for (int i = 0; i < rows; i++) { + for (int i = 0; i < rows; ++i) { int cell_id = cell_ids_[i * columns + column]; BrowserAccessibilityWin* cell = static_cast( manager_->GetFromRendererID(cell_id)); @@ -707,21 +715,24 @@ STDMETHODIMP BrowserAccessibilityWin::get_columnIndex( if (!column_index) return E_INVALIDARG; - int columns; - int rows; - if (!GetIntAttribute(WebAccessibility::ATTR_TABLE_COLUMN_COUNT, &columns) || - !GetIntAttribute(WebAccessibility::ATTR_TABLE_ROW_COUNT, &rows) || - columns <= 0 || - rows <= 0) { - return S_FALSE; - } - - if (cell_index < 0 || cell_index >= columns * rows) + int cell_id_count = static_cast(unique_cell_ids_.size()); + if (cell_index < 0) return E_INVALIDARG; + if (cell_index >= cell_id_count) + return S_FALSE; - *column_index = cell_index % columns; + int cell_id = unique_cell_ids_[cell_index]; + BrowserAccessibilityWin* cell = + manager_->GetFromRendererID(cell_id)->toBrowserAccessibilityWin(); + int col_index; + if (cell && + cell->GetIntAttribute( + WebAccessibility::ATTR_TABLE_CELL_COLUMN_INDEX, &col_index)) { + *column_index = col_index; + return S_OK; + } - return S_OK; + return S_FALSE; } STDMETHODIMP BrowserAccessibilityWin::get_nColumns( @@ -816,10 +827,10 @@ STDMETHODIMP BrowserAccessibilityWin::get_rowDescription( if (row < 0 || row >= rows) return E_INVALIDARG; - for (int i = 0; i < columns; i++) { + for (int i = 0; i < columns; ++i) { int cell_id = cell_ids_[row * columns + i]; - BrowserAccessibilityWin* cell = static_cast( - manager_->GetFromRendererID(cell_id)); + BrowserAccessibilityWin* cell = + manager_->GetFromRendererID(cell_id)->toBrowserAccessibilityWin(); if (cell && cell->role_ == WebAccessibility::ROLE_ROW_HEADER) { if (cell->name_.size() > 0) { *description = SysAllocString(cell->name_.c_str()); @@ -857,8 +868,8 @@ STDMETHODIMP BrowserAccessibilityWin::get_rowExtentAt( return E_INVALIDARG; int cell_id = cell_ids_[row * columns + column]; - BrowserAccessibilityWin* cell = static_cast( - manager_->GetFromRendererID(cell_id)); + BrowserAccessibilityWin* cell = + manager_->GetFromRendererID(cell_id)->toBrowserAccessibilityWin(); int rowspan; if (cell && cell->GetIntAttribute( @@ -887,21 +898,24 @@ STDMETHODIMP BrowserAccessibilityWin::get_rowIndex( if (!row_index) return E_INVALIDARG; - int columns; - int rows; - if (!GetIntAttribute(WebAccessibility::ATTR_TABLE_COLUMN_COUNT, &columns) || - !GetIntAttribute(WebAccessibility::ATTR_TABLE_ROW_COUNT, &rows) || - columns <= 0 || - rows <= 0) { - return S_FALSE; - } - - if (cell_index < 0 || cell_index >= columns * rows) + int cell_id_count = static_cast(unique_cell_ids_.size()); + if (cell_index < 0) return E_INVALIDARG; + if (cell_index >= cell_id_count) + return S_FALSE; - *row_index = cell_index / columns; + int cell_id = unique_cell_ids_[cell_index]; + BrowserAccessibilityWin* cell = + manager_->GetFromRendererID(cell_id)->toBrowserAccessibilityWin(); + int cell_row_index; + if (cell && + cell->GetIntAttribute( + WebAccessibility::ATTR_TABLE_CELL_ROW_INDEX, &cell_row_index)) { + *row_index = cell_row_index; + return S_OK; + } - return S_OK; + return S_FALSE; } STDMETHODIMP BrowserAccessibilityWin::get_selectedChildren( @@ -914,6 +928,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_selectedChildren( if (!children || !n_children) return E_INVALIDARG; + // TODO(dmazzoni): Implement this. *n_children = 0; return S_OK; } @@ -928,6 +943,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_selectedColumns( if (!columns || !n_columns) return E_INVALIDARG; + // TODO(dmazzoni): Implement this. *n_columns = 0; return S_OK; } @@ -942,6 +958,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_selectedRows( if (!rows || !n_rows) return E_INVALIDARG; + // TODO(dmazzoni): Implement this. *n_rows = 0; return S_OK; } @@ -967,6 +984,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_isColumnSelected( if (!is_selected) return E_INVALIDARG; + // TODO(dmazzoni): Implement this. *is_selected = false; return S_OK; } @@ -980,6 +998,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_isRowSelected( if (!is_selected) return E_INVALIDARG; + // TODO(dmazzoni): Implement this. *is_selected = false; return S_OK; } @@ -994,6 +1013,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_isSelected( if (!is_selected) return E_INVALIDARG; + // TODO(dmazzoni): Implement this. *is_selected = false; return S_OK; } @@ -1011,23 +1031,15 @@ STDMETHODIMP BrowserAccessibilityWin::get_rowColumnExtentsAtIndex( if (!row || !column || !row_extents || !column_extents || !is_selected) return E_INVALIDARG; - int columns; - int rows; - if (!GetIntAttribute(WebAccessibility::ATTR_TABLE_COLUMN_COUNT, &columns) || - !GetIntAttribute(WebAccessibility::ATTR_TABLE_ROW_COUNT, &rows) || - columns <= 0 || - rows <= 0) { - return S_FALSE; - } - - if (index < 0 || index >= columns * rows) + int cell_id_count = static_cast(unique_cell_ids_.size()); + if (index < 0) return E_INVALIDARG; + if (index >= cell_id_count) + return S_FALSE; - *column = index % columns; - *row = index / columns; - int cell_id = cell_ids_[index]; - BrowserAccessibilityWin* cell = static_cast( - manager_->GetFromRendererID(cell_id)); + int cell_id = unique_cell_ids_[index]; + BrowserAccessibilityWin* cell = + manager_->GetFromRendererID(cell_id)->toBrowserAccessibilityWin(); int rowspan; int colspan; if (cell && @@ -1046,6 +1058,64 @@ STDMETHODIMP BrowserAccessibilityWin::get_rowColumnExtentsAtIndex( } // +// IAccessibleTable2 methods. +// + +STDMETHODIMP BrowserAccessibilityWin::get_cellAt( + long row, + long column, + IUnknown** cell) { + return get_accessibleAt(row, column, cell); +} + +STDMETHODIMP BrowserAccessibilityWin::get_nSelectedCells(long* cell_count) { + return get_nSelectedChildren(cell_count); +} + +STDMETHODIMP BrowserAccessibilityWin::get_selectedCells( + IUnknown*** cells, + long* n_selected_cells) { + if (!instance_active_) + return E_FAIL; + + if (!cells || !n_selected_cells) + return E_INVALIDARG; + + // TODO(dmazzoni): Implement this. + *n_selected_cells = 0; + return S_OK; +} + +STDMETHODIMP BrowserAccessibilityWin::get_selectedColumns( + long** columns, + long* n_columns) { + if (!instance_active_) + return E_FAIL; + + if (!columns || !n_columns) + return E_INVALIDARG; + + // TODO(dmazzoni): Implement this. + *n_columns = 0; + return S_OK; +} + +STDMETHODIMP BrowserAccessibilityWin::get_selectedRows( + long** rows, + long* n_rows) { + if (!instance_active_) + return E_FAIL; + + if (!rows || !n_rows) + return E_INVALIDARG; + + // TODO(dmazzoni): Implement this. + *n_rows = 0; + return S_OK; +} + + +// // IAccessibleTableCell methods. // @@ -1088,8 +1158,10 @@ STDMETHODIMP BrowserAccessibilityWin::get_columnHeaderCells( BrowserAccessibility* table = parent(); while (table && table->role() != WebAccessibility::ROLE_TABLE) table = table->parent(); - if (!table) + if (!table) { + NOTREACHED(); return S_FALSE; + } int columns; int rows; @@ -1102,10 +1174,10 @@ STDMETHODIMP BrowserAccessibilityWin::get_columnHeaderCells( if (columns <= 0 || rows <= 0 || column < 0 || column >= columns) return S_FALSE; - for (int i = 0; i < rows; i++) { + for (int i = 0; i < rows; ++i) { int cell_id = table->cell_ids()[i * columns + column]; - BrowserAccessibilityWin* cell = static_cast( - manager_->GetFromRendererID(cell_id)); + BrowserAccessibilityWin* cell = + manager_->GetFromRendererID(cell_id)->toBrowserAccessibilityWin(); if (cell && cell->role_ == WebAccessibility::ROLE_COLUMN_HEADER) (*n_column_header_cells)++; } @@ -1113,14 +1185,14 @@ STDMETHODIMP BrowserAccessibilityWin::get_columnHeaderCells( *cell_accessibles = static_cast(CoTaskMemAlloc( (*n_column_header_cells) * sizeof(cell_accessibles[0]))); int index = 0; - for (int i = 0; i < rows; i++) { + for (int i = 0; i < rows; ++i) { int cell_id = table->cell_ids()[i * columns + column]; - BrowserAccessibilityWin* cell = static_cast( - manager_->GetFromRendererID(cell_id)); + BrowserAccessibilityWin* cell = + manager_->GetFromRendererID(cell_id)->toBrowserAccessibilityWin(); if (cell && cell->role_ == WebAccessibility::ROLE_COLUMN_HEADER) { (*cell_accessibles)[index] = static_cast(cell->NewReference()); - index++; + ++index; } } @@ -1184,8 +1256,10 @@ STDMETHODIMP BrowserAccessibilityWin::get_rowHeaderCells( BrowserAccessibility* table = parent(); while (table && table->role() != WebAccessibility::ROLE_TABLE) table = table->parent(); - if (!table) + if (!table) { + NOTREACHED(); return S_FALSE; + } int columns; int rows; @@ -1198,10 +1272,10 @@ STDMETHODIMP BrowserAccessibilityWin::get_rowHeaderCells( if (columns <= 0 || rows <= 0 || row < 0 || row >= rows) return S_FALSE; - for (int i = 0; i < columns; i++) { + for (int i = 0; i < columns; ++i) { int cell_id = table->cell_ids()[row * columns + i]; - BrowserAccessibilityWin* cell = static_cast( - manager_->GetFromRendererID(cell_id)); + BrowserAccessibilityWin* cell = + manager_->GetFromRendererID(cell_id)->toBrowserAccessibilityWin(); if (cell && cell->role_ == WebAccessibility::ROLE_ROW_HEADER) (*n_row_header_cells)++; } @@ -1209,14 +1283,14 @@ STDMETHODIMP BrowserAccessibilityWin::get_rowHeaderCells( *cell_accessibles = static_cast(CoTaskMemAlloc( (*n_row_header_cells) * sizeof(cell_accessibles[0]))); int index = 0; - for (int i = 0; i < columns; i++) { + for (int i = 0; i < columns; ++i) { int cell_id = table->cell_ids()[row * columns + i]; - BrowserAccessibilityWin* cell = static_cast( - manager_->GetFromRendererID(cell_id)); + BrowserAccessibilityWin* cell = + manager_->GetFromRendererID(cell_id)->toBrowserAccessibilityWin(); if (cell && cell->role_ == WebAccessibility::ROLE_ROW_HEADER) { (*cell_accessibles)[index] = static_cast(cell->NewReference()); - index++; + ++index; } } @@ -1298,14 +1372,22 @@ STDMETHODIMP BrowserAccessibilityWin::get_table( if (!table) return E_INVALIDARG; + + int row; + int column; + GetIntAttribute(WebAccessibility::ATTR_TABLE_CELL_ROW_INDEX, &row); + GetIntAttribute(WebAccessibility::ATTR_TABLE_CELL_COLUMN_INDEX, &column); + BrowserAccessibility* find_table = parent(); while (find_table && find_table->role() != WebAccessibility::ROLE_TABLE) find_table = find_table->parent(); - if (!find_table) + if (!find_table) { + NOTREACHED(); return S_FALSE; + } - *table = static_cast( - static_cast(find_table)->NewReference()); + *table = static_cast( + find_table->toBrowserAccessibilityWin()->NewReference()); return S_OK; } @@ -1526,6 +1608,114 @@ STDMETHODIMP BrowserAccessibilityWin::get_textAfterOffset( return get_text(*start_offset, *end_offset, text); } +STDMETHODIMP BrowserAccessibilityWin::get_newText(IA2TextSegment* new_text) { + if (!instance_active_) + return E_FAIL; + + if (!new_text) + return E_INVALIDARG; + + string16 text = TextForIAccessibleText(); + + new_text->text = SysAllocString(text.c_str()); + new_text->start = 0; + new_text->end = static_cast(text.size()); + return S_OK; +} + +STDMETHODIMP BrowserAccessibilityWin::get_oldText(IA2TextSegment* old_text) { + if (!instance_active_) + return E_FAIL; + + if (!old_text) + return E_INVALIDARG; + + old_text->text = SysAllocString(old_text_.c_str()); + old_text->start = 0; + old_text->end = static_cast(old_text_.size()); + return S_OK; +} + +STDMETHODIMP BrowserAccessibilityWin::get_offsetAtPoint( + LONG x, LONG y, enum IA2CoordinateType coord_type, LONG* offset) { + if (!instance_active_) + return E_FAIL; + + if (!offset) + return E_INVALIDARG; + + // TODO(dmazzoni): implement this. We're returning S_OK for now so that + // screen readers still return partially accurate results rather than + // completely failing. + *offset = 0; + return S_OK; +} + +// +// IAccessibleValue methods. +// + +STDMETHODIMP BrowserAccessibilityWin::get_currentValue(VARIANT* value) { + if (!instance_active_) + return E_FAIL; + + if (!value) + return E_INVALIDARG; + + float float_val; + if (GetFloatAttribute(WebAccessibility::ATTR_VALUE_FOR_RANGE, &float_val)) { + value->vt = VT_R8; + value->dblVal = float_val; + return S_OK; + } + + value->vt = VT_EMPTY; + return S_FALSE; +} + +STDMETHODIMP BrowserAccessibilityWin::get_minimumValue(VARIANT* value) { + if (!instance_active_) + return E_FAIL; + + if (!value) + return E_INVALIDARG; + + float float_val; + if (GetFloatAttribute(WebAccessibility::ATTR_MIN_VALUE_FOR_RANGE, + &float_val)) { + value->vt = VT_R8; + value->dblVal = float_val; + return S_OK; + } + + value->vt = VT_EMPTY; + return S_FALSE; +} + +STDMETHODIMP BrowserAccessibilityWin::get_maximumValue(VARIANT* value) { + if (!instance_active_) + return E_FAIL; + + if (!value) + return E_INVALIDARG; + + float float_val; + if (GetFloatAttribute(WebAccessibility::ATTR_MAX_VALUE_FOR_RANGE, + &float_val)) { + value->vt = VT_R8; + value->dblVal = float_val; + return S_OK; + } + + value->vt = VT_EMPTY; + return S_FALSE; +} + +STDMETHODIMP BrowserAccessibilityWin::setCurrentValue(VARIANT new_value) { + // TODO(dmazzoni): Implement this. + return E_NOTIMPL; +} + // // ISimpleDOMDocument methods. // @@ -1707,7 +1897,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_computedStyleForProperties( // We only cache a single style property for now: DISPLAY - for (unsigned short i = 0; i < num_style_properties; i++) { + for (unsigned short i = 0; i < num_style_properties; ++i) { string16 name = (LPCWSTR)style_properties[i]; StringToLowerASCII(&name); if (name == L"display") { @@ -1853,19 +2043,14 @@ STDMETHODIMP BrowserAccessibilityWin::QueryService( if (!instance_active_) return E_FAIL; - if (guidService == IID_IAccessibleTable) { - printf("iatable"); - } - if (guidService == IID_IAccessibleTableCell) { - printf("iatable"); - } - if (guidService == IID_IAccessible || guidService == IID_IAccessible2 || guidService == IID_IAccessibleImage || guidService == IID_IAccessibleTable || + guidService == IID_IAccessibleTable2 || guidService == IID_IAccessibleTableCell || guidService == IID_IAccessibleText || + guidService == IID_IAccessibleValue || guidService == IID_ISimpleDOMDocument || guidService == IID_ISimpleDOMNode || guidService == IID_ISimpleDOMText || @@ -1896,7 +2081,7 @@ HRESULT WINAPI BrowserAccessibilityWin::InternalQueryInterface( *object = NULL; return E_NOINTERFACE; } - } else if (iid == IID_IAccessibleTable) { + } else if (iid == IID_IAccessibleTable || iid == IID_IAccessibleTable2) { if (ia_role_ != ROLE_SYSTEM_TABLE) { *object = NULL; return E_NOINTERFACE; @@ -1906,6 +2091,13 @@ HRESULT WINAPI BrowserAccessibilityWin::InternalQueryInterface( *object = NULL; return E_NOINTERFACE; } + } else if (iid == IID_IAccessibleValue) { + if (ia_role_ != ROLE_SYSTEM_PROGRESSBAR && + ia_role_ != ROLE_SYSTEM_SCROLLBAR && + ia_role_ != ROLE_SYSTEM_SLIDER) { + *object = NULL; + return E_NOINTERFACE; + } } else if (iid == IID_ISimpleDOMDocument) { if (ia_role_ != ROLE_SYSTEM_DOCUMENT) { *object = NULL; @@ -1927,17 +2119,72 @@ void BrowserAccessibilityWin::Initialize() { InitRoleAndState(); - // Expose headings levels to NVDA with the "level" object attribute. + // Expose headings levels with the "level" attribute. if (role_ == WebAccessibility::ROLE_HEADING && role_name_.size() == 2 && IsAsciiDigit(role_name_[1])) { - html_attributes_.push_back(std::make_pair(L"level", role_name_.substr(1))); + ia2_attributes_.push_back(string16(L"level:") + role_name_.substr(1)); + } + + // Expose the "display" and "tag" attributes. + StringAttributeToIA2(WebAccessibility::ATTR_DISPLAY, "display"); + StringAttributeToIA2(WebAccessibility::ATTR_HTML_TAG, "tag"); + StringAttributeToIA2(WebAccessibility::ATTR_ROLE, "xml-roles"); + + // Expose "level" attribute for tree nodes. + IntAttributeToIA2(WebAccessibility::ATTR_HIERARCHICAL_LEVEL, "level"); + + // Expose live region attributes. + StringAttributeToIA2(WebAccessibility::ATTR_LIVE_STATUS, "live"); + StringAttributeToIA2(WebAccessibility::ATTR_LIVE_RELEVANT, "relevant"); + BoolAttributeToIA2(WebAccessibility::ATTR_LIVE_ATOMIC, "atomic"); + BoolAttributeToIA2(WebAccessibility::ATTR_LIVE_BUSY, "busy"); + + // Expose container live region attributes. + StringAttributeToIA2(WebAccessibility::ATTR_CONTAINER_LIVE_STATUS, + "container-live"); + StringAttributeToIA2(WebAccessibility::ATTR_CONTAINER_LIVE_RELEVANT, + "container-relevant"); + BoolAttributeToIA2(WebAccessibility::ATTR_CONTAINER_LIVE_ATOMIC, + "container-atomic"); + BoolAttributeToIA2(WebAccessibility::ATTR_CONTAINER_LIVE_BUSY, + "container-busy"); + + // Expose slider value. + if (ia_role_ == ROLE_SYSTEM_PROGRESSBAR || + ia_role_ == ROLE_SYSTEM_SCROLLBAR || + ia_role_ == ROLE_SYSTEM_SLIDER) { + float fval; + if (value_.empty() && + GetFloatAttribute(WebAccessibility::ATTR_VALUE_FOR_RANGE, &fval)) { + // TODO(dmazzoni): Use ICU to localize this? + value_ = UTF8ToUTF16(base::DoubleToString(fval)); + } + ia2_attributes_.push_back(L"valuetext:" + value_); + } + + // Expose table cell index. + if (ia_role_ == ROLE_SYSTEM_CELL) { + BrowserAccessibility* table = parent(); + while (table && table->role() != WebAccessibility::ROLE_TABLE) + table = table->parent(); + if (table) { + const std::vector& unique_cell_ids = table->unique_cell_ids(); + int index = -1; + for (size_t i = 0; i < unique_cell_ids.size(); ++i) { + if (unique_cell_ids[i] == renderer_id_) { + index = static_cast(i); + break; + } + } + if (index >= 0) { + ia2_attributes_.push_back(string16(L"table-cell-index:") + + base::IntToString16(index)); + } + } else { + NOTREACHED(); + } } - // Expose the "display" object attribute. - string16 display; - if (GetStringAttribute(WebAccessibility::ATTR_DISPLAY, &display)) - html_attributes_.push_back(std::make_pair(L"display", display)); - // If this is static text, put the text in the name rather than the value. if (role_ == WebAccessibility::ROLE_STATIC_TEXT && name_.empty()) name_.swap(value_); @@ -1955,6 +2202,28 @@ void BrowserAccessibilityWin::Initialize() { GetStringAttribute(WebAccessibility::ATTR_URL, &value_); } +void BrowserAccessibilityWin::SendNodeUpdateEvents() { + // Fire an event when an alert first appears. + if (role_ == WebAccessibility::ROLE_ALERT && first_time_) + manager_->NotifyAccessibilityEvent(ViewHostMsg_AccEvent::ALERT, this); + + // Fire events if text has changed. + string16 text = TextForIAccessibleText(); + if (previous_text_ != text) { + if (!previous_text_.empty() && !text.empty()) { + manager_->NotifyAccessibilityEvent( + ViewHostMsg_AccEvent::OBJECT_SHOW, this); + } + + // TODO(dmazzoni): Look into HIDE events, too. + + old_text_ = previous_text_; + previous_text_ = text; + } + + first_time_ = false; +} + void BrowserAccessibilityWin::NativeAddReference() { AddRef(); } @@ -1999,13 +2268,36 @@ HRESULT BrowserAccessibilityWin::GetStringAttributeAsBstr( return S_OK; } +void BrowserAccessibilityWin::StringAttributeToIA2( + WebAccessibility::StringAttribute attribute, const char* ia2_attr) { + string16 value; + if (GetStringAttribute(attribute, &value)) + ia2_attributes_.push_back(ASCIIToUTF16(ia2_attr) + L":" + value); +} + +void BrowserAccessibilityWin::BoolAttributeToIA2( + WebAccessibility::BoolAttribute attribute, const char* ia2_attr) { + bool value; + if (GetBoolAttribute(attribute, &value)) { + ia2_attributes_.push_back((ASCIIToUTF16(ia2_attr) + L":") + + (value ? L"true" : L"false")); + } +} + +void BrowserAccessibilityWin::IntAttributeToIA2( + WebAccessibility::IntAttribute attribute, const char* ia2_attr) { + int value; + if (GetIntAttribute(attribute, &value)) + ia2_attributes_.push_back(ASCIIToUTF16(ia2_attr) + L":" + + base::IntToString16(value)); +} + string16 BrowserAccessibilityWin::Escape(const string16& str) { return EscapeQueryParamValueUTF8(str, false); } const string16& BrowserAccessibilityWin::TextForIAccessibleText() { - if (role_ == WebAccessibility::ROLE_TEXT_FIELD || - role_ == WebAccessibility::ROLE_TEXTAREA) { + if (IsEditableText()) { return value_; } else { return name_; @@ -2041,7 +2333,7 @@ LONG BrowserAccessibilityWin::FindBoundary( return start_offset; } else if (boundary == IA2_TEXT_BOUNDARY_LINE) { if (direction == 1) { - for (int j = 0; j < static_cast(line_breaks_.size()); j++) { + for (int j = 0; j < static_cast(line_breaks_.size()); ++j) { if (line_breaks_[j] > start_offset) return line_breaks_[j]; } @@ -2112,7 +2404,10 @@ BrowserAccessibilityWin* BrowserAccessibilityWin::GetFromRendererID( void BrowserAccessibilityWin::InitRoleAndState() { ia_state_ = 0; ia2_state_ = IA2_STATE_OPAQUE; + ia2_attributes_.clear(); + if ((state_ >> WebAccessibility::STATE_BUSY) & 1) + ia_state_|= STATE_SYSTEM_BUSY; if ((state_ >> WebAccessibility::STATE_CHECKED) & 1) ia_state_ |= STATE_SYSTEM_CHECKED; if ((state_ >> WebAccessibility::STATE_COLLAPSED) & 1) @@ -2140,18 +2435,34 @@ void BrowserAccessibilityWin::InitRoleAndState() { ia_state_|= STATE_SYSTEM_PRESSED; if ((state_ >> WebAccessibility::STATE_PROTECTED) & 1) ia_state_|= STATE_SYSTEM_PROTECTED; + if ((state_ >> WebAccessibility::STATE_READONLY) & 1) + ia_state_|= STATE_SYSTEM_READONLY; + if ((state_ >> WebAccessibility::STATE_REQUIRED) & 1) + ia2_state_|= IA2_STATE_REQUIRED; if ((state_ >> WebAccessibility::STATE_SELECTABLE) & 1) ia_state_|= STATE_SYSTEM_SELECTABLE; if ((state_ >> WebAccessibility::STATE_SELECTED) & 1) ia_state_|= STATE_SYSTEM_SELECTED; - if ((state_ >> WebAccessibility::STATE_READONLY) & 1) - ia_state_|= STATE_SYSTEM_READONLY; if ((state_ >> WebAccessibility::STATE_TRAVERSED) & 1) ia_state_|= STATE_SYSTEM_TRAVERSED; - if ((state_ >> WebAccessibility::STATE_BUSY) & 1) - ia_state_|= STATE_SYSTEM_BUSY; if ((state_ >> WebAccessibility::STATE_UNAVAILABLE) & 1) ia_state_|= STATE_SYSTEM_UNAVAILABLE; + if ((state_ >> WebAccessibility::STATE_VERTICAL) & 1) { + ia2_state_|= IA2_STATE_VERTICAL; + } else { + ia2_state_|= IA2_STATE_HORIZONTAL; + } + if ((state_ >> WebAccessibility::STATE_VISITED) & 1) + ia_state_|= STATE_SYSTEM_TRAVERSED; + + string16 invalid; + if (GetHtmlAttribute("aria-invalid", &invalid)) + ia2_state_|= IA2_STATE_INVALID_ENTRY; + + bool mixed = false; + GetBoolAttribute(WebAccessibility::ATTR_BUTTON_MIXED, &mixed); + if (mixed) + ia_state_|= STATE_SYSTEM_MIXED; string16 html_tag; GetStringAttribute(WebAccessibility::ATTR_HTML_TAG, &html_tag); @@ -2159,18 +2470,22 @@ void BrowserAccessibilityWin::InitRoleAndState() { ia2_role_ = 0; switch (role_) { case WebAccessibility::ROLE_ALERT: - case WebAccessibility::ROLE_ALERT_DIALOG: ia_role_ = ROLE_SYSTEM_ALERT; break; + case WebAccessibility::ROLE_ALERT_DIALOG: + ia_role_ = ROLE_SYSTEM_DIALOG; + break; case WebAccessibility::ROLE_APPLICATION: ia_role_ = ROLE_SYSTEM_APPLICATION; break; case WebAccessibility::ROLE_ARTICLE: ia_role_ = ROLE_SYSTEM_GROUPING; ia2_role_ = IA2_ROLE_SECTION; + ia_state_|= STATE_SYSTEM_READONLY; break; case WebAccessibility::ROLE_BUSY_INDICATOR: ia_role_ = ROLE_SYSTEM_ANIMATION; + ia_state_|= STATE_SYSTEM_READONLY; break; case WebAccessibility::ROLE_BUTTON: ia_role_ = ROLE_SYSTEM_PUSHBUTTON; @@ -2187,9 +2502,11 @@ void BrowserAccessibilityWin::InitRoleAndState() { break; case WebAccessibility::ROLE_COLUMN: ia_role_ = ROLE_SYSTEM_COLUMN; + ia_state_|= STATE_SYSTEM_READONLY; break; case WebAccessibility::ROLE_COLUMN_HEADER: ia_role_ = ROLE_SYSTEM_COLUMNHEADER; + ia_state_|= STATE_SYSTEM_READONLY; break; case WebAccessibility::ROLE_COMBO_BOX: ia_role_ = ROLE_SYSTEM_COMBOBOX; @@ -2197,15 +2514,19 @@ void BrowserAccessibilityWin::InitRoleAndState() { case WebAccessibility::ROLE_DEFINITION_LIST_DEFINITION: role_name_ = html_tag; ia2_role_ = IA2_ROLE_PARAGRAPH; + ia_state_|= STATE_SYSTEM_READONLY; break; case WebAccessibility::ROLE_DEFINITION_LIST_TERM: ia_role_ = ROLE_SYSTEM_LISTITEM; + ia_state_|= STATE_SYSTEM_READONLY; break; case WebAccessibility::ROLE_DIALOG: ia_role_ = ROLE_SYSTEM_DIALOG; + ia_state_|= STATE_SYSTEM_READONLY; break; case WebAccessibility::ROLE_DISCLOSURE_TRIANGLE: ia_role_ = ROLE_SYSTEM_OUTLINEBUTTON; + ia_state_|= STATE_SYSTEM_READONLY; break; case WebAccessibility::ROLE_DOCUMENT: case WebAccessibility::ROLE_WEB_AREA: @@ -2220,10 +2541,17 @@ void BrowserAccessibilityWin::InitRoleAndState() { break; case WebAccessibility::ROLE_GRID: ia_role_ = ROLE_SYSTEM_TABLE; + ia_state_|= STATE_SYSTEM_READONLY; break; case WebAccessibility::ROLE_GROUP: if (html_tag == L"li") { ia_role_ = ROLE_SYSTEM_LISTITEM; + } else if (html_tag == L"form") { + role_name_ = html_tag; + ia2_role_ = IA2_ROLE_FORM; + } else if (html_tag == L"p") { + role_name_ = html_tag; + ia2_role_ = IA2_ROLE_PARAGRAPH; } else { if (html_tag.empty()) role_name_ = L"div"; @@ -2231,24 +2559,30 @@ void BrowserAccessibilityWin::InitRoleAndState() { role_name_ = html_tag; ia2_role_ = IA2_ROLE_SECTION; } + ia_state_|= STATE_SYSTEM_READONLY; break; case WebAccessibility::ROLE_GROW_AREA: ia_role_ = ROLE_SYSTEM_GRIP; + ia_state_|= STATE_SYSTEM_READONLY; break; case WebAccessibility::ROLE_HEADING: role_name_ = html_tag; ia2_role_ = IA2_ROLE_HEADING; + ia_state_|= STATE_SYSTEM_READONLY; break; case WebAccessibility::ROLE_IMAGE: ia_role_ = ROLE_SYSTEM_GRAPHIC; + ia_state_|= STATE_SYSTEM_READONLY; break; case WebAccessibility::ROLE_IMAGE_MAP: role_name_ = html_tag; ia2_role_ = IA2_ROLE_IMAGE_MAP; + ia_state_|= STATE_SYSTEM_READONLY; break; case WebAccessibility::ROLE_IMAGE_MAP_LINK: ia_role_ = ROLE_SYSTEM_LINK; ia_state_|= STATE_SYSTEM_LINKED; + ia_state_|= STATE_SYSTEM_READONLY; break; case WebAccessibility::ROLE_LANDMARK_APPLICATION: case WebAccessibility::ROLE_LANDMARK_BANNER: @@ -2259,6 +2593,7 @@ void BrowserAccessibilityWin::InitRoleAndState() { case WebAccessibility::ROLE_LANDMARK_SEARCH: ia_role_ = ROLE_SYSTEM_GROUPING; ia2_role_ = IA2_ROLE_SECTION; + ia_state_|= STATE_SYSTEM_READONLY; break; case WebAccessibility::ROLE_LINK: case WebAccessibility::ROLE_WEBCORE_LINK: @@ -2267,17 +2602,25 @@ void BrowserAccessibilityWin::InitRoleAndState() { break; case WebAccessibility::ROLE_LIST: ia_role_ = ROLE_SYSTEM_LIST; + ia_state_|= STATE_SYSTEM_READONLY; break; case WebAccessibility::ROLE_LISTBOX: ia_role_ = ROLE_SYSTEM_LIST; break; case WebAccessibility::ROLE_LISTBOX_OPTION: + ia_role_ = ROLE_SYSTEM_LISTITEM; + break; case WebAccessibility::ROLE_LIST_ITEM: - case WebAccessibility::ROLE_LIST_MARKER: ia_role_ = ROLE_SYSTEM_LISTITEM; + ia_state_|= STATE_SYSTEM_READONLY; + break; + case WebAccessibility::ROLE_LIST_MARKER: + ia_role_ = ROLE_SYSTEM_TEXT; + ia_state_|= STATE_SYSTEM_READONLY; break; case WebAccessibility::ROLE_MATH: ia_role_ = ROLE_SYSTEM_EQUATION; + ia_state_|= STATE_SYSTEM_READONLY; break; case WebAccessibility::ROLE_MENU: case WebAccessibility::ROLE_MENU_BUTTON: @@ -2296,15 +2639,22 @@ void BrowserAccessibilityWin::InitRoleAndState() { case WebAccessibility::ROLE_NOTE: ia_role_ = ROLE_SYSTEM_GROUPING; ia2_role_ = IA2_ROLE_NOTE; + ia_state_|= STATE_SYSTEM_READONLY; break; case WebAccessibility::ROLE_OUTLINE: ia_role_ = ROLE_SYSTEM_OUTLINE; + ia_state_|= STATE_SYSTEM_READONLY; break; case WebAccessibility::ROLE_POPUP_BUTTON: - ia_role_ = ROLE_SYSTEM_COMBOBOX; + if (html_tag == L"select") { + ia_role_ = ROLE_SYSTEM_COMBOBOX; + } else { + ia_role_ = ROLE_SYSTEM_BUTTONMENU; + } break; case WebAccessibility::ROLE_PROGRESS_INDICATOR: ia_role_ = ROLE_SYSTEM_PROGRESSBAR; + ia_state_|= STATE_SYSTEM_READONLY; break; case WebAccessibility::ROLE_RADIO_BUTTON: ia_role_ = ROLE_SYSTEM_RADIOBUTTON; @@ -2316,20 +2666,25 @@ void BrowserAccessibilityWin::InitRoleAndState() { case WebAccessibility::ROLE_REGION: ia_role_ = ROLE_SYSTEM_GROUPING; ia2_role_ = IA2_ROLE_SECTION; + ia_state_|= STATE_SYSTEM_READONLY; break; case WebAccessibility::ROLE_ROW: ia_role_ = ROLE_SYSTEM_ROW; + ia_state_|= STATE_SYSTEM_READONLY; break; case WebAccessibility::ROLE_ROW_HEADER: ia_role_ = ROLE_SYSTEM_ROWHEADER; + ia_state_|= STATE_SYSTEM_READONLY; break; case WebAccessibility::ROLE_RULER: ia_role_ = ROLE_SYSTEM_CLIENT; ia2_role_ = IA2_ROLE_RULER; + ia_state_|= STATE_SYSTEM_READONLY; break; case WebAccessibility::ROLE_SCROLLAREA: ia_role_ = ROLE_SYSTEM_CLIENT; ia2_role_ = IA2_ROLE_SCROLL_PANE; + ia_state_|= STATE_SYSTEM_READONLY; break; case WebAccessibility::ROLE_SCROLLBAR: ia_role_ = ROLE_SYSTEM_SCROLLBAR; @@ -2340,13 +2695,16 @@ void BrowserAccessibilityWin::InitRoleAndState() { case WebAccessibility::ROLE_SPLIT_GROUP: ia_role_ = ROLE_SYSTEM_CLIENT; ia2_role_ = IA2_ROLE_SPLIT_PANE; + ia_state_|= STATE_SYSTEM_READONLY; break; case WebAccessibility::ROLE_ANNOTATION: case WebAccessibility::ROLE_STATIC_TEXT: ia_role_ = ROLE_SYSTEM_TEXT; + ia_state_|= STATE_SYSTEM_READONLY; break; case WebAccessibility::ROLE_STATUS: ia_role_ = ROLE_SYSTEM_STATUSBAR; + ia_state_|= STATE_SYSTEM_READONLY; break; case WebAccessibility::ROLE_SPLITTER: ia_role_ = ROLE_SYSTEM_SEPARATOR; @@ -2356,10 +2714,12 @@ void BrowserAccessibilityWin::InitRoleAndState() { break; case WebAccessibility::ROLE_TABLE: ia_role_ = ROLE_SYSTEM_TABLE; + ia_state_|= STATE_SYSTEM_READONLY; break; case WebAccessibility::ROLE_TABLE_HEADER_CONTAINER: ia_role_ = ROLE_SYSTEM_GROUPING; ia2_role_ = IA2_ROLE_SECTION; + ia_state_|= STATE_SYSTEM_READONLY; break; case WebAccessibility::ROLE_TAB_GROUP: case WebAccessibility::ROLE_TAB_LIST: @@ -2380,21 +2740,27 @@ void BrowserAccessibilityWin::InitRoleAndState() { break; case WebAccessibility::ROLE_TIMER: ia_role_ = ROLE_SYSTEM_CLOCK; + ia_state_|= STATE_SYSTEM_READONLY; break; case WebAccessibility::ROLE_TOOLBAR: ia_role_ = ROLE_SYSTEM_TOOLBAR; + ia_state_|= STATE_SYSTEM_READONLY; break; case WebAccessibility::ROLE_TOOLTIP: ia_role_ = ROLE_SYSTEM_TOOLTIP; + ia_state_|= STATE_SYSTEM_READONLY; break; case WebAccessibility::ROLE_TREE: ia_role_ = ROLE_SYSTEM_OUTLINE; + ia_state_|= STATE_SYSTEM_READONLY; break; case WebAccessibility::ROLE_TREE_GRID: ia_role_ = ROLE_SYSTEM_OUTLINE; + ia_state_|= STATE_SYSTEM_READONLY; break; case WebAccessibility::ROLE_TREE_ITEM: ia_role_ = ROLE_SYSTEM_OUTLINEITEM; + ia_state_|= STATE_SYSTEM_READONLY; break; case WebAccessibility::ROLE_WINDOW: ia_role_ = ROLE_SYSTEM_WINDOW; diff --git a/content/browser/accessibility/browser_accessibility_win.h b/content/browser/accessibility/browser_accessibility_win.h index 23a1b5f..fbcc942 100644 --- a/content/browser/accessibility/browser_accessibility_win.h +++ b/content/browser/accessibility/browser_accessibility_win.h @@ -39,8 +39,10 @@ class BrowserAccessibilityWin &LIBID_IAccessible2Lib>, public IAccessibleImage, public IAccessibleTable, + public IAccessibleTable2, public IAccessibleTableCell, public IAccessibleText, + public IAccessibleValue, public IServiceProvider, public ISimpleDOMDocument, public ISimpleDOMNode, @@ -52,8 +54,10 @@ class BrowserAccessibilityWin COM_INTERFACE_ENTRY(IAccessible2) COM_INTERFACE_ENTRY(IAccessibleImage) COM_INTERFACE_ENTRY(IAccessibleTable) + COM_INTERFACE_ENTRY(IAccessibleTable2) COM_INTERFACE_ENTRY(IAccessibleTableCell) COM_INTERFACE_ENTRY(IAccessibleText) + COM_INTERFACE_ENTRY(IAccessibleValue) COM_INTERFACE_ENTRY(IServiceProvider) COM_INTERFACE_ENTRY(ISimpleDOMDocument) COM_INTERFACE_ENTRY(ISimpleDOMNode) @@ -68,6 +72,7 @@ class BrowserAccessibilityWin // BrowserAccessibility methods. // virtual void Initialize(); + virtual void SendNodeUpdateEvents(); virtual void NativeAddReference(); virtual void NativeReleaseReference(); @@ -332,6 +337,28 @@ class BrowserAccessibilityWin } // + // IAccessibleTable2 methods. + // + // (Most of these are duplicates of IAccessibleTable methods, only the + // unique ones are included here.) + // + + STDMETHODIMP get_cellAt(long row, + long column, + IUnknown** cell); + + STDMETHODIMP get_nSelectedCells(long* cell_count); + + STDMETHODIMP get_selectedCells(IUnknown*** cells, + long* n_selected_cells); + + STDMETHODIMP get_selectedColumns(long** columns, + long* n_columns); + + STDMETHODIMP get_selectedRows(long** rows, + long* n_rows); + + // // IAccessibleTableCell methods. // @@ -390,6 +417,14 @@ class BrowserAccessibilityWin LONG* start_offset, LONG* end_offset, BSTR* text); + STDMETHODIMP get_newText(IA2TextSegment* new_text); + + STDMETHODIMP get_oldText(IA2TextSegment* old_text); + + STDMETHODIMP get_offsetAtPoint(LONG x, LONG y, + enum IA2CoordinateType coord_type, + LONG* offset); + // IAccessibleText methods not implemented. STDMETHODIMP addSelection(LONG start_offset, LONG end_offset) { return E_NOTIMPL; @@ -404,11 +439,6 @@ class BrowserAccessibilityWin LONG* width, LONG* height) { return E_NOTIMPL; } - STDMETHODIMP get_offsetAtPoint(LONG x, LONG y, - enum IA2CoordinateType coord_type, - LONG* offset) { - return E_NOTIMPL; - } STDMETHODIMP removeSelection(LONG selection_index) { return E_NOTIMPL; } @@ -430,12 +460,18 @@ class BrowserAccessibilityWin LONG x, LONG y) { return E_NOTIMPL; } - STDMETHODIMP get_newText(IA2TextSegment* new_text) { - return E_NOTIMPL; - } - STDMETHODIMP get_oldText(IA2TextSegment* old_text) { - return E_NOTIMPL; - } + + // + // IAccessibleValue methods. + // + + STDMETHODIMP get_currentValue(VARIANT* value); + + STDMETHODIMP get_minimumValue(VARIANT* value); + + STDMETHODIMP get_maximumValue(VARIANT* value); + + STDMETHODIMP setCurrentValue(VARIANT new_value); // // ISimpleDOMDocument methods. @@ -598,6 +634,21 @@ class BrowserAccessibilityWin HRESULT GetStringAttributeAsBstr( WebAccessibility::StringAttribute attribute, BSTR* value_bstr); + // If the string attribute |attribute| is present, add its value as an + // IAccessible2 attribute with the name |ia2_attr|. + void StringAttributeToIA2( + WebAccessibility::StringAttribute attribute, const char* ia2_attr); + + // If the bool attribute |attribute| is present, add its value as an + // IAccessible2 attribute with the name |ia2_attr|. + void BoolAttributeToIA2( + WebAccessibility::BoolAttribute attribute, const char* ia2_attr); + + // If the int attribute |attribute| is present, add its value as an + // IAccessible2 attribute with the name |ia2_attr|. + void IntAttributeToIA2( + WebAccessibility::IntAttribute attribute, const char* ia2_attr); + // Escape a string like it would be escaped for a URL or HTML form. string16 Escape(const string16& str); @@ -629,6 +680,21 @@ class BrowserAccessibilityWin int32 ia2_role_; int32 ia2_state_; + // IAccessible2 attributes. + std::vector ia2_attributes_; + + // True in Initialize when the object is first created, and false + // subsequent times. + bool first_time_; + + // The previous text, before the last update to this object. + string16 previous_text_; + + // The old text to return in IAccessibleText::get_oldText - this is like + // previous_text_ except that it's NOT updated when the object + // is initialized again but the text doesn't change. + string16 old_text_; + // Give BrowserAccessibility::Create access to our constructor. friend class BrowserAccessibility; diff --git a/content/browser/renderer_host/browser_render_process_host.cc b/content/browser/renderer_host/browser_render_process_host.cc index ea762b9..51f7b5c 100644 --- a/content/browser/renderer_host/browser_render_process_host.cc +++ b/content/browser/renderer_host/browser_render_process_host.cc @@ -555,6 +555,7 @@ void BrowserRenderProcessHost::PropagateBrowserCommandLineToRenderer( switches::kDisableSpeechInput, switches::kDisableWebAudio, switches::kDisableWebSockets, + switches::kEnableAccessibilityLogging, switches::kEnableAdaptive, switches::kEnableBenchmarking, switches::kEnableDCHECK, diff --git a/content/browser/renderer_host/render_view_host.cc b/content/browser/renderer_host/render_view_host.cc index 792151a..f88940e 100644 --- a/content/browser/renderer_host/render_view_host.cc +++ b/content/browser/renderer_host/render_view_host.cc @@ -1267,11 +1267,9 @@ void RenderViewHost::OnAccessibilityNotifications( for (unsigned i = 0; i < params.size(); i++) { const ViewHostMsg_AccessibilityNotification_Params& param = params[i]; - if (param.notification_type == - ViewHostMsg_AccessibilityNotification_Type:: - NOTIFICATION_TYPE_LOAD_COMPLETE) { - if (save_accessibility_tree_for_testing_) - accessibility_tree_ = param.acc_obj; + if (param.notification_type == ViewHostMsg_AccEvent::LOAD_COMPLETE && + save_accessibility_tree_for_testing_) { + accessibility_tree_ = param.acc_obj; } } diff --git a/content/common/content_switches.cc b/content/common/content_switches.cc index f4d380b..3df8a44 100644 --- a/content/common/content_switches.cc +++ b/content/common/content_switches.cc @@ -159,6 +159,9 @@ const char kEnableAcceleratedDrawing[] = "enable-accelerated-drawing"; // Enables WebKit accessibility within the renderer process. const char kEnableAccessibility[] = "enable-accessibility"; +// Turns on extremely verbose logging of accessibility events. +const char kEnableAccessibilityLogging[] = "enable-accessibility-logging"; + // Enables the benchmarking extensions. const char kEnableBenchmarking[] = "enable-benchmarking"; diff --git a/content/common/content_switches.h b/content/common/content_switches.h index a248391..31e643c 100644 --- a/content/common/content_switches.h +++ b/content/common/content_switches.h @@ -59,6 +59,7 @@ extern const char kDisableWebSockets[]; extern const char kEnableAccelerated2dCanvas[]; extern const char kEnableAcceleratedDrawing[]; extern const char kEnableAccessibility[]; +extern const char kEnableAccessibilityLogging[]; extern const char kEnableBenchmarking[]; extern const char kEnableDeviceMotion[]; extern const char kEnableFullScreen[]; diff --git a/content/common/view_messages.h b/content/common/view_messages.h index 47ecf7d..395ff9e 100644 --- a/content/common/view_messages.h +++ b/content/common/view_messages.h @@ -47,25 +47,67 @@ #ifndef CONTENT_COMMON_VIEW_MESSAGES_H_ #define CONTENT_COMMON_VIEW_MESSAGES_H_ -struct ViewHostMsg_AccessibilityNotification_Type { +struct ViewHostMsg_AccEvent { enum Value { + // The active descendant of a node has changed. + ACTIVE_DESCENDANT_CHANGED, + + // An alert appeared. + ALERT, + // The node checked state has changed. - NOTIFICATION_TYPE_CHECK_STATE_CHANGED, + CHECK_STATE_CHANGED, // The node tree structure has changed. - NOTIFICATION_TYPE_CHILDREN_CHANGED, + CHILDREN_CHANGED, // The node in focus has changed. - NOTIFICATION_TYPE_FOCUS_CHANGED, + FOCUS_CHANGED, + + // Page layout has completed. + LAYOUT_COMPLETE, + + // Content within a part of the page marked as a live region changed. + LIVE_REGION_CHANGED, // The document node has loaded. - NOTIFICATION_TYPE_LOAD_COMPLETE, + LOAD_COMPLETE, - // The node value has changed. - NOTIFICATION_TYPE_VALUE_CHANGED, + // A menu list value changed. + MENU_LIST_VALUE_CHANGED, + + // An object was shown. + OBJECT_SHOW, + + // An object was hidden. + OBJECT_HIDE, + + // The number of rows in a grid or tree control changed. + ROW_COUNT_CHANGED, + + // A row in a grid or tree control was collapsed. + ROW_COLLAPSED, + + // A row in a grid or tree control was expanded. + ROW_EXPANDED, + + // The document was scrolled to an anchor node. + SCROLLED_TO_ANCHOR, + + // One or more selected children of this node have changed. + SELECTED_CHILDREN_CHANGED, // The text cursor or selection changed. - NOTIFICATION_TYPE_SELECTED_TEXT_CHANGED, + SELECTED_TEXT_CHANGED, + + // Text was inserted in a node with text content. + TEXT_INSERTED, + + // Text was removed in a node with text content. + TEXT_REMOVED, + + // The node value has changed. + VALUE_CHANGED, }; }; @@ -160,7 +202,7 @@ IPC_ENUM_TRAITS(NavigationGesture) IPC_ENUM_TRAITS(PageZoom::Function) IPC_ENUM_TRAITS(RendererPreferencesHintingEnum) IPC_ENUM_TRAITS(RendererPreferencesSubpixelRenderingEnum) -IPC_ENUM_TRAITS(ViewHostMsg_AccessibilityNotification_Type::Value) +IPC_ENUM_TRAITS(ViewHostMsg_AccEvent::Value) IPC_ENUM_TRAITS(ViewHostMsg_RunFileChooser_Mode::Value) IPC_ENUM_TRAITS(ViewMsg_Navigate_Type::Value) IPC_ENUM_TRAITS(ViewMsg_StopFinding_Params::Action) @@ -171,6 +213,8 @@ IPC_ENUM_TRAITS(WebKit::WebTextDirection) IPC_ENUM_TRAITS(ui::TextInputType) IPC_ENUM_TRAITS(WebMenuItem::Type) IPC_ENUM_TRAITS(WindowContainerType) +IPC_ENUM_TRAITS(webkit_glue::WebAccessibility::BoolAttribute) +IPC_ENUM_TRAITS(webkit_glue::WebAccessibility::FloatAttribute) IPC_ENUM_TRAITS(webkit_glue::WebAccessibility::IntAttribute) IPC_ENUM_TRAITS(webkit_glue::WebAccessibility::Role) IPC_ENUM_TRAITS(webkit_glue::WebAccessibility::State) @@ -374,11 +418,14 @@ IPC_STRUCT_TRAITS_BEGIN(webkit_glue::WebAccessibility) IPC_STRUCT_TRAITS_MEMBER(location) IPC_STRUCT_TRAITS_MEMBER(string_attributes) IPC_STRUCT_TRAITS_MEMBER(int_attributes) + IPC_STRUCT_TRAITS_MEMBER(float_attributes) + IPC_STRUCT_TRAITS_MEMBER(bool_attributes) IPC_STRUCT_TRAITS_MEMBER(children) IPC_STRUCT_TRAITS_MEMBER(indirect_child_ids) IPC_STRUCT_TRAITS_MEMBER(html_attributes) IPC_STRUCT_TRAITS_MEMBER(line_breaks) IPC_STRUCT_TRAITS_MEMBER(cell_ids) + IPC_STRUCT_TRAITS_MEMBER(unique_cell_ids) IPC_STRUCT_TRAITS_END() IPC_STRUCT_TRAITS_BEGIN(webkit_glue::WebCookie) @@ -560,8 +607,7 @@ IPC_STRUCT_END() IPC_STRUCT_BEGIN(ViewHostMsg_AccessibilityNotification_Params) // Type of notification. - IPC_STRUCT_MEMBER(ViewHostMsg_AccessibilityNotification_Type::Value, - notification_type) + IPC_STRUCT_MEMBER(ViewHostMsg_AccEvent::Value, notification_type) // The accessibility node tree. IPC_STRUCT_MEMBER(webkit_glue::WebAccessibility, acc_obj) diff --git a/content/renderer/render_view.cc b/content/renderer/render_view.cc index 70d8b5f..9ea7d42 100644 --- a/content/renderer/render_view.cc +++ b/content/renderer/render_view.cc @@ -274,34 +274,54 @@ static void GetRedirectChain(WebDataSource* ds, std::vector* result) { static bool WebAccessibilityNotificationToViewHostMsg( WebAccessibilityNotification notification, - ViewHostMsg_AccessibilityNotification_Type::Value* type) { + ViewHostMsg_AccEvent::Value* type) { switch (notification) { + case WebKit::WebAccessibilityNotificationActiveDescendantChanged: + *type = ViewHostMsg_AccEvent::ACTIVE_DESCENDANT_CHANGED; + break; case WebKit::WebAccessibilityNotificationCheckedStateChanged: - *type = ViewHostMsg_AccessibilityNotification_Type:: - NOTIFICATION_TYPE_CHECK_STATE_CHANGED; + *type = ViewHostMsg_AccEvent::CHECK_STATE_CHANGED; break; case WebKit::WebAccessibilityNotificationChildrenChanged: - *type = ViewHostMsg_AccessibilityNotification_Type:: - NOTIFICATION_TYPE_CHILDREN_CHANGED; + *type = ViewHostMsg_AccEvent::CHILDREN_CHANGED; break; case WebKit::WebAccessibilityNotificationFocusedUIElementChanged: - *type = ViewHostMsg_AccessibilityNotification_Type:: - NOTIFICATION_TYPE_FOCUS_CHANGED; + *type = ViewHostMsg_AccEvent::FOCUS_CHANGED; + break; + case WebKit::WebAccessibilityNotificationLayoutComplete: + *type = ViewHostMsg_AccEvent::LAYOUT_COMPLETE; + break; + case WebKit::WebAccessibilityNotificationLiveRegionChanged: + *type = ViewHostMsg_AccEvent::LIVE_REGION_CHANGED; break; case WebKit::WebAccessibilityNotificationLoadComplete: - *type = ViewHostMsg_AccessibilityNotification_Type:: - NOTIFICATION_TYPE_LOAD_COMPLETE; + *type = ViewHostMsg_AccEvent::LOAD_COMPLETE; break; - case WebKit::WebAccessibilityNotificationValueChanged: - *type = ViewHostMsg_AccessibilityNotification_Type:: - NOTIFICATION_TYPE_VALUE_CHANGED; + case WebKit::WebAccessibilityNotificationMenuListValueChanged: + *type = ViewHostMsg_AccEvent::MENU_LIST_VALUE_CHANGED; + break; + case WebKit::WebAccessibilityNotificationRowCollapsed: + *type = ViewHostMsg_AccEvent::ROW_COLLAPSED; + break; + case WebKit::WebAccessibilityNotificationRowCountChanged: + *type = ViewHostMsg_AccEvent::ROW_COUNT_CHANGED; + break; + case WebKit::WebAccessibilityNotificationRowExpanded: + *type = ViewHostMsg_AccEvent::ROW_EXPANDED; + break; + case WebKit::WebAccessibilityNotificationScrolledToAnchor: + *type = ViewHostMsg_AccEvent::SCROLLED_TO_ANCHOR; + break; + case WebKit::WebAccessibilityNotificationSelectedChildrenChanged: + *type = ViewHostMsg_AccEvent::SELECTED_CHILDREN_CHANGED; break; case WebKit::WebAccessibilityNotificationSelectedTextChanged: - *type = ViewHostMsg_AccessibilityNotification_Type:: - NOTIFICATION_TYPE_SELECTED_TEXT_CHANGED; + *type = ViewHostMsg_AccEvent::SELECTED_TEXT_CHANGED; + break; + case WebKit::WebAccessibilityNotificationValueChanged: + *type = ViewHostMsg_AccEvent::VALUE_CHANGED; break; default: - // TODO(ctguil): Support additional webkit notifications. return false; } return true; @@ -365,6 +385,7 @@ RenderView::RenderView(RenderThreadBase* render_thread, speech_input_dispatcher_(NULL), device_orientation_dispatcher_(NULL), accessibility_ack_pending_(false), + accessibility_logging_(false), p2p_socket_dispatcher_(NULL), devtools_agent_(NULL), session_storage_namespace_id_(session_storage_namespace_id), @@ -412,6 +433,8 @@ RenderView::RenderView(RenderThreadBase* render_thread, const CommandLine& command_line = *CommandLine::ForCurrentProcess(); if (command_line.HasSwitch(switches::kEnableAccessibility)) WebAccessibilityCache::enableAccessibility(); + if (command_line.HasSwitch(switches::kEnableAccessibilityLogging)) + accessibility_logging_ = true; #if defined(ENABLE_P2P_APIS) p2p_socket_dispatcher_ = new content::P2PSocketDispatcher(this); @@ -526,7 +549,8 @@ void RenderView::RemoveObserver(RenderViewObserver* observer) { bool RenderView::RendererAccessibilityNotification::ShouldIncludeChildren() { typedef ViewHostMsg_AccessibilityNotification_Params params; if (type == WebKit::WebAccessibilityNotificationChildrenChanged || - type == WebKit::WebAccessibilityNotificationLoadComplete) { + type == WebKit::WebAccessibilityNotificationLoadComplete || + type == WebKit::WebAccessibilityNotificationLiveRegionChanged) { return true; } return false; @@ -1574,6 +1598,13 @@ void RenderView::SendPendingAccessibilityNotifications() { param.acc_obj = WebAccessibility( obj, accessibility_.get(), notification.ShouldIncludeChildren()); notifications.push_back(param); + +#ifndef NDEBUG + if (accessibility_logging_) { + LOG(INFO) << "Accessibility update: \n" + << param.acc_obj.DebugString(true); + } +#endif } pending_accessibility_notifications_.clear(); Send(new ViewHostMsg_AccessibilityNotifications(routing_id_, notifications)); @@ -4083,7 +4114,7 @@ void RenderView::postAccessibilityNotification( if (acc_notification.id < 0) return; - ViewHostMsg_AccessibilityNotification_Type::Value temp; + ViewHostMsg_AccEvent::Value temp; if (!WebAccessibilityNotificationToViewHostMsg(notification, &temp)) return; diff --git a/content/renderer/render_view.h b/content/renderer/render_view.h index 352ed0b..cf90e5e 100644 --- a/content/renderer/render_view.h +++ b/content/renderer/render_view.h @@ -1162,6 +1162,9 @@ class RenderView : public RenderWidget, // Set if we are waiting for a accessibility notification ack. bool accessibility_ack_pending_; + // True if verbose logging of accessibility events is on. + bool accessibility_logging_; + // Dispatches all P2P socket used by the renderer. content::P2PSocketDispatcher* p2p_socket_dispatcher_; -- cgit v1.1