1/*
2 * Copyright (C) 2010 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 *     * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *     * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 *     * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include "config.h"
32#include "AccessibilityUIElement.h"
33
34#include "WebAccessibilityObject.h"
35#include "WebCString.h"
36#include "WebString.h"
37#include <wtf/Assertions.h>
38
39using namespace WebKit;
40using namespace std;
41
42// Map role value to string, matching Safari/Mac platform implementation to
43// avoid rebaselining layout tests.
44static string roleToString(WebAccessibilityRole role)
45{
46    string result = "AXRole: AX";
47    switch (role) {
48    case WebAccessibilityRoleButton:
49        return result.append("Button");
50    case WebAccessibilityRoleRadioButton:
51        return result.append("RadioButton");
52    case WebAccessibilityRoleCheckBox:
53        return result.append("CheckBox");
54    case WebAccessibilityRoleSlider:
55        return result.append("Slider");
56    case WebAccessibilityRoleTabGroup:
57        return result.append("TabGroup");
58    case WebAccessibilityRoleTextField:
59        return result.append("TextField");
60    case WebAccessibilityRoleStaticText:
61        return result.append("StaticText");
62    case WebAccessibilityRoleTextArea:
63        return result.append("TextArea");
64    case WebAccessibilityRoleScrollArea:
65        return result.append("ScrollArea");
66    case WebAccessibilityRolePopUpButton:
67        return result.append("PopUpButton");
68    case WebAccessibilityRoleMenuButton:
69        return result.append("MenuButton");
70    case WebAccessibilityRoleTable:
71        return result.append("Table");
72    case WebAccessibilityRoleApplication:
73        return result.append("Application");
74    case WebAccessibilityRoleGroup:
75        return result.append("Group");
76    case WebAccessibilityRoleRadioGroup:
77        return result.append("RadioGroup");
78    case WebAccessibilityRoleList:
79        return result.append("List");
80    case WebAccessibilityRoleScrollBar:
81        return result.append("ScrollBar");
82    case WebAccessibilityRoleValueIndicator:
83        return result.append("ValueIndicator");
84    case WebAccessibilityRoleImage:
85        return result.append("Image");
86    case WebAccessibilityRoleMenuBar:
87        return result.append("MenuBar");
88    case WebAccessibilityRoleMenu:
89        return result.append("Menu");
90    case WebAccessibilityRoleMenuItem:
91        return result.append("MenuItem");
92    case WebAccessibilityRoleColumn:
93        return result.append("Column");
94    case WebAccessibilityRoleRow:
95        return result.append("Row");
96    case WebAccessibilityRoleToolbar:
97        return result.append("Toolbar");
98    case WebAccessibilityRoleBusyIndicator:
99        return result.append("BusyIndicator");
100    case WebAccessibilityRoleProgressIndicator:
101        return result.append("ProgressIndicator");
102    case WebAccessibilityRoleWindow:
103        return result.append("Window");
104    case WebAccessibilityRoleDrawer:
105        return result.append("Drawer");
106    case WebAccessibilityRoleSystemWide:
107        return result.append("SystemWide");
108    case WebAccessibilityRoleOutline:
109        return result.append("Outline");
110    case WebAccessibilityRoleIncrementor:
111        return result.append("Incrementor");
112    case WebAccessibilityRoleBrowser:
113        return result.append("Browser");
114    case WebAccessibilityRoleComboBox:
115        return result.append("ComboBox");
116    case WebAccessibilityRoleSplitGroup:
117        return result.append("SplitGroup");
118    case WebAccessibilityRoleSplitter:
119        return result.append("Splitter");
120    case WebAccessibilityRoleColorWell:
121        return result.append("ColorWell");
122    case WebAccessibilityRoleGrowArea:
123        return result.append("GrowArea");
124    case WebAccessibilityRoleSheet:
125        return result.append("Sheet");
126    case WebAccessibilityRoleHelpTag:
127        return result.append("HelpTag");
128    case WebAccessibilityRoleMatte:
129        return result.append("Matte");
130    case WebAccessibilityRoleRuler:
131        return result.append("Ruler");
132    case WebAccessibilityRoleRulerMarker:
133        return result.append("RulerMarker");
134    case WebAccessibilityRoleLink:
135        return result.append("Link");
136    case WebAccessibilityRoleDisclosureTriangle:
137        return result.append("DisclosureTriangle");
138    case WebAccessibilityRoleGrid:
139        return result.append("Grid");
140    case WebAccessibilityRoleCell:
141        return result.append("Cell");
142    case WebAccessibilityRoleColumnHeader:
143        return result.append("ColumnHeader");
144    case WebAccessibilityRoleRowHeader:
145        return result.append("RowHeader");
146    case WebAccessibilityRoleWebCoreLink:
147        // Maps to Link role.
148        return result.append("Link");
149    case WebAccessibilityRoleImageMapLink:
150        return result.append("ImageMapLink");
151    case WebAccessibilityRoleImageMap:
152        return result.append("ImageMap");
153    case WebAccessibilityRoleListMarker:
154        return result.append("ListMarker");
155    case WebAccessibilityRoleWebArea:
156        return result.append("WebArea");
157    case WebAccessibilityRoleHeading:
158        return result.append("Heading");
159    case WebAccessibilityRoleListBox:
160        return result.append("ListBox");
161    case WebAccessibilityRoleListBoxOption:
162        return result.append("ListBoxOption");
163    case WebAccessibilityRoleTableHeaderContainer:
164        return result.append("TableHeaderContainer");
165    case WebAccessibilityRoleDefinitionListTerm:
166        return result.append("DefinitionListTerm");
167    case WebAccessibilityRoleDefinitionListDefinition:
168        return result.append("DefinitionListDefinition");
169    case WebAccessibilityRoleAnnotation:
170        return result.append("Annotation");
171    case WebAccessibilityRoleSliderThumb:
172        return result.append("SliderThumb");
173    case WebAccessibilityRoleLandmarkApplication:
174        return result.append("LandmarkApplication");
175    case WebAccessibilityRoleLandmarkBanner:
176        return result.append("LandmarkBanner");
177    case WebAccessibilityRoleLandmarkComplementary:
178        return result.append("LandmarkComplementary");
179    case WebAccessibilityRoleLandmarkContentInfo:
180        return result.append("LandmarkContentInfo");
181    case WebAccessibilityRoleLandmarkMain:
182        return result.append("LandmarkMain");
183    case WebAccessibilityRoleLandmarkNavigation:
184        return result.append("LandmarkNavigation");
185    case WebAccessibilityRoleLandmarkSearch:
186        return result.append("LandmarkSearch");
187    case WebAccessibilityRoleApplicationLog:
188        return result.append("ApplicationLog");
189    case WebAccessibilityRoleApplicationMarquee:
190        return result.append("ApplicationMarquee");
191    case WebAccessibilityRoleApplicationStatus:
192        return result.append("ApplicationStatus");
193    case WebAccessibilityRoleApplicationTimer:
194        return result.append("ApplicationTimer");
195    case WebAccessibilityRoleDocument:
196        return result.append("Document");
197    case WebAccessibilityRoleDocumentArticle:
198        return result.append("DocumentArticle");
199    case WebAccessibilityRoleDocumentNote:
200        return result.append("DocumentNote");
201    case WebAccessibilityRoleDocumentRegion:
202        return result.append("DocumentRegion");
203    case WebAccessibilityRoleUserInterfaceTooltip:
204        return result.append("UserInterfaceTooltip");
205    default:
206        // Also matches WebAccessibilityRoleUnknown.
207        return result.append("Unknown");
208    }
209}
210
211string getDescription(const WebAccessibilityObject& object)
212{
213    string description = object.accessibilityDescription().utf8();
214    return description.insert(0, "AXDescription: ");
215}
216
217string getRole(const WebAccessibilityObject& object)
218{
219    return roleToString(object.roleValue());
220}
221
222string getTitle(const WebAccessibilityObject& object)
223{
224    string title = object.title().utf8();
225    return title.insert(0, "AXTitle: ");
226}
227
228string getAttributes(const WebAccessibilityObject& object)
229{
230    // FIXME: Concatenate all attributes of the AccessibilityObject.
231    string attributes(getTitle(object));
232    attributes.append("\n");
233    attributes.append(getRole(object));
234    attributes.append("\n");
235    attributes.append(getDescription(object));
236    return attributes;
237}
238
239
240// Collects attributes into a string, delimited by dashes. Used by all methods
241// that output lists of attributes: attributesOfLinkedUIElementsCallback,
242// AttributesOfChildrenCallback, etc.
243class AttributesCollector {
244public:
245    void collectAttributes(const WebAccessibilityObject& object)
246    {
247        m_attributes.append("\n------------\n");
248        m_attributes.append(getAttributes(object));
249    }
250
251    string attributes() const { return m_attributes; }
252
253private:
254    string m_attributes;
255};
256
257AccessibilityUIElement::AccessibilityUIElement(const WebAccessibilityObject& object, Factory* factory)
258    : m_accessibilityObject(object)
259    , m_factory(factory)
260{
261
262    ASSERT(factory);
263
264    bindMethod("allAttributes", &AccessibilityUIElement::allAttributesCallback);
265    bindMethod("attributesOfLinkedUIElements",
266               &AccessibilityUIElement::attributesOfLinkedUIElementsCallback);
267    bindMethod("attributesOfDocumentLinks",
268               &AccessibilityUIElement::attributesOfDocumentLinksCallback);
269    bindMethod("attributesOfChildren",
270               &AccessibilityUIElement::attributesOfChildrenCallback);
271    bindMethod("parameterizedAttributeNames",
272               &AccessibilityUIElement::parametrizedAttributeNamesCallback);
273    bindMethod("lineForIndex", &AccessibilityUIElement::lineForIndexCallback);
274    bindMethod("boundsForRange", &AccessibilityUIElement::boundsForRangeCallback);
275    bindMethod("stringForRange", &AccessibilityUIElement::stringForRangeCallback);
276    bindMethod("childAtIndex", &AccessibilityUIElement::childAtIndexCallback);
277    bindMethod("elementAtPoint", &AccessibilityUIElement::elementAtPointCallback);
278    bindMethod("attributesOfColumnHeaders",
279               &AccessibilityUIElement::attributesOfColumnHeadersCallback);
280    bindMethod("attributesOfRowHeaders",
281               &AccessibilityUIElement::attributesOfRowHeadersCallback);
282    bindMethod("attributesOfColumns",
283               &AccessibilityUIElement::attributesOfColumnsCallback);
284    bindMethod("attributesOfRows",
285               &AccessibilityUIElement::attributesOfRowsCallback);
286    bindMethod("attributesOfVisibleCells",
287               &AccessibilityUIElement::attributesOfVisibleCellsCallback);
288    bindMethod("attributesOfHeader",
289               &AccessibilityUIElement::attributesOfHeaderCallback);
290    bindMethod("indexInTable", &AccessibilityUIElement::indexInTableCallback);
291    bindMethod("rowIndexRange", &AccessibilityUIElement::rowIndexRangeCallback);
292    bindMethod("columnIndexRange",
293               &AccessibilityUIElement::columnIndexRangeCallback);
294    bindMethod("cellForColumnAndRow",
295               &AccessibilityUIElement::cellForColumnAndRowCallback);
296    bindMethod("titleUIElement", &AccessibilityUIElement::titleUIElementCallback);
297    bindMethod("setSelectedTextRange",
298               &AccessibilityUIElement::setSelectedTextRangeCallback);
299    bindMethod("attributeValue", &AccessibilityUIElement::attributeValueCallback);
300    bindMethod("isAttributeSettable",
301               &AccessibilityUIElement::isAttributeSettableCallback);
302    bindMethod("isActionSupported",
303               &AccessibilityUIElement::isActionSupportedCallback);
304    bindMethod("parentElement", &AccessibilityUIElement::parentElementCallback);
305    bindMethod("increment", &AccessibilityUIElement::incrementCallback);
306    bindMethod("decrement", &AccessibilityUIElement::decrementCallback);
307
308    bindProperty("role", &AccessibilityUIElement::roleGetterCallback);
309    bindProperty("subrole", &m_subrole);
310    bindProperty("title", &AccessibilityUIElement::titleGetterCallback);
311    bindProperty("description",
312                 &AccessibilityUIElement::descriptionGetterCallback);
313    bindProperty("language", &m_language);
314    bindProperty("x", &m_x);
315    bindProperty("y", &m_y);
316    bindProperty("width", &m_width);
317    bindProperty("height", &m_height);
318    bindProperty("clickPointX", &m_clickPointX);
319    bindProperty("clickPointY", &m_clickPointY);
320    bindProperty("intValue", &m_intValue);
321    bindProperty("minValue", &m_minValue);
322    bindProperty("maxValue", &m_maxValue);
323    bindProperty("childrenCount",
324                 &AccessibilityUIElement::childrenCountGetterCallback);
325    bindProperty("insertionPointLineNumber", &m_insertionPointLineNumber);
326    bindProperty("selectedTextRange", &m_selectedTextRange);
327    bindProperty("isEnabled", &AccessibilityUIElement::isEnabledGetterCallback);
328    bindProperty("isRequired", &m_isRequired);
329    bindProperty("isSelected", &AccessibilityUIElement::isSelectedGetterCallback);
330    bindProperty("valueDescription", &m_valueDescription);
331
332    bindFallbackMethod(&AccessibilityUIElement::fallbackCallback);
333}
334
335AccessibilityUIElement* AccessibilityUIElement::getChildAtIndex(unsigned index)
336{
337    return m_factory->create(accessibilityObject().childAt(index));
338}
339
340void AccessibilityUIElement::allAttributesCallback(const CppArgumentList&, CppVariant* result)
341{
342    result->set(getAttributes(accessibilityObject()));
343}
344
345void AccessibilityUIElement::attributesOfLinkedUIElementsCallback(const CppArgumentList&, CppVariant* result)
346{
347    result->setNull();
348}
349
350void AccessibilityUIElement::attributesOfDocumentLinksCallback(const CppArgumentList&, CppVariant* result)
351{
352    result->setNull();
353}
354
355void AccessibilityUIElement::attributesOfChildrenCallback(const CppArgumentList& arguments, CppVariant* result)
356{
357    AttributesCollector collector;
358    unsigned size = accessibilityObject().childCount();
359    for (unsigned i = 0; i < size; ++i)
360        collector.collectAttributes(accessibilityObject().childAt(i));
361    result->set(collector.attributes());
362}
363
364void AccessibilityUIElement::parametrizedAttributeNamesCallback(const CppArgumentList&, CppVariant* result)
365{
366    result->setNull();
367}
368
369void AccessibilityUIElement::lineForIndexCallback(const CppArgumentList&, CppVariant* result)
370{
371    result->setNull();
372}
373
374void AccessibilityUIElement::boundsForRangeCallback(const CppArgumentList&, CppVariant* result)
375{
376    result->setNull();
377}
378
379void AccessibilityUIElement::stringForRangeCallback(const CppArgumentList&, CppVariant* result)
380{
381    result->setNull();
382}
383
384void AccessibilityUIElement::childAtIndexCallback(const CppArgumentList& arguments, CppVariant* result)
385{
386    if (!arguments.size() || !arguments[0].isNumber()) {
387        result->setNull();
388        return;
389    }
390
391    AccessibilityUIElement* child = getChildAtIndex(arguments[0].toInt32());
392    if (!child) {
393        result->setNull();
394        return;
395    }
396
397    result->set(*(child->getAsCppVariant()));
398}
399
400void AccessibilityUIElement::elementAtPointCallback(const CppArgumentList&, CppVariant* result)
401{
402    result->setNull();
403}
404
405void AccessibilityUIElement::attributesOfColumnHeadersCallback(const CppArgumentList&, CppVariant* result)
406{
407    result->setNull();
408}
409
410void AccessibilityUIElement::attributesOfRowHeadersCallback(const CppArgumentList&, CppVariant* result)
411{
412    result->setNull();
413}
414
415void AccessibilityUIElement::attributesOfColumnsCallback(const CppArgumentList&, CppVariant* result)
416{
417    result->setNull();
418}
419
420void AccessibilityUIElement::attributesOfRowsCallback(const CppArgumentList&, CppVariant* result)
421{
422    result->setNull();
423}
424
425void AccessibilityUIElement::attributesOfVisibleCellsCallback(const CppArgumentList&, CppVariant* result)
426{
427    result->setNull();
428}
429
430void AccessibilityUIElement::attributesOfHeaderCallback(const CppArgumentList&, CppVariant* result)
431{
432    result->setNull();
433}
434
435void AccessibilityUIElement::indexInTableCallback(const CppArgumentList&, CppVariant* result)
436{
437    result->setNull();
438}
439
440void AccessibilityUIElement::rowIndexRangeCallback(const CppArgumentList&, CppVariant* result)
441{
442    result->setNull();
443}
444
445void AccessibilityUIElement::columnIndexRangeCallback(const CppArgumentList&, CppVariant* result)
446{
447    result->setNull();
448}
449
450void AccessibilityUIElement::cellForColumnAndRowCallback(const CppArgumentList&, CppVariant* result)
451{
452    result->setNull();
453}
454
455void AccessibilityUIElement::titleUIElementCallback(const CppArgumentList&, CppVariant* result)
456{
457    result->setNull();
458}
459
460void AccessibilityUIElement::setSelectedTextRangeCallback(const CppArgumentList&, CppVariant* result)
461{
462    result->setNull();
463}
464
465void AccessibilityUIElement::attributeValueCallback(const CppArgumentList&, CppVariant* result)
466{
467    result->setNull();
468}
469
470void AccessibilityUIElement::isAttributeSettableCallback(const CppArgumentList& arguments, CppVariant* result)
471{
472    if (arguments.size() < 1 && !arguments[0].isString()) {
473        result->setNull();
474        return;
475    }
476
477    string attribute = arguments[0].toString();
478    bool settable = false;
479    if (attribute == "AXValue")
480        settable = accessibilityObject().canSetValueAttribute();
481    result->set(settable);
482}
483
484void AccessibilityUIElement::isActionSupportedCallback(const CppArgumentList&, CppVariant* result)
485{
486    // This one may be really hard to implement.
487    // Not exposed by AccessibilityObject.
488    result->setNull();
489}
490
491void AccessibilityUIElement::parentElementCallback(const CppArgumentList&, CppVariant* result)
492{
493    result->setNull();
494}
495
496void AccessibilityUIElement::incrementCallback(const CppArgumentList&, CppVariant* result)
497{
498    result->setNull();
499}
500
501void AccessibilityUIElement::decrementCallback(const CppArgumentList&, CppVariant* result)
502{
503    result->setNull();
504}
505
506void AccessibilityUIElement::fallbackCallback(const CppArgumentList &, CppVariant* result)
507{
508    // FIXME: Implement this.
509    result->setNull();
510}
511
512void AccessibilityUIElement::childrenCountGetterCallback(CppVariant* result)
513{
514    int count = 1; // Root object always has only one child, the WebView.
515    if (!isRoot())
516        count = accessibilityObject().childCount();
517    result->set(count);
518}
519
520void AccessibilityUIElement::descriptionGetterCallback(CppVariant* result)
521{
522    result->set(getDescription(accessibilityObject()));
523}
524
525void AccessibilityUIElement::isEnabledGetterCallback(CppVariant* result)
526{
527    result->set(accessibilityObject().isEnabled());
528}
529
530void AccessibilityUIElement::isSelectedGetterCallback(CppVariant* result)
531{
532    result->setNull();
533}
534
535void AccessibilityUIElement::roleGetterCallback(CppVariant* result)
536{
537    result->set(getRole(accessibilityObject()));
538}
539
540void AccessibilityUIElement::titleGetterCallback(CppVariant* result)
541{
542    result->set(getTitle(accessibilityObject()));
543}
544
545
546RootAccessibilityUIElement::RootAccessibilityUIElement(const WebAccessibilityObject &object, Factory *factory)
547    : AccessibilityUIElement(object, factory) { }
548
549AccessibilityUIElement* RootAccessibilityUIElement::getChildAtIndex(unsigned index)
550{
551    if (index)
552        return 0;
553
554    return factory()->create(accessibilityObject());
555}
556
557
558AccessibilityUIElementList ::~AccessibilityUIElementList()
559{
560    clear();
561}
562
563void AccessibilityUIElementList::clear()
564{
565    for (ElementList::iterator i = m_elements.begin(); i != m_elements.end(); ++i)
566        delete (*i);
567    m_elements.clear();
568}
569
570AccessibilityUIElement* AccessibilityUIElementList::create(const WebAccessibilityObject& object)
571{
572    if (object.isNull())
573        return 0;
574
575    AccessibilityUIElement* element = new AccessibilityUIElement(object, this);
576    m_elements.append(element);
577    return element;
578}
579
580AccessibilityUIElement* AccessibilityUIElementList::createRoot(const WebAccessibilityObject& object)
581{
582    AccessibilityUIElement* element = new RootAccessibilityUIElement(object, this);
583    m_elements.append(element);
584    return element;
585}
586