root/content/shell/renderer/test_runner/web_ax_object_proxy.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. RoleToString
  2. GetDescription
  3. GetHelpText
  4. GetStringValue
  5. GetRole
  6. GetTitle
  7. GetOrientation
  8. GetValueDescription
  9. GetAttributes
  10. BoundsForCharacter
  11. GetBoundariesForOneWord
  12. CollectAttributes
  13. attributes
  14. factory_
  15. GetObjectTemplateBuilder
  16. GetChildAtIndex
  17. IsRoot
  18. IsEqualToObject
  19. NotificationReceived
  20. Role
  21. Title
  22. Description
  23. HelpText
  24. StringValue
  25. Y
  26. Width
  27. Height
  28. IntValue
  29. MinValue
  30. MaxValue
  31. ValueDescription
  32. ChildrenCount
  33. InsertionPointLineNumber
  34. SelectedTextRange
  35. IsEnabled
  36. IsRequired
  37. IsFocused
  38. IsFocusable
  39. IsSelected
  40. IsSelectable
  41. IsMultiSelectable
  42. IsSelectedOptionActive
  43. IsExpanded
  44. IsChecked
  45. IsVisible
  46. IsOffScreen
  47. IsCollapsed
  48. HasPopup
  49. IsValid
  50. IsReadOnly
  51. Orientation
  52. ClickPointX
  53. ClickPointY
  54. RowCount
  55. ColumnCount
  56. IsClickable
  57. AllAttributes
  58. AttributesOfChildren
  59. LineForIndex
  60. BoundsForRange
  61. ChildAtIndex
  62. ElementAtPoint
  63. TableHeader
  64. RowIndexRange
  65. ColumnIndexRange
  66. CellForColumnAndRow
  67. TitleUIElement
  68. SetSelectedTextRange
  69. IsAttributeSettable
  70. IsPressActionSupported
  71. IsIncrementActionSupported
  72. IsDecrementActionSupported
  73. ParentElement
  74. Increment
  75. Decrement
  76. ShowMenu
  77. Press
  78. IsEqual
  79. SetNotificationListener
  80. UnsetNotificationListener
  81. TakeFocus
  82. ScrollToMakeVisible
  83. ScrollToMakeVisibleWithSubFocus
  84. ScrollToGlobalPoint
  85. WordStart
  86. WordEnd
  87. GetChildAtIndex
  88. IsRoot
  89. Clear
  90. GetOrCreate
  91. CreateRoot

// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// TODO(hajimehoshi): Remove this when UnsafePersistent is removed.
#define V8_ALLOW_ACCESS_TO_RAW_HANDLE_CONSTRUCTOR

#include "content/shell/renderer/test_runner/web_ax_object_proxy.h"

#include "base/strings/stringprintf.h"
#include "gin/handle.h"
#include "third_party/WebKit/public/platform/WebPoint.h"
#include "third_party/WebKit/public/platform/WebRect.h"
#include "third_party/WebKit/public/platform/WebString.h"
#include "third_party/WebKit/public/web/WebFrame.h"
#include "third_party/WebKit/public/web/WebKit.h"

