/*
* Copyright (C) 2010 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "AccessibilityUIElement.h"
#include "WebAccessibilityObject.h"
#include "WebCString.h"
#include "WebString.h"
#include <wtf/Assertions.h>
using namespace WebKit;
using namespace std;
// Map role value to string, matching Safari/Mac platform implementation to
// avoid rebaselining layout tests.
static string roleToString(WebAccessibilityRole role)
{
string result = "AXRole: AX";
switch (role) {
case WebAccessibilityRoleButton:
return result.append("Button");
case WebAccessibilityRoleRadioButton:
return result.append("RadioButton");
case WebAccessibilityRoleCheckBox:
return result.append("CheckBox");
case WebAccessibilityRoleSlider:
return result.append("Slider");
case WebAccessibilityRoleTabGroup:
return result.append("TabGroup");
case WebAccessibilityRoleTextField:
return result.append("TextField");
case WebAccessibilityRoleStaticText:
return result.append("StaticText");
case WebAccessibilityRoleTextArea:
return result.append("TextArea");
case WebAccessibilityRoleScrollArea:
return result.append("ScrollArea");
case WebAccessibilityRolePopUpButton:
return result.append("PopUpButton");
case WebAccessibilityRoleMenuButton:
return result.append("MenuButton");
case WebAccessibilityRoleTable:
return result.append("Table");
case WebAccessibilityRoleApplication:
return result.append("Application");
case WebAccessibilityRoleGroup:
return result.append("Group");
case WebAccessibilityRoleRadioGroup:
return result.append("RadioGroup");
case WebAccessibilityRoleList:
return result.append("List");
case WebAccessibilityRoleScrollBar:
return result.append("ScrollBar");
case WebAccessibilityRoleValueIndicator:
return result.append("ValueIndicator");
case WebAccessibilityRoleImage:
return result.append("Image");
case WebAccessibilityRoleMenuBar:
return result.append("MenuBar");
case WebAccessibilityRoleMenu:
return result.append("Menu");
case WebAccessibilityRoleMenuItem:
return result.append("MenuItem");
case WebAccessibilityRoleColumn:
return result.append("Column");
case WebAccessibilityRoleRow:
return result.append("Row");
case WebAccessibilityRoleToolbar:
return result.append("Toolbar");
case WebAccessibilityRoleBusyIndicator:
return result.append("BusyIndicator");
case WebAccessibilityRoleProgressIndicator:
return result.append("ProgressIndicator");
case WebAccessibilityRoleWindow:
return result.append("Window");
case WebAccessibilityRoleDrawer:
return result.append("Drawer");
case WebAccessibilityRoleSystemWide:
return result.append("SystemWide");
case WebAccessibilityRoleOutline:
return result.append("Outline");
case WebAccessibilityRoleIncrementor:
return result.append("Incrementor");
case WebAccessibilityRoleBrowser:
return result.append("Browser");
case WebAccessibilityRoleComboBox:
return result.append("ComboBox");
case WebAccessibilityRoleSplitGroup:
return result.append("SplitGroup");
case WebAccessibilityRoleSplitter:
return result.append("Splitter");
case WebAccessibilityRoleColorWell:
return result.append("ColorWell");
case WebAccessibilityRoleGrowArea:
return result.append("GrowArea");
case WebAccessibilityRoleSheet:
return result.append("Sheet");
case WebAccessibilityRoleHelpTag:
return result.append("HelpTag");
case WebAccessibilityRoleMatte:
return result.append("Matte");
case WebAccessibilityRoleRuler:
return result.append("Ruler");
case WebAccessibilityRoleRulerMarker:
return result.append("RulerMarker");
case WebAccessibilityRoleLink:
return result.append("Link");
case WebAccessibilityRoleDisclosureTriangle:
return result.append("DisclosureTriangle");
case WebAccessibilityRoleGrid:
return result.append("Grid");
case WebAccessibilityRoleCell:
return result.append("Cell");
case WebAccessibilityRoleColumnHeader:
return result.append("ColumnHeader");
case WebAccessibilityRoleRowHeader:
return result.append("RowHeader");
case WebAccessibilityRoleWebCoreLink:
// Maps to Link role.
return result.append("Link");
case WebAccessibilityRoleImageMapLink:
return result.append("ImageMapLink");
case WebAccessibilityRoleImageMap:
return result.append("ImageMap");
case WebAccessibilityRoleListMarker:
return result.append("ListMarker");
case WebAccessibilityRoleWebArea:
return result.append("WebArea");
case WebAccessibilityRoleHeading:
return result.append("Heading");
case WebAccessibilityRoleListBox:
return result.append("ListBox");
case WebAccessibilityRoleListBoxOption:
return result.append("ListBoxOption");
case WebAccessibilityRoleTableHeaderContainer:
return result.append("TableHeaderContainer");
case WebAccessibilityRoleDefinitionListTerm:
return result.append("DefinitionListTerm");
case WebAccessibilityRoleDefinitionListDefinition:
return result.append("DefinitionListDefinition");
case WebAccessibilityRoleAnnotation:
return result.append("Annotation");
case WebAccessibilityRoleSliderThumb:
return result.append("SliderThumb");
case WebAccessibilityRoleLandmarkApplication:
return result.append("LandmarkApplication");
case WebAccessibilityRoleLandmarkBanner:
return result.append("LandmarkBanner");
case WebAccessibilityRoleLandmarkComplementary:
return result.append("LandmarkComplementary");
case WebAccessibilityRoleLandmarkContentInfo:
return result.append("LandmarkContentInfo");
case WebAccessibilityRoleLandmarkMain:
return result.append("LandmarkMain");
case WebAccessibilityRoleLandmarkNavigation:
return result.append("LandmarkNavigation");
case WebAccessibilityRoleLandmarkSearch:
return result.append("LandmarkSearch");
case WebAccessibilityRoleApplicationLog:
return result.append("ApplicationLog");
case WebAccessibilityRoleApplicationMarquee:
return result.append("ApplicationMarquee");
case WebAccessibilityRoleApplicationStatus:
return result.append("ApplicationStatus");
case WebAccessibilityRoleApplicationTimer:
return result.append("ApplicationTimer");
case WebAccessibilityRoleDocument:
return result.append("Document");
case WebAccessibilityRoleDocumentArticle:
return result.append("DocumentArticle");
case WebAccessibilityRoleDocumentNote:
return result.append("DocumentNote");
case WebAccessibilityRoleDocumentRegion:
return result.append("DocumentRegion");
case WebAccessibilityRoleUserInterfaceTooltip:
return result.append("UserInterfaceTooltip");
default:
// Also matches WebAccessibilityRoleUnknown.
return result.append("Unknown");
}
}
string getDescription(const WebAccessibilityObject& object)
{
string description = object.accessibilityDescription().utf8();
return description.insert(0, "AXDescription: ");
}
string getRole(const WebAccessibilityObject& object)
{
return roleToString(object.roleValue());
}
string getTitle(const WebAccessibilityObject& object)
{
string title = object.title().utf8();
return title.insert(0, "AXTitle: ");
}
string getAttributes(const WebAccessibilityObject& object)
{
// FIXME: Concatenate all attributes of the AccessibilityObject.
string attributes(getTitle(object));
attributes.append("\n");
attributes.append(getRole(object));
attributes.append("\n");
attributes.append(getDescription(object));
return attributes;
}
// Collects attributes into a string, delimited by dashes. Used by all methods
// that output lists of attributes: attributesOfLinkedUIElementsCallback,
// AttributesOfChildrenCallback, etc.
class AttributesCollector {
public:
void collectAttributes(const WebAccessibilityObject& object)
{
m_attributes.append("\n------------\n");
m_attributes.append(getAttributes(object));
}
string attributes() const { return m_attributes; }
private:
string m_attributes;
};
AccessibilityUIElement::AccessibilityUIElement(const WebAccessibilityObject& object, Factory* factory)
: m_accessibilityObject(object)
, m_factory(factory)
{
ASSERT(factory);
bindMethod("allAttributes", &AccessibilityUIElement::allAttributesCallback);
bindMethod("attributesOfLinkedUIElements",
&AccessibilityUIElement::attributesOfLinkedUIElementsCallback);
bindMethod("attributesOfDocumentLinks",
&AccessibilityUIElement::attributesOfDocumentLinksCallback);
bindMethod("attributesOfChildren",
&AccessibilityUIElement::attributesOfChildrenCallback);
bindMethod("parameterizedAttributeNames",
&AccessibilityUIElement::parametrizedAttributeNamesCallback);
bindMethod("lineForIndex", &AccessibilityUIElement::lineForIndexCallback);
bindMethod("boundsForRange", &AccessibilityUIElement::boundsForRangeCallback);
bindMethod("stringForRange", &AccessibilityUIElement::stringForRangeCallback);
bindMethod("childAtIndex", &AccessibilityUIElement::childAtIndexCallback);
bindMethod("elementAtPoint", &AccessibilityUIElement::elementAtPointCallback);
bindMethod("attributesOfColumnHeaders",
&AccessibilityUIElement::attributesOfColumnHeadersCallback);
bindMethod("attributesOfRowHeaders",
&AccessibilityUIElement::attributesOfRowHeadersCallback);
bindMethod("attributesOfColumns",
&AccessibilityUIElement::attributesOfColumnsCallback);
bindMethod("attributesOfRows",
&AccessibilityUIElement::attributesOfRowsCallback);
bindMethod("attributesOfVisibleCells",
&AccessibilityUIElement::attributesOfVisibleCellsCallback);
bindMethod("attributesOfHeader",
&AccessibilityUIElement::attributesOfHeaderCallback);
bindMethod("indexInTable", &AccessibilityUIElement::indexInTableCallback);
bindMethod("rowIndexRange", &AccessibilityUIElement::rowIndexRangeCallback);
bindMethod("columnIndexRange",
&AccessibilityUIElement::columnIndexRangeCallback);
bindMethod("cellForColumnAndRow",
&AccessibilityUIElement::cellForColumnAndRowCallback);
bindMethod("titleUIElement", &AccessibilityUIElement::titleUIElementCallback);
bindMethod("setSelectedTextRange",
&AccessibilityUIElement::setSelectedTextRangeCallback);
bindMethod("attributeValue", &AccessibilityUIElement::attributeValueCallback);
bindMethod("isAttributeSettable",
&AccessibilityUIElement::isAttributeSettableCallback);
bindMethod("isActionSupported",
&AccessibilityUIElement::isActionSupportedCallback);
bindMethod("parentElement", &AccessibilityUIElement::parentElementCallback);
bindMethod("increment", &AccessibilityUIElement::incrementCallback);
bindMethod("decrement", &AccessibilityUIElement::decrementCallback);
bindProperty("role", &AccessibilityUIElement::roleGetterCallback);
bindProperty("subrole", &m_subrole);
bindProperty("title", &AccessibilityUIElement::titleGetterCallback);
bindProperty("description",
&AccessibilityUIElement::descriptionGetterCallback);
bindProperty("language", &m_language);
bindProperty("x", &m_x);
bindProperty("y", &m_y);
bindProperty("width", &m_width);
bindProperty("height", &m_height);
bindProperty("clickPointX", &m_clickPointX);
bindProperty("clickPointY", &m_clickPointY);
bindProperty("intValue", &m_intValue);
bindProperty("minValue", &m_minValue);
bindProperty("maxValue", &m_maxValue);
bindProperty("childrenCount",
&AccessibilityUIElement::childrenCountGetterCallback);
bindProperty("insertionPointLineNumber", &m_insertionPointLineNumber);
bindProperty("selectedTextRange", &m_selectedTextRange);
bindProperty("isEnabled", &AccessibilityUIElement::isEnabledGetterCallback);
bindProperty("isRequired", &m_isRequired);
bindProperty("isSelected", &AccessibilityUIElement::isSelectedGetterCallback);
bindProperty("valueDescription", &m_valueDescription);
bindFallbackMethod(&AccessibilityUIElement::fallbackCallback);
}
AccessibilityUIElement* AccessibilityUIElement::getChildAtIndex(unsigned index)
{
return m_factory->create(accessibilityObject().childAt(index));
}
void AccessibilityUIElement::allAttributesCallback(const CppArgumentList&, CppVariant* result)
{
result->set(getAttributes(accessibilityObject()));
}
void AccessibilityUIElement::attributesOfLinkedUIElementsCallback(const CppArgumentList&, CppVariant* result)
{
result->setNull();
}
void AccessibilityUIElement::attributesOfDocumentLinksCallback(const CppArgumentList&, CppVariant* result)
{
result->setNull();
}
void AccessibilityUIElement::attributesOfChildrenCallback(const CppArgumentList& arguments, CppVariant* result)
{
AttributesCollector collector;
unsigned size = accessibilityObject().childCount();
for (unsigned i = 0; i < size; ++i)
collector.collectAttributes(accessibilityObject().childAt(i));
result->set(collector.attributes());
}
void AccessibilityUIElement::parametrizedAttributeNamesCallback(const CppArgumentList&, CppVariant* result)
{
result->setNull();
}
void AccessibilityUIElement::lineForIndexCallback(const CppArgumentList&, CppVariant* result)
{
result->setNull();
}
void AccessibilityUIElement::boundsForRangeCallback(const CppArgumentList&, CppVariant* result)
{
result->setNull();
}
void AccessibilityUIElement::stringForRangeCallback(const CppArgumentList&, CppVariant* result)
{
result->setNull();
}
void AccessibilityUIElement::childAtIndexCallback(const CppArgumentList& arguments, CppVariant* result)
{
if (!arguments.size() || !arguments[0].isNumber()) {
result->setNull();
return;
}
AccessibilityUIElement* child = getChildAtIndex(arguments[0].toInt32());
if (!child) {
result->setNull();
return;
}
result->set(*(child->getAsCppVariant()));
}
void AccessibilityUIElement::elementAtPointCallback(const CppArgumentList&, CppVariant* result)
{
result->setNull();
}
void AccessibilityUIElement::attributesOfColumnHeadersCallback(const CppArgumentList&, CppVariant* result)
{
result->setNull();
}
void AccessibilityUIElement::attributesOfRowHeadersCallback(const CppArgumentList&, CppVariant* result)
{
result->setNull();
}
void AccessibilityUIElement::attributesOfColumnsCallback(const CppArgumentList&, CppVariant* result)
{
result->setNull();
}
void AccessibilityUIElement::attributesOfRowsCallback(const CppArgumentList&, CppVariant* result)
{
result->setNull();
}
void AccessibilityUIElement::attributesOfVisibleCellsCallback(const CppArgumentList&, CppVariant* result)
{
result->setNull();
}
void AccessibilityUIElement::attributesOfHeaderCallback(const CppArgumentList&, CppVariant* result)
{
result->setNull();
}
void AccessibilityUIElement::indexInTableCallback(const CppArgumentList&, CppVariant* result)
{
result->setNull();
}
void AccessibilityUIElement::rowIndexRangeCallback(const CppArgumentList&, CppVariant* result)
{
result->setNull();
}
void AccessibilityUIElement::columnIndexRangeCallback(const CppArgumentList&, CppVariant* result)
{
result->setNull();
}
void AccessibilityUIElement::cellForColumnAndRowCallback(const CppArgumentList&, CppVariant* result)
{
result->setNull();
}
void AccessibilityUIElement::titleUIElementCallback(const CppArgumentList&, CppVariant* result)
{
result->setNull();
}
void AccessibilityUIElement::setSelectedTextRangeCallback(const CppArgumentList&, CppVariant* result)
{
result->setNull();
}
void AccessibilityUIElement::attributeValueCallback(const CppArgumentList&, CppVariant* result)
{
result->setNull();
}
void AccessibilityUIElement::isAttributeSettableCallback(const CppArgumentList& arguments, CppVariant* result)
{
if (arguments.size() < 1 && !arguments[0].isString()) {
result->setNull();
return;
}
string attribute = arguments[0].toString();
bool settable = false;
if (attribute == "AXValue")
settable = accessibilityObject().canSetValueAttribute();
result->set(settable);
}
void AccessibilityUIElement::isActionSupportedCallback(const CppArgumentList&, CppVariant* result)
{
// This one may be really hard to implement.
// Not exposed by AccessibilityObject.
result->setNull();
}
void AccessibilityUIElement::parentElementCallback(const CppArgumentList&, CppVariant* result)
{
result->setNull();
}
void AccessibilityUIElement::incrementCallback(const CppArgumentList&, CppVariant* result)
{
result->setNull();
}
void AccessibilityUIElement::decrementCallback(const CppArgumentList&, CppVariant* result)
{
result->setNull();
}
void AccessibilityUIElement::fallbackCallback(const CppArgumentList &, CppVariant* result)
{
// FIXME: Implement this.
result->setNull();
}
void AccessibilityUIElement::childrenCountGetterCallback(CppVariant* result)
{
int count = 1; // Root object always has only one child, the WebView.
if (!isRoot())
count = accessibilityObject().childCount();
result->set(count);
}
void AccessibilityUIElement::descriptionGetterCallback(CppVariant* result)
{
result->set(getDescription(accessibilityObject()));
}
void AccessibilityUIElement::isEnabledGetterCallback(CppVariant* result)
{
result->set(accessibilityObject().isEnabled());
}
void AccessibilityUIElement::isSelectedGetterCallback(CppVariant* result)
{
result->setNull();
}
void AccessibilityUIElement::roleGetterCallback(CppVariant* result)
{
result->set(getRole(accessibilityObject()));
}
void AccessibilityUIElement::titleGetterCallback(CppVariant* result)
{
result->set(getTitle(accessibilityObject()));
}
RootAccessibilityUIElement::RootAccessibilityUIElement(const WebAccessibilityObject &object, Factory *factory)
: AccessibilityUIElement(object, factory) { }
AccessibilityUIElement* RootAccessibilityUIElement::getChildAtIndex(unsigned index)
{
if (index)
return 0;
return factory()->create(accessibilityObject());
}
AccessibilityUIElementList ::~AccessibilityUIElementList()
{
clear();
}
void AccessibilityUIElementList::clear()
{
for (ElementList::iterator i = m_elements.begin(); i != m_elements.end(); ++i)
delete (*i);
m_elements.clear();
}
AccessibilityUIElement* AccessibilityUIElementList::create(const WebAccessibilityObject& object)
{
if (object.isNull())
return 0;
AccessibilityUIElement* element = new AccessibilityUIElement(object, this);
m_elements.append(element);
return element;
}
AccessibilityUIElement* AccessibilityUIElementList::createRoot(const WebAccessibilityObject& object)
{
AccessibilityUIElement* element = new RootAccessibilityUIElement(object, this);
m_elements.append(element);
return element;
}