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