Chromium Code Reviews| 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 bf3b0affdd751d7d45d1dc592723149d05660882..82353f06f92e19713eba39ad39cde788e1f46373 100644 |
| --- a/content/browser/accessibility/browser_accessibility_win.cc |
| +++ b/content/browser/accessibility/browser_accessibility_win.cc |
| @@ -20,6 +20,7 @@ |
| #include "content/browser/accessibility/browser_accessibility_state_impl.h" |
| #include "content/common/accessibility_messages.h" |
| #include "content/public/common/content_client.h" |
| +#include "third_party/skia/include/core/SkColor.h" |
| #include "ui/accessibility/ax_text_utils.h" |
| #include "ui/base/win/accessibility_ids_win.h" |
| #include "ui/base/win/accessibility_misc_utils.h" |
| @@ -115,9 +116,8 @@ STDMETHODIMP BrowserAccessibilityRelation::get_nTargets(long* n_targets) { |
| *n_targets = static_cast<long>(target_ids_.size()); |
| - BrowserAccessibilityManager* manager = owner_->manager(); |
| for (long i = *n_targets - 1; i >= 0; --i) { |
| - BrowserAccessibility* result = manager->GetFromID(target_ids_[i]); |
| + BrowserAccessibilityWin* result = owner_->GetFromID(target_ids_[i]); |
| if (!result || !result->instance_active()) { |
| *n_targets = 0; |
| break; |
| @@ -139,9 +139,7 @@ STDMETHODIMP BrowserAccessibilityRelation::get_target(long target_index, |
| return E_INVALIDARG; |
| } |
| - BrowserAccessibilityManager* manager = owner_->manager(); |
| - BrowserAccessibility* result = |
| - manager->GetFromID(target_ids_[target_index]); |
| + BrowserAccessibility* result = owner_->GetFromID(target_ids_[target_index]); |
| if (!result || !result->instance_active()) |
| return E_FAIL; |
| @@ -576,14 +574,15 @@ STDMETHODIMP BrowserAccessibilityWin::get_accValue(VARIANT var_id, |
| // Expose color well value. |
| if (target->ia2_role() == IA2_ROLE_COLOR_CHOOSER) { |
| - int color = target->GetIntAttribute(ui::AX_ATTR_COLOR_VALUE); |
| - int red = (color >> 16) & 0xFF; |
| - int green = (color >> 8) & 0xFF; |
| - int blue = color & 0xFF; |
| + unsigned int color = static_cast<unsigned int>( |
| + target->GetIntAttribute(ui::AX_ATTR_COLOR_VALUE)); |
| + unsigned int red = SkColorGetR(color); |
| + unsigned int green = SkColorGetG(color); |
| + unsigned int blue = SkColorGetB(color); |
| base::string16 value_text; |
| - value_text = base::IntToString16((red * 100) / 255) + L"% red " + |
| - base::IntToString16((green * 100) / 255) + L"% green " + |
| - base::IntToString16((blue * 100) / 255) + L"% blue"; |
| + value_text = base::UintToString16(red * 100 / 255) + L"% red " + |
| + base::UintToString16(green * 100 / 255) + L"% green " + |
| + base::UintToString16(blue * 100 / 255) + L"% blue"; |
| *value = SysAllocString(value_text.c_str()); |
| DCHECK(*value); |
| return S_OK; |
| @@ -687,19 +686,16 @@ STDMETHODIMP BrowserAccessibilityWin::role(LONG* role) { |
| } |
| STDMETHODIMP BrowserAccessibilityWin::get_attributes(BSTR* attributes) { |
| - if (!instance_active()) |
| - return E_FAIL; |
| - |
| if (!attributes) |
| return E_INVALIDARG; |
| + *attributes = nullptr; |
| + |
| + if (!instance_active()) |
| + return E_FAIL; |
| - // The iaccessible2 attributes are a set of key-value pairs |
| - // separated by semicolons, with a colon between the key and the value. |
| base::string16 str; |
| - const std::vector<base::string16>& attributes_list = ia2_attributes(); |
| - for (unsigned int i = 0; i < attributes_list.size(); ++i) { |
| - str += attributes_list[i] + L';'; |
| - } |
| + for (const base::string16& attribute : ia2_attributes()) |
| + str += attribute + L';'; |
| if (str.empty()) |
| return S_FALSE; |
| @@ -847,7 +843,6 @@ STDMETHODIMP BrowserAccessibilityWin::scrollTo(IA2ScrollType scroll_type) { |
| } |
| manager()->ToBrowserAccessibilityManagerWin()->TrackScrollingObject(this); |
| - |
| return S_OK; |
| } |
| @@ -1181,8 +1176,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_columnDescription(long column, |
| GetIntListAttribute(ui::AX_ATTR_CELL_IDS); |
| for (int i = 0; i < rows; ++i) { |
| int cell_id = cell_ids[i * columns + column]; |
| - BrowserAccessibilityWin* cell = static_cast<BrowserAccessibilityWin*>( |
| - manager()->GetFromID(cell_id)); |
| + BrowserAccessibilityWin* cell = GetFromID(cell_id); |
| if (cell && cell->GetRole() == ui::AX_ROLE_COLUMN_HEADER) { |
| base::string16 cell_name = cell->GetString16Attribute( |
| ui::AX_ATTR_NAME); |
| @@ -1227,8 +1221,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_columnExtentAt( |
| const std::vector<int32_t>& cell_ids = |
| GetIntListAttribute(ui::AX_ATTR_CELL_IDS); |
| int cell_id = cell_ids[row * columns + column]; |
| - BrowserAccessibilityWin* cell = static_cast<BrowserAccessibilityWin*>( |
| - manager()->GetFromID(cell_id)); |
| + BrowserAccessibilityWin* cell = GetFromID(cell_id); |
| int colspan; |
| if (cell && |
| cell->GetIntAttribute( |
| @@ -1265,8 +1258,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_columnIndex(long cell_index, |
| return S_FALSE; |
| int cell_id = unique_cell_ids[cell_index]; |
| - BrowserAccessibilityWin* cell = |
| - ToBrowserAccessibilityWin(manager()->GetFromID(cell_id)); |
| + BrowserAccessibilityWin* cell = GetFromID(cell_id); |
| int col_index; |
| if (cell && |
| cell->GetIntAttribute( |
| @@ -1370,8 +1362,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_rowDescription(long row, |
| GetIntListAttribute(ui::AX_ATTR_CELL_IDS); |
| for (int i = 0; i < columns; ++i) { |
| int cell_id = cell_ids[row * columns + i]; |
| - BrowserAccessibilityWin* cell = |
| - ToBrowserAccessibilityWin(manager()->GetFromID(cell_id)); |
| + BrowserAccessibilityWin* cell = GetFromID(cell_id); |
| if (cell && cell->GetRole() == ui::AX_ROLE_ROW_HEADER) { |
| base::string16 cell_name = cell->GetString16Attribute( |
| ui::AX_ATTR_NAME); |
| @@ -1415,8 +1406,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_rowExtentAt(long row, |
| const std::vector<int32_t>& cell_ids = |
| GetIntListAttribute(ui::AX_ATTR_CELL_IDS); |
| int cell_id = cell_ids[row * columns + column]; |
| - BrowserAccessibilityWin* cell = |
| - ToBrowserAccessibilityWin(manager()->GetFromID(cell_id)); |
| + BrowserAccessibilityWin* cell = GetFromID(cell_id); |
| int rowspan; |
| if (cell && |
| cell->GetIntAttribute( |
| @@ -1453,8 +1443,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_rowIndex(long cell_index, |
| return S_FALSE; |
| int cell_id = unique_cell_ids[cell_index]; |
| - BrowserAccessibilityWin* cell = |
| - ToBrowserAccessibilityWin(manager()->GetFromID(cell_id)); |
| + BrowserAccessibilityWin* cell = GetFromID(cell_id); |
| int cell_row_index; |
| if (cell && |
| cell->GetIntAttribute( |
| @@ -1582,8 +1571,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_rowColumnExtentsAtIndex( |
| return S_FALSE; |
| int cell_id = unique_cell_ids[index]; |
| - BrowserAccessibilityWin* cell = |
| - ToBrowserAccessibilityWin(manager()->GetFromID(cell_id)); |
| + BrowserAccessibilityWin* cell = GetFromID(cell_id); |
| int rowspan; |
| int colspan; |
| if (cell && |
| @@ -1741,8 +1729,7 @@ STDMETHODIMP BrowserAccessibilityWin::get_columnHeaderCells( |
| for (int i = 0; i < rows; ++i) { |
| int cell_id = cell_ids[i * columns + column]; |
| - BrowserAccessibilityWin* cell = |
| - ToBrowserAccessibilityWin(manager()->GetFromID(cell_id)); |
| + BrowserAccessibilityWin* cell = GetFromID(cell_id); |
| if (cell && cell->GetRole() == ui::AX_ROLE_COLUMN_HEADER) |
| (*n_column_header_cells)++; |
| } |
| @@ -2349,15 +2336,40 @@ STDMETHODIMP BrowserAccessibilityWin::setSelection(LONG selection_index, |
| return S_OK; |
| } |
| -// |
| -// IAccessibleText methods not implemented. |
| -// |
| - |
| STDMETHODIMP BrowserAccessibilityWin::get_attributes(LONG offset, |
| LONG* start_offset, |
| LONG* end_offset, |
| BSTR* text_attributes) { |
| - return E_NOTIMPL; |
| + if (!start_offset || !end_offset || !text_attributes) |
| + return E_INVALIDARG; |
| + |
| + *start_offset = *end_offset = 0; |
| + *text_attributes = nullptr; |
| + if (!instance_active()) |
| + return E_FAIL; |
| + |
| + const base::string16& text = GetText(); |
| + HandleSpecialTextOffset(text, &offset); |
| + if (offset < 0 || offset > static_cast<LONG>(text.size())) |
| + return E_INVALIDARG; |
| + |
| + MayBeComputeStyles(); |
|
dmazzoni
2016/03/14 17:38:21
nit: no capital B in maybe, just spell it MaybeCom
|
| + *start_offset = FindStartOfStyle(offset, ui::BACKWARDS_DIRECTION); |
| + *end_offset = FindStartOfStyle(offset, ui::FORWARDS_DIRECTION); |
| + |
| + base::string16 attributes_str; |
| + const std::vector<base::string16>& attributes = |
| + offset_to_text_attributes().find(*start_offset)->second; |
| + for (const base::string16& attribute : attributes) { |
| + attributes_str += attribute + L';'; |
| + } |
| + |
| + if (attributes.empty()) |
| + return S_FALSE; |
| + |
| + *text_attributes = SysAllocString(attributes_str.c_str()); |
| + DCHECK(*text_attributes); |
| + return S_OK; |
| } |
| // |
| @@ -2388,14 +2400,12 @@ STDMETHODIMP BrowserAccessibilityWin::get_hyperlink( |
| } |
| int32_t id = hyperlinks()[index]; |
| - BrowserAccessibilityWin* child = |
| - ToBrowserAccessibilityWin(manager()->GetFromID(id)); |
| - if (child) { |
| - *hyperlink = static_cast<IAccessibleHyperlink*>(child->NewReference()); |
| - return S_OK; |
| - } |
| + BrowserAccessibilityWin* link = GetFromID(id); |
| + if (!link) |
| + return E_FAIL; |
| - return E_FAIL; |
| + *hyperlink = static_cast<IAccessibleHyperlink*>(link->NewReference()); |
| + return S_OK; |
| } |
| STDMETHODIMP BrowserAccessibilityWin::get_hyperlinkIndex( |
| @@ -2973,7 +2983,20 @@ BrowserAccessibilityWin::get_localInterface(void** local_interface) { |
| } |
| STDMETHODIMP BrowserAccessibilityWin::get_language(BSTR* language) { |
| - return E_NOTIMPL; |
| + if (!language) |
| + return E_INVALIDARG; |
| + *language = nullptr; |
| + |
| + if (!instance_active()) |
| + return E_FAIL; |
| + |
| + base::string16 lang = GetInheritedString16Attribute(ui::AX_ATTR_LANGUAGE); |
| + if (lang.empty()) |
| + lang = L"en-US"; |
| + |
| + *language = SysAllocString(lang.c_str()); |
| + DCHECK(*language); |
| + return S_OK; |
| } |
| // |
| @@ -3052,7 +3075,21 @@ STDMETHODIMP BrowserAccessibilityWin::scrollToSubstring( |
| } |
| STDMETHODIMP BrowserAccessibilityWin::get_fontFamily(BSTR* font_family) { |
| - return E_NOTIMPL; |
| + if (!font_family) |
| + return E_INVALIDARG; |
| + *font_family = nullptr; |
| + |
| + if (!instance_active()) |
| + return E_FAIL; |
| + |
| + base::string16 family = |
| + GetInheritedString16Attribute(ui::AX_ATTR_FONT_FAMILY); |
| + if (family.empty()) |
| + return S_FALSE; |
| + |
| + *font_family = SysAllocString(family.c_str()); |
| + DCHECK(*font_family); |
| + return S_OK; |
| } |
| // |
| @@ -3317,44 +3354,6 @@ void BrowserAccessibilityWin::UpdateStep1ComputeWinAttributes() { |
| } |
| } |
| - // Expose invalid state for form controls and elements with aria-invalid. |
| - int invalid_state; |
| - if (GetIntAttribute(ui::AX_ATTR_INVALID_STATE, &invalid_state)) { |
| - // TODO(nektar): Handle the possibility of having multiple aria-invalid |
| - // attributes defined, e.g., "invalid:spelling,grammar". |
| - switch (invalid_state) { |
| - case ui::AX_INVALID_STATE_FALSE: |
| - win_attributes_->ia2_attributes.push_back(L"invalid:false"); |
| - break; |
| - case ui::AX_INVALID_STATE_TRUE: |
| - win_attributes_->ia2_attributes.push_back(L"invalid:true"); |
| - break; |
| - case ui::AX_INVALID_STATE_SPELLING: |
| - win_attributes_->ia2_attributes.push_back(L"invalid:spelling"); |
| - break; |
| - case ui::AX_INVALID_STATE_GRAMMAR: |
| - win_attributes_->ia2_attributes.push_back(L"invalid:grammar"); |
| - break; |
| - case ui::AX_INVALID_STATE_OTHER: |
| - { |
| - base::string16 aria_invalid_value; |
| - if (GetString16Attribute(ui::AX_ATTR_ARIA_INVALID_VALUE, |
| - &aria_invalid_value)) { |
| - SanitizeStringAttributeForIA2(aria_invalid_value, |
| - &aria_invalid_value); |
| - win_attributes_->ia2_attributes.push_back( |
| - L"invalid:" + aria_invalid_value); |
| - } else { |
| - // Set the attribute to L"true", since we cannot be more specific. |
| - win_attributes_->ia2_attributes.push_back(L"invalid:true"); |
| - } |
| - } |
| - break; |
| - default: |
| - NOTREACHED(); |
| - } |
| - } |
| - |
| // Expose row or column header sort direction. |
| int32_t sort_direction; |
| if ((ia_role() == ROLE_SYSTEM_COLUMNHEADER || |
| @@ -3434,8 +3433,7 @@ void BrowserAccessibilityWin::UpdateStep2ComputeHypertext() { |
| // 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 = |
| - ToBrowserAccessibilityWin(PlatformGetChild(i)); |
| + const auto child = ToBrowserAccessibilityWin(PlatformGetChild(i)); |
| DCHECK(child); |
| // Similar to Firefox, we don't expose text-only objects in IA2 hypertext. |
| if (child->IsTextOnlyObject()) { |
| @@ -3573,6 +3571,179 @@ void BrowserAccessibilityWin::OnLocationChanged() { |
| EVENT_OBJECT_LOCATIONCHANGE, this); |
| } |
| +std::vector<base::string16> BrowserAccessibilityWin::ComputeTextAttributes() |
| + const { |
| + std::vector<base::string16> attributes; |
| + |
| + // We include list markers for now, but there might be other objects that are |
| + // auto generated. |
| + // TODO(nektar): Compute what objects are auto-generated in Blink. |
| + if (GetRole() == ui::AX_ROLE_LIST_MARKER) |
| + attributes.push_back(L"auto-generated:true"); |
| + else |
| + attributes.push_back(L"auto-generated:false"); |
| + |
| + int color; |
| + base::string16 color_value(L"transparent"); |
| + if (GetIntAttribute(ui::AX_ATTR_BACKGROUND_COLOR, &color)) { |
| + unsigned int alpha = SkColorGetA(color); |
| + unsigned int red = SkColorGetR(color); |
| + unsigned int green = SkColorGetG(color); |
| + unsigned int blue = SkColorGetB(color); |
| + if (alpha) { |
| + color_value = L"rgb(" + base::UintToString16(red) + L',' + |
| + base::UintToString16(blue) + L',' + |
| + base::UintToString16(green) + L')'; |
| + } |
| + } |
| + attributes.push_back(L"background-color:" + color_value); |
| + |
| + if (GetIntAttribute(ui::AX_ATTR_COLOR, &color)) { |
| + unsigned int red = SkColorGetR(color); |
| + unsigned int green = SkColorGetG(color); |
| + unsigned int blue = SkColorGetB(color); |
| + color_value = L"rgb(" + base::UintToString16(red) + L',' + |
| + base::UintToString16(blue) + L',' + |
| + base::UintToString16(green) + L')'; |
| + } else { |
| + color_value = L"rgb(0,0,0)"; |
| + } |
| + attributes.push_back(L"color:" + color_value); |
| + |
| + base::string16 font_family( |
| + GetInheritedString16Attribute(ui::AX_ATTR_FONT_FAMILY)); |
| + // Attribute has no default value. |
| + if (!font_family.empty()) { |
| + SanitizeStringAttributeForIA2(font_family, &font_family); |
| + attributes.push_back(L"font-family:" + font_family); |
| + } |
| + |
| + float font_size; |
| + // Attribute has no default value. |
| + if (GetFloatAttribute(ui::AX_ATTR_FONT_SIZE, &font_size)) { |
| + // The IA2 Spec requires the value to be in pt, not in pixels. |
| + // There are 72 points per inch. |
| + // We assume that there are 96 pixels per inch on a standard display. |
| + // TODO(nektar): Figure out the current value of pixels per inch. |
| + float points = font_size * 72.0 / 96.0; |
| + attributes.push_back(L"font-size:" + |
| + base::UTF8ToUTF16(base::DoubleToString(points))); |
| + } |
| + |
| + auto text_style = |
| + static_cast<ui::AXTextStyle>(GetIntAttribute(ui::AX_ATTR_TEXT_STYLE)); |
| + if (text_style == ui::AX_TEXT_STYLE_NONE) { |
| + attributes.push_back(L"font-style:normal"); |
| + attributes.push_back(L"font-weight:normal"); |
| + } else { |
| + if (text_style & ui::AX_TEXT_STYLE_BOLD) |
| + attributes.push_back(L"font-weight:bold"); |
| + |
| + base::string16 font_style; |
| + if (text_style & ui::AX_TEXT_STYLE_ITALIC) |
| + font_style += L",italic"; |
| + if (text_style & ui::AX_TEXT_STYLE_UNDERLINE) |
| + font_style += L",underline"; |
| + if (text_style & ui::AX_TEXT_STYLE_LINE_THROUGH) |
| + font_style += L",line-through"; |
| + // TODO(nektar): Support more font style attributes in Blink. |
| + |
| + if (font_style.empty()) { |
| + font_style = L"normal"; |
| + } else { |
| + // Remove the leading comma. |
| + font_style.erase(0, 1); |
| + } |
| + attributes.push_back(L"font-style:" + font_style); |
| + } |
| + |
| + auto invalid_state = static_cast<ui::AXInvalidState>( |
| + GetIntAttribute(ui::AX_ATTR_INVALID_STATE)); |
| + switch (invalid_state) { |
| + case ui::AX_INVALID_STATE_NONE: |
| + case ui::AX_INVALID_STATE_FALSE: |
| + attributes.push_back(L"invalid:false"); |
| + break; |
| + case ui::AX_INVALID_STATE_TRUE: |
| + attributes.push_back(L"invalid:true"); |
| + break; |
| + case ui::AX_INVALID_STATE_SPELLING: |
| + case ui::AX_INVALID_STATE_GRAMMAR: { |
| + base::string16 spelling_grammar_value; |
| + if (invalid_state & ui::AX_INVALID_STATE_SPELLING) |
| + spelling_grammar_value = L"spelling"; |
| + else if (invalid_state & ui::AX_INVALID_STATE_GRAMMAR) |
| + spelling_grammar_value = L"grammar"; |
| + else |
| + spelling_grammar_value = L"spelling,grammar"; |
| + attributes.push_back(L"invalid:" + spelling_grammar_value); |
| + break; |
| + } |
| + case ui::AX_INVALID_STATE_OTHER: { |
| + base::string16 aria_invalid_value; |
| + if (GetString16Attribute(ui::AX_ATTR_ARIA_INVALID_VALUE, |
| + &aria_invalid_value)) { |
| + SanitizeStringAttributeForIA2(aria_invalid_value, &aria_invalid_value); |
| + attributes.push_back(L"invalid:" + aria_invalid_value); |
| + } else { |
| + // Set the attribute to L"true", since we cannot be more specific. |
| + attributes.push_back(L"invalid:true"); |
| + } |
| + break; |
| + } |
| + default: |
| + NOTREACHED(); |
| + } |
| + |
| + base::string16 language(GetInheritedString16Attribute(ui::AX_ATTR_LANGUAGE)); |
| + // Default value should be L"en-US". |
| + if (language.empty()) { |
| + attributes.push_back(L"language:en-US"); |
| + } else { |
| + SanitizeStringAttributeForIA2(language, &language); |
| + attributes.push_back(L"language:" + language); |
| + } |
| + |
| + // TODO(nektar): Add Blink support for the following attributes. |
| + // Currently set to their default values as dictated by the IA2 Spec. |
| + attributes.push_back(L"text-line-through-mode:continuous"); |
| + attributes.push_back(L"text-line-through-style:none"); |
| + // Default value must be the empty string. |
| + attributes.push_back(L"text-line-through-text:"); |
| + attributes.push_back(L"text-line-through-type:none"); |
| + attributes.push_back(L"text-line-through-width:auto"); |
| + attributes.push_back(L"text-outline:false"); |
| + attributes.push_back(L"text-position:baseline"); |
| + attributes.push_back(L"text-shadow:none"); |
| + attributes.push_back(L"text-underline-mode:continuous"); |
| + attributes.push_back(L"text-underline-style:none"); |
| + attributes.push_back(L"text-underline-type:none"); |
| + attributes.push_back(L"text-underline-width:auto"); |
| + |
| + auto text_direction = static_cast<ui::AXTextDirection>( |
| + GetIntAttribute(ui::AX_ATTR_TEXT_DIRECTION)); |
| + switch (text_direction) { |
| + case ui::AX_TEXT_DIRECTION_NONE: |
| + case ui::AX_TEXT_DIRECTION_LTR: |
| + attributes.push_back(L"writing-mode:lr"); |
| + break; |
| + case ui::AX_TEXT_DIRECTION_RTL: |
| + attributes.push_back(L"writing-mode:rl"); |
| + break; |
| + case ui::AX_TEXT_DIRECTION_TTB: |
| + attributes.push_back(L"writing-mode:tb"); |
| + break; |
| + case ui::AX_TEXT_DIRECTION_BTT: |
| + // Not listed in the IA2 Spec. |
| + attributes.push_back(L"writing-mode:bt"); |
| + break; |
| + default: |
| + NOTREACHED(); |
| + } |
| + |
| + return attributes; |
| +} |
| + |
| BrowserAccessibilityWin* BrowserAccessibilityWin::NewReference() { |
| AddRef(); |
| return this; |
| @@ -3672,6 +3843,23 @@ bool BrowserAccessibilityWin::IsHyperlink() const { |
| return false; |
| } |
| +BrowserAccessibilityWin* |
| +BrowserAccessibilityWin::GetHyperlinkFromHypertextOffset(int offset) const { |
| + std::map<int32_t, int32_t>::iterator iterator = |
| + hyperlink_offset_to_index().find(offset); |
| + if (iterator == hyperlink_offset_to_index().end()) |
| + return nullptr; |
| + |
| + int32_t index = iterator->second; |
| + DCHECK_GE(index, 0); |
| + DCHECK_LT(index, static_cast<int32_t>(hyperlinks().size())); |
| + int32_t id = hyperlinks()[index]; |
| + BrowserAccessibilityWin* hyperlink = GetFromID(id); |
| + if (!hyperlink) |
| + return nullptr; |
| + return hyperlink; |
| +} |
| + |
| int32_t BrowserAccessibilityWin::GetHyperlinkIndexFromChild( |
| const BrowserAccessibilityWin& child) const { |
| if (hyperlinks().empty()) |
| @@ -3815,8 +4003,7 @@ int BrowserAccessibilityWin::GetHypertextOffsetFromEndpoint( |
| int BrowserAccessibilityWin::GetSelectionAnchor() const { |
| int32_t anchor_id = manager()->GetTreeData().sel_anchor_object_id; |
| - const auto anchor_object = |
| - ToBrowserAccessibilityWin(manager()->GetFromID(anchor_id)); |
| + const BrowserAccessibilityWin* anchor_object = GetFromID(anchor_id); |
| if (!anchor_object) |
| return -1; |
| @@ -3826,8 +4013,7 @@ int BrowserAccessibilityWin::GetSelectionAnchor() const { |
| int BrowserAccessibilityWin::GetSelectionFocus() const { |
| int32_t focus_id = manager()->GetTreeData().sel_focus_object_id; |
| - const auto focus_object = |
| - ToBrowserAccessibilityWin(manager()->GetFromID(focus_id)); |
| + const BrowserAccessibilityWin* focus_object = GetFromID(focus_id); |
| if (!focus_object) |
| return -1; |
| @@ -3877,22 +4063,13 @@ void BrowserAccessibilityWin::GetSelectionOffsets( |
| // the selection. |
| int* largest_offset = |
| (*selection_start <= *selection_end) ? selection_end : selection_start; |
| - auto current_object = const_cast<BrowserAccessibilityWin*>(this); |
| - LONG hyperlink_index; |
| - HRESULT hr = |
| - current_object->get_hyperlinkIndex(*largest_offset, &hyperlink_index); |
| - if (hr != S_OK) |
| + BrowserAccessibilityWin* hyperlink = |
| + GetHyperlinkFromHypertextOffset(*largest_offset); |
| + if (!hyperlink) |
| return; |
| - DCHECK_GE(hyperlink_index, 0); |
| - base::win::ScopedComPtr<IAccessibleHyperlink> hyperlink; |
| - hr = current_object->get_hyperlink(hyperlink_index, hyperlink.Receive()); |
| - DCHECK(SUCCEEDED(hr)); |
| - base::win::ScopedComPtr<IAccessibleText> hyperlink_text; |
| - hr = hyperlink.QueryInterface(hyperlink_text.Receive()); |
| - DCHECK(SUCCEEDED(hr)); |
| LONG n_selections = 0; |
| - hr = hyperlink_text->get_nSelections(&n_selections); |
| + HRESULT hr = hyperlink->get_nSelections(&n_selections); |
| DCHECK(SUCCEEDED(hr)); |
| if (n_selections > 0) |
| ++(*largest_offset); |
| @@ -3978,6 +4155,45 @@ void BrowserAccessibilityWin::ComputeHypertextRemovedAndInserted( |
| *new_len = new_text.size() - common_prefix - common_suffix; |
| } |
| +void BrowserAccessibilityWin::MayBeComputeStyles() { |
| + if (!offset_to_text_attributes().empty()) |
| + return; |
| + |
| + base::hash_map<int, std::vector<base::string16>> attributes_map; |
| + if (PlatformIsLeaf()) { |
| + attributes_map[0] = ComputeTextAttributes(); |
| + win_attributes_->offset_to_text_attributes = attributes_map; |
|
dmazzoni
2016/03/14 17:38:21
This is a deep copy. Instead you could either make
|
| + return; |
| + } |
| + |
| + int start_offset = 0; |
| + for (size_t i = 0; i < PlatformChildCount(); ++i) { |
| + const auto child = ToBrowserAccessibilityWin(PlatformGetChild(i)); |
| + DCHECK(child); |
| + std::vector<base::string16> attributes(child->ComputeTextAttributes()); |
| + |
| + if (attributes_map.empty()) { |
| + attributes_map[start_offset] = attributes; |
| + } else { |
| + // Only add the attributes for this child if we are at the start of a new |
| + // style span. |
| + std::vector<base::string16> previous_attributes = |
| + attributes_map.rbegin()->second; |
| + if (!std::equal(attributes.begin(), attributes.end(), |
| + previous_attributes.begin())) { |
| + attributes_map[start_offset] = attributes; |
| + } |
| + } |
| + |
| + if (child->IsTextOnlyObject()) |
| + start_offset += child->GetText().length(); |
| + else |
| + start_offset += 1; |
| + } |
| + |
| + win_attributes_->offset_to_text_attributes = attributes_map; |
| +} |
| + |
| void BrowserAccessibilityWin::HandleSpecialTextOffset( |
| const base::string16& text, |
| LONG* offset) { |
| @@ -4024,7 +4240,39 @@ LONG BrowserAccessibilityWin::FindBoundary( |
| text, line_breaks, boundary, start_offset, direction); |
| } |
| -BrowserAccessibilityWin* BrowserAccessibilityWin::GetFromID(int32_t id) { |
| +LONG BrowserAccessibilityWin::FindStartOfStyle( |
| + LONG start_offset, |
| + ui::TextBoundaryDirection direction) const { |
| + LONG text_length = static_cast<LONG>(GetText().length()); |
| + DCHECK_GE(start_offset, 0); |
| + DCHECK_LE(start_offset, text_length); |
| + |
| + switch (direction) { |
| + case ui::BACKWARDS_DIRECTION: { |
| + if (offset_to_text_attributes().empty()) |
| + return 0; |
| + |
| + auto iterator = offset_to_text_attributes().upper_bound(start_offset); |
| + --iterator; |
| + return static_cast<LONG>(iterator->first); |
| + } |
| + case ui::FORWARDS_DIRECTION: { |
| + const auto iterator = |
| + offset_to_text_attributes().upper_bound(start_offset); |
|
dmazzoni
2016/03/14 17:38:20
Just checking, is it correct that both BACKWARDS a
|
| + if (iterator == offset_to_text_attributes().end()) |
| + return text_length; |
| + return static_cast<LONG>(iterator->first); |
| + } |
| + default: |
| + NOTREACHED(); |
| + } |
| + |
| + return start_offset; |
| +} |
| + |
| +BrowserAccessibilityWin* BrowserAccessibilityWin::GetFromID(int32_t id) const { |
| + if (!instance_active()) |
| + return nullptr; |
| return ToBrowserAccessibilityWin(manager()->GetFromID(id)); |
| } |
| @@ -4048,6 +4296,25 @@ bool BrowserAccessibilityWin::IsListBoxOptionOrMenuListOption() { |
| return false; |
| } |
| +void BrowserAccessibilityWin::AddRelations( |
| + ui::AXIntListAttribute src_attr, |
| + const base::string16& iaccessiblerelation_type) { |
| + if (!HasIntListAttribute(src_attr)) |
| + return; |
| + |
| + const std::vector<int32_t>& ids = GetIntListAttribute(src_attr); |
| + for (size_t i = 0; i < ids.size(); ++i) { |
| + CComObject<BrowserAccessibilityRelation>* relation; |
| + HRESULT hr = |
| + CComObject<BrowserAccessibilityRelation>::CreateInstance(&relation); |
| + DCHECK(SUCCEEDED(hr)); |
| + relation->AddRef(); |
| + relation->Initialize(this, iaccessiblerelation_type); |
| + relation->AddTarget(ids[i]); |
| + relations_.push_back(relation); |
| + } |
| +} |
| + |
| void BrowserAccessibilityWin::UpdateRequiredAttributes() { |
| // Expose slider value. |
| if (ia_role() == ROLE_SYSTEM_PROGRESSBAR || |
| @@ -4111,25 +4378,6 @@ void BrowserAccessibilityWin::UpdateRequiredAttributes() { |
| } |
| } |
| -void BrowserAccessibilityWin::AddRelations( |
| - ui::AXIntListAttribute src_attr, |
| - const base::string16& iaccessiblerelation_type) { |
| - if (!HasIntListAttribute(src_attr)) |
| - return; |
| - |
| - const std::vector<int32_t>& ids = GetIntListAttribute(src_attr); |
| - for (size_t i = 0; i < ids.size(); ++i) { |
| - CComObject<BrowserAccessibilityRelation>* relation; |
| - HRESULT hr = CComObject<BrowserAccessibilityRelation>::CreateInstance( |
| - &relation); |
| - DCHECK(SUCCEEDED(hr)); |
| - relation->AddRef(); |
| - relation->Initialize(this, iaccessiblerelation_type); |
| - relation->AddTarget(ids[i]); |
| - relations_.push_back(relation); |
| - } |
| -} |
| - |
| void BrowserAccessibilityWin::InitRoleAndState() { |
| int32_t ia_role = 0; |
| int32_t ia_state = 0; |