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.
RoleToString(blink::WebAXRole role)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
GetDescription(const blink::WebAXObject & object)252 std::string GetDescription(const blink::WebAXObject& object) {
253 std::string description = object.accessibilityDescription().utf8();
254 return description.insert(0, "AXDescription: ");
255 }
256
GetHelpText(const blink::WebAXObject & object)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
GetStringValue(const blink::WebAXObject & object)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
GetRole(const blink::WebAXObject & object)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
GetTitle(const blink::WebAXObject & object)289 std::string GetTitle(const blink::WebAXObject& object) {
290 std::string title = object.title().utf8();
291 return title.insert(0, "AXTitle: ");
292 }
293
GetOrientation(const blink::WebAXObject & object)294 std::string GetOrientation(const blink::WebAXObject& object) {
295 if (object.isVertical())
296 return "AXOrientation: AXVerticalOrientation";
297
298 return "AXOrientation: AXHorizontalOrientation";
299 }
300
GetValueDescription(const blink::WebAXObject & object)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
GetAttributes(const blink::WebAXObject & object)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
BoundsForCharacter(const blink::WebAXObject & object,int characterIndex)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
GetBoundariesForOneWord(const blink::WebAXObject & object,int character_index,int & word_start,int & word_end)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:
AttributesCollector()441 AttributesCollector() {}
~AttributesCollector()442 ~AttributesCollector() {}
443
CollectAttributes(const blink::WebAXObject & object)444 void CollectAttributes(const blink::WebAXObject& object) {
445 attributes_.append("\n------------\n");
446 attributes_.append(GetAttributes(object));
447 }
448
attributes() const449 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
WebAXObjectProxy(const blink::WebAXObject & object,WebAXObjectProxy::Factory * factory)462 WebAXObjectProxy::WebAXObjectProxy(const blink::WebAXObject& object,
463 WebAXObjectProxy::Factory* factory)
464 : accessibility_object_(object),
465 factory_(factory) {
466 }
467
~WebAXObjectProxy()468 WebAXObjectProxy::~WebAXObjectProxy() {}
469
470 gin::ObjectTemplateBuilder
GetObjectTemplateBuilder(v8::Isolate * isolate)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
GetChildAtIndex(unsigned index)558 v8::Handle<v8::Object> WebAXObjectProxy::GetChildAtIndex(unsigned index) {
559 return factory_->GetOrCreate(accessibility_object().childAt(index));
560 }
561
IsRoot() const562 bool WebAXObjectProxy::IsRoot() const {
563 return false;
564 }
565
IsEqualToObject(const blink::WebAXObject & other)566 bool WebAXObjectProxy::IsEqualToObject(const blink::WebAXObject& other) {
567 return accessibility_object().equals(other);
568 }
569
NotificationReceived(blink::WebFrame * frame,const std::string & notification_name)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
Role()594 std::string WebAXObjectProxy::Role() {
595 return GetRole(accessibility_object());
596 }
597
Title()598 std::string WebAXObjectProxy::Title() {
599 return GetTitle(accessibility_object());
600 }
601
Description()602 std::string WebAXObjectProxy::Description() {
603 return GetDescription(accessibility_object());
604 }
605
HelpText()606 std::string WebAXObjectProxy::HelpText() {
607 return GetHelpText(accessibility_object());
608 }
609
StringValue()610 std::string WebAXObjectProxy::StringValue() {
611 return GetStringValue(accessibility_object());
612 }
613
X()614 int WebAXObjectProxy::X() {
615 accessibility_object_.updateBackingStoreAndCheckValidity();
616 return accessibility_object().boundingBoxRect().x;
617 }
618
Y()619 int WebAXObjectProxy::Y() {
620 accessibility_object_.updateBackingStoreAndCheckValidity();
621 return accessibility_object().boundingBoxRect().y;
622 }
623
Width()624 int WebAXObjectProxy::Width() {
625 accessibility_object_.updateBackingStoreAndCheckValidity();
626 return accessibility_object().boundingBoxRect().width;
627 }
628
Height()629 int WebAXObjectProxy::Height() {
630 accessibility_object_.updateBackingStoreAndCheckValidity();
631 return accessibility_object().boundingBoxRect().height;
632 }
633
IntValue()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
MinValue()643 int WebAXObjectProxy::MinValue() {
644 return accessibility_object().minValueForRange();
645 }
646
MaxValue()647 int WebAXObjectProxy::MaxValue() {
648 return accessibility_object().maxValueForRange();
649 }
650
ValueDescription()651 std::string WebAXObjectProxy::ValueDescription() {
652 return GetValueDescription(accessibility_object());
653 }
654
ChildrenCount()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
InsertionPointLineNumber()662 int WebAXObjectProxy::InsertionPointLineNumber() {
663 if (!accessibility_object().isFocused())
664 return -1;
665 return accessibility_object().selectionEndLineNumber();
666 }
667
SelectedTextRange()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
IsEnabled()675 bool WebAXObjectProxy::IsEnabled() {
676 return accessibility_object().isEnabled();
677 }
678
IsRequired()679 bool WebAXObjectProxy::IsRequired() {
680 return accessibility_object().isRequired();
681 }
682
IsFocused()683 bool WebAXObjectProxy::IsFocused() {
684 return accessibility_object().isFocused();
685 }
686
IsFocusable()687 bool WebAXObjectProxy::IsFocusable() {
688 return accessibility_object().canSetFocusAttribute();
689 }
690
IsSelected()691 bool WebAXObjectProxy::IsSelected() {
692 return accessibility_object().isSelected();
693 }
694
IsSelectable()695 bool WebAXObjectProxy::IsSelectable() {
696 return accessibility_object().canSetSelectedAttribute();
697 }
698
IsMultiSelectable()699 bool WebAXObjectProxy::IsMultiSelectable() {
700 return accessibility_object().isMultiSelectable();
701 }
702
IsSelectedOptionActive()703 bool WebAXObjectProxy::IsSelectedOptionActive() {
704 return accessibility_object().isSelectedOptionActive();
705 }
706
IsExpanded()707 bool WebAXObjectProxy::IsExpanded() {
708 return !accessibility_object().isCollapsed();
709 }
710
IsChecked()711 bool WebAXObjectProxy::IsChecked() {
712 return accessibility_object().isChecked();
713 }
714
IsVisible()715 bool WebAXObjectProxy::IsVisible() {
716 return accessibility_object().isVisible();
717 }
718
IsOffScreen()719 bool WebAXObjectProxy::IsOffScreen() {
720 return accessibility_object().isOffScreen();
721 }
722
IsCollapsed()723 bool WebAXObjectProxy::IsCollapsed() {
724 return accessibility_object().isCollapsed();
725 }
726
HasPopup()727 bool WebAXObjectProxy::HasPopup() {
728 return accessibility_object().ariaHasPopup();
729 }
730
IsValid()731 bool WebAXObjectProxy::IsValid() {
732 return !accessibility_object().isDetached();
733 }
734
IsReadOnly()735 bool WebAXObjectProxy::IsReadOnly() {
736 return accessibility_object().isReadOnly();
737 }
738
Orientation()739 std::string WebAXObjectProxy::Orientation() {
740 return GetOrientation(accessibility_object());
741 }
742
ClickPointX()743 int WebAXObjectProxy::ClickPointX() {
744 return accessibility_object().clickPoint().x;
745 }
746
ClickPointY()747 int WebAXObjectProxy::ClickPointY() {
748 return accessibility_object().clickPoint().y;
749 }
750
RowCount()751 int32_t WebAXObjectProxy::RowCount() {
752 return static_cast<int32_t>(accessibility_object().rowCount());
753 }
754
ColumnCount()755 int32_t WebAXObjectProxy::ColumnCount() {
756 return static_cast<int32_t>(accessibility_object().columnCount());
757 }
758
IsClickable()759 bool WebAXObjectProxy::IsClickable() {
760 return accessibility_object().isClickable();
761 }
762
AllAttributes()763 std::string WebAXObjectProxy::AllAttributes() {
764 return GetAttributes(accessibility_object());
765 }
766
AttributesOfChildren()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
LineForIndex(int index)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
BoundsForRange(int start,int end)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
ChildAtIndex(int index)811 v8::Handle<v8::Object> WebAXObjectProxy::ChildAtIndex(int index) {
812 return GetChildAtIndex(index);
813 }
814
ElementAtPoint(int x,int y)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
TableHeader()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
RowIndexRange()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
ColumnIndexRange()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
CellForColumnAndRow(int column,int row)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
TitleUIElement()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
SetSelectedTextRange(int selection_start,int length)862 void WebAXObjectProxy::SetSelectedTextRange(int selection_start,
863 int length) {
864 accessibility_object().setSelectedTextRange(selection_start,
865 selection_start + length);
866 }
867
IsAttributeSettable(const std::string & attribute)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
IsPressActionSupported()875 bool WebAXObjectProxy::IsPressActionSupported() {
876 return accessibility_object().canPress();
877 }
878
IsIncrementActionSupported()879 bool WebAXObjectProxy::IsIncrementActionSupported() {
880 return accessibility_object().canIncrement();
881 }
882
IsDecrementActionSupported()883 bool WebAXObjectProxy::IsDecrementActionSupported() {
884 return accessibility_object().canDecrement();
885 }
886
ParentElement()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
Increment()894 void WebAXObjectProxy::Increment() {
895 accessibility_object().increment();
896 }
897
Decrement()898 void WebAXObjectProxy::Decrement() {
899 accessibility_object().decrement();
900 }
901
ShowMenu()902 void WebAXObjectProxy::ShowMenu() {
903 }
904
Press()905 void WebAXObjectProxy::Press() {
906 accessibility_object().press();
907 }
908
IsEqual(v8::Handle<v8::Object> proxy)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
SetNotificationListener(v8::Handle<v8::Function> callback)916 void WebAXObjectProxy::SetNotificationListener(
917 v8::Handle<v8::Function> callback) {
918 v8::Isolate* isolate = blink::mainThreadIsolate();
919 notification_callback_.Reset(isolate, callback);
920 }
921
UnsetNotificationListener()922 void WebAXObjectProxy::UnsetNotificationListener() {
923 notification_callback_.Reset();
924 }
925
TakeFocus()926 void WebAXObjectProxy::TakeFocus() {
927 accessibility_object().setFocused(true);
928 }
929
ScrollToMakeVisible()930 void WebAXObjectProxy::ScrollToMakeVisible() {
931 accessibility_object().scrollToMakeVisible();
932 }
933
ScrollToMakeVisibleWithSubFocus(int x,int y,int width,int height)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
ScrollToGlobalPoint(int x,int y)940 void WebAXObjectProxy::ScrollToGlobalPoint(int x, int y) {
941 accessibility_object().scrollToGlobalPoint(blink::WebPoint(x, y));
942 }
943
WordStart(int character_index)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
WordEnd(int character_index)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
RootWebAXObjectProxy(const blink::WebAXObject & object,Factory * factory)964 RootWebAXObjectProxy::RootWebAXObjectProxy(
965 const blink::WebAXObject &object, Factory *factory)
966 : WebAXObjectProxy(object, factory) {
967 }
968
GetChildAtIndex(unsigned index)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
IsRoot() const976 bool RootWebAXObjectProxy::IsRoot() const {
977 return true;
978 }
979
WebAXObjectProxyList()980 WebAXObjectProxyList::WebAXObjectProxyList()
981 : elements_(blink::mainThreadIsolate()) {
982 }
983
~WebAXObjectProxyList()984 WebAXObjectProxyList::~WebAXObjectProxyList() {
985 Clear();
986 }
987
Clear()988 void WebAXObjectProxyList::Clear() {
989 elements_.Clear();
990 }
991
GetOrCreate(const blink::WebAXObject & object)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
CreateRoot(const blink::WebAXObject & object)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