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