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