diff options
author | dmazzoni@chromium.org <dmazzoni@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-06-12 22:55:15 +0000 |
---|---|---|
committer | dmazzoni@chromium.org <dmazzoni@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-06-12 22:55:15 +0000 |
commit | 0a935a0d077703a4c2d91a56d8764585fdb8674a (patch) | |
tree | 12fb697231943352f4dd6a0f4c931978c37d72bb /content/renderer | |
parent | 00737f8f5c56fc51352f4aabb89292c7e30637dd (diff) | |
download | chromium_src-0a935a0d077703a4c2d91a56d8764585fdb8674a.zip chromium_src-0a935a0d077703a4c2d91a56d8764585fdb8674a.tar.gz chromium_src-0a935a0d077703a4c2d91a56d8764585fdb8674a.tar.bz2 |
Refactor all accessibility code out of webkit/glue.
Moves the data definitions into content/common and the serialization code
into content/renderer.
There are no code changes here, this is just a refactoring.
BUG=132129
TEST=none
Review URL: https://chromiumcodereview.appspot.com/10544099
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@141768 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content/renderer')
-rw-r--r-- | content/renderer/accessibility_node_serializer.cc | 594 | ||||
-rw-r--r-- | content/renderer/accessibility_node_serializer.h | 21 | ||||
-rw-r--r-- | content/renderer/renderer_accessibility.cc | 2 | ||||
-rw-r--r-- | content/renderer/renderer_accessibility.h | 4 | ||||
-rw-r--r-- | content/renderer/renderer_accessibility_browsertest.cc | 50 | ||||
-rw-r--r-- | content/renderer/renderer_accessibility_complete.cc | 27 | ||||
-rw-r--r-- | content/renderer/renderer_accessibility_complete.h | 13 | ||||
-rw-r--r-- | content/renderer/renderer_accessibility_focus_only.cc | 23 |
8 files changed, 666 insertions, 68 deletions
diff --git a/content/renderer/accessibility_node_serializer.cc b/content/renderer/accessibility_node_serializer.cc new file mode 100644 index 0000000..b838ab1 --- /dev/null +++ b/content/renderer/accessibility_node_serializer.cc @@ -0,0 +1,594 @@ +// Copyright (c) 2012 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. + +#include "content/renderer/accessibility_node_serializer.h" + +#include <set> + +#include "base/string_number_conversions.h" +#include "base/string_util.h" +#include "base/utf_string_conversions.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebAccessibilityObject.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebAccessibilityRole.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebDocumentType.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebElement.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebFormControlElement.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebInputElement.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebNode.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebRect.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebSize.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebVector.h" + +using content::AccessibilityNodeData; +using WebKit::WebAccessibilityRole; +using WebKit::WebAccessibilityObject; +using WebKit::WebDocument; +using WebKit::WebDocumentType; +using WebKit::WebElement; +using WebKit::WebNode; +using WebKit::WebVector; + +namespace { + +// Returns true if |ancestor| is the first unignored parent of |child|, +// which means that when walking up the parent chain from |child|, +// |ancestor| is the *first* ancestor that isn't marked as +// accessibilityIsIgnored(). +bool IsParentUnignoredOf(const WebAccessibilityObject& ancestor, + const WebAccessibilityObject& child) { + WebAccessibilityObject parent = child.parentObject(); + while (!parent.isNull() && parent.accessibilityIsIgnored()) + parent = parent.parentObject(); + return parent.equals(ancestor); +} + +// Provides a conversion between the WebKit::WebAccessibilityRole and a role +// supported on the Browser side. Listed alphabetically by the +// WebKit::WebAccessibilityRole (except for default role). +AccessibilityNodeData::Role ConvertRole(WebKit::WebAccessibilityRole role) { + switch (role) { + case WebKit::WebAccessibilityRoleAnnotation: + return AccessibilityNodeData::ROLE_ANNOTATION; + case WebKit::WebAccessibilityRoleApplication: + return AccessibilityNodeData::ROLE_APPLICATION; + case WebKit::WebAccessibilityRoleApplicationAlert: + return AccessibilityNodeData::ROLE_ALERT; + case WebKit::WebAccessibilityRoleApplicationAlertDialog: + return AccessibilityNodeData::ROLE_ALERT_DIALOG; + case WebKit::WebAccessibilityRoleApplicationDialog: + return AccessibilityNodeData::ROLE_DIALOG; + case WebKit::WebAccessibilityRoleApplicationLog: + return AccessibilityNodeData::ROLE_LOG; + case WebKit::WebAccessibilityRoleApplicationMarquee: + return AccessibilityNodeData::ROLE_MARQUEE; + case WebKit::WebAccessibilityRoleApplicationStatus: + return AccessibilityNodeData::ROLE_STATUS; + case WebKit::WebAccessibilityRoleApplicationTimer: + return AccessibilityNodeData::ROLE_TIMER; + case WebKit::WebAccessibilityRoleBrowser: + return AccessibilityNodeData::ROLE_BROWSER; + case WebKit::WebAccessibilityRoleBusyIndicator: + return AccessibilityNodeData::ROLE_BUSY_INDICATOR; + case WebKit::WebAccessibilityRoleButton: + return AccessibilityNodeData::ROLE_BUTTON; + case WebKit::WebAccessibilityRoleCell: + return AccessibilityNodeData::ROLE_CELL; + case WebKit::WebAccessibilityRoleCheckBox: + return AccessibilityNodeData::ROLE_CHECKBOX; + case WebKit::WebAccessibilityRoleColorWell: + return AccessibilityNodeData::ROLE_COLOR_WELL; + case WebKit::WebAccessibilityRoleColumn: + return AccessibilityNodeData::ROLE_COLUMN; + case WebKit::WebAccessibilityRoleColumnHeader: + return AccessibilityNodeData::ROLE_COLUMN_HEADER; + case WebKit::WebAccessibilityRoleComboBox: + return AccessibilityNodeData::ROLE_COMBO_BOX; + case WebKit::WebAccessibilityRoleDefinitionListDefinition: + return AccessibilityNodeData::ROLE_DEFINITION_LIST_DEFINITION; + case WebKit::WebAccessibilityRoleDefinitionListTerm: + return AccessibilityNodeData::ROLE_DEFINITION_LIST_TERM; + case WebKit::WebAccessibilityRoleDirectory: + return AccessibilityNodeData::ROLE_DIRECTORY; + case WebKit::WebAccessibilityRoleDisclosureTriangle: + return AccessibilityNodeData::ROLE_DISCLOSURE_TRIANGLE; + case WebKit::WebAccessibilityRoleDocument: + return AccessibilityNodeData::ROLE_DOCUMENT; + case WebKit::WebAccessibilityRoleDocumentArticle: + return AccessibilityNodeData::ROLE_ARTICLE; + case WebKit::WebAccessibilityRoleDocumentMath: + return AccessibilityNodeData::ROLE_MATH; + case WebKit::WebAccessibilityRoleDocumentNote: + return AccessibilityNodeData::ROLE_NOTE; + case WebKit::WebAccessibilityRoleDocumentRegion: + return AccessibilityNodeData::ROLE_REGION; + case WebKit::WebAccessibilityRoleDrawer: + return AccessibilityNodeData::ROLE_DRAWER; + case WebKit::WebAccessibilityRoleEditableText: + return AccessibilityNodeData::ROLE_EDITABLE_TEXT; + case WebKit::WebAccessibilityRoleFooter: + return AccessibilityNodeData::ROLE_FOOTER; + case WebKit::WebAccessibilityRoleGrid: + return AccessibilityNodeData::ROLE_GRID; + case WebKit::WebAccessibilityRoleGroup: + return AccessibilityNodeData::ROLE_GROUP; + case WebKit::WebAccessibilityRoleGrowArea: + return AccessibilityNodeData::ROLE_GROW_AREA; + case WebKit::WebAccessibilityRoleHeading: + return AccessibilityNodeData::ROLE_HEADING; + case WebKit::WebAccessibilityRoleHelpTag: + return AccessibilityNodeData::ROLE_HELP_TAG; + case WebKit::WebAccessibilityRoleIgnored: + return AccessibilityNodeData::ROLE_IGNORED; + case WebKit::WebAccessibilityRoleImage: + return AccessibilityNodeData::ROLE_IMAGE; + case WebKit::WebAccessibilityRoleImageMap: + return AccessibilityNodeData::ROLE_IMAGE_MAP; + case WebKit::WebAccessibilityRoleImageMapLink: + return AccessibilityNodeData::ROLE_IMAGE_MAP_LINK; + case WebKit::WebAccessibilityRoleIncrementor: + return AccessibilityNodeData::ROLE_INCREMENTOR; + case WebKit::WebAccessibilityRoleLandmarkApplication: + return AccessibilityNodeData::ROLE_LANDMARK_APPLICATION; + case WebKit::WebAccessibilityRoleLandmarkBanner: + return AccessibilityNodeData::ROLE_LANDMARK_BANNER; + case WebKit::WebAccessibilityRoleLandmarkComplementary: + return AccessibilityNodeData::ROLE_LANDMARK_COMPLEMENTARY; + case WebKit::WebAccessibilityRoleLandmarkContentInfo: + return AccessibilityNodeData::ROLE_LANDMARK_CONTENTINFO; + case WebKit::WebAccessibilityRoleLandmarkMain: + return AccessibilityNodeData::ROLE_LANDMARK_MAIN; + case WebKit::WebAccessibilityRoleLandmarkNavigation: + return AccessibilityNodeData::ROLE_LANDMARK_NAVIGATION; + case WebKit::WebAccessibilityRoleLandmarkSearch: + return AccessibilityNodeData::ROLE_LANDMARK_SEARCH; + case WebKit::WebAccessibilityRoleLink: + return AccessibilityNodeData::ROLE_LINK; + case WebKit::WebAccessibilityRoleList: + return AccessibilityNodeData::ROLE_LIST; + case WebKit::WebAccessibilityRoleListBox: + return AccessibilityNodeData::ROLE_LISTBOX; + case WebKit::WebAccessibilityRoleListBoxOption: + return AccessibilityNodeData::ROLE_LISTBOX_OPTION; + case WebKit::WebAccessibilityRoleListItem: + return AccessibilityNodeData::ROLE_LIST_ITEM; + case WebKit::WebAccessibilityRoleListMarker: + return AccessibilityNodeData::ROLE_LIST_MARKER; + case WebKit::WebAccessibilityRoleMatte: + return AccessibilityNodeData::ROLE_MATTE; + case WebKit::WebAccessibilityRoleMenu: + return AccessibilityNodeData::ROLE_MENU; + case WebKit::WebAccessibilityRoleMenuBar: + return AccessibilityNodeData::ROLE_MENU_BAR; + case WebKit::WebAccessibilityRoleMenuButton: + return AccessibilityNodeData::ROLE_MENU_BUTTON; + case WebKit::WebAccessibilityRoleMenuItem: + return AccessibilityNodeData::ROLE_MENU_ITEM; + case WebKit::WebAccessibilityRoleMenuListOption: + return AccessibilityNodeData::ROLE_MENU_LIST_OPTION; + case WebKit::WebAccessibilityRoleMenuListPopup: + return AccessibilityNodeData::ROLE_MENU_LIST_POPUP; + case WebKit::WebAccessibilityRoleOutline: + return AccessibilityNodeData::ROLE_OUTLINE; + case WebKit::WebAccessibilityRolePopUpButton: + return AccessibilityNodeData::ROLE_POPUP_BUTTON; + case WebKit::WebAccessibilityRoleProgressIndicator: + return AccessibilityNodeData::ROLE_PROGRESS_INDICATOR; + case WebKit::WebAccessibilityRoleRadioButton: + return AccessibilityNodeData::ROLE_RADIO_BUTTON; + case WebKit::WebAccessibilityRoleRadioGroup: + return AccessibilityNodeData::ROLE_RADIO_GROUP; + case WebKit::WebAccessibilityRoleRow: + return AccessibilityNodeData::ROLE_ROW; + case WebKit::WebAccessibilityRoleRowHeader: + return AccessibilityNodeData::ROLE_ROW_HEADER; + case WebKit::WebAccessibilityRoleRuler: + return AccessibilityNodeData::ROLE_RULER; + case WebKit::WebAccessibilityRoleRulerMarker: + return AccessibilityNodeData::ROLE_RULER_MARKER; + case WebKit::WebAccessibilityRoleScrollArea: + return AccessibilityNodeData::ROLE_SCROLLAREA; + case WebKit::WebAccessibilityRoleScrollBar: + return AccessibilityNodeData::ROLE_SCROLLBAR; + case WebKit::WebAccessibilityRoleSheet: + return AccessibilityNodeData::ROLE_SHEET; + case WebKit::WebAccessibilityRoleSlider: + return AccessibilityNodeData::ROLE_SLIDER; + case WebKit::WebAccessibilityRoleSliderThumb: + return AccessibilityNodeData::ROLE_SLIDER_THUMB; + case WebKit::WebAccessibilityRoleSplitGroup: + return AccessibilityNodeData::ROLE_SPLIT_GROUP; + case WebKit::WebAccessibilityRoleSplitter: + return AccessibilityNodeData::ROLE_SPLITTER; + case WebKit::WebAccessibilityRoleStaticText: + return AccessibilityNodeData::ROLE_STATIC_TEXT; + case WebKit::WebAccessibilityRoleSystemWide: + return AccessibilityNodeData::ROLE_SYSTEM_WIDE; + case WebKit::WebAccessibilityRoleTab: + return AccessibilityNodeData::ROLE_TAB; + case WebKit::WebAccessibilityRoleTabGroup: + return AccessibilityNodeData::ROLE_TAB_GROUP_UNUSED; + case WebKit::WebAccessibilityRoleTabList: + return AccessibilityNodeData::ROLE_TAB_LIST; + case WebKit::WebAccessibilityRoleTabPanel: + return AccessibilityNodeData::ROLE_TAB_PANEL; + case WebKit::WebAccessibilityRoleTable: + return AccessibilityNodeData::ROLE_TABLE; + case WebKit::WebAccessibilityRoleTableHeaderContainer: + return AccessibilityNodeData::ROLE_TABLE_HEADER_CONTAINER; + case WebKit::WebAccessibilityRoleTextArea: + return AccessibilityNodeData::ROLE_TEXTAREA; + case WebKit::WebAccessibilityRoleTextField: + return AccessibilityNodeData::ROLE_TEXT_FIELD; + case WebKit::WebAccessibilityRoleToolbar: + return AccessibilityNodeData::ROLE_TOOLBAR; + case WebKit::WebAccessibilityRoleTreeGrid: + return AccessibilityNodeData::ROLE_TREE_GRID; + case WebKit::WebAccessibilityRoleTreeItemRole: + return AccessibilityNodeData::ROLE_TREE_ITEM; + case WebKit::WebAccessibilityRoleTreeRole: + return AccessibilityNodeData::ROLE_TREE; + case WebKit::WebAccessibilityRoleUserInterfaceTooltip: + return AccessibilityNodeData::ROLE_TOOLTIP; + case WebKit::WebAccessibilityRoleValueIndicator: + return AccessibilityNodeData::ROLE_VALUE_INDICATOR; + case WebKit::WebAccessibilityRoleWebArea: + return AccessibilityNodeData::ROLE_WEB_AREA; + case WebKit::WebAccessibilityRoleWebCoreLink: + return AccessibilityNodeData::ROLE_WEBCORE_LINK; + case WebKit::WebAccessibilityRoleWindow: + return AccessibilityNodeData::ROLE_WINDOW; + + default: + return AccessibilityNodeData::ROLE_UNKNOWN; + } +} + +// Provides a conversion between the WebAccessibilityObject state +// accessors and a state bitmask that can be serialized and sent to the +// Browser process. Rare state are sent as boolean attributes instead. +uint32 ConvertState(const WebAccessibilityObject& o) { + uint32 state = 0; + if (o.isChecked()) + state |= (1 << AccessibilityNodeData::STATE_CHECKED); + + if (o.isCollapsed()) + state |= (1 << AccessibilityNodeData::STATE_COLLAPSED); + + if (o.canSetFocusAttribute()) + state |= (1 << AccessibilityNodeData::STATE_FOCUSABLE); + + if (o.isFocused()) + state |= (1 << AccessibilityNodeData::STATE_FOCUSED); + + if (o.roleValue() == WebKit::WebAccessibilityRolePopUpButton || + o.ariaHasPopup()) { + state |= (1 << AccessibilityNodeData::STATE_HASPOPUP); + if (!o.isCollapsed()) + state |= (1 << AccessibilityNodeData::STATE_EXPANDED); + } + + if (o.isHovered()) + state |= (1 << AccessibilityNodeData::STATE_HOTTRACKED); + + if (o.isIndeterminate()) + state |= (1 << AccessibilityNodeData::STATE_INDETERMINATE); + + if (!o.isVisible()) + state |= (1 << AccessibilityNodeData::STATE_INVISIBLE); + + if (o.isLinked()) + state |= (1 << AccessibilityNodeData::STATE_LINKED); + + if (o.isMultiSelectable()) + state |= (1 << AccessibilityNodeData::STATE_MULTISELECTABLE); + + if (o.isOffScreen()) + state |= (1 << AccessibilityNodeData::STATE_OFFSCREEN); + + if (o.isPressed()) + state |= (1 << AccessibilityNodeData::STATE_PRESSED); + + if (o.isPasswordField()) + state |= (1 << AccessibilityNodeData::STATE_PROTECTED); + + if (o.isReadOnly()) + state |= (1 << AccessibilityNodeData::STATE_READONLY); + + if (o.isRequired()) + state |= (1 << AccessibilityNodeData::STATE_REQUIRED); + + if (o.canSetSelectedAttribute()) + state |= (1 << AccessibilityNodeData::STATE_SELECTABLE); + + if (o.isSelected()) + state |= (1 << AccessibilityNodeData::STATE_SELECTED); + + if (o.isVisited()) + state |= (1 << AccessibilityNodeData::STATE_TRAVERSED); + + if (!o.isEnabled()) + state |= (1 << AccessibilityNodeData::STATE_UNAVAILABLE); + + if (o.isVertical()) + state |= (1 << AccessibilityNodeData::STATE_VERTICAL); + + if (o.isVisited()) + state |= (1 << AccessibilityNodeData::STATE_VISITED); + + return state; +} + +} // Anonymous namespace + +namespace content { + +void SerializeAccessibilityNode( + const WebAccessibilityObject& src, + AccessibilityNodeData* dst, + bool include_children) { + dst->name = src.title(); + dst->role = ConvertRole(src.roleValue()); + dst->state = ConvertState(src); + dst->location = src.boundingBoxRect(); + dst->id = src.axID(); + + if (src.valueDescription().length()) + dst->value = src.valueDescription(); + else + dst->value = src.stringValue(); + + if (src.accessKey().length()) + dst->string_attributes[dst->ATTR_ACCESS_KEY] = src.accessKey(); + if (src.actionVerb().length()) + dst->string_attributes[dst->ATTR_ACTION] = src.actionVerb(); + if (src.isAriaReadOnly()) + dst->bool_attributes[dst->ATTR_ARIA_READONLY] = true; + if (src.isButtonStateMixed()) + dst->bool_attributes[dst->ATTR_BUTTON_MIXED] = true; + if (src.canSetValueAttribute()) + dst->bool_attributes[dst->ATTR_CAN_SET_VALUE] = true; + if (src.accessibilityDescription().length()) + dst->string_attributes[dst->ATTR_DESCRIPTION] = + src.accessibilityDescription(); + if (src.hasComputedStyle()) + dst->string_attributes[dst->ATTR_DISPLAY] = src.computedStyleDisplay(); + if (src.helpText().length()) + dst->string_attributes[dst->ATTR_HELP] = src.helpText(); + if (src.keyboardShortcut().length()) + dst->string_attributes[dst->ATTR_SHORTCUT] = src.keyboardShortcut(); + if (src.titleUIElement().isValid()) { + dst->int_attributes[dst->ATTR_TITLE_UI_ELEMENT] = + src.titleUIElement().axID(); + } + if (!src.url().isEmpty()) + dst->string_attributes[dst->ATTR_URL] = src.url().spec().utf16(); + + if (dst->role == dst->ROLE_TREE_ITEM) + dst->int_attributes[dst->ATTR_HIERARCHICAL_LEVEL] = src.hierarchicalLevel(); + + if (dst->role == dst->ROLE_SLIDER) + include_children = false; + + // Treat the active list box item as focused. + if (dst->role == dst->ROLE_LISTBOX_OPTION && src.isSelectedOptionActive()) + dst->state |= (1 << AccessibilityNodeData::STATE_FOCUSED); + + WebNode node = src.node(); + bool is_iframe = false; + + if (!node.isNull() && node.isElementNode()) { + WebElement element = node.to<WebElement>(); + is_iframe = (element.tagName() == ASCIIToUTF16("IFRAME")); + + // TODO(ctguil): The tagName in WebKit is lower cased but + // HTMLElement::nodeName calls localNameUpper. Consider adding + // a WebElement method that returns the original lower cased tagName. + dst->string_attributes[dst->ATTR_HTML_TAG] = + StringToLowerASCII(string16(element.tagName())); + for (unsigned i = 0; i < element.attributeCount(); ++i) { + string16 name = StringToLowerASCII(string16( + element.attributeLocalName(i))); + string16 value = element.attributeValue(i); + dst->html_attributes.push_back( + std::pair<string16, string16>(name, value)); + } + + if (dst->role == dst->ROLE_EDITABLE_TEXT || + dst->role == dst->ROLE_TEXTAREA || + dst->role == dst->ROLE_TEXT_FIELD) { + // Jaws gets confused by children of text fields, so we ignore them. + include_children = false; + + dst->int_attributes[dst->ATTR_TEXT_SEL_START] = src.selectionStart(); + dst->int_attributes[dst->ATTR_TEXT_SEL_END] = src.selectionEnd(); + + WebVector<int> src_line_breaks; + src.lineBreaks(src_line_breaks); + dst->line_breaks.reserve(src_line_breaks.size()); + for (size_t i = 0; i < src_line_breaks.size(); ++i) + dst->line_breaks.push_back(src_line_breaks[i]); + } + + // ARIA role. + if (element.hasAttribute("role")) { + dst->string_attributes[dst->ATTR_ROLE] = element.getAttribute("role"); + } + + // Live region attributes + if (element.hasAttribute("aria-atomic")) { + dst->bool_attributes[dst->ATTR_LIVE_ATOMIC] = + LowerCaseEqualsASCII(element.getAttribute("aria-atomic"), "true"); + } + if (element.hasAttribute("aria-busy")) { + dst->bool_attributes[dst->ATTR_LIVE_BUSY] = + LowerCaseEqualsASCII(element.getAttribute("aria-busy"), "true"); + } + if (element.hasAttribute("aria-live")) { + dst->string_attributes[dst->ATTR_LIVE_STATUS] = + element.getAttribute("aria-live"); + } + if (element.hasAttribute("aria-relevant")) { + dst->string_attributes[dst->ATTR_LIVE_RELEVANT] = + element.getAttribute("aria-relevant"); + } + } + + // Walk up the parent chain to set live region attributes of containers + + WebAccessibilityObject container_accessible = src; + while (!container_accessible.isNull()) { + WebNode container_node = container_accessible.node(); + if (!container_node.isNull() && container_node.isElementNode()) { + WebElement container_elem = + container_node.to<WebElement>(); + if (container_elem.hasAttribute("aria-atomic") && + dst->bool_attributes.find(dst->ATTR_CONTAINER_LIVE_ATOMIC) == + dst->bool_attributes.end()) { + dst->bool_attributes[dst->ATTR_CONTAINER_LIVE_ATOMIC] = + LowerCaseEqualsASCII(container_elem.getAttribute("aria-atomic"), + "true"); + } + if (container_elem.hasAttribute("aria-busy") && + dst->bool_attributes.find(dst->ATTR_CONTAINER_LIVE_BUSY) == + dst->bool_attributes.end()) { + dst->bool_attributes[dst->ATTR_CONTAINER_LIVE_BUSY] = + LowerCaseEqualsASCII(container_elem.getAttribute("aria-busy"), + "true"); + } + if (container_elem.hasAttribute("aria-live") && + dst->string_attributes.find(dst->ATTR_CONTAINER_LIVE_STATUS) == + dst->string_attributes.end()) { + dst->string_attributes[dst->ATTR_CONTAINER_LIVE_STATUS] = + container_elem.getAttribute("aria-live"); + } + if (container_elem.hasAttribute("aria-relevant") && + dst->string_attributes.find(dst->ATTR_CONTAINER_LIVE_RELEVANT) == + dst->string_attributes.end()) { + dst->string_attributes[dst->ATTR_CONTAINER_LIVE_RELEVANT] = + container_elem.getAttribute("aria-relevant"); + } + } + container_accessible = container_accessible.parentObject(); + } + + if (dst->role == dst->ROLE_PROGRESS_INDICATOR || + dst->role == dst->ROLE_SCROLLBAR || + dst->role == dst->ROLE_SLIDER) { + dst->float_attributes[dst->ATTR_VALUE_FOR_RANGE] = src.valueForRange(); + dst->float_attributes[dst->ATTR_MAX_VALUE_FOR_RANGE] = + src.minValueForRange(); + dst->float_attributes[dst->ATTR_MIN_VALUE_FOR_RANGE] = + src.maxValueForRange(); + } + + if (dst->role == dst->ROLE_DOCUMENT || + dst->role == dst->ROLE_WEB_AREA) { + const WebDocument& document = src.document(); + if (dst->name.empty()) + dst->name = document.title(); + dst->string_attributes[dst->ATTR_DOC_TITLE] = document.title(); + dst->string_attributes[dst->ATTR_DOC_URL] = document.url().spec().utf16(); + dst->string_attributes[dst->ATTR_DOC_MIMETYPE] = + ASCIIToUTF16(document.isXHTMLDocument() ? "text/xhtml" : "text/html"); + dst->bool_attributes[dst->ATTR_DOC_LOADED] = src.isLoaded(); + dst->float_attributes[dst->ATTR_DOC_LOADING_PROGRESS] = + src.estimatedLoadingProgress(); + + const WebDocumentType& doctype = document.doctype(); + if (!doctype.isNull()) + dst->string_attributes[dst->ATTR_DOC_DOCTYPE] = doctype.name(); + + const gfx::Size& scroll_offset = document.frame()->scrollOffset(); + dst->int_attributes[dst->ATTR_SCROLL_X] = scroll_offset.width(); + dst->int_attributes[dst->ATTR_SCROLL_Y] = scroll_offset.height(); + + const gfx::Size& min_offset = document.frame()->minimumScrollOffset(); + dst->int_attributes[dst->ATTR_SCROLL_X_MIN] = min_offset.width(); + dst->int_attributes[dst->ATTR_SCROLL_Y_MIN] = min_offset.height(); + + const gfx::Size& max_offset = document.frame()->maximumScrollOffset(); + dst->int_attributes[dst->ATTR_SCROLL_X_MAX] = max_offset.width(); + dst->int_attributes[dst->ATTR_SCROLL_Y_MAX] = max_offset.height(); + } + + if (dst->role == dst->ROLE_TABLE) { + int column_count = src.columnCount(); + int row_count = src.rowCount(); + if (column_count > 0 && row_count > 0) { + std::set<int> unique_cell_id_set; + dst->int_attributes[dst->ATTR_TABLE_COLUMN_COUNT] = column_count; + dst->int_attributes[dst->ATTR_TABLE_ROW_COUNT] = row_count; + for (int i = 0; i < column_count * row_count; ++i) { + WebAccessibilityObject cell = src.cellForColumnAndRow( + i % column_count, i / column_count); + int cell_id = -1; + if (!cell.isNull()) { + cell_id = cell.axID(); + if (unique_cell_id_set.find(cell_id) == unique_cell_id_set.end()) { + unique_cell_id_set.insert(cell_id); + dst->unique_cell_ids.push_back(cell_id); + } + } + dst->cell_ids.push_back(cell_id); + } + } + } + + if (dst->role == dst->ROLE_CELL || + dst->role == dst->ROLE_ROW_HEADER || + dst->role == dst->ROLE_COLUMN_HEADER) { + dst->int_attributes[dst->ATTR_TABLE_CELL_COLUMN_INDEX] = + src.cellColumnIndex(); + dst->int_attributes[dst->ATTR_TABLE_CELL_COLUMN_SPAN] = + src.cellColumnSpan(); + dst->int_attributes[dst->ATTR_TABLE_CELL_ROW_INDEX] = src.cellRowIndex(); + dst->int_attributes[dst->ATTR_TABLE_CELL_ROW_SPAN] = src.cellRowSpan(); + } + + if (include_children) { + // Recursively create children. + int child_count = src.childCount(); + std::set<int32> child_ids; + for (int i = 0; i < child_count; ++i) { + WebAccessibilityObject child = src.childAt(i); + int32 child_id = child.axID(); + + // The child may be invalid due to issues in webkit accessibility code. + // Don't add children that are invalid thus preventing a crash. + // https://bugs.webkit.org/show_bug.cgi?id=44149 + // TODO(ctguil): We may want to remove this check as webkit stabilizes. + if (!child.isValid()) + continue; + + // Children may duplicated in the webkit accessibility tree. Only add a + // child once for the web accessibility tree. + // https://bugs.webkit.org/show_bug.cgi?id=58930 + if (child_ids.find(child_id) != child_ids.end()) + continue; + child_ids.insert(child_id); + + // Some nodes appear in the tree in more than one place: for example, + // a cell in a table appears as a child of both a row and a column. + // Only recursively add child nodes that have this node as its + // unignored parent. For child nodes that are actually parented to + // somethinng else, store only the ID. + // + // As an exception, also add children of an iframe element. + // https://bugs.webkit.org/show_bug.cgi?id=57066 + if (is_iframe || IsParentUnignoredOf(src, child)) { + dst->children.push_back(AccessibilityNodeData()); + SerializeAccessibilityNode(child, + &dst->children.back(), + include_children); + } else { + dst->indirect_child_ids.push_back(child_id); + } + } + } +} + +} // namespace content diff --git a/content/renderer/accessibility_node_serializer.h b/content/renderer/accessibility_node_serializer.h new file mode 100644 index 0000000..3ebe6ef --- /dev/null +++ b/content/renderer/accessibility_node_serializer.h @@ -0,0 +1,21 @@ +// Copyright (c) 2012 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. + +#ifndef CONTENT_RENDERER_ACCESSIBILITY_NODE_SERIALIZER_H_ +#define CONTENT_RENDERER_ACCESSIBILITY_NODE_SERIALIZER_H_ +#pragma once + +#include "content/common/accessibility_node_data.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebAccessibilityObject.h" + +namespace content { + +void SerializeAccessibilityNode( + const WebKit::WebAccessibilityObject& src, + AccessibilityNodeData* dst, + bool include_children); + +} // namespace content + +#endif // CONTENT_RENDERER_ACCESSIBILITY_NODE_SERIALIZER_H_ diff --git a/content/renderer/renderer_accessibility.cc b/content/renderer/renderer_accessibility.cc index ab562da..63729cc 100644 --- a/content/renderer/renderer_accessibility.cc +++ b/content/renderer/renderer_accessibility.cc @@ -11,14 +11,12 @@ #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" -#include "webkit/glue/webaccessibility.h" using WebKit::WebAccessibilityNotification; using WebKit::WebAccessibilityObject; using WebKit::WebDocument; using WebKit::WebFrame; using WebKit::WebView; -using webkit_glue::WebAccessibility; namespace content { diff --git a/content/renderer/renderer_accessibility.h b/content/renderer/renderer_accessibility.h index e2e36bc..4f05a31 100644 --- a/content/renderer/renderer_accessibility.h +++ b/content/renderer/renderer_accessibility.h @@ -17,10 +17,6 @@ class WebAccessibilityObject; class WebDocument; }; -namespace webkit_glue { -struct WebAccessibility; -}; - namespace content { // The browser process implement native accessibility APIs, allowing diff --git a/content/renderer/renderer_accessibility_browsertest.cc b/content/renderer/renderer_accessibility_browsertest.cc index 0419edc..f244074 100644 --- a/content/renderer/renderer_accessibility_browsertest.cc +++ b/content/renderer/renderer_accessibility_browsertest.cc @@ -4,15 +4,15 @@ #include "base/utf_string_conversions.h" #include "content/common/accessibility_messages.h" +#include "content/common/accessibility_node_data.h" #include "content/common/view_messages.h" #include "content/renderer/render_view_impl.h" #include "content/public/test/render_view_test.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebSize.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" -#include "webkit/glue/webaccessibility.h" -using webkit_glue::WebAccessibility; +using content::AccessibilityNodeData; class RendererAccessibilityTest : public content::RenderViewTest { public: @@ -79,11 +79,12 @@ TEST_F(RendererAccessibilityTest, EditableTextModeFocusNotifications) { EXPECT_EQ(notification.includes_children, true); EXPECT_EQ(notification.id, 1); EXPECT_EQ(notification.acc_tree.id, 1); - EXPECT_EQ(notification.acc_tree.role, WebAccessibility::ROLE_ROOT_WEB_AREA); + EXPECT_EQ(notification.acc_tree.role, + AccessibilityNodeData::ROLE_ROOT_WEB_AREA); EXPECT_EQ(notification.acc_tree.state, - (1U << WebAccessibility::STATE_READONLY) | - (1U << WebAccessibility::STATE_FOCUSABLE) | - (1U << WebAccessibility::STATE_FOCUSED)); + (1U << AccessibilityNodeData::STATE_READONLY) | + (1U << AccessibilityNodeData::STATE_FOCUSABLE) | + (1U << AccessibilityNodeData::STATE_FOCUSED)); EXPECT_EQ(notification.acc_tree.children.size(), 1U); } @@ -99,17 +100,18 @@ TEST_F(RendererAccessibilityTest, EditableTextModeFocusNotifications) { EXPECT_EQ(notification.includes_children, true); EXPECT_EQ(notification.id, 3); EXPECT_EQ(notification.acc_tree.id, 1); - EXPECT_EQ(notification.acc_tree.role, WebAccessibility::ROLE_ROOT_WEB_AREA); + EXPECT_EQ(notification.acc_tree.role, + AccessibilityNodeData::ROLE_ROOT_WEB_AREA); EXPECT_EQ(notification.acc_tree.state, - (1U << WebAccessibility::STATE_READONLY) | - (1U << WebAccessibility::STATE_FOCUSABLE)); + (1U << AccessibilityNodeData::STATE_READONLY) | + (1U << AccessibilityNodeData::STATE_FOCUSABLE)); EXPECT_EQ(notification.acc_tree.children.size(), 1U); EXPECT_EQ(notification.acc_tree.children[0].id, 3); EXPECT_EQ(notification.acc_tree.children[0].role, - WebAccessibility::ROLE_GROUP); + AccessibilityNodeData::ROLE_GROUP); EXPECT_EQ(notification.acc_tree.children[0].state, - (1U << WebAccessibility::STATE_FOCUSABLE) | - (1U << WebAccessibility::STATE_FOCUSED)); + (1U << AccessibilityNodeData::STATE_FOCUSABLE) | + (1U << AccessibilityNodeData::STATE_FOCUSED)); } // Check other editable text nodes. @@ -121,8 +123,8 @@ TEST_F(RendererAccessibilityTest, EditableTextModeFocusNotifications) { GetLastAccNotification(¬ification); EXPECT_EQ(notification.id, 4); EXPECT_EQ(notification.acc_tree.children[0].state, - (1U << WebAccessibility::STATE_FOCUSABLE) | - (1U << WebAccessibility::STATE_FOCUSED)); + (1U << AccessibilityNodeData::STATE_FOCUSABLE) | + (1U << AccessibilityNodeData::STATE_FOCUSED)); } { @@ -133,8 +135,8 @@ TEST_F(RendererAccessibilityTest, EditableTextModeFocusNotifications) { GetLastAccNotification(¬ification); EXPECT_EQ(notification.id, 5); EXPECT_EQ(notification.acc_tree.children[0].state, - (1U << WebAccessibility::STATE_FOCUSABLE) | - (1U << WebAccessibility::STATE_FOCUSED)); + (1U << AccessibilityNodeData::STATE_FOCUSABLE) | + (1U << AccessibilityNodeData::STATE_FOCUSED)); } { @@ -145,8 +147,8 @@ TEST_F(RendererAccessibilityTest, EditableTextModeFocusNotifications) { GetLastAccNotification(¬ification); EXPECT_EQ(notification.id, 6); EXPECT_EQ(notification.acc_tree.children[0].state, - (1U << WebAccessibility::STATE_FOCUSABLE) | - (1U << WebAccessibility::STATE_FOCUSED)); + (1U << AccessibilityNodeData::STATE_FOCUSABLE) | + (1U << AccessibilityNodeData::STATE_FOCUSED)); } // Try focusing things that aren't editable text. @@ -158,9 +160,9 @@ TEST_F(RendererAccessibilityTest, EditableTextModeFocusNotifications) { GetLastAccNotification(¬ification); EXPECT_EQ(notification.id, 7); EXPECT_EQ(notification.acc_tree.children[0].state, - (1U << WebAccessibility::STATE_FOCUSABLE) | - (1U << WebAccessibility::STATE_FOCUSED) | - (1U << WebAccessibility::STATE_READONLY)); + (1U << AccessibilityNodeData::STATE_FOCUSABLE) | + (1U << AccessibilityNodeData::STATE_FOCUSED) | + (1U << AccessibilityNodeData::STATE_READONLY)); } { @@ -171,9 +173,9 @@ TEST_F(RendererAccessibilityTest, EditableTextModeFocusNotifications) { GetLastAccNotification(¬ification); EXPECT_EQ(notification.id, 8); EXPECT_EQ(notification.acc_tree.children[0].state, - (1U << WebAccessibility::STATE_FOCUSABLE) | - (1U << WebAccessibility::STATE_FOCUSED) | - (1U << WebAccessibility::STATE_READONLY)); + (1U << AccessibilityNodeData::STATE_FOCUSABLE) | + (1U << AccessibilityNodeData::STATE_FOCUSED) | + (1U << AccessibilityNodeData::STATE_READONLY)); } // Clear focus. diff --git a/content/renderer/renderer_accessibility_complete.cc b/content/renderer/renderer_accessibility_complete.cc index 20755a6..b28e714 100644 --- a/content/renderer/renderer_accessibility_complete.cc +++ b/content/renderer/renderer_accessibility_complete.cc @@ -6,6 +6,7 @@ #include "base/bind.h" #include "base/message_loop.h" +#include "content/renderer/accessibility_node_serializer.h" #include "content/renderer/render_view_impl.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebAccessibilityObject.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h" @@ -13,7 +14,6 @@ #include "third_party/WebKit/Source/WebKit/chromium/public/WebInputElement.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebNode.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" -#include "webkit/glue/webaccessibility.h" using WebKit::WebAccessibilityNotification; using WebKit::WebAccessibilityObject; @@ -24,7 +24,6 @@ using WebKit::WebPoint; using WebKit::WebRect; using WebKit::WebSize; using WebKit::WebView; -using webkit_glue::WebAccessibility; namespace content { @@ -316,11 +315,14 @@ void RendererAccessibilityComplete::SendPendingAccessibilityNotifications() { notification_msg.notification_type = notification.notification_type; notification_msg.id = notification.id; notification_msg.includes_children = includes_children; - BuildAccessibilityTree(obj, includes_children, ¬ification_msg.acc_tree); + SerializeAccessibilityNode(obj, + ¬ification_msg.acc_tree, + includes_children); if (obj.axID() == root_id) { DCHECK_EQ(notification_msg.acc_tree.role, - WebAccessibility::ROLE_WEB_AREA); - notification_msg.acc_tree.role = WebAccessibility::ROLE_ROOT_WEB_AREA; + AccessibilityNodeData::ROLE_WEB_AREA); + notification_msg.acc_tree.role = + AccessibilityNodeData::ROLE_ROOT_WEB_AREA; } notification_msgs.push_back(notification_msg); @@ -342,7 +344,7 @@ void RendererAccessibilityComplete::SendPendingAccessibilityNotifications() { } void RendererAccessibilityComplete::UpdateBrowserTree( - const webkit_glue::WebAccessibility& renderer_node) { + const AccessibilityNodeData& renderer_node) { BrowserTreeNode* browser_node = NULL; base::hash_map<int32, BrowserTreeNode*>::iterator iter = browser_id_map_.find(renderer_node.id); @@ -350,7 +352,7 @@ void RendererAccessibilityComplete::UpdateBrowserTree( browser_node = iter->second; ClearBrowserTreeNode(browser_node); } else { - DCHECK_EQ(renderer_node.role, WebAccessibility::ROLE_ROOT_WEB_AREA); + DCHECK_EQ(renderer_node.role, AccessibilityNodeData::ROLE_ROOT_WEB_AREA); if (browser_root_) { ClearBrowserTreeNode(browser_root_); browser_id_map_.erase(browser_root_->id); @@ -530,15 +532,4 @@ bool RendererAccessibilityComplete::ShouldIncludeChildren( return false; } -void RendererAccessibilityComplete::BuildAccessibilityTree( - const WebAccessibilityObject& src, - bool include_children, - WebAccessibility* dst) { - dst->Init(src, - include_children ? - WebAccessibility::INCLUDE_CHILDREN : - WebAccessibility::NO_CHILDREN, - WebAccessibility::INCLUDE_LINE_BREAKS); -} - } // namespace content diff --git a/content/renderer/renderer_accessibility_complete.h b/content/renderer/renderer_accessibility_complete.h index a318256..c0461b1 100644 --- a/content/renderer/renderer_accessibility_complete.h +++ b/content/renderer/renderer_accessibility_complete.h @@ -10,6 +10,7 @@ #include "base/hash_tables.h" #include "base/memory/weak_ptr.h" +#include "content/common/accessibility_node_data.h" #include "content/public/renderer/render_view_observer.h" #include "content/renderer/renderer_accessibility.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebAccessibilityNotification.h" @@ -22,10 +23,6 @@ class WebDocument; class WebNode; }; -namespace webkit_glue { -struct WebAccessibility; -}; - namespace content { // This is the subclass of RendererAccessibility that implements @@ -72,7 +69,7 @@ class RendererAccessibilityComplete : public RendererAccessibility { // Update our representation of what nodes the browser has, given a // tree of nodes. - void UpdateBrowserTree(const webkit_glue::WebAccessibility& renderer_node); + void UpdateBrowserTree(const AccessibilityNodeData& renderer_node); // Clear the given node and recursively delete all of its descendants // from the browser tree. (Does not delete |browser_node|). @@ -101,14 +98,14 @@ class RendererAccessibilityComplete : public RendererAccessibility { // corresponding WebAccessibility node as a child of |dst|. void RecursiveAddEditableTextNodesToTree( const WebKit::WebAccessibilityObject& src, - webkit_glue::WebAccessibility* dst); + AccessibilityNodeData* dst); - // Build a tree of serializable WebAccessibility nodes to send to the + // Build a tree of serializable AccessibilityNodeData nodes to send to the // browser process, given a WebAccessibilityObject node from WebKit. // Modifies |dst| in-place, it's assumed to be empty. void BuildAccessibilityTree(const WebKit::WebAccessibilityObject& src, bool include_children, - webkit_glue::WebAccessibility* dst); + AccessibilityNodeData* dst); // So we can queue up tasks to be executed later. base::WeakPtrFactory<RendererAccessibilityComplete> weak_factory_; diff --git a/content/renderer/renderer_accessibility_focus_only.cc b/content/renderer/renderer_accessibility_focus_only.cc index 66a5328..5cc1409 100644 --- a/content/renderer/renderer_accessibility_focus_only.cc +++ b/content/renderer/renderer_accessibility_focus_only.cc @@ -4,18 +4,17 @@ #include "content/renderer/renderer_accessibility_focus_only.h" +#include "content/common/accessibility_node_data.h" #include "content/renderer/render_view_impl.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebNode.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" -#include "webkit/glue/webaccessibility.h" using WebKit::WebDocument; using WebKit::WebFrame; using WebKit::WebNode; using WebKit::WebView; -using webkit_glue::WebAccessibility; namespace { // The root node will always have id 1. Let each child node have a new @@ -87,25 +86,25 @@ void RendererAccessibilityFocusOnly::HandleFocusedNodeChanged( // Always include the root of the tree, the document. It always has id 1. notification.acc_tree.id = 1; - notification.acc_tree.role = WebAccessibility::ROLE_ROOT_WEB_AREA; + notification.acc_tree.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA; notification.acc_tree.state = - (1 << WebAccessibility::STATE_READONLY) | - (1 << WebAccessibility::STATE_FOCUSABLE); + (1 << AccessibilityNodeData::STATE_READONLY) | + (1 << AccessibilityNodeData::STATE_FOCUSABLE); if (node.isNull()) - notification.acc_tree.state |= (1 << WebAccessibility::STATE_FOCUSED); + notification.acc_tree.state |= (1 << AccessibilityNodeData::STATE_FOCUSED); notification.acc_tree.location = gfx::Rect(render_view_->size()); - notification.acc_tree.children.push_back(WebAccessibility()); - WebAccessibility& child = notification.acc_tree.children[0]; + notification.acc_tree.children.push_back(AccessibilityNodeData()); + AccessibilityNodeData& child = notification.acc_tree.children[0]; child.id = next_id_; - child.role = WebAccessibility::ROLE_GROUP; + child.role = AccessibilityNodeData::ROLE_GROUP; child.location = gfx::Rect(render_view_->size()); if (!node.isNull()) { child.state = - (1 << WebAccessibility::STATE_FOCUSABLE) | - (1 << WebAccessibility::STATE_FOCUSED); + (1 << AccessibilityNodeData::STATE_FOCUSABLE) | + (1 << AccessibilityNodeData::STATE_FOCUSED); if (!render_view_->IsEditableNode(node)) - child.state |= (1 << WebAccessibility::STATE_READONLY); + child.state |= (1 << AccessibilityNodeData::STATE_READONLY); } #ifndef NDEBUG |