| Index: content/browser/accessibility/browser_accessibility_win.cc
|
| diff --git a/content/browser/accessibility/browser_accessibility_win.cc b/content/browser/accessibility/browser_accessibility_win.cc
|
| index 71534a4a173ca3a989fd07b70c49dc3d786fff8f..e4d2f3797e3238630e32127ff19d7ad61c0e4935 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;
|
| }
|
|
|
|
|