diff options
author | dmazzoni <dmazzoni@chromium.org> | 2015-02-02 22:56:18 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-02-03 06:57:13 +0000 |
commit | 57831c0d68b35843942e19e30d0c9c86d2005099 (patch) | |
tree | 2f5f91710258cd95a17e5f1adf75385a0a7b492b /content/browser/accessibility/browser_accessibility_win.cc | |
parent | 5eef262146d69b3773e69fa6f886cf869b7adb24 (diff) | |
download | chromium_src-57831c0d68b35843942e19e30d0c9c86d2005099.zip chromium_src-57831c0d68b35843942e19e30d0c9c86d2005099.tar.gz chromium_src-57831c0d68b35843942e19e30d0c9c86d2005099.tar.bz2 |
Fire AX text inserted event when embedded obj char changes.
See bug for details. This patch makes it so that we fire
the events IA2_EVENT_TEXT_INSERTED and IA2_EVENT_TEXT_REMOVED
when the children referenced by the embedded object characters
changed, not just when the text changed.
This patch also avoids incorrectly firing these same two
events on a newly-created node.
BUG=425861
Review URL: https://codereview.chromium.org/859133003
Cr-Commit-Position: refs/heads/master@{#314280}
Diffstat (limited to 'content/browser/accessibility/browser_accessibility_win.cc')
-rw-r--r-- | content/browser/accessibility/browser_accessibility_win.cc | 209 |
1 files changed, 123 insertions, 86 deletions
diff --git a/content/browser/accessibility/browser_accessibility_win.cc b/content/browser/accessibility/browser_accessibility_win.cc index 71534a4..e4d2f37 100644 --- a/content/browser/accessibility/browser_accessibility_win.cc +++ b/content/browser/accessibility/browser_accessibility_win.cc @@ -35,7 +35,7 @@ const GUID GUID_IAccessibleContentDocument = { 0xa5d8e1f3, 0x3571, 0x4d8f, 0x95, 0x21, 0x07, 0xed, 0x28, 0xfb, 0x07, 0x2e}; -const base::char16 BrowserAccessibilityWin::kEmbeddedCharacter[] = L"\xfffc"; +const base::char16 BrowserAccessibilityWin::kEmbeddedCharacter = L'\xfffc'; // static LONG BrowserAccessibilityWin::next_unique_id_win_ = @@ -2174,12 +2174,15 @@ STDMETHODIMP BrowserAccessibilityWin::get_newText(IA2TextSegment* new_text) { if (!new_text) return E_INVALIDARG; + if (!old_win_attributes_) + return E_FAIL; + int start, old_len, new_len; ComputeHypertextRemovedAndInserted(&start, &old_len, &new_len); if (new_len == 0) return E_FAIL; - base::string16 substr = hypertext_.substr(start, new_len); + base::string16 substr = hypertext().substr(start, new_len); new_text->text = SysAllocString(substr.c_str()); new_text->start = static_cast<long>(start); new_text->end = static_cast<long>(start + new_len); @@ -2193,12 +2196,16 @@ STDMETHODIMP BrowserAccessibilityWin::get_oldText(IA2TextSegment* old_text) { if (!old_text) return E_INVALIDARG; + if (!old_win_attributes_) + return E_FAIL; + int start, old_len, new_len; ComputeHypertextRemovedAndInserted(&start, &old_len, &new_len); if (old_len == 0) return E_FAIL; - base::string16 substr = old_hypertext_.substr(start, old_len); + base::string16 old_hypertext = old_win_attributes_->hypertext; + base::string16 substr = old_hypertext.substr(start, old_len); old_text->text = SysAllocString(substr.c_str()); old_text->start = static_cast<long>(start); old_text->end = static_cast<long>(start + old_len); @@ -2302,7 +2309,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_nHyperlinks(long* hyperlink_count) { if (!hyperlink_count) return E_INVALIDARG; - *hyperlink_count = hyperlink_offset_to_index_.size(); + *hyperlink_count = hyperlink_offset_to_index().size(); return S_OK; } @@ -2314,14 +2321,19 @@ STDMETHODIMP BrowserAccessibilityWin::get_hyperlink( if (!hyperlink || index < 0 || - index >= static_cast<long>(hyperlinks_.size())) { + index >= static_cast<long>(hyperlinks().size())) { return E_INVALIDARG; } + int32 id = hyperlinks()[index]; BrowserAccessibilityWin* child = - InternalGetChild(hyperlinks_[index])->ToBrowserAccessibilityWin(); - *hyperlink = static_cast<IAccessibleHyperlink*>(child->NewReference()); - return S_OK; + manager()->GetFromID(id)->ToBrowserAccessibilityWin(); + if (child) { + *hyperlink = static_cast<IAccessibleHyperlink*>(child->NewReference()); + return S_OK; + } + + return E_FAIL; } STDMETHODIMP BrowserAccessibilityWin::get_hyperlinkIndex( @@ -2336,13 +2348,13 @@ STDMETHODIMP BrowserAccessibilityWin::get_hyperlinkIndex( *hyperlink_index = -1; if (char_index < 0 || - char_index >= static_cast<long>(hypertext_.size())) { + char_index >= static_cast<long>(hypertext().size())) { return E_INVALIDARG; } std::map<int32, int32>::iterator it = - hyperlink_offset_to_index_.find(char_index); - if (it == hyperlink_offset_to_index_.end()) + hyperlink_offset_to_index().find(char_index); + if (it == hyperlink_offset_to_index().end()) return E_FAIL; *hyperlink_index = it->second; @@ -2943,21 +2955,10 @@ HRESULT WINAPI BrowserAccessibilityWin::InternalQueryInterface( // Private methods. // -// Called every time this node's data changes, while the tree update is -// still in progress. -void BrowserAccessibilityWin::OnDataChanged() { - BrowserAccessibility::OnDataChanged(); -} - -// Called every time this node's data changes, after an atomic tree update. -void BrowserAccessibilityWin::OnUpdateFinished() { - BrowserAccessibility::OnUpdateFinished(); - - if (PlatformIsChildOfLeaf()) - return; - - bool is_new_object = ia_role() == 0 && role_name().empty(); - +void BrowserAccessibilityWin::UpdateStep1ComputeWinAttributes() { + // Swap win_attributes_ to old_win_attributes_, allowing us to see + // exactly what changed and fire appropriate events. Note that + // old_win_attributes_ is cleared at the end of UpdateStep3FireEvents. old_win_attributes_.swap(win_attributes_); win_attributes_.reset(new WinAttributes()); @@ -3195,7 +3196,31 @@ void BrowserAccessibilityWin::OnUpdateFinished() { win_attributes_->ia_role = ROLE_SYSTEM_GROUPING; win_attributes_->ia2_role = ROLE_SYSTEM_GROUPING; } +} +void BrowserAccessibilityWin::UpdateStep2ComputeHypertext() { + // Construct the hypertext for this node, which contains the concatenation + // of all of the static text of this node's children and an embedded object + // character for all non-static-text children. Build up a map from the + // character index of each embedded object character to the id of the + // child object it points to. + for (unsigned int i = 0; i < PlatformChildCount(); ++i) { + BrowserAccessibilityWin* child = + PlatformGetChild(i)->ToBrowserAccessibilityWin(); + if (child->GetRole() == ui::AX_ROLE_STATIC_TEXT) { + win_attributes_->hypertext += child->name(); + } else { + int32 char_offset = hypertext().size(); + int32 child_id = child->GetId(); + int32 index = hyperlinks().size(); + win_attributes_->hyperlink_offset_to_index[char_offset] = index; + win_attributes_->hyperlinks.push_back(child_id); + win_attributes_->hypertext += kEmbeddedCharacter; + } + } +} + +void BrowserAccessibilityWin::UpdateStep3FireEvents(bool is_subtree_creation) { BrowserAccessibilityManagerWin* manager = this->manager()->ToBrowserAccessibilityManagerWin(); @@ -3205,22 +3230,24 @@ void BrowserAccessibilityWin::OnUpdateFinished() { manager->NotifyAccessibilityEvent(ui::AX_EVENT_ALERT, this); } - // Fire an event if the name, description, help, or value changes. - if (!is_new_object) { - if (name != old_win_attributes_->name) + // Fire an event when a new subtree is created. + if (is_subtree_creation) + manager->MaybeCallNotifyWinEvent(EVENT_OBJECT_SHOW, this); + + // The rest of the events only fire on changes, not on new objects. + if (old_win_attributes_->ia_role != 0 || + !old_win_attributes_->role_name.empty()) { + // Fire an event if the name, description, help, or value changes. + if (name() != old_win_attributes_->name) manager->MaybeCallNotifyWinEvent(EVENT_OBJECT_NAMECHANGE, this); - if (description != old_win_attributes_->description) + if (description() != old_win_attributes_->description) manager->MaybeCallNotifyWinEvent(EVENT_OBJECT_DESCRIPTIONCHANGE, this); - if (help != old_win_attributes_->help) + if (help() != old_win_attributes_->help) manager->MaybeCallNotifyWinEvent(EVENT_OBJECT_HELPCHANGE, this); - if (value != old_win_attributes_->value) + if (value() != old_win_attributes_->value) manager->MaybeCallNotifyWinEvent(EVENT_OBJECT_VALUECHANGE, this); - if (ia_state() != old_win_attributes_->ia_state) { - LOG(INFO) << "State change:" - << " from " << old_win_attributes_->ia_state - << " to " << ia_state(); + if (ia_state() != old_win_attributes_->ia_state) manager->MaybeCallNotifyWinEvent(EVENT_OBJECT_STATECHANGE, this); - } // Normally focus events are handled elsewhere, however // focus for managed descendants is platform-specific. @@ -3256,57 +3283,32 @@ void BrowserAccessibilityWin::OnUpdateFinished() { } // Changing a static text node can affect the IAccessibleText hypertext - // of the parent node, so force it to be recomputed here. - if (GetParent() && + // of the parent node, so force an update on the parent. + BrowserAccessibilityWin* parent = GetParent()->ToBrowserAccessibilityWin(); + if (parent && GetRole() == ui::AX_ROLE_STATIC_TEXT && - name != old_win_attributes_->name) { - GetParent()->ToBrowserAccessibilityWin()->UpdateIAccessibleText(); - } - } - - old_win_attributes_.reset(nullptr); -} - -void BrowserAccessibilityWin::UpdateIAccessibleText() { - old_hypertext_ = hypertext_; - hypertext_.clear(); - - // Construct the hypertext for this node. - hyperlink_offset_to_index_.clear(); - hyperlinks_.clear(); - for (unsigned int i = 0; i < PlatformChildCount(); ++i) { - BrowserAccessibilityWin* child = - PlatformGetChild(i)->ToBrowserAccessibilityWin(); - if (child->GetRole() == ui::AX_ROLE_STATIC_TEXT) { - hypertext_ += child->name(); - } else { - hyperlink_offset_to_index_[hypertext_.size()] = - hyperlinks_.size(); - hypertext_ += kEmbeddedCharacter; - hyperlinks_.push_back(i); + name() != old_win_attributes_->name) { + parent->UpdateStep1ComputeWinAttributes(); + parent->UpdateStep2ComputeHypertext(); + parent->UpdateStep3FireEvents(false); } - } - DCHECK_EQ(hyperlink_offset_to_index_.size(), hyperlinks_.size()); - - if (hypertext_ != old_hypertext_) { - BrowserAccessibilityManagerWin* manager = - this->manager()->ToBrowserAccessibilityManagerWin(); + // Fire hypertext-related events. int start, old_len, new_len; ComputeHypertextRemovedAndInserted(&start, &old_len, &new_len); - if (old_len) { + if (old_len > 0) { // In-process screen readers may call IAccessibleText::get_oldText - // to retrieve the text that was removed. + // in reaction to this event to retrieve the text that was removed. manager->MaybeCallNotifyWinEvent(IA2_EVENT_TEXT_REMOVED, this); } - if (new_len) { + if (new_len > 0) { // In-process screen readers may call IAccessibleText::get_newText - // to retrieve the text that was inserted. + // in reaction to this event to retrieve the text that was inserted. manager->MaybeCallNotifyWinEvent(IA2_EVENT_TEXT_INSERTED, this); } } - old_hypertext_.clear(); + old_win_attributes_.reset(nullptr); } void BrowserAccessibilityWin::OnSubtreeWillBeDeleted() { @@ -3314,11 +3316,6 @@ void BrowserAccessibilityWin::OnSubtreeWillBeDeleted() { EVENT_OBJECT_HIDE, this); } -void BrowserAccessibilityWin::OnSubtreeCreationFinished() { - manager()->ToBrowserAccessibilityManagerWin()->MaybeCallNotifyWinEvent( - EVENT_OBJECT_SHOW, this); -} - void BrowserAccessibilityWin::NativeAddReference() { AddRef(); } @@ -3433,30 +3430,70 @@ base::string16 BrowserAccessibilityWin::GetValueText() { base::string16 BrowserAccessibilityWin::TextForIAccessibleText() { if (IsEditableText()) return value(); - return (GetRole() == ui::AX_ROLE_STATIC_TEXT) ? name() : hypertext_; + return (GetRole() == ui::AX_ROLE_STATIC_TEXT) ? name() : hypertext(); +} + +bool BrowserAccessibilityWin::IsSameHypertextCharacter(size_t old_char_index, + size_t new_char_index) { + CHECK(old_win_attributes_); + + // For anything other than the "embedded character", we just compare the + // characters directly. + base::char16 old_ch = old_win_attributes_->hypertext[old_char_index]; + base::char16 new_ch = win_attributes_->hypertext[new_char_index]; + if (old_ch != new_ch) + return false; + if (old_ch == new_ch && new_ch != kEmbeddedCharacter) + return true; + + // If it's an embedded character, they're only identical if the child id + // the hyperlink points to is the same. + std::map<int32, int32>& old_offset_to_index = + old_win_attributes_->hyperlink_offset_to_index; + std::vector<int32>& old_hyperlinks = old_win_attributes_->hyperlinks; + int32 old_hyperlinks_count = static_cast<int32>(old_hyperlinks.size()); + std::map<int32, int32>::iterator iter; + iter = old_offset_to_index.find(old_char_index); + int old_index = (iter != old_offset_to_index.end()) ? iter->second : -1; + int old_child_id = (old_index >= 0 && old_index < old_hyperlinks_count) ? + old_hyperlinks[old_index] : -1; + + std::map<int32, int32>& new_offset_to_index = + win_attributes_->hyperlink_offset_to_index; + std::vector<int32>& new_hyperlinks = win_attributes_->hyperlinks; + int32 new_hyperlinks_count = static_cast<int32>(new_hyperlinks.size()); + iter = new_offset_to_index.find(new_char_index); + int new_index = (iter != new_offset_to_index.end()) ? iter->second : -1; + int new_child_id = (new_index >= 0 && new_index < new_hyperlinks_count) ? + new_hyperlinks[new_index] : -1; + + return old_child_id == new_child_id; } void BrowserAccessibilityWin::ComputeHypertextRemovedAndInserted( int* start, int* old_len, int* new_len) { + CHECK(old_win_attributes_); + *start = 0; *old_len = 0; *new_len = 0; - const base::string16& old_text = old_hypertext_; - const base::string16& new_text = hypertext_; + const base::string16& old_text = old_win_attributes_->hypertext; + const base::string16& new_text = hypertext(); size_t common_prefix = 0; while (common_prefix < old_text.size() && common_prefix < new_text.size() && - old_text[common_prefix] == new_text[common_prefix]) { + IsSameHypertextCharacter(common_prefix, common_prefix)) { ++common_prefix; } size_t common_suffix = 0; while (common_prefix + common_suffix < old_text.size() && common_prefix + common_suffix < new_text.size() && - old_text[old_text.size() - common_suffix - 1] == - new_text[new_text.size() - common_suffix - 1]) { + IsSameHypertextCharacter( + old_text.size() - common_suffix - 1, + new_text.size() - common_suffix - 1)) { ++common_suffix; } |