Home | History | Annotate | Download | only in accessibility
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "content/renderer/accessibility/accessibility_node_serializer.h"
      6 
      7 #include <set>
      8 
      9 #include "base/strings/string_number_conversions.h"
     10 #include "base/strings/string_util.h"
     11 #include "base/strings/utf_string_conversions.h"
     12 #include "third_party/WebKit/public/platform/WebRect.h"
     13 #include "third_party/WebKit/public/platform/WebSize.h"
     14 #include "third_party/WebKit/public/platform/WebString.h"
     15 #include "third_party/WebKit/public/platform/WebVector.h"
     16 #include "third_party/WebKit/public/web/WebAccessibilityObject.h"
     17 #include "third_party/WebKit/public/web/WebAccessibilityRole.h"
     18 #include "third_party/WebKit/public/web/WebDocument.h"
     19 #include "third_party/WebKit/public/web/WebDocumentType.h"
     20 #include "third_party/WebKit/public/web/WebElement.h"
     21 #include "third_party/WebKit/public/web/WebFormControlElement.h"
     22 #include "third_party/WebKit/public/web/WebFrame.h"
     23 #include "third_party/WebKit/public/web/WebInputElement.h"
     24 #include "third_party/WebKit/public/web/WebNode.h"
     25 
     26 using WebKit::WebAccessibilityRole;
     27 using WebKit::WebAccessibilityObject;
     28 using WebKit::WebDocument;
     29 using WebKit::WebDocumentType;
     30 using WebKit::WebElement;
     31 using WebKit::WebNode;
     32 using WebKit::WebVector;
     33 
     34 namespace content {
     35 namespace {
     36 
     37 // Returns true if |ancestor| is the first unignored parent of |child|,
     38 // which means that when walking up the parent chain from |child|,
     39 // |ancestor| is the *first* ancestor that isn't marked as
     40 // accessibilityIsIgnored().
     41 bool IsParentUnignoredOf(const WebAccessibilityObject& ancestor,
     42                          const WebAccessibilityObject& child) {
     43   WebAccessibilityObject parent = child.parentObject();
     44   while (!parent.isDetached() && parent.accessibilityIsIgnored())
     45     parent = parent.parentObject();
     46   return parent.equals(ancestor);
     47 }
     48 
     49 // Provides a conversion between the WebKit::WebAccessibilityRole and a role
     50 // supported on the Browser side. Listed alphabetically by the
     51 // WebKit::WebAccessibilityRole (except for default role).
     52 AccessibilityNodeData::Role ConvertRole(WebKit::WebAccessibilityRole role) {
     53   switch (role) {
     54     case WebKit::WebAccessibilityRoleAnnotation:
     55       return AccessibilityNodeData::ROLE_ANNOTATION;
     56     case WebKit::WebAccessibilityRoleApplication:
     57       return AccessibilityNodeData::ROLE_APPLICATION;
     58     case WebKit::WebAccessibilityRoleApplicationAlert:
     59       return AccessibilityNodeData::ROLE_ALERT;
     60     case WebKit::WebAccessibilityRoleApplicationAlertDialog:
     61       return AccessibilityNodeData::ROLE_ALERT_DIALOG;
     62     case WebKit::WebAccessibilityRoleApplicationDialog:
     63       return AccessibilityNodeData::ROLE_DIALOG;
     64     case WebKit::WebAccessibilityRoleApplicationLog:
     65       return AccessibilityNodeData::ROLE_LOG;
     66     case WebKit::WebAccessibilityRoleApplicationMarquee:
     67       return AccessibilityNodeData::ROLE_MARQUEE;
     68     case WebKit::WebAccessibilityRoleApplicationStatus:
     69       return AccessibilityNodeData::ROLE_STATUS;
     70     case WebKit::WebAccessibilityRoleApplicationTimer:
     71       return AccessibilityNodeData::ROLE_TIMER;
     72     case WebKit::WebAccessibilityRoleBrowser:
     73       return AccessibilityNodeData::ROLE_BROWSER;
     74     case WebKit::WebAccessibilityRoleBusyIndicator:
     75       return AccessibilityNodeData::ROLE_BUSY_INDICATOR;
     76     case WebKit::WebAccessibilityRoleButton:
     77       return AccessibilityNodeData::ROLE_BUTTON;
     78     case WebKit::WebAccessibilityRoleCanvas:
     79       return AccessibilityNodeData::ROLE_CANVAS;
     80     case WebKit::WebAccessibilityRoleCell:
     81       return AccessibilityNodeData::ROLE_CELL;
     82     case WebKit::WebAccessibilityRoleCheckBox:
     83       return AccessibilityNodeData::ROLE_CHECKBOX;
     84     case WebKit::WebAccessibilityRoleColorWell:
     85       return AccessibilityNodeData::ROLE_COLOR_WELL;
     86     case WebKit::WebAccessibilityRoleColumn:
     87       return AccessibilityNodeData::ROLE_COLUMN;
     88     case WebKit::WebAccessibilityRoleColumnHeader:
     89       return AccessibilityNodeData::ROLE_COLUMN_HEADER;
     90     case WebKit::WebAccessibilityRoleComboBox:
     91       return AccessibilityNodeData::ROLE_COMBO_BOX;
     92     case WebKit::WebAccessibilityRoleDefinition:
     93       return AccessibilityNodeData::ROLE_DEFINITION;
     94     case WebKit::WebAccessibilityRoleDescriptionListTerm:
     95       return AccessibilityNodeData::ROLE_DESCRIPTION_LIST_TERM;
     96     case WebKit::WebAccessibilityRoleDescriptionListDetail:
     97       return AccessibilityNodeData::ROLE_DESCRIPTION_LIST_DETAIL;
     98     case WebKit::WebAccessibilityRoleDirectory:
     99       return AccessibilityNodeData::ROLE_DIRECTORY;
    100     case WebKit::WebAccessibilityRoleDisclosureTriangle:
    101       return AccessibilityNodeData::ROLE_DISCLOSURE_TRIANGLE;
    102     case WebKit::WebAccessibilityRoleDiv:
    103       return AccessibilityNodeData::ROLE_DIV;
    104     case WebKit::WebAccessibilityRoleDocument:
    105       return AccessibilityNodeData::ROLE_DOCUMENT;
    106     case WebKit::WebAccessibilityRoleDocumentArticle:
    107       return AccessibilityNodeData::ROLE_ARTICLE;
    108     case WebKit::WebAccessibilityRoleDocumentMath:
    109       return AccessibilityNodeData::ROLE_MATH;
    110     case WebKit::WebAccessibilityRoleDocumentNote:
    111       return AccessibilityNodeData::ROLE_NOTE;
    112     case WebKit::WebAccessibilityRoleDocumentRegion:
    113       return AccessibilityNodeData::ROLE_REGION;
    114     case WebKit::WebAccessibilityRoleDrawer:
    115       return AccessibilityNodeData::ROLE_DRAWER;
    116     case WebKit::WebAccessibilityRoleEditableText:
    117       return AccessibilityNodeData::ROLE_EDITABLE_TEXT;
    118     case WebKit::WebAccessibilityRoleFooter:
    119       return AccessibilityNodeData::ROLE_FOOTER;
    120     case WebKit::WebAccessibilityRoleForm:
    121       return AccessibilityNodeData::ROLE_FORM;
    122     case WebKit::WebAccessibilityRoleGrid:
    123       return AccessibilityNodeData::ROLE_GRID;
    124     case WebKit::WebAccessibilityRoleGroup:
    125       return AccessibilityNodeData::ROLE_GROUP;
    126     case WebKit::WebAccessibilityRoleGrowArea:
    127       return AccessibilityNodeData::ROLE_GROW_AREA;
    128     case WebKit::WebAccessibilityRoleHeading:
    129       return AccessibilityNodeData::ROLE_HEADING;
    130     case WebKit::WebAccessibilityRoleHelpTag:
    131       return AccessibilityNodeData::ROLE_HELP_TAG;
    132     case WebKit::WebAccessibilityRoleHorizontalRule:
    133       return AccessibilityNodeData::ROLE_HORIZONTAL_RULE;
    134     case WebKit::WebAccessibilityRoleIgnored:
    135       return AccessibilityNodeData::ROLE_IGNORED;
    136     case WebKit::WebAccessibilityRoleImage:
    137       return AccessibilityNodeData::ROLE_IMAGE;
    138     case WebKit::WebAccessibilityRoleImageMap:
    139       return AccessibilityNodeData::ROLE_IMAGE_MAP;
    140     case WebKit::WebAccessibilityRoleImageMapLink:
    141       return AccessibilityNodeData::ROLE_IMAGE_MAP_LINK;
    142     case WebKit::WebAccessibilityRoleIncrementor:
    143       return AccessibilityNodeData::ROLE_INCREMENTOR;
    144     case WebKit::WebAccessibilityRoleLabel:
    145       return AccessibilityNodeData::ROLE_LABEL;
    146     case WebKit::WebAccessibilityRoleLandmarkApplication:
    147       return AccessibilityNodeData::ROLE_LANDMARK_APPLICATION;
    148     case WebKit::WebAccessibilityRoleLandmarkBanner:
    149       return AccessibilityNodeData::ROLE_LANDMARK_BANNER;
    150     case WebKit::WebAccessibilityRoleLandmarkComplementary:
    151       return AccessibilityNodeData::ROLE_LANDMARK_COMPLEMENTARY;
    152     case WebKit::WebAccessibilityRoleLandmarkContentInfo:
    153       return AccessibilityNodeData::ROLE_LANDMARK_CONTENTINFO;
    154     case WebKit::WebAccessibilityRoleLandmarkMain:
    155       return AccessibilityNodeData::ROLE_LANDMARK_MAIN;
    156     case WebKit::WebAccessibilityRoleLandmarkNavigation:
    157       return AccessibilityNodeData::ROLE_LANDMARK_NAVIGATION;
    158     case WebKit::WebAccessibilityRoleLandmarkSearch:
    159       return AccessibilityNodeData::ROLE_LANDMARK_SEARCH;
    160     case WebKit::WebAccessibilityRoleLink:
    161       return AccessibilityNodeData::ROLE_LINK;
    162     case WebKit::WebAccessibilityRoleList:
    163       return AccessibilityNodeData::ROLE_LIST;
    164     case WebKit::WebAccessibilityRoleListBox:
    165       return AccessibilityNodeData::ROLE_LISTBOX;
    166     case WebKit::WebAccessibilityRoleListBoxOption:
    167       return AccessibilityNodeData::ROLE_LISTBOX_OPTION;
    168     case WebKit::WebAccessibilityRoleListItem:
    169       return AccessibilityNodeData::ROLE_LIST_ITEM;
    170     case WebKit::WebAccessibilityRoleListMarker:
    171       return AccessibilityNodeData::ROLE_LIST_MARKER;
    172     case WebKit::WebAccessibilityRoleMatte:
    173       return AccessibilityNodeData::ROLE_MATTE;
    174     case WebKit::WebAccessibilityRoleMenu:
    175       return AccessibilityNodeData::ROLE_MENU;
    176     case WebKit::WebAccessibilityRoleMenuBar:
    177       return AccessibilityNodeData::ROLE_MENU_BAR;
    178     case WebKit::WebAccessibilityRoleMenuButton:
    179       return AccessibilityNodeData::ROLE_MENU_BUTTON;
    180     case WebKit::WebAccessibilityRoleMenuItem:
    181       return AccessibilityNodeData::ROLE_MENU_ITEM;
    182     case WebKit::WebAccessibilityRoleMenuListOption:
    183       return AccessibilityNodeData::ROLE_MENU_LIST_OPTION;
    184     case WebKit::WebAccessibilityRoleMenuListPopup:
    185       return AccessibilityNodeData::ROLE_MENU_LIST_POPUP;
    186     case WebKit::WebAccessibilityRoleOutline:
    187       return AccessibilityNodeData::ROLE_OUTLINE;
    188     case WebKit::WebAccessibilityRoleParagraph:
    189       return AccessibilityNodeData::ROLE_PARAGRAPH;
    190     case WebKit::WebAccessibilityRolePopUpButton:
    191       return AccessibilityNodeData::ROLE_POPUP_BUTTON;
    192     case WebKit::WebAccessibilityRolePresentational:
    193       return AccessibilityNodeData::ROLE_PRESENTATIONAL;
    194     case WebKit::WebAccessibilityRoleProgressIndicator:
    195       return AccessibilityNodeData::ROLE_PROGRESS_INDICATOR;
    196     case WebKit::WebAccessibilityRoleRadioButton:
    197       return AccessibilityNodeData::ROLE_RADIO_BUTTON;
    198     case WebKit::WebAccessibilityRoleRadioGroup:
    199       return AccessibilityNodeData::ROLE_RADIO_GROUP;
    200     case WebKit::WebAccessibilityRoleRow:
    201       return AccessibilityNodeData::ROLE_ROW;
    202     case WebKit::WebAccessibilityRoleRowHeader:
    203       return AccessibilityNodeData::ROLE_ROW_HEADER;
    204     case WebKit::WebAccessibilityRoleRuler:
    205       return AccessibilityNodeData::ROLE_RULER;
    206     case WebKit::WebAccessibilityRoleRulerMarker:
    207       return AccessibilityNodeData::ROLE_RULER_MARKER;
    208     case WebKit::WebAccessibilityRoleScrollArea:
    209       return AccessibilityNodeData::ROLE_SCROLLAREA;
    210     case WebKit::WebAccessibilityRoleScrollBar:
    211       return AccessibilityNodeData::ROLE_SCROLLBAR;
    212     case WebKit::WebAccessibilityRoleSheet:
    213       return AccessibilityNodeData::ROLE_SHEET;
    214     case WebKit::WebAccessibilityRoleSlider:
    215       return AccessibilityNodeData::ROLE_SLIDER;
    216     case WebKit::WebAccessibilityRoleSliderThumb:
    217       return AccessibilityNodeData::ROLE_SLIDER_THUMB;
    218     case WebKit::WebAccessibilityRoleSpinButton:
    219       return AccessibilityNodeData::ROLE_SPIN_BUTTON;
    220     case WebKit::WebAccessibilityRoleSpinButtonPart:
    221       return AccessibilityNodeData::ROLE_SPIN_BUTTON_PART;
    222     case WebKit::WebAccessibilityRoleSplitGroup:
    223       return AccessibilityNodeData::ROLE_SPLIT_GROUP;
    224     case WebKit::WebAccessibilityRoleSplitter:
    225       return AccessibilityNodeData::ROLE_SPLITTER;
    226     case WebKit::WebAccessibilityRoleStaticText:
    227       return AccessibilityNodeData::ROLE_STATIC_TEXT;
    228     case WebKit::WebAccessibilityRoleSVGRoot:
    229       return AccessibilityNodeData::ROLE_SVG_ROOT;
    230     case WebKit::WebAccessibilityRoleSystemWide:
    231       return AccessibilityNodeData::ROLE_SYSTEM_WIDE;
    232     case WebKit::WebAccessibilityRoleTab:
    233       return AccessibilityNodeData::ROLE_TAB;
    234     case WebKit::WebAccessibilityRoleTabGroup:
    235       return AccessibilityNodeData::ROLE_TAB_GROUP_UNUSED;
    236     case WebKit::WebAccessibilityRoleTabList:
    237       return AccessibilityNodeData::ROLE_TAB_LIST;
    238     case WebKit::WebAccessibilityRoleTabPanel:
    239       return AccessibilityNodeData::ROLE_TAB_PANEL;
    240     case WebKit::WebAccessibilityRoleTable:
    241       return AccessibilityNodeData::ROLE_TABLE;
    242     case WebKit::WebAccessibilityRoleTableHeaderContainer:
    243       return AccessibilityNodeData::ROLE_TABLE_HEADER_CONTAINER;
    244     case WebKit::WebAccessibilityRoleTextArea:
    245       return AccessibilityNodeData::ROLE_TEXTAREA;
    246     case WebKit::WebAccessibilityRoleTextField:
    247       return AccessibilityNodeData::ROLE_TEXT_FIELD;
    248     case WebKit::WebAccessibilityRoleToggleButton:
    249       return AccessibilityNodeData::ROLE_TOGGLE_BUTTON;
    250     case WebKit::WebAccessibilityRoleToolbar:
    251       return AccessibilityNodeData::ROLE_TOOLBAR;
    252     case WebKit::WebAccessibilityRoleTreeGrid:
    253       return AccessibilityNodeData::ROLE_TREE_GRID;
    254     case WebKit::WebAccessibilityRoleTreeItemRole:
    255       return AccessibilityNodeData::ROLE_TREE_ITEM;
    256     case WebKit::WebAccessibilityRoleTreeRole:
    257       return AccessibilityNodeData::ROLE_TREE;
    258     case WebKit::WebAccessibilityRoleUserInterfaceTooltip:
    259       return AccessibilityNodeData::ROLE_TOOLTIP;
    260     case WebKit::WebAccessibilityRoleValueIndicator:
    261       return AccessibilityNodeData::ROLE_VALUE_INDICATOR;
    262     case WebKit::WebAccessibilityRoleWebArea:
    263       return AccessibilityNodeData::ROLE_WEB_AREA;
    264     case WebKit::WebAccessibilityRoleWebCoreLink:
    265       return AccessibilityNodeData::ROLE_WEBCORE_LINK;
    266     case WebKit::WebAccessibilityRoleWindow:
    267       return AccessibilityNodeData::ROLE_WINDOW;
    268 
    269     default:
    270       return AccessibilityNodeData::ROLE_UNKNOWN;
    271   }
    272 }
    273 
    274 // Provides a conversion between the WebAccessibilityObject state
    275 // accessors and a state bitmask that can be serialized and sent to the
    276 // Browser process. Rare state are sent as boolean attributes instead.
    277 uint32 ConvertState(const WebAccessibilityObject& o) {
    278   uint32 state = 0;
    279   if (o.isChecked())
    280     state |= (1 << AccessibilityNodeData::STATE_CHECKED);
    281 
    282   if (o.isCollapsed())
    283     state |= (1 << AccessibilityNodeData::STATE_COLLAPSED);
    284 
    285   if (o.canSetFocusAttribute())
    286     state |= (1 << AccessibilityNodeData::STATE_FOCUSABLE);
    287 
    288   if (o.isFocused())
    289     state |= (1 << AccessibilityNodeData::STATE_FOCUSED);
    290 
    291   if (o.roleValue() == WebKit::WebAccessibilityRolePopUpButton ||
    292       o.ariaHasPopup()) {
    293     state |= (1 << AccessibilityNodeData::STATE_HASPOPUP);
    294     if (!o.isCollapsed())
    295       state |= (1 << AccessibilityNodeData::STATE_EXPANDED);
    296   }
    297 
    298   if (o.isHovered())
    299     state |= (1 << AccessibilityNodeData::STATE_HOTTRACKED);
    300 
    301   if (o.isIndeterminate())
    302     state |= (1 << AccessibilityNodeData::STATE_INDETERMINATE);
    303 
    304   if (!o.isVisible())
    305     state |= (1 << AccessibilityNodeData::STATE_INVISIBLE);
    306 
    307   if (o.isLinked())
    308     state |= (1 << AccessibilityNodeData::STATE_LINKED);
    309 
    310   if (o.isMultiSelectable())
    311     state |= (1 << AccessibilityNodeData::STATE_MULTISELECTABLE);
    312 
    313   if (o.isOffScreen())
    314     state |= (1 << AccessibilityNodeData::STATE_OFFSCREEN);
    315 
    316   if (o.isPressed())
    317     state |= (1 << AccessibilityNodeData::STATE_PRESSED);
    318 
    319   if (o.isPasswordField())
    320     state |= (1 << AccessibilityNodeData::STATE_PROTECTED);
    321 
    322   if (o.isReadOnly())
    323     state |= (1 << AccessibilityNodeData::STATE_READONLY);
    324 
    325   if (o.isRequired())
    326     state |= (1 << AccessibilityNodeData::STATE_REQUIRED);
    327 
    328   if (o.canSetSelectedAttribute())
    329     state |= (1 << AccessibilityNodeData::STATE_SELECTABLE);
    330 
    331   if (o.isSelected())
    332     state |= (1 << AccessibilityNodeData::STATE_SELECTED);
    333 
    334   if (o.isVisited())
    335     state |= (1 << AccessibilityNodeData::STATE_TRAVERSED);
    336 
    337   if (!o.isEnabled())
    338     state |= (1 << AccessibilityNodeData::STATE_UNAVAILABLE);
    339 
    340   if (o.isVertical())
    341     state |= (1 << AccessibilityNodeData::STATE_VERTICAL);
    342 
    343   if (o.isVisited())
    344     state |= (1 << AccessibilityNodeData::STATE_VISITED);
    345 
    346   return state;
    347 }
    348 
    349 }  // Anonymous namespace
    350 
    351 void SerializeAccessibilityNode(
    352     const WebAccessibilityObject& src,
    353     AccessibilityNodeData* dst) {
    354   dst->name = src.title();
    355   dst->role = ConvertRole(src.roleValue());
    356   dst->state = ConvertState(src);
    357   dst->location = src.boundingBoxRect();
    358   dst->id = src.axID();
    359 
    360   if (src.valueDescription().length())
    361     dst->value = src.valueDescription();
    362   else
    363     dst->value = src.stringValue();
    364 
    365   if (dst->role == AccessibilityNodeData::ROLE_COLOR_WELL) {
    366     int r, g, b;
    367     src.colorValue(r, g, b);
    368     dst->int_attributes[dst->ATTR_COLOR_VALUE_RED] = r;
    369     dst->int_attributes[dst->ATTR_COLOR_VALUE_GREEN] = g;
    370     dst->int_attributes[dst->ATTR_COLOR_VALUE_BLUE] = b;
    371   }
    372 
    373   if (src.accessKey().length())
    374     dst->string_attributes[dst->ATTR_ACCESS_KEY] = src.accessKey();
    375   if (src.actionVerb().length())
    376     dst->string_attributes[dst->ATTR_ACTION] = src.actionVerb();
    377   if (src.isAriaReadOnly())
    378     dst->bool_attributes[dst->ATTR_ARIA_READONLY] = true;
    379   if (src.isButtonStateMixed())
    380     dst->bool_attributes[dst->ATTR_BUTTON_MIXED] = true;
    381   if (src.canSetValueAttribute())
    382     dst->bool_attributes[dst->ATTR_CAN_SET_VALUE] = true;
    383   if (src.accessibilityDescription().length())
    384     dst->string_attributes[dst->ATTR_DESCRIPTION] =
    385         src.accessibilityDescription();
    386   if (src.hasComputedStyle())
    387     dst->string_attributes[dst->ATTR_DISPLAY] = src.computedStyleDisplay();
    388   if (src.helpText().length())
    389     dst->string_attributes[dst->ATTR_HELP] = src.helpText();
    390   if (src.keyboardShortcut().length())
    391     dst->string_attributes[dst->ATTR_SHORTCUT] = src.keyboardShortcut();
    392   if (!src.titleUIElement().isDetached()) {
    393     dst->int_attributes[dst->ATTR_TITLE_UI_ELEMENT] =
    394         src.titleUIElement().axID();
    395   }
    396   if (!src.url().isEmpty())
    397     dst->string_attributes[dst->ATTR_URL] = src.url().spec().utf16();
    398 
    399   if (dst->role == dst->ROLE_HEADING)
    400     dst->int_attributes[dst->ATTR_HIERARCHICAL_LEVEL] = src.headingLevel();
    401   else if ((dst->role == dst->ROLE_TREE_ITEM || dst->role == dst->ROLE_ROW) &&
    402            src.hierarchicalLevel() > 0) {
    403     dst->int_attributes[dst->ATTR_HIERARCHICAL_LEVEL] = src.hierarchicalLevel();
    404   }
    405 
    406   // Treat the active list box item as focused.
    407   if (dst->role == dst->ROLE_LISTBOX_OPTION && src.isSelectedOptionActive())
    408     dst->state |= (1 << AccessibilityNodeData::STATE_FOCUSED);
    409 
    410   if (src.canvasHasFallbackContent())
    411     dst->role = AccessibilityNodeData::ROLE_CANVAS_WITH_FALLBACK_CONTENT;
    412 
    413   WebNode node = src.node();
    414   bool is_iframe = false;
    415 
    416   if (!node.isNull() && node.isElementNode()) {
    417     WebElement element = node.to<WebElement>();
    418     is_iframe = (element.tagName() == ASCIIToUTF16("IFRAME"));
    419 
    420     if (LowerCaseEqualsASCII(element.getAttribute("aria-expanded"), "true"))
    421       dst->state |= (1 << AccessibilityNodeData::STATE_EXPANDED);
    422 
    423     // TODO(ctguil): The tagName in WebKit is lower cased but
    424     // HTMLElement::nodeName calls localNameUpper. Consider adding
    425     // a WebElement method that returns the original lower cased tagName.
    426     dst->string_attributes[dst->ATTR_HTML_TAG] =
    427         StringToLowerASCII(string16(element.tagName()));
    428     for (unsigned i = 0; i < element.attributeCount(); ++i) {
    429       string16 name = StringToLowerASCII(string16(
    430           element.attributeLocalName(i)));
    431       string16 value = element.attributeValue(i);
    432       dst->html_attributes.push_back(
    433           std::pair<string16, string16>(name, value));
    434     }
    435 
    436     if (dst->role == dst->ROLE_EDITABLE_TEXT ||
    437         dst->role == dst->ROLE_TEXTAREA ||
    438         dst->role == dst->ROLE_TEXT_FIELD) {
    439       dst->int_attributes[dst->ATTR_TEXT_SEL_START] = src.selectionStart();
    440       dst->int_attributes[dst->ATTR_TEXT_SEL_END] = src.selectionEnd();
    441 
    442       WebVector<int> src_line_breaks;
    443       src.lineBreaks(src_line_breaks);
    444       dst->line_breaks.reserve(src_line_breaks.size());
    445       for (size_t i = 0; i < src_line_breaks.size(); ++i)
    446         dst->line_breaks.push_back(src_line_breaks[i]);
    447     }
    448 
    449     // ARIA role.
    450     if (element.hasAttribute("role")) {
    451       dst->string_attributes[dst->ATTR_ROLE] = element.getAttribute("role");
    452     }
    453 
    454     // Live region attributes
    455     if (element.hasAttribute("aria-atomic")) {
    456       dst->bool_attributes[dst->ATTR_LIVE_ATOMIC] =
    457           LowerCaseEqualsASCII(element.getAttribute("aria-atomic"), "true");
    458     }
    459     if (element.hasAttribute("aria-busy")) {
    460       dst->bool_attributes[dst->ATTR_LIVE_BUSY] =
    461           LowerCaseEqualsASCII(element.getAttribute("aria-busy"), "true");
    462     }
    463     if (element.hasAttribute("aria-live")) {
    464       dst->string_attributes[dst->ATTR_LIVE_STATUS] =
    465           element.getAttribute("aria-live");
    466     }
    467     if (element.hasAttribute("aria-relevant")) {
    468       dst->string_attributes[dst->ATTR_LIVE_RELEVANT] =
    469           element.getAttribute("aria-relevant");
    470     }
    471   }
    472 
    473   // Walk up the parent chain to set live region attributes of containers
    474 
    475   WebAccessibilityObject container_accessible = src;
    476   while (!container_accessible.isDetached()) {
    477     WebNode container_node = container_accessible.node();
    478     if (!container_node.isNull() && container_node.isElementNode()) {
    479       WebElement container_elem =
    480           container_node.to<WebElement>();
    481       if (container_elem.hasAttribute("aria-atomic") &&
    482           dst->bool_attributes.find(dst->ATTR_CONTAINER_LIVE_ATOMIC) ==
    483           dst->bool_attributes.end()) {
    484         dst->bool_attributes[dst->ATTR_CONTAINER_LIVE_ATOMIC] =
    485             LowerCaseEqualsASCII(container_elem.getAttribute("aria-atomic"),
    486                                  "true");
    487       }
    488       if (container_elem.hasAttribute("aria-busy") &&
    489           dst->bool_attributes.find(dst->ATTR_CONTAINER_LIVE_BUSY) ==
    490           dst->bool_attributes.end()) {
    491         dst->bool_attributes[dst->ATTR_CONTAINER_LIVE_BUSY] =
    492             LowerCaseEqualsASCII(container_elem.getAttribute("aria-busy"),
    493                                  "true");
    494       }
    495       if (container_elem.hasAttribute("aria-live") &&
    496           dst->string_attributes.find(dst->ATTR_CONTAINER_LIVE_STATUS) ==
    497           dst->string_attributes.end()) {
    498         dst->string_attributes[dst->ATTR_CONTAINER_LIVE_STATUS] =
    499             container_elem.getAttribute("aria-live");
    500       }
    501       if (container_elem.hasAttribute("aria-relevant") &&
    502           dst->string_attributes.find(dst->ATTR_CONTAINER_LIVE_RELEVANT) ==
    503           dst->string_attributes.end()) {
    504         dst->string_attributes[dst->ATTR_CONTAINER_LIVE_RELEVANT] =
    505             container_elem.getAttribute("aria-relevant");
    506       }
    507     }
    508     container_accessible = container_accessible.parentObject();
    509   }
    510 
    511   if (dst->role == dst->ROLE_PROGRESS_INDICATOR ||
    512       dst->role == dst->ROLE_SCROLLBAR ||
    513       dst->role == dst->ROLE_SLIDER ||
    514       dst->role == dst->ROLE_SPIN_BUTTON) {
    515     dst->float_attributes[dst->ATTR_VALUE_FOR_RANGE] = src.valueForRange();
    516     dst->float_attributes[dst->ATTR_MAX_VALUE_FOR_RANGE] =
    517         src.maxValueForRange();
    518     dst->float_attributes[dst->ATTR_MIN_VALUE_FOR_RANGE] =
    519         src.minValueForRange();
    520   }
    521 
    522   if (dst->role == dst->ROLE_DOCUMENT ||
    523       dst->role == dst->ROLE_WEB_AREA) {
    524     dst->string_attributes[dst->ATTR_HTML_TAG] = ASCIIToUTF16("#document");
    525     const WebDocument& document = src.document();
    526     if (dst->name.empty())
    527       dst->name = document.title();
    528     dst->string_attributes[dst->ATTR_DOC_TITLE] = document.title();
    529     dst->string_attributes[dst->ATTR_DOC_URL] = document.url().spec().utf16();
    530     dst->string_attributes[dst->ATTR_DOC_MIMETYPE] =
    531         ASCIIToUTF16(document.isXHTMLDocument() ? "text/xhtml" : "text/html");
    532     dst->bool_attributes[dst->ATTR_DOC_LOADED] = src.isLoaded();
    533     dst->float_attributes[dst->ATTR_DOC_LOADING_PROGRESS] =
    534         src.estimatedLoadingProgress();
    535 
    536     const WebDocumentType& doctype = document.doctype();
    537     if (!doctype.isNull())
    538       dst->string_attributes[dst->ATTR_DOC_DOCTYPE] = doctype.name();
    539 
    540     const gfx::Size& scroll_offset = document.frame()->scrollOffset();
    541     dst->int_attributes[dst->ATTR_SCROLL_X] = scroll_offset.width();
    542     dst->int_attributes[dst->ATTR_SCROLL_Y] = scroll_offset.height();
    543 
    544     const gfx::Size& min_offset = document.frame()->minimumScrollOffset();
    545     dst->int_attributes[dst->ATTR_SCROLL_X_MIN] = min_offset.width();
    546     dst->int_attributes[dst->ATTR_SCROLL_Y_MIN] = min_offset.height();
    547 
    548     const gfx::Size& max_offset = document.frame()->maximumScrollOffset();
    549     dst->int_attributes[dst->ATTR_SCROLL_X_MAX] = max_offset.width();
    550     dst->int_attributes[dst->ATTR_SCROLL_Y_MAX] = max_offset.height();
    551   }
    552 
    553   if (dst->role == dst->ROLE_TABLE) {
    554     int column_count = src.columnCount();
    555     int row_count = src.rowCount();
    556     if (column_count > 0 && row_count > 0) {
    557       std::set<int> unique_cell_id_set;
    558       dst->int_attributes[dst->ATTR_TABLE_COLUMN_COUNT] = column_count;
    559       dst->int_attributes[dst->ATTR_TABLE_ROW_COUNT] = row_count;
    560       WebAccessibilityObject header = src.headerContainerObject();
    561       if (!header.isDetached())
    562         dst->int_attributes[dst->ATTR_TABLE_HEADER_ID] = header.axID();
    563       for (int i = 0; i < column_count * row_count; ++i) {
    564         WebAccessibilityObject cell = src.cellForColumnAndRow(
    565             i % column_count, i / column_count);
    566         int cell_id = -1;
    567         if (!cell.isDetached()) {
    568           cell_id = cell.axID();
    569           if (unique_cell_id_set.find(cell_id) == unique_cell_id_set.end()) {
    570             unique_cell_id_set.insert(cell_id);
    571             dst->unique_cell_ids.push_back(cell_id);
    572           }
    573         }
    574         dst->cell_ids.push_back(cell_id);
    575       }
    576     }
    577   }
    578 
    579   if (dst->role == dst->ROLE_ROW) {
    580     dst->int_attributes[dst->ATTR_TABLE_ROW_INDEX] = src.rowIndex();
    581     WebAccessibilityObject header = src.rowHeader();
    582     if (!header.isDetached())
    583       dst->int_attributes[dst->ATTR_TABLE_ROW_HEADER_ID] = header.axID();
    584   }
    585 
    586   if (dst->role == dst->ROLE_COLUMN) {
    587     dst->int_attributes[dst->ATTR_TABLE_COLUMN_INDEX] = src.columnIndex();
    588     WebAccessibilityObject header = src.columnHeader();
    589     if (!header.isDetached())
    590       dst->int_attributes[dst->ATTR_TABLE_COLUMN_HEADER_ID] = header.axID();
    591   }
    592 
    593   if (dst->role == dst->ROLE_CELL ||
    594       dst->role == dst->ROLE_ROW_HEADER ||
    595       dst->role == dst->ROLE_COLUMN_HEADER) {
    596     dst->int_attributes[dst->ATTR_TABLE_CELL_COLUMN_INDEX] =
    597         src.cellColumnIndex();
    598     dst->int_attributes[dst->ATTR_TABLE_CELL_COLUMN_SPAN] =
    599         src.cellColumnSpan();
    600     dst->int_attributes[dst->ATTR_TABLE_CELL_ROW_INDEX] = src.cellRowIndex();
    601     dst->int_attributes[dst->ATTR_TABLE_CELL_ROW_SPAN] = src.cellRowSpan();
    602   }
    603 
    604   // Add the ids of *indirect* children - those who are children of this node,
    605   // but whose parent is *not* this node. One example is a table
    606   // cell, which is a child of both a row and a column. Because the cell's
    607   // parent is the row, the row adds it as a child, and the column adds it
    608   // as an indirect child.
    609   int child_count = src.childCount();
    610   for (int i = 0; i < child_count; ++i) {
    611     WebAccessibilityObject child = src.childAt(i);
    612     if (!is_iframe && !child.isDetached() && !IsParentUnignoredOf(src, child))
    613       dst->indirect_child_ids.push_back(child.axID());
    614   }
    615 }
    616 
    617 bool ShouldIncludeChildNode(
    618     const WebAccessibilityObject& parent,
    619     const WebAccessibilityObject& child) {
    620   switch(parent.roleValue()) {
    621   case WebKit::WebAccessibilityRoleSlider:
    622   case WebKit::WebAccessibilityRoleEditableText:
    623   case WebKit::WebAccessibilityRoleTextArea:
    624   case WebKit::WebAccessibilityRoleTextField:
    625     return false;
    626   default:
    627     break;
    628   }
    629 
    630   // The child may be invalid due to issues in webkit accessibility code.
    631   // Don't add children that are invalid thus preventing a crash.
    632   // https://bugs.webkit.org/show_bug.cgi?id=44149
    633   // TODO(ctguil): We may want to remove this check as webkit stabilizes.
    634   if (child.isDetached())
    635     return false;
    636 
    637   // Skip children whose parent isn't this - see indirect_child_ids, above.
    638   // As an exception, include children of an iframe element.
    639   bool is_iframe = false;
    640   WebNode node = parent.node();
    641   if (!node.isNull() && node.isElementNode()) {
    642     WebElement element = node.to<WebElement>();
    643     is_iframe = (element.tagName() == ASCIIToUTF16("IFRAME"));
    644   }
    645 
    646   return (is_iframe || IsParentUnignoredOf(parent, child));
    647 }
    648 
    649 }  // namespace content
    650