namespace content {

namespace {

// Map role value to string, matching Safari/Mac platform implementation to
// avoid rebaselining layout tests.
std::string RoleToString(blink::WebAXRole role)
{
  std::string result = "AXRole: AX";
  switch (role) {
    case blink::WebAXRoleAlertDialog:
      return result.append("AlertDialog");
    case blink::WebAXRoleAlert:
      return result.append("Alert");
    case blink::WebAXRoleAnnotation:
      return result.append("Annotation");
    case blink::WebAXRoleApplication:
      return result.append("Application");
    case blink::WebAXRoleArticle:
      return result.append("Article");
    case blink::WebAXRoleBanner:
      return result.append("Banner");
    case blink::WebAXRoleBrowser:
      return result.append("Browser");
    case blink::WebAXRoleBusyIndicator:
      return result.append("BusyIndicator");
    case blink::WebAXRoleButton:
      return result.append("Button");
    case blink::WebAXRoleCanvas:
      return result.append("Canvas");
    case blink::WebAXRoleCell:
      return result.append("Cell");
    case blink::WebAXRoleCheckBox:
      return result.append("CheckBox");
    case blink::WebAXRoleColorWell:
      return result.append("ColorWell");
    case blink::WebAXRoleColumnHeader:
      return result.append("ColumnHeader");
    case blink::WebAXRoleColumn:
      return result.append("Column");
    case blink::WebAXRoleComboBox:
      return result.append("ComboBox");
    case blink::WebAXRoleComplementary:
      return result.append("Complementary");
    case blink::WebAXRoleContentInfo:
      return result.append("ContentInfo");
    case blink::WebAXRoleDefinition:
      return result.append("Definition");
    case blink::WebAXRoleDescriptionListDetail:
      return result.append("DescriptionListDetail");
    case blink::WebAXRoleDescriptionListTerm:
      return result.append("DescriptionListTerm");
    case blink::WebAXRoleDialog:
      return result.append("Dialog");
    case blink::WebAXRoleDirectory:
      return result.append("Directory");
    case blink::WebAXRoleDisclosureTriangle:
      return result.append("DisclosureTriangle");
    case blink::WebAXRoleDiv:
      return result.append("Div");
    case blink::WebAXRoleDocument:
      return result.append("Document");
    case blink::WebAXRoleDrawer:
      return result.append("Drawer");
    case blink::WebAXRoleEditableText:
      return result.append("EditableText");
    case blink::WebAXRoleFooter:
      return result.append("Footer");
    case blink::WebAXRoleForm:
      return result.append("Form");
    case blink::WebAXRoleGrid:
      return result.append("Grid");
    case blink::WebAXRoleGroup:
      return result.append("Group");
    case blink::WebAXRoleGrowArea:
      return result.append("GrowArea");
    case blink::WebAXRoleHeading:
      return result.append("Heading");
    case blink::WebAXRoleHelpTag:
      return result.append("HelpTag");
    case blink::WebAXRoleHorizontalRule:
      return result.append("HorizontalRule");
    case blink::WebAXRoleIgnored:
      return result.append("Ignored");
    case blink::WebAXRoleImageMapLink:
      return result.append("ImageMapLink");
    case blink::WebAXRoleImageMap:
      return result.append("ImageMap");
    case blink::WebAXRoleImage:
      return result.append("Image");
    case blink::WebAXRoleIncrementor:
      return result.append("Incrementor");
    case blink::WebAXRoleInlineTextBox:
      return result.append("InlineTextBox");
    case blink::WebAXRoleLabel:
      return result.append("Label");
    case blink::WebAXRoleLegend:
      return result.append("Legend");
    case blink::WebAXRoleLink:
      return result.append("Link");
    case blink::WebAXRoleListBoxOption:
      return result.append("ListBoxOption");
    case blink::WebAXRoleListBox:
      return result.append("ListBox");
    case blink::WebAXRoleListItem:
      return result.append("ListItem");
    case blink::WebAXRoleListMarker:
      return result.append("ListMarker");
    case blink::WebAXRoleList:
      return result.append("List");
    case blink::WebAXRoleLog:
      return result.append("Log");
    case blink::WebAXRoleMain:
      return result.append("Main");
    case blink::WebAXRoleMarquee:
      return result.append("Marquee");
    case blink::WebAXRoleMathElement:
      return result.append("MathElement");
    case blink::WebAXRoleMath:
      return result.append("Math");
    case blink::WebAXRoleMatte:
      return result.append("Matte");
    case blink::WebAXRoleMenuBar:
      return result.append("MenuBar");
    case blink::WebAXRoleMenuButton:
      return result.append("MenuButton");
    case blink::WebAXRoleMenuItem:
      return result.append("MenuItem");
    case blink::WebAXRoleMenuListOption:
      return result.append("MenuListOption");
    case blink::WebAXRoleMenuListPopup:
      return result.append("MenuListPopup");
    case blink::WebAXRoleMenu:
      return result.append("Menu");
    case blink::WebAXRoleNavigation:
      return result.append("Navigation");
    case blink::WebAXRoleNote:
      return result.append("Note");
    case blink::WebAXRoleOutline:
      return result.append("Outline");
    case blink::WebAXRoleParagraph:
      return result.append("Paragraph");
    case blink::WebAXRolePopUpButton:
      return result.append("PopUpButton");
    case blink::WebAXRolePresentational:
      return result.append("Presentational");
    case blink::WebAXRoleProgressIndicator:
      return result.append("ProgressIndicator");
    case blink::WebAXRoleRadioButton:
      return result.append("RadioButton");
    case blink::WebAXRoleRadioGroup:
      return result.append("RadioGroup");
    case blink::WebAXRoleRegion:
      return result.append("Region");
    case blink::WebAXRoleRootWebArea:
      return result.append("RootWebArea");
    case blink::WebAXRoleRowHeader:
      return result.append("RowHeader");
    case blink::WebAXRoleRow:
      return result.append("Row");
    case blink::WebAXRoleRulerMarker:
      return result.append("RulerMarker");
    case blink::WebAXRoleRuler:
      return result.append("Ruler");
    case blink::WebAXRoleSVGRoot:
      return result.append("SVGRoot");
    case blink::WebAXRoleScrollArea:
      return result.append("ScrollArea");
    case blink::WebAXRoleScrollBar:
      return result.append("ScrollBar");
    case blink::WebAXRoleSeamlessWebArea:
      return result.append("SeamlessWebArea");
    case blink::WebAXRoleSearch:
      return result.append("Search");
    case blink::WebAXRoleSheet:
      return result.append("Sheet");
    case blink::WebAXRoleSlider:
      return result.append("Slider");
    case blink::WebAXRoleSliderThumb:
      return result.append("SliderThumb");
    case blink::WebAXRoleSpinButtonPart:
      return result.append("SpinButtonPart");
    case blink::WebAXRoleSpinButton:
      return result.append("SpinButton");
    case blink::WebAXRoleSplitGroup:
      return result.append("SplitGroup");
    case blink::WebAXRoleSplitter:
      return result.append("Splitter");
    case blink::WebAXRoleStaticText:
      return result.append("StaticText");
    case blink::WebAXRoleStatus:
      return result.append("Status");
    case blink::WebAXRoleSystemWide:
      return result.append("SystemWide");
    case blink::WebAXRoleTabGroup:
      return result.append("TabGroup");
    case blink::WebAXRoleTabList:
      return result.append("TabList");
    case blink::WebAXRoleTabPanel:
      return result.append("TabPanel");
    case blink::WebAXRoleTab:
      return result.append("Tab");
    case blink::WebAXRoleTableHeaderContainer:
      return result.append("TableHeaderContainer");
    case blink::WebAXRoleTable:
      return result.append("Table");
    case blink::WebAXRoleTextArea:
      return result.append("TextArea");
    case blink::WebAXRoleTextField:
      return result.append("TextField");
    case blink::WebAXRoleTimer:
      return result.append("Timer");
    case blink::WebAXRoleToggleButton:
      return result.append("ToggleButton");
    case blink::WebAXRoleToolbar:
      return result.append("Toolbar");
    case blink::WebAXRoleTreeGrid:
      return result.append("TreeGrid");
    case blink::WebAXRoleTreeItem:
      return result.append("TreeItem");
    case blink::WebAXRoleTree:
      return result.append("Tree");
    case blink::WebAXRoleUnknown:
      return result.append("Unknown");
    case blink::WebAXRoleUserInterfaceTooltip:
      return result.append("UserInterfaceTooltip");
    case blink::WebAXRoleValueIndicator:
      return result.append("ValueIndicator");
    case blink::WebAXRoleWebArea:
      return result.append("WebArea");
    case blink::WebAXRoleWindow:
      return result.append("Window");
    default:
      return result.append("Unknown");
  }
}

std::string GetDescription(const blink::WebAXObject& object) {
  std::string description = object.accessibilityDescription().utf8();
  return description.insert(0, "AXDescription: ");
}

std::string GetHelpText(const blink::WebAXObject& object) {
  std::string help_text = object.helpText().utf8();
  return help_text.insert(0, "AXHelp: ");
}

std::string GetStringValue(const blink::WebAXObject& object) {
  std::string value;
  if (object.role() == blink::WebAXRoleColorWell) {
    int r, g, b;
    object.colorValue(r, g, b);
    value = base::StringPrintf("rgb %7.5f %7.5f %7.5f 1",
                               r / 255., g / 255., b / 255.);
  } else {
    value = object.stringValue().utf8();
  }
  return value.insert(0, "AXValue: ");
}

std::string GetRole(const blink::WebAXObject& object) {
  std::string role_string = RoleToString(object.role());

  // Special-case canvas with fallback content because Chromium wants to treat
  // this as essentially a separate role that it can map differently depending
  // on the platform.
  if (object.role() == blink::WebAXRoleCanvas &&
      object.canvasHasFallbackContent()) {
    role_string += "WithFallbackContent";
  }

  return role_string;
}

std::string GetTitle(const blink::WebAXObject& object) {
  std::string title = object.title().utf8();
  return title.insert(0, "AXTitle: ");
}

std::string GetOrientation(const blink::WebAXObject& object) {
  if (object.isVertical())
    return "AXOrientation: AXVerticalOrientation";

  return "AXOrientation: AXHorizontalOrientation";
}

std::string GetValueDescription(const blink::WebAXObject& object) {
  std::string value_description = object.valueDescription().utf8();
  return value_description.insert(0, "AXValueDescription: ");
}

std::string GetAttributes(const blink::WebAXObject& object) {
  // FIXME: Concatenate all attributes of the AXObject.
  std::string attributes(GetTitle(object));
  attributes.append("\n");
  attributes.append(GetRole(object));
  attributes.append("\n");
  attributes.append(GetDescription(object));
  return attributes;
}

blink::WebRect BoundsForCharacter(const blink::WebAXObject& object,
                                  int characterIndex) {
  DCHECK_EQ(object.role(), blink::WebAXRoleStaticText);
  int end = 0;
  for (unsigned i = 0; i < object.childCount(); i++) {
    blink::WebAXObject inline_text_box = object.childAt(i);
    DCHECK_EQ(inline_text_box.role(), blink::WebAXRoleInlineTextBox);
    int start = end;
    end += inline_text_box.stringValue().length();
    if (characterIndex < start || characterIndex >= end)
      continue;
    blink::WebRect inline_text_box_rect = inline_text_box.boundingBoxRect();
    int localIndex = characterIndex - start;
    blink::WebVector<int> character_offsets;
    inline_text_box.characterOffsets(character_offsets);
    DCHECK(character_offsets.size() > 0 &&
           character_offsets.size() == inline_text_box.stringValue().length());
    switch (inline_text_box.textDirection()) {
      case blink::WebAXTextDirectionLR: {
        if (localIndex) {
          int left = inline_text_box_rect.x + character_offsets[localIndex - 1];
          int width = character_offsets[localIndex] -
              character_offsets[localIndex - 1];
          return blink::WebRect(left, inline_text_box_rect.y,
                                width, inline_text_box_rect.height);
        }
        return blink::WebRect(
            inline_text_box_rect.x, inline_text_box_rect.y,
            character_offsets[0], inline_text_box_rect.height);
      }
      case blink::WebAXTextDirectionRL: {
        int right = inline_text_box_rect.x + inline_text_box_rect.width;

        if (localIndex) {
          int left = right - character_offsets[localIndex];
          int width = character_offsets[localIndex] -
              character_offsets[localIndex - 1];
          return blink::WebRect(left, inline_text_box_rect.y,
                                width, inline_text_box_rect.height);
        }
        int left = right - character_offsets[0];
        return blink::WebRect(
            left, inline_text_box_rect.y,
            character_offsets[0], inline_text_box_rect.height);
      }
      case blink::WebAXTextDirectionTB: {
        if (localIndex) {
          int top = inline_text_box_rect.y + character_offsets[localIndex - 1];
          int height = character_offsets[localIndex] -
              character_offsets[localIndex - 1];
          return blink::WebRect(inline_text_box_rect.x, top,
                                inline_text_box_rect.width, height);
        }
        return blink::WebRect(inline_text_box_rect.x, inline_text_box_rect.y,
                              inline_text_box_rect.width, character_offsets[0]);
      }
      case blink::WebAXTextDirectionBT: {
        int bottom = inline_text_box_rect.y + inline_text_box_rect.height;

        if (localIndex) {
          int top = bottom - character_offsets[localIndex];
          int height = character_offsets[localIndex] -
              character_offsets[localIndex - 1];
          return blink::WebRect(inline_text_box_rect.x, top,
                                inline_text_box_rect.width, height);
        }
        int top = bottom - character_offsets[0];
        return blink::WebRect(inline_text_box_rect.x, top,
                              inline_text_box_rect.width, character_offsets[0]);
      }
    }
  }

  DCHECK(false);
  return blink::WebRect();
}

void GetBoundariesForOneWord(const blink::WebAXObject& object,
                             int character_index,
                             int& word_start,
                             int& word_end) {
  int end = 0;
  for (unsigned i = 0; i < object.childCount(); i++) {
    blink::WebAXObject inline_text_box = object.childAt(i);
    DCHECK_EQ(inline_text_box.role(), blink::WebAXRoleInlineTextBox);
    int start = end;
    end += inline_text_box.stringValue().length();
    if (end <= character_index)
      continue;
    int localIndex = character_index - start;

    blink::WebVector<int> starts;
    blink::WebVector<int> ends;
    inline_text_box.wordBoundaries(starts, ends);
    size_t word_count = starts.size();
    DCHECK_EQ(ends.size(), word_count);

    // If there are no words, use the InlineTextBox boundaries.
    if (!word_count) {
      word_start = start;
      word_end = end;
      return;
    }

    // Look for a character within any word other than the last.
    for (size_t j = 0; j < word_count - 1; j++) {
      if (localIndex <= ends[j]) {
        word_start = start + starts[j];
        word_end = start + ends[j];
        return;
      }
    }

    // Return the last word by default.
    word_start = start + starts[word_count - 1];
    word_end = start + ends[word_count - 1];
    return;
  }
}

// Collects attributes into a string, delimited by dashes. Used by all methods
// that output lists of attributes: attributesOfLinkedUIElementsCallback,
// AttributesOfChildrenCallback, etc.
class AttributesCollector {
 public:
  AttributesCollector() {}
  ~AttributesCollector() {}

