1 // Copyright 2014 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/shell/renderer/test_runner/web_ax_object_proxy.h" 6 7 #include "base/strings/stringprintf.h" 8 #include "gin/handle.h" 9 #include "third_party/WebKit/public/platform/WebPoint.h" 10 #include "third_party/WebKit/public/platform/WebRect.h" 11 #include "third_party/WebKit/public/platform/WebString.h" 12 #include "third_party/WebKit/public/web/WebFrame.h" 13 #include "third_party/WebKit/public/web/WebKit.h" 14 15 namespace content { 16 17 namespace { 18 19 // Map role value to string, matching Safari/Mac platform implementation to 20 // avoid rebaselining layout tests. 21 std::string RoleToString(blink::WebAXRole role) 22 { 23 std::string result = "AXRole: AX"; 24 switch (role) { 25 case blink::WebAXRoleAlertDialog: 26 return result.append("AlertDialog"); 27 case blink::WebAXRoleAlert: 28 return result.append("Alert"); 29 case blink::WebAXRoleAnnotation: 30 return result.append("Annotation"); 31 case blink::WebAXRoleApplication: 32 return result.append("Application"); 33 case blink::WebAXRoleArticle: 34 return result.append("Article"); 35 case blink::WebAXRoleBanner: 36 return result.append("Banner"); 37 case blink::WebAXRoleBrowser: 38 return result.append("Browser"); 39 case blink::WebAXRoleBusyIndicator: 40 return result.append("BusyIndicator"); 41 case blink::WebAXRoleButton: 42 return result.append("Button"); 43 case blink::WebAXRoleCanvas: 44 return result.append("Canvas"); 45 case blink::WebAXRoleCell: 46 return result.append("Cell"); 47 case blink::WebAXRoleCheckBox: 48 return result.append("CheckBox"); 49 case blink::WebAXRoleColorWell: 50 return result.append("ColorWell"); 51 case blink::WebAXRoleColumnHeader: 52 return result.append("ColumnHeader"); 53 case blink::WebAXRoleColumn: 54 return result.append("Column"); 55 case blink::WebAXRoleComboBox: 56 return result.append("ComboBox"); 57 case blink::WebAXRoleComplementary: 58 return result.append("Complementary"); 59 case blink::WebAXRoleContentInfo: 60 return result.append("ContentInfo"); 61 case blink::WebAXRoleDefinition: 62 return result.append("Definition"); 63 case blink::WebAXRoleDescriptionListDetail: 64 return result.append("DescriptionListDetail"); 65 case blink::WebAXRoleDescriptionListTerm: 66 return result.append("DescriptionListTerm"); 67 case blink::WebAXRoleDialog: 68 return result.append("Dialog"); 69 case blink::WebAXRoleDirectory: 70 return result.append("Directory"); 71 case blink::WebAXRoleDisclosureTriangle: 72 return result.append("DisclosureTriangle"); 73 case blink::WebAXRoleDiv: 74 return result.append("Div"); 75 case blink::WebAXRoleDocument: 76 return result.append("Document"); 77 case blink::WebAXRoleDrawer: 78 return result.append("Drawer"); 79 case blink::WebAXRoleEditableText: 80 return result.append("EditableText"); 81 case blink::WebAXRoleFooter: 82 return result.append("Footer"); 83 case blink::WebAXRoleForm: 84 return result.append("Form"); 85 case blink::WebAXRoleGrid: 86 return result.append("Grid"); 87 case blink::WebAXRoleGroup: 88 return result.append("Group"); 89 case blink::WebAXRoleGrowArea: 90 return result.append("GrowArea"); 91 case blink::WebAXRoleHeading: 92 return result.append("Heading"); 93 case blink::WebAXRoleHelpTag: 94 return result.append("HelpTag"); 95 case blink::WebAXRoleHorizontalRule: 96 return result.append("HorizontalRule"); 97 case blink::WebAXRoleIgnored: 98 return result.append("Ignored"); 99 case blink::WebAXRoleImageMapLink: 100 return result.append("ImageMapLink"); 101 case blink::WebAXRoleImageMap: 102 return result.append("ImageMap"); 103 case blink::WebAXRoleImage: 104 return result.append("Image"); 105 case blink::WebAXRoleIncrementor: 106 return result.append("Incrementor"); 107 case blink::WebAXRoleInlineTextBox: 108 return result.append("InlineTextBox"); 109 case blink::WebAXRoleLabel: 110 return result.append("Label"); 111 case blink::WebAXRoleLegend: 112 return result.append("Legend"); 113 case blink::WebAXRoleLink: 114 return result.append("Link"); 115 case blink::WebAXRoleListBoxOption: 116 return result.append("ListBoxOption"); 117 case blink::WebAXRoleListBox: 118 return result.append("ListBox"); 119 case blink::WebAXRoleListItem: 120 return result.append("ListItem"); 121 case blink::WebAXRoleListMarker: 122 return result.append("ListMarker"); 123 case blink::WebAXRoleList: 124 return result.append("List"); 125 case blink::WebAXRoleLog: 126 return result.append("Log"); 127 case blink::WebAXRoleMain: 128 return result.append("Main"); 129 case blink::WebAXRoleMarquee: 130 return result.append("Marquee"); 131 case blink::WebAXRoleMathElement: 132 return result.append("MathElement"); 133 case blink::WebAXRoleMath: 134 return result.append("Math"); 135 case blink::WebAXRoleMatte: 136 return result.append("Matte"); 137 case blink::WebAXRoleMenuBar: 138 return result.append("MenuBar"); 139 case blink::WebAXRoleMenuButton: 140 return result.append("MenuButton"); 141 case blink::WebAXRoleMenuItem: 142 return result.append("MenuItem"); 143 case blink::WebAXRoleMenuListOption: 144 return result.append("MenuListOption"); 145 case blink::WebAXRoleMenuListPopup: 146 return result.append("MenuListPopup"); 147 case blink::WebAXRoleMenu: 148 return result.append("Menu"); 149 case blink::WebAXRoleNavigation: 150 return result.append("Navigation"); 151 case blink::WebAXRoleNote: 152 return result.append("Note"); 153 case blink::WebAXRoleOutline: 154 return result.append("Outline"); 155 case blink::WebAXRoleParagraph: 156 return result.append("Paragraph"); 157 case blink::WebAXRolePopUpButton: 158 return result.append("PopUpButton"); 159 case blink::WebAXRolePresentational: 160 return result.append("Presentational"); 161 case blink::WebAXRoleProgressIndicator: 162 return result.append("ProgressIndicator"); 163 case blink::WebAXRoleRadioButton: 164 return result.append("RadioButton"); 165 case blink::WebAXRoleRadioGroup: 166 return result.append("RadioGroup"); 167 case blink::WebAXRoleRegion: 168 return result.append("Region"); 169 case blink::WebAXRoleRootWebArea: 170 return result.append("RootWebArea"); 171 case blink::WebAXRoleRowHeader: 172 return result.append("RowHeader"); 173 case blink::WebAXRoleRow: 174 return result.append("Row"); 175 case blink::WebAXRoleRulerMarker: 176 return result.append("RulerMarker"); 177 case blink::WebAXRoleRuler: 178 return result.append("Ruler"); 179 case blink::WebAXRoleSVGRoot: 180 return result.append("SVGRoot"); 181 case blink::WebAXRoleScrollArea: 182 return result.append("ScrollArea"); 183 case blink::WebAXRoleScrollBar: 184 return result.append("ScrollBar"); 185 case blink::WebAXRoleSeamlessWebArea: 186 return result.append("SeamlessWebArea"); 187 case blink::WebAXRoleSearch: 188 return result.append("Search"); 189 case blink::WebAXRoleSheet: 190 return result.append("Sheet"); 191 case blink::WebAXRoleSlider: 192 return result.append("Slider"); 193 case blink::WebAXRoleSliderThumb: 194 return result.append("SliderThumb"); 195 case blink::WebAXRoleSpinButtonPart: 196 return result.append("SpinButtonPart"); 197 case blink::WebAXRoleSpinButton: 198 return result.append("SpinButton"); 199 case blink::WebAXRoleSplitGroup: 200 return result.append("SplitGroup"); 201 case blink::WebAXRoleSplitter: 202 return result.append("Splitter"); 203 case blink::WebAXRoleStaticText: 204 return result.append("StaticText"); 205 case blink::WebAXRoleStatus: 206 return result.append("Status"); 207 case blink::WebAXRoleSystemWide: 208 return result.append("SystemWide"); 209 case blink::WebAXRoleTabGroup: 210 return result.append("TabGroup"); 211 case blink::WebAXRoleTabList: 212 return result.append("TabList"); 213 case blink::WebAXRoleTabPanel: 214 return result.append("TabPanel"); 215 case blink::WebAXRoleTab: 216 return result.append("Tab"); 217 case blink::WebAXRoleTableHeaderContainer: 218 return result.append("TableHeaderContainer"); 219 case blink::WebAXRoleTable: 220 return result.append("Table"); 221 case blink::WebAXRoleTextArea: 222 return result.append("TextArea"); 223 case blink::WebAXRoleTextField: 224 return result.append("TextField"); 225 case blink::WebAXRoleTimer: 226 return result.append("Timer"); 227 case blink::WebAXRoleToggleButton: 228 return result.append("ToggleButton"); 229 case blink::WebAXRoleToolbar: 230 return result.append("Toolbar"); 231 case blink::WebAXRoleTreeGrid: 232 return result.append("TreeGrid"); 233 case blink::WebAXRoleTreeItem: 234 return result.append("TreeItem"); 235 case blink::WebAXRoleTree: 236 return result.append("Tree"); 237 case blink::WebAXRoleUnknown: 238 return result.append("Unknown"); 239 case blink::WebAXRoleUserInterfaceTooltip: 240 return result.append("UserInterfaceTooltip"); 241 case blink::WebAXRoleValueIndicator: 242 return result.append("ValueIndicator"); 243 case blink::WebAXRoleWebArea: 244 return result.append("WebArea"); 245 case blink::WebAXRoleWindow: 246 return result.append("Window"); 247 default: 248 return result.append("Unknown"); 249 } 250 } 251 252 std::string GetDescription(const blink::WebAXObject& object) { 253 std::string description = object.accessibilityDescription().utf8(); 254 return description.insert(0, "AXDescription: "); 255 } 256 257 std::string GetHelpText(const blink::WebAXObject& object) { 258 std::string help_text = object.helpText().utf8(); 259 return help_text.insert(0, "AXHelp: "); 260 } 261 262 std::string GetStringValue(const blink::WebAXObject& object) { 263 std::string value; 264 if (object.role() == blink::WebAXRoleColorWell) { 265 int r, g, b; 266 object.colorValue(r, g, b); 267 value = base::StringPrintf("rgb %7.5f %7.5f %7.5f 1", 268 r / 255., g / 255., b / 255.); 269 } else { 270 value = object.stringValue().utf8(); 271 } 272 return value.insert(0, "AXValue: "); 273 } 274 275 std::string GetRole(const blink::WebAXObject& object) { 276 std::string role_string = RoleToString(object.role()); 277 278 // Special-case canvas with fallback content because Chromium wants to treat 279 // this as essentially a separate role that it can map differently depending 280 // on the platform. 281 if (object.role() == blink::WebAXRoleCanvas && 282 object.canvasHasFallbackContent()) { 283 role_string += "WithFallbackContent"; 284 } 285 286 return role_string; 287 } 288 289 std::string GetTitle(const blink::WebAXObject& object) { 290 std::string title = object.title().utf8(); 291 return title.insert(0, "AXTitle: "); 292 } 293 294 std::string GetOrientation(const blink::WebAXObject& object) { 295 if (object.isVertical()) 296 return "AXOrientation: AXVerticalOrientation"; 297 298 return "AXOrientation: AXHorizontalOrientation"; 299 } 300 301 std::string GetValueDescription(const blink::WebAXObject& object) { 302 std::string value_description = object.valueDescription().utf8(); 303 return value_description.insert(0, "AXValueDescription: "); 304 } 305 306 std::string GetAttributes(const blink::WebAXObject& object) { 307 // FIXME: Concatenate all attributes of the AXObject. 308 std::string attributes(GetTitle(object)); 309 attributes.append("\n"); 310 attributes.append(GetRole(object)); 311 attributes.append("\n"); 312 attributes.append(GetDescription(object)); 313 return attributes; 314 } 315 316 blink::WebRect BoundsForCharacter(const blink::WebAXObject& object, 317 int characterIndex) { 318 DCHECK_EQ(object.role(), blink::WebAXRoleStaticText); 319 int end = 0; 320 for (unsigned i = 0; i < object.childCount(); i++) { 321 blink::WebAXObject inline_text_box = object.childAt(i); 322 DCHECK_EQ(inline_text_box.role(), blink::WebAXRoleInlineTextBox); 323 int start = end; 324 end += inline_text_box.stringValue().length(); 325 if (characterIndex < start || characterIndex >= end) 326 continue; 327 blink::WebRect inline_text_box_rect = inline_text_box.boundingBoxRect(); 328 int localIndex = characterIndex - start; 329 blink::WebVector<int> character_offsets; 330 inline_text_box.characterOffsets(character_offsets); 331 DCHECK(character_offsets.size() > 0 && 332 character_offsets.size() == inline_text_box.stringValue().length()); 333 switch (inline_text_box.textDirection()) { 334 case blink::WebAXTextDirectionLR: { 335 if (localIndex) { 336 int left = inline_text_box_rect.x + character_offsets[localIndex - 1]; 337 int width = character_offsets[localIndex] - 338 character_offsets[localIndex - 1]; 339 return blink::WebRect(left, inline_text_box_rect.y, 340 width, inline_text_box_rect.height); 341 } 342 return blink::WebRect( 343 inline_text_box_rect.x, inline_text_box_rect.y, 344 character_offsets[0], inline_text_box_rect.height); 345 } 346 case blink::WebAXTextDirectionRL: { 347 int right = inline_text_box_rect.x + inline_text_box_rect.width; 348 349 if (localIndex) { 350 int left = right - character_offsets[localIndex]; 351 int width = character_offsets[localIndex] - 352 character_offsets[localIndex - 1]; 353 return blink::WebRect(left, inline_text_box_rect.y, 354 width, inline_text_box_rect.height); 355 } 356 int left = right - character_offsets[0]; 357 return blink::WebRect( 358 left, inline_text_box_rect.y, 359 character_offsets[0], inline_text_box_rect.height); 360 } 361 case blink::WebAXTextDirectionTB: { 362 if (localIndex) { 363 int top = inline_text_box_rect.y + character_offsets[localIndex - 1]; 364 int height = character_offsets[localIndex] - 365 character_offsets[localIndex - 1]; 366 return blink::WebRect(inline_text_box_rect.x, top, 367 inline_text_box_rect.width, height); 368 } 369 return blink::WebRect(inline_text_box_rect.x, inline_text_box_rect.y, 370 inline_text_box_rect.width, character_offsets[0]); 371 } 372 case blink::WebAXTextDirectionBT: { 373 int bottom = inline_text_box_rect.y + inline_text_box_rect.height; 374 375 if (localIndex) { 376 int top = bottom - character_offsets[localIndex]; 377 int height = character_offsets[localIndex] - 378 character_offsets[localIndex - 1]; 379 return blink::WebRect(inline_text_box_rect.x, top, 380 inline_text_box_rect.width, height); 381 } 382 int top = bottom - character_offsets[0]; 383 return blink::WebRect(inline_text_box_rect.x, top, 384 inline_text_box_rect.width, character_offsets[0]); 385 } 386 } 387 } 388 389 DCHECK(false); 390 return blink::WebRect(); 391 } 392 393 void GetBoundariesForOneWord(const blink::WebAXObject& object, 394 int character_index, 395 int& word_start, 396 int& word_end) { 397 int end = 0; 398 for (unsigned i = 0; i < object.childCount(); i++) { 399 blink::WebAXObject inline_text_box = object.childAt(i); 400 DCHECK_EQ(inline_text_box.role(), blink::WebAXRoleInlineTextBox); 401 int start = end; 402 end += inline_text_box.stringValue().length(); 403 if (end <= character_index) 404 continue; 405 int localIndex = character_index - start; 406 407 blink::WebVector<int> starts; 408 blink::WebVector<int> ends; 409 inline_text_box.wordBoundaries(starts, ends); 410 size_t word_count = starts.size(); 411 DCHECK_EQ(ends.size(), word_count); 412 413 // If there are no words, use the InlineTextBox boundaries. 414 if (!word_count) { 415 word_start = start; 416 word_end = end; 417 return; 418 } 419 420 // Look for a character within any word other than the last. 421 for (size_t j = 0; j < word_count - 1; j++) { 422 if (localIndex <= ends[j]) { 423 word_start = start + starts[j]; 424 word_end = start + ends[j]; 425 return; 426 } 427 } 428 429 // Return the last word by default. 430 word_start = start + starts[word_count - 1]; 431 word_end = start + ends[word_count - 1]; 432 return; 433 } 434 } 435 436 // Collects attributes into a string, delimited by dashes. Used by all methods 437 // that output lists of attributes: attributesOfLinkedUIElementsCallback, 438 // AttributesOfChildrenCallback, etc. 439 class AttributesCollector { 440 public: 441 AttributesCollector() {} 442 ~AttributesCollector() {} 443 444 void CollectAttributes(const blink::WebAXObject& object) { 445 attributes_.append("\n------------\n"); 446 attributes_.append(GetAttributes(object)); 447 } 448 449 std::string attributes() const { return attributes_; } 450 451 private: 452 std::string attributes_; 453 454 DISALLOW_COPY_AND_ASSIGN(AttributesCollector); 455 }; 456 457 } // namespace 458 459 gin::WrapperInfo WebAXObjectProxy::kWrapperInfo = { 460 gin::kEmbedderNativeGin}; 461 462 WebAXObjectProxy::WebAXObjectProxy(const blink::WebAXObject& object, 463 WebAXObjectProxy::Factory* factory) 464 : accessibility_object_(object), 465 factory_(factory) { 466 } 467 468 WebAXObjectProxy::~WebAXObjectProxy() {} 469 470 gin::ObjectTemplateBuilder 471 WebAXObjectProxy::GetObjectTemplateBuilder(v8::Isolate* isolate) { 472 return gin::Wrappable<WebAXObjectProxy>::GetObjectTemplateBuilder(isolate) 473 .SetProperty("role", &WebAXObjectProxy::Role) 474 .SetProperty("title", &WebAXObjectProxy::Title) 475 .SetProperty("description", &WebAXObjectProxy::Description) 476 .SetProperty("helpText", &WebAXObjectProxy::HelpText) 477 .SetProperty("stringValue", &WebAXObjectProxy::StringValue) 478 .SetProperty("x", &WebAXObjectProxy::X) 479 .SetProperty("y", &WebAXObjectProxy::Y) 480 .SetProperty("width", &WebAXObjectProxy::Width) 481 .SetProperty("height", &WebAXObjectProxy::Height) 482 .SetProperty("intValue", &WebAXObjectProxy::IntValue) 483 .SetProperty("minValue", &WebAXObjectProxy::MinValue) 484 .SetProperty("maxValue", &WebAXObjectProxy::MaxValue) 485 .SetProperty("valueDescription", &WebAXObjectProxy::ValueDescription) 486 .SetProperty("childrenCount", &WebAXObjectProxy::ChildrenCount) 487 .SetProperty("insertionPointLineNumber", 488 &WebAXObjectProxy::InsertionPointLineNumber) 489 .SetProperty("selectedTextRange", &WebAXObjectProxy::SelectedTextRange) 490 .SetProperty("isEnabled", &WebAXObjectProxy::IsEnabled) 491 .SetProperty("isRequired", &WebAXObjectProxy::IsRequired) 492 .SetProperty("isFocused", &WebAXObjectProxy::IsFocused) 493 .SetProperty("isFocusable", &WebAXObjectProxy::IsFocusable) 494 .SetProperty("isSelected", &WebAXObjectProxy::IsSelected) 495 .SetProperty("isSelectable", &WebAXObjectProxy::IsSelectable) 496 .SetProperty("isMultiSelectable", &WebAXObjectProxy::IsMultiSelectable) 497 .SetProperty("isSelectedOptionActive", 498 &WebAXObjectProxy::IsSelectedOptionActive) 499 .SetProperty("isExpanded", &WebAXObjectProxy::IsExpanded) 500 .SetProperty("isChecked", &WebAXObjectProxy::IsChecked) 501 .SetProperty("isVisible", &WebAXObjectProxy::IsVisible) 502 .SetProperty("isOffScreen", &WebAXObjectProxy::IsOffScreen) 503 .SetProperty("isCollapsed", &WebAXObjectProxy::IsCollapsed) 504 .SetProperty("hasPopup", &WebAXObjectProxy::HasPopup) 505 .SetProperty("isValid", &WebAXObjectProxy::IsValid) 506 .SetProperty("isReadOnly", &WebAXObjectProxy::IsReadOnly) 507 .SetProperty("orientation", &WebAXObjectProxy::Orientation) 508 .SetProperty("clickPointX", &WebAXObjectProxy::ClickPointX) 509 .SetProperty("clickPointY", &WebAXObjectProxy::ClickPointY) 510 .SetProperty("rowCount", &WebAXObjectProxy::RowCount) 511 .SetProperty("columnCount", &WebAXObjectProxy::ColumnCount) 512 .SetProperty("isClickable", &WebAXObjectProxy::IsClickable) 513 .SetMethod("allAttributes", &WebAXObjectProxy::AllAttributes) 514 .SetMethod("attributesOfChildren", 515 &WebAXObjectProxy::AttributesOfChildren) 516 .SetMethod("lineForIndex", &WebAXObjectProxy::LineForIndex) 517 .SetMethod("boundsForRange", &WebAXObjectProxy::BoundsForRange) 518 .SetMethod("childAtIndex", &WebAXObjectProxy::ChildAtIndex) 519 .SetMethod("elementAtPoint", &WebAXObjectProxy::ElementAtPoint) 520 .SetMethod("tableHeader", &WebAXObjectProxy::TableHeader) 521 .SetMethod("rowIndexRange", &WebAXObjectProxy::RowIndexRange) 522 .SetMethod("columnIndexRange", &WebAXObjectProxy::ColumnIndexRange) 523 .SetMethod("cellForColumnAndRow", &WebAXObjectProxy::CellForColumnAndRow) 524 .SetMethod("titleUIElement", &WebAXObjectProxy::TitleUIElement) 525 .SetMethod("setSelectedTextRange", 526 &WebAXObjectProxy::SetSelectedTextRange) 527 .SetMethod("isAttributeSettable", &WebAXObjectProxy::IsAttributeSettable) 528 .SetMethod("isPressActionSupported", 529 &WebAXObjectProxy::IsPressActionSupported) 530 .SetMethod("isIncrementActionSupported", 531 &WebAXObjectProxy::IsIncrementActionSupported) 532 .SetMethod("isDecrementActionSupported", 533 &WebAXObjectProxy::IsDecrementActionSupported) 534 .SetMethod("parentElement", &WebAXObjectProxy::ParentElement) 535 .SetMethod("increment", &WebAXObjectProxy::Increment) 536 .SetMethod("decrement", &WebAXObjectProxy::Decrement) 537 .SetMethod("showMenu", &WebAXObjectProxy::ShowMenu) 538 .SetMethod("press", &WebAXObjectProxy::Press) 539 .SetMethod("isEqual", &WebAXObjectProxy::IsEqual) 540 .SetMethod("setNotificationListener", 541 &WebAXObjectProxy::SetNotificationListener) 542 .SetMethod("unsetNotificationListener", 543 &WebAXObjectProxy::UnsetNotificationListener) 544 .SetMethod("takeFocus", &WebAXObjectProxy::TakeFocus) 545 .SetMethod("scrollToMakeVisible", &WebAXObjectProxy::ScrollToMakeVisible) 546 .SetMethod("scrollToMakeVisibleWithSubFocus", 547 &WebAXObjectProxy::ScrollToMakeVisibleWithSubFocus) 548 .SetMethod("scrollToGlobalPoint", &WebAXObjectProxy::ScrollToGlobalPoint) 549 .SetMethod("wordStart", &WebAXObjectProxy::WordStart) 550 .SetMethod("wordEnd", &WebAXObjectProxy::WordEnd) 551 // TODO(hajimehoshi): This is for backward compatibility. Remove them. 552 .SetMethod("addNotificationListener", 553 &WebAXObjectProxy::SetNotificationListener) 554 .SetMethod("removeNotificationListener", 555 &WebAXObjectProxy::UnsetNotificationListener); 556 } 557 558 v8::Handle<v8::Object> WebAXObjectProxy::GetChildAtIndex(unsigned index) { 559 return factory_->GetOrCreate(accessibility_object().childAt(index)); 560 } 561 562 bool WebAXObjectProxy::IsRoot() const { 563 return false; 564 } 565 566 bool WebAXObjectProxy::IsEqualToObject(const blink::WebAXObject& other) { 567 return accessibility_object().equals(other); 568 } 569 570 void WebAXObjectProxy::NotificationReceived( 571 blink::WebFrame* frame, 572 const std::string& notification_name) { 573 if (notification_callback_.IsEmpty()) 574 return; 575 576 v8::Handle<v8::Context> context = frame->mainWorldScriptContext(); 577 if (context.IsEmpty()) 578 return; 579 580 v8::Isolate* isolate = blink::mainThreadIsolate(); 581 582 v8::Handle<v8::Value> argv[] = { 583 v8::String::NewFromUtf8(isolate, notification_name.data(), 584 v8::String::kNormalString, 585 notification_name.size()), 586 }; 587 frame->callFunctionEvenIfScriptDisabled( 588 v8::Local<v8::Function>::New(isolate, notification_callback_), 589 context->Global(), 590 arraysize(argv), 591 argv); 592 } 593 594 std::string WebAXObjectProxy::Role() { 595 return GetRole(accessibility_object()); 596 } 597 598 std::string WebAXObjectProxy::Title() { 599 return GetTitle(accessibility_object()); 600 } 601 602 std::string WebAXObjectProxy::Description() { 603 return GetDescription(accessibility_object()); 604 } 605 606 std::string WebAXObjectProxy::HelpText() { 607 return GetHelpText(accessibility_object()); 608 } 609 610 std::string WebAXObjectProxy::StringValue() { 611 return GetStringValue(accessibility_object()); 612 } 613 614 int WebAXObjectProxy::X() { 615 accessibility_object_.updateBackingStoreAndCheckValidity(); 616 return accessibility_object().boundingBoxRect().x; 617 } 618 619 int WebAXObjectProxy::Y() { 620 accessibility_object_.updateBackingStoreAndCheckValidity(); 621 return accessibility_object().boundingBoxRect().y; 622 } 623 624 int WebAXObjectProxy::Width() { 625 accessibility_object_.updateBackingStoreAndCheckValidity(); 626 return accessibility_object().boundingBoxRect().width; 627 } 628 629 int WebAXObjectProxy::Height() { 630 accessibility_object_.updateBackingStoreAndCheckValidity(); 631 return accessibility_object().boundingBoxRect().height; 632 } 633 634 int WebAXObjectProxy::IntValue() { 635 if (accessibility_object().supportsRangeValue()) 636 return accessibility_object().valueForRange(); 637 else if (accessibility_object().role() == blink::WebAXRoleHeading) 638 return accessibility_object().headingLevel(); 639 else 640 return atoi(accessibility_object().stringValue().utf8().data()); 641 } 642 643 int WebAXObjectProxy::MinValue() { 644 return accessibility_object().minValueForRange(); 645 } 646 647 int WebAXObjectProxy::MaxValue() { 648 return accessibility_object().maxValueForRange(); 649 } 650 651 std::string WebAXObjectProxy::ValueDescription() { 652 return GetValueDescription(accessibility_object()); 653 } 654 655 int WebAXObjectProxy::ChildrenCount() { 656 int count = 1; // Root object always has only one child, the WebView. 657 if (!IsRoot()) 658 count = accessibility_object().childCount(); 659 return count; 660 } 661 662 int WebAXObjectProxy::InsertionPointLineNumber() { 663 if (!accessibility_object().isFocused()) 664 return -1; 665 return accessibility_object().selectionEndLineNumber(); 666 } 667 668 std::string WebAXObjectProxy::SelectedTextRange() { 669 unsigned selection_start = accessibility_object().selectionStart(); 670 unsigned selection_end = accessibility_object().selectionEnd(); 671 return base::StringPrintf("{%d, %d}", 672 selection_start, selection_end - selection_start); 673 } 674 675 bool WebAXObjectProxy::IsEnabled() { 676 return accessibility_object().isEnabled(); 677 } 678 679 bool WebAXObjectProxy::IsRequired() { 680 return accessibility_object().isRequired(); 681 } 682 683 bool WebAXObjectProxy::IsFocused() { 684 return accessibility_object().isFocused(); 685 } 686 687 bool WebAXObjectProxy::IsFocusable() { 688 return accessibility_object().canSetFocusAttribute(); 689 } 690 691 bool WebAXObjectProxy::IsSelected() { 692 return accessibility_object().isSelected(); 693 } 694 695 bool WebAXObjectProxy::IsSelectable() { 696 return accessibility_object().canSetSelectedAttribute(); 697 } 698 699 bool WebAXObjectProxy::IsMultiSelectable() { 700 return accessibility_object().isMultiSelectable(); 701 } 702 703 bool WebAXObjectProxy::IsSelectedOptionActive() { 704 return accessibility_object().isSelectedOptionActive(); 705 } 706 707 bool WebAXObjectProxy::IsExpanded() { 708 return !accessibility_object().isCollapsed(); 709 } 710 711 bool WebAXObjectProxy::IsChecked() { 712 return accessibility_object().isChecked(); 713 } 714 715 bool WebAXObjectProxy::IsVisible() { 716 return accessibility_object().isVisible(); 717 } 718 719 bool WebAXObjectProxy::IsOffScreen() { 720 return accessibility_object().isOffScreen(); 721 } 722 723 bool WebAXObjectProxy::IsCollapsed() { 724 return accessibility_object().isCollapsed(); 725 } 726 727 bool WebAXObjectProxy::HasPopup() { 728 return accessibility_object().ariaHasPopup(); 729 } 730 731 bool WebAXObjectProxy::IsValid() { 732 return !accessibility_object().isDetached(); 733 } 734 735 bool WebAXObjectProxy::IsReadOnly() { 736 return accessibility_object().isReadOnly(); 737 } 738 739 std::string WebAXObjectProxy::Orientation() { 740 return GetOrientation(accessibility_object()); 741 } 742 743 int WebAXObjectProxy::ClickPointX() { 744 return accessibility_object().clickPoint().x; 745 } 746 747 int WebAXObjectProxy::ClickPointY() { 748 return accessibility_object().clickPoint().y; 749 } 750 751 int32_t WebAXObjectProxy::RowCount() { 752 return static_cast<int32_t>(accessibility_object().rowCount()); 753 } 754 755 int32_t WebAXObjectProxy::ColumnCount() { 756 return static_cast<int32_t>(accessibility_object().columnCount()); 757 } 758 759 bool WebAXObjectProxy::IsClickable() { 760 return accessibility_object().isClickable(); 761 } 762 763 std::string WebAXObjectProxy::AllAttributes() { 764 return GetAttributes(accessibility_object()); 765 } 766 767 std::string WebAXObjectProxy::AttributesOfChildren() { 768 AttributesCollector collector; 769 unsigned size = accessibility_object().childCount(); 770 for (unsigned i = 0; i < size; ++i) 771 collector.CollectAttributes(accessibility_object().childAt(i)); 772 return collector.attributes(); 773 } 774 775 int WebAXObjectProxy::LineForIndex(int index) { 776 blink::WebVector<int> line_breaks; 777 accessibility_object().lineBreaks(line_breaks); 778 int line = 0; 779 int vector_size = static_cast<int>(line_breaks.size()); 780 while (line < vector_size && line_breaks[line] <= index) 781 line++; 782 return line; 783 } 784 785 std::string WebAXObjectProxy::BoundsForRange(int start, int end) { 786 if (accessibility_object().role() != blink::WebAXRoleStaticText) 787 return std::string(); 788 789 if (!accessibility_object_.updateBackingStoreAndCheckValidity()) 790 return std::string(); 791 792 int len = end - start; 793 794 // Get the bounds for each character and union them into one large rectangle. 795 // This is just for testing so it doesn't need to be efficient. 796 blink::WebRect bounds = BoundsForCharacter(accessibility_object(), start); 797 for (int i = 1; i < len; i++) { 798 blink::WebRect next = BoundsForCharacter(accessibility_object(), start + i); 799 int right = std::max(bounds.x + bounds.width, next.x + next.width); 800 int bottom = std::max(bounds.y + bounds.height, next.y + next.height); 801 bounds.x = std::min(bounds.x, next.x); 802 bounds.y = std::min(bounds.y, next.y); 803 bounds.width = right - bounds.x; 804 bounds.height = bottom - bounds.y; 805 } 806 807 return base::StringPrintf("{x: %d, y: %d, width: %d, height: %d}", 808 bounds.x, bounds.y, bounds.width, bounds.height); 809 } 810 811 v8::Handle<v8::Object> WebAXObjectProxy::ChildAtIndex(int index) { 812 return GetChildAtIndex(index); 813 } 814 815 v8::Handle<v8::Object> WebAXObjectProxy::ElementAtPoint(int x, int y) { 816 blink::WebPoint point(x, y); 817 blink::WebAXObject obj = accessibility_object().hitTest(point); 818 if (obj.isNull()) 819 return v8::Handle<v8::Object>(); 820 821 return factory_->GetOrCreate(obj); 822 } 823 824 v8::Handle<v8::Object> WebAXObjectProxy::TableHeader() { 825 blink::WebAXObject obj = accessibility_object().headerContainerObject(); 826 if (obj.isNull()) 827 return v8::Handle<v8::Object>(); 828 829 return factory_->GetOrCreate(obj); 830 } 831 832 std::string WebAXObjectProxy::RowIndexRange() { 833 unsigned row_index = accessibility_object().cellRowIndex(); 834 unsigned row_span = accessibility_object().cellRowSpan(); 835 return base::StringPrintf("{%d, %d}", row_index, row_span); 836 } 837 838 std::string WebAXObjectProxy::ColumnIndexRange() { 839 unsigned column_index = accessibility_object().cellColumnIndex(); 840 unsigned column_span = accessibility_object().cellColumnSpan(); 841 return base::StringPrintf("{%d, %d}", column_index, column_span); 842 } 843 844 v8::Handle<v8::Object> WebAXObjectProxy::CellForColumnAndRow( 845 int column, int row) { 846 blink::WebAXObject obj = 847 accessibility_object().cellForColumnAndRow(column, row); 848 if (obj.isNull()) 849 return v8::Handle<v8::Object>(); 850 851 return factory_->GetOrCreate(obj); 852 } 853 854 v8::Handle<v8::Object> WebAXObjectProxy::TitleUIElement() { 855 blink::WebAXObject obj = accessibility_object().titleUIElement(); 856 if (obj.isNull()) 857 return v8::Handle<v8::Object>(); 858 859 return factory_->GetOrCreate(obj); 860 } 861 862 void WebAXObjectProxy::SetSelectedTextRange(int selection_start, 863 int length) { 864 accessibility_object().setSelectedTextRange(selection_start, 865 selection_start + length); 866 } 867 868 bool WebAXObjectProxy::IsAttributeSettable(const std::string& attribute) { 869 bool settable = false; 870 if (attribute == "AXValue") 871 settable = accessibility_object().canSetValueAttribute(); 872 return settable; 873 } 874 875 bool WebAXObjectProxy::IsPressActionSupported() { 876 return accessibility_object().canPress(); 877 } 878 879 bool WebAXObjectProxy::IsIncrementActionSupported() { 880 return accessibility_object().canIncrement(); 881 } 882 883 bool WebAXObjectProxy::IsDecrementActionSupported() { 884 return accessibility_object().canDecrement(); 885 } 886 887 v8::Handle<v8::Object> WebAXObjectProxy::ParentElement() { 888 blink::WebAXObject parent_object = accessibility_object().parentObject(); 889 while (parent_object.accessibilityIsIgnored()) 890 parent_object = parent_object.parentObject(); 891 return factory_->GetOrCreate(parent_object); 892 } 893 894 void WebAXObjectProxy::Increment() { 895 accessibility_object().increment(); 896 } 897 898 void WebAXObjectProxy::Decrement() { 899 accessibility_object().decrement(); 900 } 901 902 void WebAXObjectProxy::ShowMenu() { 903 } 904 905 void WebAXObjectProxy::Press() { 906 accessibility_object().press(); 907 } 908 909 bool WebAXObjectProxy::IsEqual(v8::Handle<v8::Object> proxy) { 910 WebAXObjectProxy* unwrapped_proxy = NULL; 911 if (!gin::ConvertFromV8(blink::mainThreadIsolate(), proxy, &unwrapped_proxy)) 912 return false; 913 return unwrapped_proxy->IsEqualToObject(accessibility_object_); 914 } 915 916 void WebAXObjectProxy::SetNotificationListener( 917 v8::Handle<v8::Function> callback) { 918 v8::Isolate* isolate = blink::mainThreadIsolate(); 919 notification_callback_.Reset(isolate, callback); 920 } 921 922 void WebAXObjectProxy::UnsetNotificationListener() { 923 notification_callback_.Reset(); 924 } 925 926 void WebAXObjectProxy::TakeFocus() { 927 accessibility_object().setFocused(true); 928 } 929 930 void WebAXObjectProxy::ScrollToMakeVisible() { 931 accessibility_object().scrollToMakeVisible(); 932 } 933 934 void WebAXObjectProxy::ScrollToMakeVisibleWithSubFocus(int x, int y, 935 int width, int height) { 936 accessibility_object().scrollToMakeVisibleWithSubFocus( 937 blink::WebRect(x, y, width, height)); 938 } 939 940 void WebAXObjectProxy::ScrollToGlobalPoint(int x, int y) { 941 accessibility_object().scrollToGlobalPoint(blink::WebPoint(x, y)); 942 } 943 944 int WebAXObjectProxy::WordStart(int character_index) { 945 if (accessibility_object().role() != blink::WebAXRoleStaticText) 946 return -1; 947 948 int word_start, word_end; 949 GetBoundariesForOneWord(accessibility_object(), character_index, 950 word_start, word_end); 951 return word_start; 952 } 953 954 int WebAXObjectProxy::WordEnd(int character_index) { 955 if (accessibility_object().role() != blink::WebAXRoleStaticText) 956 return -1; 957 958 int word_start, word_end; 959 GetBoundariesForOneWord(accessibility_object(), character_index, 960 word_start, word_end); 961 return word_end; 962 } 963 964 RootWebAXObjectProxy::RootWebAXObjectProxy( 965 const blink::WebAXObject &object, Factory *factory) 966 : WebAXObjectProxy(object, factory) { 967 } 968 969 v8::Handle<v8::Object> RootWebAXObjectProxy::GetChildAtIndex(unsigned index) { 970 if (index) 971 return v8::Handle<v8::Object>(); 972 973 return factory()->GetOrCreate(accessibility_object()); 974 } 975 976 bool RootWebAXObjectProxy::IsRoot() const { 977 return true; 978 } 979 980 WebAXObjectProxyList::WebAXObjectProxyList() 981 : elements_(blink::mainThreadIsolate()) { 982 } 983 984 WebAXObjectProxyList::~WebAXObjectProxyList() { 985 Clear(); 986 } 987 988 void WebAXObjectProxyList::Clear() { 989 elements_.Clear(); 990 } 991 992 v8::Handle<v8::Object> WebAXObjectProxyList::GetOrCreate( 993 const blink::WebAXObject& object) { 994 if (object.isNull()) 995 return v8::Handle<v8::Object>(); 996 997 v8::Isolate* isolate = blink::mainThreadIsolate(); 998 999 size_t elementCount = elements_.Size(); 1000 for (size_t i = 0; i < elementCount; i++) { 1001 WebAXObjectProxy* unwrapped_object = NULL; 1002 bool result = gin::ConvertFromV8(isolate, elements_.Get(i), 1003 &unwrapped_object); 1004 DCHECK(result); 1005 DCHECK(unwrapped_object); 1006 if (unwrapped_object->IsEqualToObject(object)) 1007 return elements_.Get(i); 1008 } 1009 1010 v8::Handle<v8::Value> value_handle = gin::CreateHandle( 1011 isolate, new WebAXObjectProxy(object, this)).ToV8(); 1012 if (value_handle.IsEmpty()) 1013 return v8::Handle<v8::Object>(); 1014 v8::Handle<v8::Object> handle = value_handle->ToObject(); 1015 elements_.Append(handle); 1016 return handle; 1017 } 1018 1019 v8::Handle<v8::Object> WebAXObjectProxyList::CreateRoot( 1020 const blink::WebAXObject& object) { 1021 v8::Isolate* isolate = blink::mainThreadIsolate(); 1022 v8::Handle<v8::Value> value_handle = gin::CreateHandle( 1023 isolate, new RootWebAXObjectProxy(object, this)).ToV8(); 1024 if (value_handle.IsEmpty()) 1025 return v8::Handle<v8::Object>(); 1026 v8::Handle<v8::Object> handle = value_handle->ToObject(); 1027 elements_.Append(handle); 1028 return handle; 1029 } 1030 1031 } // namespace content 1032