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