  void CollectAttributes(const blink::WebAXObject& object) {
    attributes_.append("\n------------\n");
    attributes_.append(GetAttributes(object));
  }

  std::string attributes() const { return attributes_; }

 private:
  std::string attributes_;

  DISALLOW_COPY_AND_ASSIGN(AttributesCollector);
};

}  // namespace

gin::WrapperInfo WebAXObjectProxy::kWrapperInfo = {
    gin::kEmbedderNativeGin};

WebAXObjectProxy::WebAXObjectProxy(const blink::WebAXObject& object,
                                   WebAXObjectProxy::Factory* factory)
    : accessibility_object_(object),
      factory_(factory) {
}

WebAXObjectProxy::~WebAXObjectProxy() {}

gin::ObjectTemplateBuilder
WebAXObjectProxy::GetObjectTemplateBuilder(v8::Isolate* isolate) {
  return gin::Wrappable<WebAXObjectProxy>::GetObjectTemplateBuilder(isolate)
      .SetProperty("role", &WebAXObjectProxy::Role)
      .SetProperty("title", &WebAXObjectProxy::Title)
      .SetProperty("description", &WebAXObjectProxy::Description)
      .SetProperty("helpText", &WebAXObjectProxy::HelpText)
      .SetProperty("stringValue", &WebAXObjectProxy::StringValue)
      .SetProperty("x", &WebAXObjectProxy::X)
      .SetProperty("y", &WebAXObjectProxy::Y)
      .SetProperty("width", &WebAXObjectProxy::Width)
      .SetProperty("height", &WebAXObjectProxy::Height)
      .SetProperty("intValue", &WebAXObjectProxy::IntValue)
      .SetProperty("minValue", &WebAXObjectProxy::MinValue)
      .SetProperty("maxValue", &WebAXObjectProxy::MaxValue)
      .SetProperty("valueDescription", &WebAXObjectProxy::ValueDescription)
      .SetProperty("childrenCount", &WebAXObjectProxy::ChildrenCount)
      .SetProperty("insertionPointLineNumber",
                   &WebAXObjectProxy::InsertionPointLineNumber)
      .SetProperty("selectedTextRange", &WebAXObjectProxy::SelectedTextRange)
      .SetProperty("isEnabled", &WebAXObjectProxy::IsEnabled)
      .SetProperty("isRequired", &WebAXObjectProxy::IsRequired)
      .SetProperty("isFocused", &WebAXObjectProxy::IsFocused)
      .SetProperty("isFocusable", &WebAXObjectProxy::IsFocusable)
      .SetProperty("isSelected", &WebAXObjectProxy::IsSelected)
      .SetProperty("isSelectable", &WebAXObjectProxy::IsSelectable)
      .SetProperty("isMultiSelectable", &WebAXObjectProxy::IsMultiSelectable)
      .SetProperty("isSelectedOptionActive",
                   &WebAXObjectProxy::IsSelectedOptionActive)
      .SetProperty("isExpanded", &WebAXObjectProxy::IsExpanded)
      .SetProperty("isChecked", &WebAXObjectProxy::IsChecked)
      .SetProperty("isVisible", &WebAXObjectProxy::IsVisible)
      .SetProperty("isOffScreen", &WebAXObjectProxy::IsOffScreen)
      .SetProperty("isCollapsed", &WebAXObjectProxy::IsCollapsed)
      .SetProperty("hasPopup", &WebAXObjectProxy::HasPopup)
      .SetProperty("isValid", &WebAXObjectProxy::IsValid)
      .SetProperty("isReadOnly", &WebAXObjectProxy::IsReadOnly)
      .SetProperty("orientation", &WebAXObjectProxy::Orientation)
      .SetProperty("clickPointX", &WebAXObjectProxy::ClickPointX)
      .SetProperty("clickPointY", &WebAXObjectProxy::ClickPointY)
      .SetProperty("rowCount", &WebAXObjectProxy::RowCount)
      .SetProperty("columnCount", &WebAXObjectProxy::ColumnCount)
      .SetProperty("isClickable", &WebAXObjectProxy::IsClickable)
      .SetMethod("allAttributes", &WebAXObjectProxy::AllAttributes)
      .SetMethod("attributesOfChildren",
                 &WebAXObjectProxy::AttributesOfChildren)
      .SetMethod("lineForIndex", &WebAXObjectProxy::LineForIndex)
      .SetMethod("boundsForRange", &WebAXObjectProxy::BoundsForRange)
      .SetMethod("childAtIndex", &WebAXObjectProxy::ChildAtIndex)
      .SetMethod("elementAtPoint", &WebAXObjectProxy::ElementAtPoint)
      .SetMethod("tableHeader", &WebAXObjectProxy::TableHeader)
      .SetMethod("rowIndexRange", &WebAXObjectProxy::RowIndexRange)
      .SetMethod("columnIndexRange", &WebAXObjectProxy::ColumnIndexRange)
      .SetMethod("cellForColumnAndRow", &WebAXObjectProxy::CellForColumnAndRow)
      .SetMethod("titleUIElement", &WebAXObjectProxy::TitleUIElement)
      .SetMethod("setSelectedTextRange",
                 &WebAXObjectProxy::SetSelectedTextRange)
      .SetMethod("isAttributeSettable", &WebAXObjectProxy::IsAttributeSettable)
      .SetMethod("isPressActionSupported",
                 &WebAXObjectProxy::IsPressActionSupported)
      .SetMethod("isIncrementActionSupported",
                 &WebAXObjectProxy::IsIncrementActionSupported)
      .SetMethod("isDecrementActionSupported",
                 &WebAXObjectProxy::IsDecrementActionSupported)
      .SetMethod("parentElement", &WebAXObjectProxy::ParentElement)
      .SetMethod("increment", &WebAXObjectProxy::Increment)
      .SetMethod("decrement", &WebAXObjectProxy::Decrement)
      .SetMethod("showMenu", &WebAXObjectProxy::ShowMenu)
      .SetMethod("press", &WebAXObjectProxy::Press)
      .SetMethod("isEqual", &WebAXObjectProxy::IsEqual)
      .SetMethod("setNotificationListener",
                 &WebAXObjectProxy::SetNotificationListener)
      .SetMethod("unsetNotificationListener",
                 &WebAXObjectProxy::UnsetNotificationListener)
      .SetMethod("takeFocus", &WebAXObjectProxy::TakeFocus)
      .SetMethod("scrollToMakeVisible", &WebAXObjectProxy::ScrollToMakeVisible)
      .SetMethod("scrollToMakeVisibleWithSubFocus",
                 &WebAXObjectProxy::ScrollToMakeVisibleWithSubFocus)
      .SetMethod("scrollToGlobalPoint", &WebAXObjectProxy::ScrollToGlobalPoint)
      .SetMethod("wordStart", &WebAXObjectProxy::WordStart)
      .SetMethod("wordEnd", &WebAXObjectProxy::WordEnd)
      // TODO(hajimehoshi): This is for backward compatibility. Remove them.
      .SetMethod("addNotificationListener",
                 &WebAXObjectProxy::SetNotificationListener)
      .SetMethod("removeNotificationListener",
                 &WebAXObjectProxy::UnsetNotificationListener);
}

v8::Handle<v8::Object> WebAXObjectProxy::GetChildAtIndex(unsigned index) {
  return factory_->GetOrCreate(accessibility_object().childAt(index));
}

bool WebAXObjectProxy::IsRoot() const {
  return false;
}

bool WebAXObjectProxy::IsEqualToObject(const blink::WebAXObject& other) {
  return accessibility_object().equals(other);
}

void WebAXObjectProxy::NotificationReceived(
    blink::WebFrame* frame,
    const std::string& notification_name) {
  if (notification_callback_.IsEmpty())
    return;

  v8::Handle<v8::Context> context = frame->mainWorldScriptContext();
  if (context.IsEmpty())
    return;

  v8::Isolate* isolate = blink::mainThreadIsolate();

  v8::Handle<v8::Value> argv[] = {
    v8::String::NewFromUtf8(isolate, notification_name.data(),
                            v8::String::kNormalString,
                            notification_name.size()),
  };
  frame->callFunctionEvenIfScriptDisabled(
      v8::Local<v8::Function>::New(isolate, notification_callback_),
      context->Global(),
      arraysize(argv),
      argv);
}

std::string WebAXObjectProxy::Role() {
  return GetRole(accessibility_object());
}

std::string WebAXObjectProxy::Title() {
  return GetTitle(accessibility_object());
}

std::string WebAXObjectProxy::Description() {
  return GetDescription(accessibility_object());
}

std::string WebAXObjectProxy::HelpText() {
  return GetHelpText(accessibility_object());
}

std::string WebAXObjectProxy::StringValue() {
  return GetStringValue(accessibility_object());
}

int WebAXObjectProxy::X() {
  accessibility_object_.updateBackingStoreAndCheckValidity();
  return accessibility_object().boundingBoxRect().x;
}

int WebAXObjectProxy::Y() {
  accessibility_object_.updateBackingStoreAndCheckValidity();
  return accessibility_object().boundingBoxRect().y;
}

int WebAXObjectProxy::Width() {
  accessibility_object_.updateBackingStoreAndCheckValidity();
  return accessibility_object().boundingBoxRect().width;
}

int WebAXObjectProxy::Height() {
  accessibility_object_.updateBackingStoreAndCheckValidity();
  return accessibility_object().boundingBoxRect().height;
}

int WebAXObjectProxy::IntValue() {
  if (accessibility_object().supportsRangeValue())
    return accessibility_object().valueForRange();
  else if (accessibility_object().role() == blink::WebAXRoleHeading)
    return accessibility_object().headingLevel();
  else
    return atoi(accessibility_object().stringValue().utf8().data());
}

int WebAXObjectProxy::MinValue() {
  return accessibility_object().minValueForRange();
}

int WebAXObjectProxy::MaxValue() {
  return accessibility_object().maxValueForRange();
}

std::string WebAXObjectProxy::ValueDescription() {
  return GetValueDescription(accessibility_object());
}

int WebAXObjectProxy::ChildrenCount() {
  int count = 1; // Root object always has only one child, the WebView.
  if (!IsRoot())
    count = accessibility_object().childCount();
  return count;
}

int WebAXObjectProxy::InsertionPointLineNumber() {
  if (!accessibility_object().isFocused())
    return -1;
  return accessibility_object().selectionEndLineNumber();
}

std::string WebAXObjectProxy::SelectedTextRange() {
  unsigned selection_start = accessibility_object().selectionStart();
  unsigned selection_end = accessibility_object().selectionEnd();
  return base::StringPrintf("{%d, %d}",
                            selection_start, selection_end - selection_start);
}

bool WebAXObjectProxy::IsEnabled() {
  return accessibility_object().isEnabled();
}

bool WebAXObjectProxy::IsRequired() {
  return accessibility_object().isRequired();
}

bool WebAXObjectProxy::IsFocused() {
  return accessibility_object().isFocused();
}

bool WebAXObjectProxy::IsFocusable() {
  return accessibility_object().canSetFocusAttribute();
}

bool WebAXObjectProxy::IsSelected() {
  return accessibility_object().isSelected();
}

bool WebAXObjectProxy::IsSelectable() {
  return accessibility_object().canSetSelectedAttribute();
}

bool WebAXObjectProxy::IsMultiSelectable() {
  return accessibility_object().isMultiSelectable();
}

bool WebAXObjectProxy::IsSelectedOptionActive() {
  return accessibility_object().isSelectedOptionActive();
}

bool WebAXObjectProxy::IsExpanded() {
  return !accessibility_object().isCollapsed();
}

bool WebAXObjectProxy::IsChecked() {
  return accessibility_object().isChecked();
}

bool WebAXObjectProxy::IsVisible() {
  return accessibility_object().isVisible();
}

bool WebAXObjectProxy::IsOffScreen() {
  return accessibility_object().isOffScreen();
}

bool WebAXObjectProxy::IsCollapsed() {
  return accessibility_object().isCollapsed();
}

bool WebAXObjectProxy::HasPopup() {
  return accessibility_object().ariaHasPopup();
}

bool WebAXObjectProxy::IsValid() {
  return !accessibility_object().isDetached();
}

bool WebAXObjectProxy::IsReadOnly() {
  return accessibility_object().isReadOnly();
}

std::string WebAXObjectProxy::Orientation() {
  return GetOrientation(accessibility_object());
}

int WebAXObjectProxy::ClickPointX() {
  return accessibility_object().clickPoint().x;
}

int WebAXObjectProxy::ClickPointY() {
  return accessibility_object().clickPoint().y;
}

int32_t WebAXObjectProxy::RowCount() {
  return static_cast<int32_t>(accessibility_object().rowCount());
}

int32_t WebAXObjectProxy::ColumnCount() {
  return static_cast<int32_t>(accessibility_object().columnCount());
}

bool WebAXObjectProxy::IsClickable() {
  return accessibility_object().isClickable();
}

std::string WebAXObjectProxy::AllAttributes() {
  return GetAttributes(accessibility_object());
}

std::string WebAXObjectProxy::AttributesOfChildren() {
  AttributesCollector collector;
  unsigned size = accessibility_object().childCount();
  for (unsigned i = 0; i < size; ++i)
    collector.CollectAttributes(accessibility_object().childAt(i));
  return collector.attributes();
}

int WebAXObjectProxy::LineForIndex(int index) {
  blink::WebVector<int> line_breaks;
  accessibility_object().lineBreaks(line_breaks);
  int line = 0;
  int vector_size = static_cast<int>(line_breaks.size());
  while (line < vector_size && line_breaks[line] <= index)
    line++;
  return line;
}

std::string WebAXObjectProxy::BoundsForRange(int start, int end) {
  if (accessibility_object().role() != blink::WebAXRoleStaticText)
    return std::string();

  if (!accessibility_object_.updateBackingStoreAndCheckValidity())
    return std::string();

  int len = end - start;

  // Get the bounds for each character and union them into one large rectangle.
  // This is just for testing so it doesn't need to be efficient.
  blink::WebRect bounds = BoundsForCharacter(accessibility_object(), start);
  for (int i = 1; i < len; i++) {
    blink::WebRect next = BoundsForCharacter(accessibility_object(), start + i);
    int right = std::max(bounds.x + bounds.width, next.x + next.width);
    int bottom = std::max(bounds.y + bounds.height, next.y + next.height);
    bounds.x = std::min(bounds.x, next.x);
    bounds.y = std::min(bounds.y, next.y);
    bounds.width = right - bounds.x;
    bounds.height = bottom - bounds.y;
  }

  return base::StringPrintf("{x: %d, y: %d, width: %d, height: %d}",
                            bounds.x, bounds.y, bounds.width, bounds.height);
}

v8::Handle<v8::Object> WebAXObjectProxy::ChildAtIndex(int index) {
  return GetChildAtIndex(index);
}

v8::Handle<v8::Object> WebAXObjectProxy::ElementAtPoint(int x, int y) {
  blink::WebPoint point(x, y);
  blink::WebAXObject obj = accessibility_object().hitTest(point);
  if (obj.isNull())
    return v8::Handle<v8::Object>();

  return factory_->GetOrCreate(obj);
}

v8::Handle<v8::Object> WebAXObjectProxy::TableHeader() {
  blink::WebAXObject obj = accessibility_object().headerContainerObject();
  if (obj.isNull())
    return v8::Handle<v8::Object>();

  return factory_->GetOrCreate(obj);
}

std::string WebAXObjectProxy::RowIndexRange() {
  unsigned row_index = accessibility_object().cellRowIndex();
  unsigned row_span = accessibility_object().cellRowSpan();
  return base::StringPrintf("{%d, %d}", row_index, row_span);
}

std::string WebAXObjectProxy::ColumnIndexRange() {
  unsigned column_index = accessibility_object().cellColumnIndex();
  unsigned column_span = accessibility_object().cellColumnSpan();
  return base::StringPrintf("{%d, %d}", column_index, column_span);
}

v8::Handle<v8::Object> WebAXObjectProxy::CellForColumnAndRow(
    int column, int row) {
  blink::WebAXObject obj =
      accessibility_object().cellForColumnAndRow(column, row);
  if (obj.isNull())
    return v8::Handle<v8::Object>();

  return factory_->GetOrCreate(obj);
}

v8::Handle<v8::Object> WebAXObjectProxy::TitleUIElement() {
  blink::WebAXObject obj = accessibility_object().titleUIElement();
  if (obj.isNull())
    return v8::Handle<v8::Object>();

  return factory_->GetOrCreate(obj);
}

void WebAXObjectProxy::SetSelectedTextRange(int selection_start,
                                            int length) {
  accessibility_object().setSelectedTextRange(selection_start,
                                              selection_start + length);
}

bool WebAXObjectProxy::IsAttributeSettable(const std::string& attribute) {
  bool settable = false;
  if (attribute == "AXValue")
    settable = accessibility_object().canSetValueAttribute();
  return settable;
}

bool WebAXObjectProxy::IsPressActionSupported() {
  return accessibility_object().canPress();
}

bool WebAXObjectProxy::IsIncrementActionSupported() {
  return accessibility_object().canIncrement();
}

bool WebAXObjectProxy::IsDecrementActionSupported() {
  return accessibility_object().canDecrement();
}

v8::Handle<v8::Object> WebAXObjectProxy::ParentElement() {
  blink::WebAXObject parent_object = accessibility_object().parentObject();
  while (parent_object.accessibilityIsIgnored())
    parent_object = parent_object.parentObject();
  return factory_->GetOrCreate(parent_object);
}

void WebAXObjectProxy::Increment() {
  accessibility_object().increment();
}

void WebAXObjectProxy::Decrement() {
  accessibility_object().decrement();
}

void WebAXObjectProxy::ShowMenu() {
}

void WebAXObjectProxy::Press() {
  accessibility_object().press();
}

bool WebAXObjectProxy::IsEqual(v8::Handle<v8::Object> proxy) {
  WebAXObjectProxy* unwrapped_proxy = NULL;
  if (!gin::ConvertFromV8(blink::mainThreadIsolate(), proxy, &unwrapped_proxy))
    return false;
  return unwrapped_proxy->IsEqualToObject(accessibility_object_);
}

void WebAXObjectProxy::SetNotificationListener(
    v8::Handle<v8::Function> callback) {
  v8::Isolate* isolate = blink::mainThreadIsolate();
  notification_callback_.Reset(isolate, callback);
}

void WebAXObjectProxy::UnsetNotificationListener() {
  notification_callback_.Reset();
}

void WebAXObjectProxy::TakeFocus() {
  accessibility_object().setFocused(true);
}

void WebAXObjectProxy::ScrollToMakeVisible() {
  accessibility_object().scrollToMakeVisible();
}

void WebAXObjectProxy::ScrollToMakeVisibleWithSubFocus(int x, int y,
                                                       int width, int height) {
  accessibility_object().scrollToMakeVisibleWithSubFocus(
      blink::WebRect(x, y, width, height));
}

void WebAXObjectProxy::ScrollToGlobalPoint(int x, int y) {
  accessibility_object().scrollToGlobalPoint(blink::WebPoint(x, y));
}

int WebAXObjectProxy::WordStart(int character_index) {
  if (accessibility_object().role() != blink::WebAXRoleStaticText)
    return -1;

  int word_start, word_end;
  GetBoundariesForOneWord(accessibility_object(), character_index,
                          word_start, word_end);
  return word_start;
}

int WebAXObjectProxy::WordEnd(int character_index) {
  if (accessibility_object().role() != blink::WebAXRoleStaticText)
    return -1;

  int word_start, word_end;
  GetBoundariesForOneWord(accessibility_object(), character_index,
                          word_start, word_end);
  return word_end;
}

RootWebAXObjectProxy::RootWebAXObjectProxy(
    const blink::WebAXObject &object, Factory *factory)
    : WebAXObjectProxy(object, factory) {
}

v8::Handle<v8::Object> RootWebAXObjectProxy::GetChildAtIndex(unsigned index) {
  if (index)
    return v8::Handle<v8::Object>();

  return factory()->GetOrCreate(accessibility_object());
}

bool RootWebAXObjectProxy::IsRoot() const {
  return true;
}

WebAXObjectProxyList::WebAXObjectProxyList() {
}

WebAXObjectProxyList::~WebAXObjectProxyList() {
  Clear();
}

void WebAXObjectProxyList::Clear() {
  for (ElementList::iterator i = elements_.begin(); i != elements_.end(); ++i)
    i->Dispose();
  elements_.clear();
}

v8::Handle<v8::Object> WebAXObjectProxyList::GetOrCreate(
    const blink::WebAXObject& object) {
  if (object.isNull())
    return v8::Handle<v8::Object>();

  v8::Isolate* isolate = blink::mainThreadIsolate();

  size_t elementCount = elements_.size();
  for (size_t i = 0; i < elementCount; i++) {
    WebAXObjectProxy* unwrapped_object = NULL;
    bool result = gin::ConvertFromV8(isolate, elements_[i].NewLocal(isolate),
                                     &unwrapped_object);
    DCHECK(result);
    DCHECK(unwrapped_object);
    if (unwrapped_object->IsEqualToObject(object))
      return elements_[i].NewLocal(isolate);
  }

  v8::Handle<v8::Value> value_handle = gin::CreateHandle(
      isolate, new WebAXObjectProxy(object, this)).ToV8();
  if (value_handle.IsEmpty())
    return v8::Handle<v8::Object>();
  v8::Handle<v8::Object> handle = value_handle->ToObject();
  UnsafePersistent<v8::Object> unsafe_handle(isolate, handle);
  elements_.push_back(unsafe_handle);
  return unsafe_handle.NewLocal(isolate);
}

v8::Handle<v8::Object> WebAXObjectProxyList::CreateRoot(
    const blink::WebAXObject& object) {
  v8::Isolate* isolate = blink::mainThreadIsolate();
  v8::Handle<v8::Value> value_handle = gin::CreateHandle(
      isolate, new RootWebAXObjectProxy(object, this)).ToV8();
  if (value_handle.IsEmpty())
    return v8::Handle<v8::Object>();
  v8::Handle<v8::Object> handle = value_handle->ToObject();
  UnsafePersistent<v8::Object> unsafe_handle(isolate, handle);
  elements_.push_back(unsafe_handle);
  return unsafe_handle.NewLocal(isolate);
}

}  // namespace content

/* [<][>][^][v][top][bottom][index][help] */