| OLD | NEW |
| (Empty) |
| 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_node_serializer.h" | |
| 6 | |
| 7 #include <set> | |
| 8 | |
| 9 #include "base/string_number_conversions.h" | |
| 10 #include "base/string_util.h" | |
| 11 #include "base/utf_string_conversions.h" | |
| 12 #include "third_party/WebKit/Source/Platform/chromium/public/WebRect.h" | |
| 13 #include "third_party/WebKit/Source/Platform/chromium/public/WebSize.h" | |
| 14 #include "third_party/WebKit/Source/Platform/chromium/public/WebString.h" | |
| 15 #include "third_party/WebKit/Source/Platform/chromium/public/WebVector.h" | |
| 16 #include "third_party/WebKit/Source/WebKit/chromium/public/WebAccessibilityObjec
t.h" | |
| 17 #include "third_party/WebKit/Source/WebKit/chromium/public/WebAccessibilityRole.
h" | |
| 18 #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h" | |
| 19 #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocumentType.h" | |
| 20 #include "third_party/WebKit/Source/WebKit/chromium/public/WebElement.h" | |
| 21 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFormControlElement
.h" | |
| 22 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" | |
| 23 #include "third_party/WebKit/Source/WebKit/chromium/public/WebInputElement.h" | |
| 24 #include "third_party/WebKit/Source/WebKit/chromium/public/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::WebAccessibilityRoleDirectory: | |
| 93 return AccessibilityNodeData::ROLE_DIRECTORY; | |
| 94 case WebKit::WebAccessibilityRoleDisclosureTriangle: | |
| 95 return AccessibilityNodeData::ROLE_DISCLOSURE_TRIANGLE; | |
| 96 case WebKit::WebAccessibilityRoleDiv: | |
| 97 return AccessibilityNodeData::ROLE_DIV; | |
| 98 case WebKit::WebAccessibilityRoleDocument: | |
| 99 return AccessibilityNodeData::ROLE_DOCUMENT; | |
| 100 case WebKit::WebAccessibilityRoleDocumentArticle: | |
| 101 return AccessibilityNodeData::ROLE_ARTICLE; | |
| 102 case WebKit::WebAccessibilityRoleDocumentMath: | |
| 103 return AccessibilityNodeData::ROLE_MATH; | |
| 104 case WebKit::WebAccessibilityRoleDocumentNote: | |
| 105 return AccessibilityNodeData::ROLE_NOTE; | |
| 106 case WebKit::WebAccessibilityRoleDocumentRegion: | |
| 107 return AccessibilityNodeData::ROLE_REGION; | |
| 108 case WebKit::WebAccessibilityRoleDrawer: | |
| 109 return AccessibilityNodeData::ROLE_DRAWER; | |
| 110 case WebKit::WebAccessibilityRoleEditableText: | |
| 111 return AccessibilityNodeData::ROLE_EDITABLE_TEXT; | |
| 112 case WebKit::WebAccessibilityRoleFooter: | |
| 113 return AccessibilityNodeData::ROLE_FOOTER; | |
| 114 case WebKit::WebAccessibilityRoleForm: | |
| 115 return AccessibilityNodeData::ROLE_FORM; | |
| 116 case WebKit::WebAccessibilityRoleGrid: | |
| 117 return AccessibilityNodeData::ROLE_GRID; | |
| 118 case WebKit::WebAccessibilityRoleGroup: | |
| 119 return AccessibilityNodeData::ROLE_GROUP; | |
| 120 case WebKit::WebAccessibilityRoleGrowArea: | |
| 121 return AccessibilityNodeData::ROLE_GROW_AREA; | |
| 122 case WebKit::WebAccessibilityRoleHeading: | |
| 123 return AccessibilityNodeData::ROLE_HEADING; | |
| 124 case WebKit::WebAccessibilityRoleHelpTag: | |
| 125 return AccessibilityNodeData::ROLE_HELP_TAG; | |
| 126 case WebKit::WebAccessibilityRoleHorizontalRule: | |
| 127 return AccessibilityNodeData::ROLE_HORIZONTAL_RULE; | |
| 128 case WebKit::WebAccessibilityRoleIgnored: | |
| 129 return AccessibilityNodeData::ROLE_IGNORED; | |
| 130 case WebKit::WebAccessibilityRoleImage: | |
| 131 return AccessibilityNodeData::ROLE_IMAGE; | |
| 132 case WebKit::WebAccessibilityRoleImageMap: | |
| 133 return AccessibilityNodeData::ROLE_IMAGE_MAP; | |
| 134 case WebKit::WebAccessibilityRoleImageMapLink: | |
| 135 return AccessibilityNodeData::ROLE_IMAGE_MAP_LINK; | |
| 136 case WebKit::WebAccessibilityRoleIncrementor: | |
| 137 return AccessibilityNodeData::ROLE_INCREMENTOR; | |
| 138 case WebKit::WebAccessibilityRoleLabel: | |
| 139 return AccessibilityNodeData::ROLE_LABEL; | |
| 140 case WebKit::WebAccessibilityRoleLandmarkApplication: | |
| 141 return AccessibilityNodeData::ROLE_LANDMARK_APPLICATION; | |
| 142 case WebKit::WebAccessibilityRoleLandmarkBanner: | |
| 143 return AccessibilityNodeData::ROLE_LANDMARK_BANNER; | |
| 144 case WebKit::WebAccessibilityRoleLandmarkComplementary: | |
| 145 return AccessibilityNodeData::ROLE_LANDMARK_COMPLEMENTARY; | |
| 146 case WebKit::WebAccessibilityRoleLandmarkContentInfo: | |
| 147 return AccessibilityNodeData::ROLE_LANDMARK_CONTENTINFO; | |
| 148 case WebKit::WebAccessibilityRoleLandmarkMain: | |
| 149 return AccessibilityNodeData::ROLE_LANDMARK_MAIN; | |
| 150 case WebKit::WebAccessibilityRoleLandmarkNavigation: | |
| 151 return AccessibilityNodeData::ROLE_LANDMARK_NAVIGATION; | |
| 152 case WebKit::WebAccessibilityRoleLandmarkSearch: | |
| 153 return AccessibilityNodeData::ROLE_LANDMARK_SEARCH; | |
| 154 case WebKit::WebAccessibilityRoleLink: | |
| 155 return AccessibilityNodeData::ROLE_LINK; | |
| 156 case WebKit::WebAccessibilityRoleList: | |
| 157 return AccessibilityNodeData::ROLE_LIST; | |
| 158 case WebKit::WebAccessibilityRoleListBox: | |
| 159 return AccessibilityNodeData::ROLE_LISTBOX; | |
| 160 case WebKit::WebAccessibilityRoleListBoxOption: | |
| 161 return AccessibilityNodeData::ROLE_LISTBOX_OPTION; | |
| 162 case WebKit::WebAccessibilityRoleListItem: | |
| 163 return AccessibilityNodeData::ROLE_LIST_ITEM; | |
| 164 case WebKit::WebAccessibilityRoleListMarker: | |
| 165 return AccessibilityNodeData::ROLE_LIST_MARKER; | |
| 166 case WebKit::WebAccessibilityRoleMatte: | |
| 167 return AccessibilityNodeData::ROLE_MATTE; | |
| 168 case WebKit::WebAccessibilityRoleMenu: | |
| 169 return AccessibilityNodeData::ROLE_MENU; | |
| 170 case WebKit::WebAccessibilityRoleMenuBar: | |
| 171 return AccessibilityNodeData::ROLE_MENU_BAR; | |
| 172 case WebKit::WebAccessibilityRoleMenuButton: | |
| 173 return AccessibilityNodeData::ROLE_MENU_BUTTON; | |
| 174 case WebKit::WebAccessibilityRoleMenuItem: | |
| 175 return AccessibilityNodeData::ROLE_MENU_ITEM; | |
| 176 case WebKit::WebAccessibilityRoleMenuListOption: | |
| 177 return AccessibilityNodeData::ROLE_MENU_LIST_OPTION; | |
| 178 case WebKit::WebAccessibilityRoleMenuListPopup: | |
| 179 return AccessibilityNodeData::ROLE_MENU_LIST_POPUP; | |
| 180 case WebKit::WebAccessibilityRoleOutline: | |
| 181 return AccessibilityNodeData::ROLE_OUTLINE; | |
| 182 case WebKit::WebAccessibilityRoleParagraph: | |
| 183 return AccessibilityNodeData::ROLE_PARAGRAPH; | |
| 184 case WebKit::WebAccessibilityRolePopUpButton: | |
| 185 return AccessibilityNodeData::ROLE_POPUP_BUTTON; | |
| 186 case WebKit::WebAccessibilityRolePresentational: | |
| 187 return AccessibilityNodeData::ROLE_PRESENTATIONAL; | |
| 188 case WebKit::WebAccessibilityRoleProgressIndicator: | |
| 189 return AccessibilityNodeData::ROLE_PROGRESS_INDICATOR; | |
| 190 case WebKit::WebAccessibilityRoleRadioButton: | |
| 191 return AccessibilityNodeData::ROLE_RADIO_BUTTON; | |
| 192 case WebKit::WebAccessibilityRoleRadioGroup: | |
| 193 return AccessibilityNodeData::ROLE_RADIO_GROUP; | |
| 194 case WebKit::WebAccessibilityRoleRow: | |
| 195 return AccessibilityNodeData::ROLE_ROW; | |
| 196 case WebKit::WebAccessibilityRoleRowHeader: | |
| 197 return AccessibilityNodeData::ROLE_ROW_HEADER; | |
| 198 case WebKit::WebAccessibilityRoleRuler: | |
| 199 return AccessibilityNodeData::ROLE_RULER; | |
| 200 case WebKit::WebAccessibilityRoleRulerMarker: | |
| 201 return AccessibilityNodeData::ROLE_RULER_MARKER; | |
| 202 case WebKit::WebAccessibilityRoleScrollArea: | |
| 203 return AccessibilityNodeData::ROLE_SCROLLAREA; | |
| 204 case WebKit::WebAccessibilityRoleScrollBar: | |
| 205 return AccessibilityNodeData::ROLE_SCROLLBAR; | |
| 206 case WebKit::WebAccessibilityRoleSheet: | |
| 207 return AccessibilityNodeData::ROLE_SHEET; | |
| 208 case WebKit::WebAccessibilityRoleSlider: | |
| 209 return AccessibilityNodeData::ROLE_SLIDER; | |
| 210 case WebKit::WebAccessibilityRoleSliderThumb: | |
| 211 return AccessibilityNodeData::ROLE_SLIDER_THUMB; | |
| 212 case WebKit::WebAccessibilityRoleSpinButton: | |
| 213 return AccessibilityNodeData::ROLE_SPIN_BUTTON; | |
| 214 case WebKit::WebAccessibilityRoleSpinButtonPart: | |
| 215 return AccessibilityNodeData::ROLE_SPIN_BUTTON_PART; | |
| 216 case WebKit::WebAccessibilityRoleSplitGroup: | |
| 217 return AccessibilityNodeData::ROLE_SPLIT_GROUP; | |
| 218 case WebKit::WebAccessibilityRoleSplitter: | |
| 219 return AccessibilityNodeData::ROLE_SPLITTER; | |
| 220 case WebKit::WebAccessibilityRoleStaticText: | |
| 221 return AccessibilityNodeData::ROLE_STATIC_TEXT; | |
| 222 case WebKit::WebAccessibilityRoleSVGRoot: | |
| 223 return AccessibilityNodeData::ROLE_SVG_ROOT; | |
| 224 case WebKit::WebAccessibilityRoleSystemWide: | |
| 225 return AccessibilityNodeData::ROLE_SYSTEM_WIDE; | |
| 226 case WebKit::WebAccessibilityRoleTab: | |
| 227 return AccessibilityNodeData::ROLE_TAB; | |
| 228 case WebKit::WebAccessibilityRoleTabGroup: | |
| 229 return AccessibilityNodeData::ROLE_TAB_GROUP_UNUSED; | |
| 230 case WebKit::WebAccessibilityRoleTabList: | |
| 231 return AccessibilityNodeData::ROLE_TAB_LIST; | |
| 232 case WebKit::WebAccessibilityRoleTabPanel: | |
| 233 return AccessibilityNodeData::ROLE_TAB_PANEL; | |
| 234 case WebKit::WebAccessibilityRoleTable: | |
| 235 return AccessibilityNodeData::ROLE_TABLE; | |
| 236 case WebKit::WebAccessibilityRoleTableHeaderContainer: | |
| 237 return AccessibilityNodeData::ROLE_TABLE_HEADER_CONTAINER; | |
| 238 case WebKit::WebAccessibilityRoleTextArea: | |
| 239 return AccessibilityNodeData::ROLE_TEXTAREA; | |
| 240 case WebKit::WebAccessibilityRoleTextField: | |
| 241 return AccessibilityNodeData::ROLE_TEXT_FIELD; | |
| 242 case WebKit::WebAccessibilityRoleToggleButton: | |
| 243 return AccessibilityNodeData::ROLE_TOGGLE_BUTTON; | |
| 244 case WebKit::WebAccessibilityRoleToolbar: | |
| 245 return AccessibilityNodeData::ROLE_TOOLBAR; | |
| 246 case WebKit::WebAccessibilityRoleTreeGrid: | |
| 247 return AccessibilityNodeData::ROLE_TREE_GRID; | |
| 248 case WebKit::WebAccessibilityRoleTreeItemRole: | |
| 249 return AccessibilityNodeData::ROLE_TREE_ITEM; | |
| 250 case WebKit::WebAccessibilityRoleTreeRole: | |
| 251 return AccessibilityNodeData::ROLE_TREE; | |
| 252 case WebKit::WebAccessibilityRoleUserInterfaceTooltip: | |
| 253 return AccessibilityNodeData::ROLE_TOOLTIP; | |
| 254 case WebKit::WebAccessibilityRoleValueIndicator: | |
| 255 return AccessibilityNodeData::ROLE_VALUE_INDICATOR; | |
| 256 case WebKit::WebAccessibilityRoleWebArea: | |
| 257 return AccessibilityNodeData::ROLE_WEB_AREA; | |
| 258 case WebKit::WebAccessibilityRoleWebCoreLink: | |
| 259 return AccessibilityNodeData::ROLE_WEBCORE_LINK; | |
| 260 case WebKit::WebAccessibilityRoleWindow: | |
| 261 return AccessibilityNodeData::ROLE_WINDOW; | |
| 262 | |
| 263 default: | |
| 264 return AccessibilityNodeData::ROLE_UNKNOWN; | |
| 265 } | |
| 266 } | |
| 267 | |
| 268 // Provides a conversion between the WebAccessibilityObject state | |
| 269 // accessors and a state bitmask that can be serialized and sent to the | |
| 270 // Browser process. Rare state are sent as boolean attributes instead. | |
| 271 uint32 ConvertState(const WebAccessibilityObject& o) { | |
| 272 uint32 state = 0; | |
| 273 if (o.isChecked()) | |
| 274 state |= (1 << AccessibilityNodeData::STATE_CHECKED); | |
| 275 | |
| 276 if (o.isCollapsed()) | |
| 277 state |= (1 << AccessibilityNodeData::STATE_COLLAPSED); | |
| 278 | |
| 279 if (o.canSetFocusAttribute()) | |
| 280 state |= (1 << AccessibilityNodeData::STATE_FOCUSABLE); | |
| 281 | |
| 282 if (o.isFocused()) | |
| 283 state |= (1 << AccessibilityNodeData::STATE_FOCUSED); | |
| 284 | |
| 285 if (o.roleValue() == WebKit::WebAccessibilityRolePopUpButton || | |
| 286 o.ariaHasPopup()) { | |
| 287 state |= (1 << AccessibilityNodeData::STATE_HASPOPUP); | |
| 288 if (!o.isCollapsed()) | |
| 289 state |= (1 << AccessibilityNodeData::STATE_EXPANDED); | |
| 290 } | |
| 291 | |
| 292 if (o.isHovered()) | |
| 293 state |= (1 << AccessibilityNodeData::STATE_HOTTRACKED); | |
| 294 | |
| 295 if (o.isIndeterminate()) | |
| 296 state |= (1 << AccessibilityNodeData::STATE_INDETERMINATE); | |
| 297 | |
| 298 if (!o.isVisible()) | |
| 299 state |= (1 << AccessibilityNodeData::STATE_INVISIBLE); | |
| 300 | |
| 301 if (o.isLinked()) | |
| 302 state |= (1 << AccessibilityNodeData::STATE_LINKED); | |
| 303 | |
| 304 if (o.isMultiSelectable()) | |
| 305 state |= (1 << AccessibilityNodeData::STATE_MULTISELECTABLE); | |
| 306 | |
| 307 if (o.isOffScreen()) | |
| 308 state |= (1 << AccessibilityNodeData::STATE_OFFSCREEN); | |
| 309 | |
| 310 if (o.isPressed()) | |
| 311 state |= (1 << AccessibilityNodeData::STATE_PRESSED); | |
| 312 | |
| 313 if (o.isPasswordField()) | |
| 314 state |= (1 << AccessibilityNodeData::STATE_PROTECTED); | |
| 315 | |
| 316 if (o.isReadOnly()) | |
| 317 state |= (1 << AccessibilityNodeData::STATE_READONLY); | |
| 318 | |
| 319 if (o.isRequired()) | |
| 320 state |= (1 << AccessibilityNodeData::STATE_REQUIRED); | |
| 321 | |
| 322 if (o.canSetSelectedAttribute()) | |
| 323 state |= (1 << AccessibilityNodeData::STATE_SELECTABLE); | |
| 324 | |
| 325 if (o.isSelected()) | |
| 326 state |= (1 << AccessibilityNodeData::STATE_SELECTED); | |
| 327 | |
| 328 if (o.isVisited()) | |
| 329 state |= (1 << AccessibilityNodeData::STATE_TRAVERSED); | |
| 330 | |
| 331 if (!o.isEnabled()) | |
| 332 state |= (1 << AccessibilityNodeData::STATE_UNAVAILABLE); | |
| 333 | |
| 334 if (o.isVertical()) | |
| 335 state |= (1 << AccessibilityNodeData::STATE_VERTICAL); | |
| 336 | |
| 337 if (o.isVisited()) | |
| 338 state |= (1 << AccessibilityNodeData::STATE_VISITED); | |
| 339 | |
| 340 return state; | |
| 341 } | |
| 342 | |
| 343 } // Anonymous namespace | |
| 344 | |
| 345 void SerializeAccessibilityNode( | |
| 346 const WebAccessibilityObject& src, | |
| 347 AccessibilityNodeData* dst, | |
| 348 bool include_children) { | |
| 349 dst->name = src.title(); | |
| 350 dst->role = ConvertRole(src.roleValue()); | |
| 351 dst->state = ConvertState(src); | |
| 352 dst->location = src.boundingBoxRect(); | |
| 353 dst->id = src.axID(); | |
| 354 | |
| 355 if (src.valueDescription().length()) | |
| 356 dst->value = src.valueDescription(); | |
| 357 else | |
| 358 dst->value = src.stringValue(); | |
| 359 | |
| 360 if (dst->role == AccessibilityNodeData::ROLE_COLOR_WELL) { | |
| 361 int r, g, b; | |
| 362 src.colorValue(r, g, b); | |
| 363 dst->int_attributes[dst->ATTR_COLOR_VALUE_RED] = r; | |
| 364 dst->int_attributes[dst->ATTR_COLOR_VALUE_GREEN] = g; | |
| 365 dst->int_attributes[dst->ATTR_COLOR_VALUE_BLUE] = b; | |
| 366 } | |
| 367 | |
| 368 if (src.accessKey().length()) | |
| 369 dst->string_attributes[dst->ATTR_ACCESS_KEY] = src.accessKey(); | |
| 370 if (src.actionVerb().length()) | |
| 371 dst->string_attributes[dst->ATTR_ACTION] = src.actionVerb(); | |
| 372 if (src.isAriaReadOnly()) | |
| 373 dst->bool_attributes[dst->ATTR_ARIA_READONLY] = true; | |
| 374 if (src.isButtonStateMixed()) | |
| 375 dst->bool_attributes[dst->ATTR_BUTTON_MIXED] = true; | |
| 376 if (src.canSetValueAttribute()) | |
| 377 dst->bool_attributes[dst->ATTR_CAN_SET_VALUE] = true; | |
| 378 if (src.accessibilityDescription().length()) | |
| 379 dst->string_attributes[dst->ATTR_DESCRIPTION] = | |
| 380 src.accessibilityDescription(); | |
| 381 if (src.hasComputedStyle()) | |
| 382 dst->string_attributes[dst->ATTR_DISPLAY] = src.computedStyleDisplay(); | |
| 383 if (src.helpText().length()) | |
| 384 dst->string_attributes[dst->ATTR_HELP] = src.helpText(); | |
| 385 if (src.keyboardShortcut().length()) | |
| 386 dst->string_attributes[dst->ATTR_SHORTCUT] = src.keyboardShortcut(); | |
| 387 if (!src.titleUIElement().isDetached()) { | |
| 388 dst->int_attributes[dst->ATTR_TITLE_UI_ELEMENT] = | |
| 389 src.titleUIElement().axID(); | |
| 390 } | |
| 391 if (!src.url().isEmpty()) | |
| 392 dst->string_attributes[dst->ATTR_URL] = src.url().spec().utf16(); | |
| 393 | |
| 394 if (dst->role == dst->ROLE_HEADING) | |
| 395 dst->int_attributes[dst->ATTR_HIERARCHICAL_LEVEL] = src.headingLevel(); | |
| 396 else if ((dst->role == dst->ROLE_TREE_ITEM || dst->role == dst->ROLE_ROW) && | |
| 397 src.hierarchicalLevel() > 0) { | |
| 398 dst->int_attributes[dst->ATTR_HIERARCHICAL_LEVEL] = src.hierarchicalLevel(); | |
| 399 } | |
| 400 | |
| 401 if (dst->role == dst->ROLE_SLIDER) | |
| 402 include_children = false; | |
| 403 | |
| 404 // Treat the active list box item as focused. | |
| 405 if (dst->role == dst->ROLE_LISTBOX_OPTION && src.isSelectedOptionActive()) | |
| 406 dst->state |= (1 << AccessibilityNodeData::STATE_FOCUSED); | |
| 407 | |
| 408 if (src.canvasHasFallbackContent()) | |
| 409 dst->role = AccessibilityNodeData::ROLE_CANVAS_WITH_FALLBACK_CONTENT; | |
| 410 | |
| 411 WebNode node = src.node(); | |
| 412 bool is_iframe = false; | |
| 413 | |
| 414 if (!node.isNull() && node.isElementNode()) { | |
| 415 WebElement element = node.to<WebElement>(); | |
| 416 is_iframe = (element.tagName() == ASCIIToUTF16("IFRAME")); | |
| 417 | |
| 418 if (LowerCaseEqualsASCII(element.getAttribute("aria-expanded"), "true")) | |
| 419 dst->state |= (1 << AccessibilityNodeData::STATE_EXPANDED); | |
| 420 | |
| 421 // TODO(ctguil): The tagName in WebKit is lower cased but | |
| 422 // HTMLElement::nodeName calls localNameUpper. Consider adding | |
| 423 // a WebElement method that returns the original lower cased tagName. | |
| 424 dst->string_attributes[dst->ATTR_HTML_TAG] = | |
| 425 StringToLowerASCII(string16(element.tagName())); | |
| 426 for (unsigned i = 0; i < element.attributeCount(); ++i) { | |
| 427 string16 name = StringToLowerASCII(string16( | |
| 428 element.attributeLocalName(i))); | |
| 429 string16 value = element.attributeValue(i); | |
| 430 dst->html_attributes.push_back( | |
| 431 std::pair<string16, string16>(name, value)); | |
| 432 } | |
| 433 | |
| 434 if (dst->role == dst->ROLE_EDITABLE_TEXT || | |
| 435 dst->role == dst->ROLE_TEXTAREA || | |
| 436 dst->role == dst->ROLE_TEXT_FIELD) { | |
| 437 // Jaws gets confused by children of text fields, so we ignore them. | |
| 438 include_children = false; | |
| 439 | |
| 440 dst->int_attributes[dst->ATTR_TEXT_SEL_START] = src.selectionStart(); | |
| 441 dst->int_attributes[dst->ATTR_TEXT_SEL_END] = src.selectionEnd(); | |
| 442 | |
| 443 WebVector<int> src_line_breaks; | |
| 444 src.lineBreaks(src_line_breaks); | |
| 445 dst->line_breaks.reserve(src_line_breaks.size()); | |
| 446 for (size_t i = 0; i < src_line_breaks.size(); ++i) | |
| 447 dst->line_breaks.push_back(src_line_breaks[i]); | |
| 448 } | |
| 449 | |
| 450 // ARIA role. | |
| 451 if (element.hasAttribute("role")) { | |
| 452 dst->string_attributes[dst->ATTR_ROLE] = element.getAttribute("role"); | |
| 453 } | |
| 454 | |
| 455 // Live region attributes | |
| 456 if (element.hasAttribute("aria-atomic")) { | |
| 457 dst->bool_attributes[dst->ATTR_LIVE_ATOMIC] = | |
| 458 LowerCaseEqualsASCII(element.getAttribute("aria-atomic"), "true"); | |
| 459 } | |
| 460 if (element.hasAttribute("aria-busy")) { | |
| 461 dst->bool_attributes[dst->ATTR_LIVE_BUSY] = | |
| 462 LowerCaseEqualsASCII(element.getAttribute("aria-busy"), "true"); | |
| 463 } | |
| 464 if (element.hasAttribute("aria-live")) { | |
| 465 dst->string_attributes[dst->ATTR_LIVE_STATUS] = | |
| 466 element.getAttribute("aria-live"); | |
| 467 } | |
| 468 if (element.hasAttribute("aria-relevant")) { | |
| 469 dst->string_attributes[dst->ATTR_LIVE_RELEVANT] = | |
| 470 element.getAttribute("aria-relevant"); | |
| 471 } | |
| 472 } | |
| 473 | |
| 474 // Walk up the parent chain to set live region attributes of containers | |
| 475 | |
| 476 WebAccessibilityObject container_accessible = src; | |
| 477 while (!container_accessible.isDetached()) { | |
| 478 WebNode container_node = container_accessible.node(); | |
| 479 if (!container_node.isNull() && container_node.isElementNode()) { | |
| 480 WebElement container_elem = | |
| 481 container_node.to<WebElement>(); | |
| 482 if (container_elem.hasAttribute("aria-atomic") && | |
| 483 dst->bool_attributes.find(dst->ATTR_CONTAINER_LIVE_ATOMIC) == | |
| 484 dst->bool_attributes.end()) { | |
| 485 dst->bool_attributes[dst->ATTR_CONTAINER_LIVE_ATOMIC] = | |
| 486 LowerCaseEqualsASCII(container_elem.getAttribute("aria-atomic"), | |
| 487 "true"); | |
| 488 } | |
| 489 if (container_elem.hasAttribute("aria-busy") && | |
| 490 dst->bool_attributes.find(dst->ATTR_CONTAINER_LIVE_BUSY) == | |
| 491 dst->bool_attributes.end()) { | |
| 492 dst->bool_attributes[dst->ATTR_CONTAINER_LIVE_BUSY] = | |
| 493 LowerCaseEqualsASCII(container_elem.getAttribute("aria-busy"), | |
| 494 "true"); | |
| 495 } | |
| 496 if (container_elem.hasAttribute("aria-live") && | |
| 497 dst->string_attributes.find(dst->ATTR_CONTAINER_LIVE_STATUS) == | |
| 498 dst->string_attributes.end()) { | |
| 499 dst->string_attributes[dst->ATTR_CONTAINER_LIVE_STATUS] = | |
| 500 container_elem.getAttribute("aria-live"); | |
| 501 } | |
| 502 if (container_elem.hasAttribute("aria-relevant") && | |
| 503 dst->string_attributes.find(dst->ATTR_CONTAINER_LIVE_RELEVANT) == | |
| 504 dst->string_attributes.end()) { | |
| 505 dst->string_attributes[dst->ATTR_CONTAINER_LIVE_RELEVANT] = | |
| 506 container_elem.getAttribute("aria-relevant"); | |
| 507 } | |
| 508 } | |
| 509 container_accessible = container_accessible.parentObject(); | |
| 510 } | |
| 511 | |
| 512 if (dst->role == dst->ROLE_PROGRESS_INDICATOR || | |
| 513 dst->role == dst->ROLE_SCROLLBAR || | |
| 514 dst->role == dst->ROLE_SLIDER || | |
| 515 dst->role == dst->ROLE_SPIN_BUTTON) { | |
| 516 dst->float_attributes[dst->ATTR_VALUE_FOR_RANGE] = src.valueForRange(); | |
| 517 dst->float_attributes[dst->ATTR_MAX_VALUE_FOR_RANGE] = | |
| 518 src.maxValueForRange(); | |
| 519 dst->float_attributes[dst->ATTR_MIN_VALUE_FOR_RANGE] = | |
| 520 src.minValueForRange(); | |
| 521 } | |
| 522 | |
| 523 if (dst->role == dst->ROLE_DOCUMENT || | |
| 524 dst->role == dst->ROLE_WEB_AREA) { | |
| 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 for (int i = 0; i < column_count * row_count; ++i) { | |
| 561 WebAccessibilityObject cell = src.cellForColumnAndRow( | |
| 562 i % column_count, i / column_count); | |
| 563 int cell_id = -1; | |
| 564 if (!cell.isDetached()) { | |
| 565 cell_id = cell.axID(); | |
| 566 if (unique_cell_id_set.find(cell_id) == unique_cell_id_set.end()) { | |
| 567 unique_cell_id_set.insert(cell_id); | |
| 568 dst->unique_cell_ids.push_back(cell_id); | |
| 569 } | |
| 570 } | |
| 571 dst->cell_ids.push_back(cell_id); | |
| 572 } | |
| 573 } | |
| 574 } | |
| 575 | |
| 576 if (dst->role == dst->ROLE_CELL || | |
| 577 dst->role == dst->ROLE_ROW_HEADER || | |
| 578 dst->role == dst->ROLE_COLUMN_HEADER) { | |
| 579 dst->int_attributes[dst->ATTR_TABLE_CELL_COLUMN_INDEX] = | |
| 580 src.cellColumnIndex(); | |
| 581 dst->int_attributes[dst->ATTR_TABLE_CELL_COLUMN_SPAN] = | |
| 582 src.cellColumnSpan(); | |
| 583 dst->int_attributes[dst->ATTR_TABLE_CELL_ROW_INDEX] = src.cellRowIndex(); | |
| 584 dst->int_attributes[dst->ATTR_TABLE_CELL_ROW_SPAN] = src.cellRowSpan(); | |
| 585 } | |
| 586 | |
| 587 if (include_children) { | |
| 588 // Recursively create children. | |
| 589 int child_count = src.childCount(); | |
| 590 std::set<int32> child_ids; | |
| 591 for (int i = 0; i < child_count; ++i) { | |
| 592 WebAccessibilityObject child = src.childAt(i); | |
| 593 int32 child_id = child.axID(); | |
| 594 | |
| 595 // The child may be invalid due to issues in webkit accessibility code. | |
| 596 // Don't add children that are invalid thus preventing a crash. | |
| 597 // https://bugs.webkit.org/show_bug.cgi?id=44149 | |
| 598 // TODO(ctguil): We may want to remove this check as webkit stabilizes. | |
| 599 if (child.isDetached()) | |
| 600 continue; | |
| 601 | |
| 602 // Children may duplicated in the webkit accessibility tree. Only add a | |
| 603 // child once for the web accessibility tree. | |
| 604 // https://bugs.webkit.org/show_bug.cgi?id=58930 | |
| 605 if (child_ids.find(child_id) != child_ids.end()) | |
| 606 continue; | |
| 607 child_ids.insert(child_id); | |
| 608 | |
| 609 // Some nodes appear in the tree in more than one place: for example, | |
| 610 // a cell in a table appears as a child of both a row and a column. | |
| 611 // Only recursively add child nodes that have this node as its | |
| 612 // unignored parent. For child nodes that are actually parented to | |
| 613 // somethinng else, store only the ID. | |
| 614 // | |
| 615 // As an exception, also add children of an iframe element. | |
| 616 // https://bugs.webkit.org/show_bug.cgi?id=57066 | |
| 617 if (is_iframe || IsParentUnignoredOf(src, child)) { | |
| 618 dst->children.push_back(AccessibilityNodeData()); | |
| 619 SerializeAccessibilityNode(child, | |
| 620 &dst->children.back(), | |
| 621 include_children); | |
| 622 } else { | |
| 623 dst->indirect_child_ids.push_back(child_id); | |
| 624 } | |
| 625 } | |
| 626 } | |
| 627 } | |
| 628 | |
| 629 } // namespace content | |
| OLD | NEW |