diff options
| author | nektar <nektar@chromium.org> | 2016-03-24 13:44:25 -0700 |
|---|---|---|
| committer | Commit bot <commit-bot@chromium.org> | 2016-03-24 20:46:35 +0000 |
| commit | 5ca8b4fa7b1f9d53be5f5f1a30b4cecf1c1c4fa5 (patch) | |
| tree | 31d3728633b9ac8c6d3c896e424352299504331f | |
| parent | 75eab41568b41bde52a213b53ce31e164cd3ab2d (diff) | |
| download | chromium_src-5ca8b4fa7b1f9d53be5f5f1a30b4cecf1c1c4fa5.zip chromium_src-5ca8b4fa7b1f9d53be5f5f1a30b4cecf1c1c4fa5.tar.gz chromium_src-5ca8b4fa7b1f9d53be5f5f1a30b4cecf1c1c4fa5.tar.bz2 | |
Fixes typing and deleting text in simple text fields on Mac OS X 10.11.
BUG=530826
TESTED=VoiceOver on Mac OS X 10.11
R=dmazzoni@chromium.org, avi@chromium.org, mark@chromium.org
Review URL: https://codereview.chromium.org/1814163003
Cr-Commit-Position: refs/heads/master@{#383141}
4 files changed, 172 insertions, 20 deletions
diff --git a/content/browser/accessibility/browser_accessibility_manager.h b/content/browser/accessibility/browser_accessibility_manager.h index 908029b..9b5c381 100644 --- a/content/browser/accessibility/browser_accessibility_manager.h +++ b/content/browser/accessibility/browser_accessibility_manager.h @@ -226,7 +226,7 @@ class CONTENT_EXPORT BrowserAccessibilityManager : public ui::AXTreeDelegate { void ActivateFindInPageResult(int request_id, int match_index); // Called when the renderer process has notified us of about tree changes. - void OnAccessibilityEvents( + virtual void OnAccessibilityEvents( const std::vector<AXEventNotificationDetails>& details); // Called when the renderer process updates the location of accessibility diff --git a/content/browser/accessibility/browser_accessibility_manager_mac.h b/content/browser/accessibility/browser_accessibility_manager_mac.h index f659059..7150819 100644 --- a/content/browser/accessibility/browser_accessibility_manager_mac.h +++ b/content/browser/accessibility/browser_accessibility_manager_mac.h @@ -6,9 +6,16 @@ #define CONTENT_BROWSER_ACCESSIBILITY_BROWSER_ACCESSIBILITY_MANAGER_MAC_H_ #import <Cocoa/Cocoa.h> +#include <stdint.h> + +#include <map> +#include <vector> #include "base/macros.h" +#include "base/strings/string16.h" +#import "content/browser/accessibility/browser_accessibility_cocoa.h" #include "content/browser/accessibility/browser_accessibility_manager.h" +#include "content/public/browser/ax_event_notification_details.h" namespace content { @@ -21,6 +28,8 @@ class CONTENT_EXPORT BrowserAccessibilityManagerMac BrowserAccessibilityDelegate* delegate, BrowserAccessibilityFactory* factory = new BrowserAccessibilityFactory()); + ~BrowserAccessibilityManagerMac() override; + static ui::AXTreeUpdate GetEmptyDocument(); BrowserAccessibility* GetFocus() override; @@ -29,9 +38,16 @@ class CONTENT_EXPORT BrowserAccessibilityManagerMac void NotifyAccessibilityEvent(ui::AXEvent event_type, BrowserAccessibility* node) override; + void OnAccessibilityEvents( + const std::vector<AXEventNotificationDetails>& details) override; + NSView* parent_view() { return parent_view_; } private: + // AXTreeDelegate methods. + void OnNodeDataWillChange(ui::AXTree* tree, + const ui::AXNodeData& old_node_data, + const ui::AXNodeData& new_node_data) override; void OnAtomicUpdateFinished( ui::AXTree* tree, bool root_changed, @@ -40,12 +56,23 @@ class CONTENT_EXPORT BrowserAccessibilityManagerMac // Returns an autoreleased object. NSDictionary* GetUserInfoForSelectedTextChangedNotification(); + // Returns an autoreleased object. + NSDictionary* GetUserInfoForValueChangedNotification( + const BrowserAccessibilityCocoa* native_node, + const base::string16& deleted_text, + const base::string16& inserted_text) const; + // This gives BrowserAccessibilityManager::Create access to the class // constructor. friend class BrowserAccessibilityManager; NSView* parent_view_; + // Keeps track of any edits that have been made by the user during a tree + // update. Used by NSAccessibilityValueChangedNotification. + // Maps AXNode IDs to name or value attribute changes. + std::map<int32_t, base::string16> text_edits_; + DISALLOW_COPY_AND_ASSIGN(BrowserAccessibilityManagerMac); }; diff --git a/content/browser/accessibility/browser_accessibility_manager_mac.mm b/content/browser/accessibility/browser_accessibility_manager_mac.mm index 0df142e..48f8023 100644 --- a/content/browser/accessibility/browser_accessibility_manager_mac.mm +++ b/content/browser/accessibility/browser_accessibility_manager_mac.mm @@ -4,19 +4,19 @@ #include "content/browser/accessibility/browser_accessibility_manager_mac.h" -#include <stddef.h> - #import "base/mac/mac_util.h" #import "base/mac/sdk_forward_declarations.h" #include "base/logging.h" +#include "base/strings/sys_string_conversions.h" +#include "base/strings/utf_string_conversions.h" #import "content/browser/accessibility/browser_accessibility_cocoa.h" #import "content/browser/accessibility/browser_accessibility_mac.h" #include "content/common/accessibility_messages.h" - namespace { -// Declare accessibility constants and enums only present in WebKit. +// Declare undocumented accessibility constants and enums only present in +// WebKit. enum AXTextStateChangeType { AXTextStateChangeTypeUnknown, @@ -46,6 +46,17 @@ enum AXTextSelectionGranularity { AXTextSelectionGranularityAll }; +enum AXTextEditType { + AXTextEditTypeUnknown, + AXTextEditTypeDelete, + AXTextEditTypeInsert, + AXTextEditTypeTyping, + AXTextEditTypeDictation, + AXTextEditTypeCut, + AXTextEditTypePaste, + AXTextEditTypeAttributesChange +}; + NSString* const NSAccessibilityAutocorrectionOccurredNotification = @"AXAutocorrectionOccurred"; NSString* const NSAccessibilityLayoutCompleteNotification = @@ -59,7 +70,8 @@ NSString* const NSAccessibilityLiveRegionChangedNotification = NSString* const NSAccessibilityMenuItemSelectedNotification = @"AXMenuItemSelected"; -// Attributes used for NSAccessibilitySelectedTextChangedNotification. +// Attributes used for NSAccessibilitySelectedTextChangedNotification and +// NSAccessibilityValueChangedNotification. NSString* const NSAccessibilityTextStateChangeTypeKey = @"AXTextStateChangeType"; NSString* const NSAccessibilityTextStateSyncKey = @"AXTextStateSync"; @@ -72,6 +84,11 @@ NSString* const NSAccessibilityTextSelectionChangedFocus = NSString* const NSAccessibilitySelectedTextMarkerRangeAttribute = @"AXSelectedTextMarkerRange"; NSString* const NSAccessibilityTextChangeElement = @"AXTextChangeElement"; +NSString* const NSAccessibilityTextEditType = @"AXTextEditType"; +NSString* const NSAccessibilityTextChangeValue = @"AXTextChangeValue"; +NSString* const NSAccessibilityTextChangeValueLength = + @"AXTextChangeValueLength"; +NSString* const NSAccessibilityTextChangeValues = @"AXTextChangeValues"; } // namespace @@ -96,6 +113,8 @@ BrowserAccessibilityManagerMac::BrowserAccessibilityManagerMac( Initialize(initial_tree); } +BrowserAccessibilityManagerMac::~BrowserAccessibilityManagerMac() {} + // static ui::AXTreeUpdate BrowserAccessibilityManagerMac::GetEmptyDocument() { @@ -186,7 +205,7 @@ void BrowserAccessibilityManagerMac::NotifyAccessibilityEvent( // WebKit fires a notification both on the focused object and the root. BrowserAccessibility* focus = GetFocus(); if (!focus) - break; + break; // Just fire a notification on the root. NSAccessibilityPostNotification(ToBrowserAccessibilityCocoa(focus), mac_notification); @@ -211,8 +230,32 @@ void BrowserAccessibilityManagerMac::NotifyAccessibilityEvent( break; } case ui::AX_EVENT_CHECKED_STATE_CHANGED: + mac_notification = NSAccessibilityValueChangedNotification; + break; case ui::AX_EVENT_VALUE_CHANGED: mac_notification = NSAccessibilityValueChangedNotification; + if (base::mac::IsOSElCapitanOrLater() && text_edits_.size()) { + // It seems that we don't need to distinguish between deleted and + // inserted text for now. + base::string16 deleted_text; + base::string16 inserted_text; + int32_t id = node->GetId(); + const auto iterator = text_edits_.find(id); + if (iterator != text_edits_.end()) + inserted_text = iterator->second; + NSDictionary* user_info = GetUserInfoForValueChangedNotification( + native_node, deleted_text, inserted_text); + + BrowserAccessibility* root = GetRoot(); + if (!root) + return; + + NSAccessibilityPostNotificationWithUserInfo( + native_node, mac_notification, user_info); + NSAccessibilityPostNotificationWithUserInfo( + ToBrowserAccessibilityCocoa(root), mac_notification, user_info); + return; + } break; // TODO(nektar): Need to add an event for live region created. case ui::AX_EVENT_LIVE_REGION_CHANGED: @@ -259,6 +302,61 @@ void BrowserAccessibilityManagerMac::NotifyAccessibilityEvent( NSAccessibilityPostNotification(native_node, mac_notification); } +void BrowserAccessibilityManagerMac::OnAccessibilityEvents( + const std::vector<AXEventNotificationDetails>& details) { + BrowserAccessibilityManager::OnAccessibilityEvents(details); + text_edits_.clear(); +} + +void BrowserAccessibilityManagerMac::OnNodeDataWillChange( + ui::AXTree* tree, + const ui::AXNodeData& old_node_data, + const ui::AXNodeData& new_node_data) { + BrowserAccessibilityManager::OnNodeDataWillChange(tree, old_node_data, + new_node_data); + + // Starting from OS X 10.11, if the user has edited some text we need to + // dispatch the actual text that changed on the value changed notification. + // We run this code on all OS X versions to get the highest test coverage. + base::string16 old_text, new_text; + ui::AXRole role = new_node_data.role; + if (role == ui::AX_ROLE_COMBO_BOX || role == ui::AX_ROLE_SEARCH_BOX || + role == ui::AX_ROLE_TEXT_FIELD) { + old_text = old_node_data.GetString16Attribute(ui::AX_ATTR_VALUE); + new_text = new_node_data.GetString16Attribute(ui::AX_ATTR_VALUE); + } else if (new_node_data.state & (1 << ui::AX_STATE_EDITABLE)) { + old_text = old_node_data.GetString16Attribute(ui::AX_ATTR_NAME); + new_text = new_node_data.GetString16Attribute(ui::AX_ATTR_NAME); + } + + if ((old_text.empty() && new_text.empty()) || + old_text.length() == new_text.length()) { + return; + } + + if (old_text.length() < new_text.length()) { + // Insertion. + size_t i = 0; + while (i < old_text.length() && i < new_text.length() && + old_text[i] == new_text[i]) { + ++i; + } + size_t length = (new_text.length() - i) - (old_text.length() - i); + base::string16 inserted_text = new_text.substr(i, length); + text_edits_[new_node_data.id] = inserted_text; + } else { + // Deletion. + size_t i = 0; + while (i < old_text.length() && i < new_text.length() && + old_text[i] == new_text[i]) { + ++i; + } + size_t length = (old_text.length() - i) - (new_text.length() - i); + base::string16 deleted_text = old_text.substr(i, length); + text_edits_[new_node_data.id] = deleted_text; + } +} + void BrowserAccessibilityManagerMac::OnAtomicUpdateFinished( ui::AXTree* tree, bool root_changed, @@ -296,23 +394,18 @@ void BrowserAccessibilityManagerMac::OnAtomicUpdateFinished( } } -// Returns an autoreleased object. NSDictionary* BrowserAccessibilityManagerMac:: GetUserInfoForSelectedTextChangedNotification() { NSMutableDictionary* user_info = [[[NSMutableDictionary alloc] init] autorelease]; - [user_info setObject:[NSNumber numberWithBool:YES] - forKey:NSAccessibilityTextStateSyncKey]; - [user_info setObject:[NSNumber numberWithInt:AXTextStateChangeTypeUnknown] + [user_info setObject:@YES forKey:NSAccessibilityTextStateSyncKey]; + [user_info setObject:@(AXTextStateChangeTypeUnknown) forKey:NSAccessibilityTextStateChangeTypeKey]; - [user_info - setObject:[NSNumber numberWithInt:AXTextSelectionDirectionUnknown] - forKey:NSAccessibilityTextSelectionDirection]; - [user_info - setObject:[NSNumber numberWithInt:AXTextSelectionGranularityUnknown] - forKey:NSAccessibilityTextSelectionGranularity]; - [user_info setObject:[NSNumber numberWithBool:YES] - forKey:NSAccessibilityTextSelectionChangedFocus]; + [user_info setObject:@(AXTextSelectionDirectionUnknown) + forKey:NSAccessibilityTextSelectionDirection]; + [user_info setObject:@(AXTextSelectionGranularityUnknown) + forKey:NSAccessibilityTextSelectionGranularity]; + [user_info setObject:@YES forKey:NSAccessibilityTextSelectionChangedFocus]; int32_t focus_id = GetTreeData().sel_focus_object_id; BrowserAccessibility* focus_object = GetFromID(focus_id); @@ -330,4 +423,36 @@ NSDictionary* BrowserAccessibilityManagerMac:: return user_info; } +NSDictionary* +BrowserAccessibilityManagerMac::GetUserInfoForValueChangedNotification( + const BrowserAccessibilityCocoa* native_node, + const base::string16& deleted_text, + const base::string16& inserted_text) const { + DCHECK(native_node); + if (deleted_text.empty() && inserted_text.empty()) + return nil; + + NSMutableArray* changes = [[[NSMutableArray alloc] init] autorelease]; + if (!deleted_text.empty()) { + [changes addObject:@{ + NSAccessibilityTextEditType : @(AXTextEditTypeUnknown), + NSAccessibilityTextChangeValueLength : @(deleted_text.length()), + NSAccessibilityTextChangeValue : base::SysUTF16ToNSString(deleted_text) + }]; + } + if (!inserted_text.empty()) { + [changes addObject:@{ + NSAccessibilityTextEditType : @(AXTextEditTypeUnknown), + NSAccessibilityTextChangeValueLength : @(inserted_text.length()), + NSAccessibilityTextChangeValue : base::SysUTF16ToNSString(inserted_text) + }]; + } + + return @{ + NSAccessibilityTextStateChangeTypeKey : @(AXTextStateChangeTypeEdit), + NSAccessibilityTextChangeValues : changes, + NSAccessibilityTextChangeElement : native_node + }; +} + } // namespace content diff --git a/content/browser/accessibility/browser_accessibility_manager_win.h b/content/browser/accessibility/browser_accessibility_manager_win.h index 42c7da8..04b8aa2 100644 --- a/content/browser/accessibility/browser_accessibility_manager_win.h +++ b/content/browser/accessibility/browser_accessibility_manager_win.h @@ -58,7 +58,7 @@ class CONTENT_EXPORT BrowserAccessibilityManagerWin void OnAccessibleHwndDeleted(); protected: - // AXTree methods. + // AXTreeDelegate methods. void OnNodeWillBeDeleted(ui::AXTree* tree, ui::AXNode* node) override; void OnNodeCreated(ui::AXTree* tree, ui::AXNode* node) override; void OnAtomicUpdateFinished( |
