/*
* Copyright (C) 2008 Apple 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:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. 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.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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 "AccessibilityController.h"
#include "DumpRenderTree.h"
#include "FrameLoadDelegate.h"
#include <JavaScriptCore/JSStringRef.h>
#include <tchar.h>
#include <string>
using std::wstring;
static COMPtr<IAccessibleComparable> comparableObject(IAccessible* accessible)
{
COMPtr<IServiceProvider> serviceProvider(Query, accessible);
if (!serviceProvider)
return 0;
COMPtr<IAccessibleComparable> comparable;
serviceProvider->QueryService(SID_AccessibleComparable, __uuidof(IAccessibleComparable), reinterpret_cast<void**>(&comparable));
return comparable;
}
AccessibilityUIElement::AccessibilityUIElement(PlatformUIElement element)
: m_element(element)
{
}
AccessibilityUIElement::AccessibilityUIElement(const AccessibilityUIElement& other)
: m_element(other.m_element)
{
}
AccessibilityUIElement::~AccessibilityUIElement()
{
}
bool AccessibilityUIElement::isEqual(AccessibilityUIElement* otherElement)
{
COMPtr<IAccessibleComparable> comparable = comparableObject(m_element.get());
COMPtr<IAccessibleComparable> otherComparable = comparableObject(otherElement->m_element.get());
if (!comparable || !otherComparable)
return false;
BOOL isSame = FALSE;
if (FAILED(comparable->isSameObject(otherComparable.get(), &isSame)))
return false;
return isSame;
}
void AccessibilityUIElement::getLinkedUIElements(Vector<AccessibilityUIElement>&)
{
}
void AccessibilityUIElement::getDocumentLinks(Vector<AccessibilityUIElement>&)
{
}
void AccessibilityUIElement::getChildren(Vector<AccessibilityUIElement>& children)
{
long childCount;
if (FAILED(m_element->get_accChildCount(&childCount)))
return;
for (long i = 0; i < childCount; ++i)
children.append(getChildAtIndex(i));
}
void AccessibilityUIElement::getChildrenWithRange(Vector<AccessibilityUIElement>& elementVector, unsigned location, unsigned length)
{
long childCount;
unsigned appendedCount = 0;
if (FAILED(m_element->get_accChildCount(&childCount)))
return;
for (long i = location; i < childCount && appendedCount < length; ++i, ++appendedCount)
elementVector.append(getChildAtIndex(i));
}
int AccessibilityUIElement::childrenCount()
{
long childCount;
m_element->get_accChildCount(&childCount);
return childCount;
}
int AccessibilityUIElement::rowCount()
{
// FIXME: implement
return 0;
}
int AccessibilityUIElement::columnCount()
{
// FIXME: implement
return 0;
}
AccessibilityUIElement AccessibilityUIElement::elementAtPoint(int x, int y)
{
return 0;
}
AccessibilityUIElement AccessibilityUIElement::linkedUIElementAtIndex(unsigned index)
{
// FIXME: implement
return 0;
}
AccessibilityUIElement AccessibilityUIElement::getChildAtIndex(unsigned index)
{
COMPtr<IDispatch> child;
VARIANT vChild;
::VariantInit(&vChild);
V_VT(&vChild) = VT_I4;
// In MSAA, index 0 is the object itself.
V_I4(&vChild) = index + 1;
if (FAILED(m_element->get_accChild(vChild, &child)))
return 0;
return COMPtr<IAccessible>(Query, child);
}
unsigned AccessibilityUIElement::indexOfChild(AccessibilityUIElement* element)
{
// FIXME: implement
return 0;
}
JSStringRef AccessibilityUIElement::allAttributes()
{
return JSStringCreateWithCharacters(0, 0);
}
JSStringRef AccessibilityUIElement::attributesOfLinkedUIElements()
{
return JSStringCreateWithCharacters(0, 0);
}
JSStringRef AccessibilityUIElement::attributesOfDocumentLinks()
{
return JSStringCreateWithCharacters(0, 0);
}
AccessibilityUIElement AccessibilityUIElement::titleUIElement()
{
return 0;
}
AccessibilityUIElement AccessibilityUIElement::parentElement()
{
COMPtr<IDispatch> parent;
m_element->get_accParent(&parent);
COMPtr<IAccessible> parentAccessible(Query, parent);
return parentAccessible;
}
JSStringRef AccessibilityUIElement::attributesOfChildren()
{
return JSStringCreateWithCharacters(0, 0);
}
JSStringRef AccessibilityUIElement::parameterizedAttributeNames()
{
return JSStringCreateWithCharacters(0, 0);
}
static VARIANT& self()
{
static VARIANT vSelf;
static bool haveInitialized;
if (!haveInitialized) {
::VariantInit(&vSelf);
V_VT(&vSelf) = VT_I4;
V_I4(&vSelf) = CHILDID_SELF;
}
return vSelf;
}
JSStringRef AccessibilityUIElement::role()
{
VARIANT vRole;
if (FAILED(m_element->get_accRole(self(), &vRole)))
return JSStringCreateWithCharacters(0, 0);
ASSERT(V_VT(&vRole) == VT_I4 || V_VT(&vRole) == VT_BSTR);
wstring result;
if (V_VT(&vRole) == VT_I4) {
unsigned roleTextLength = ::GetRoleText(V_I4(&vRole), 0, 0) + 1;
Vector<TCHAR> roleText(roleTextLength);
::GetRoleText(V_I4(&vRole), roleText.data(), roleTextLength);
result = roleText.data();
} else if (V_VT(&vRole) == VT_BSTR)
result = wstring(V_BSTR(&vRole), ::SysStringLen(V_BSTR(&vRole)));
::VariantClear(&vRole);
return JSStringCreateWithCharacters(result.data(), result.length());
}
JSStringRef AccessibilityUIElement::subrole()
{
return 0;
}
JSStringRef AccessibilityUIElement::roleDescription()
{
return 0;
}
JSStringRef AccessibilityUIElement::title()
{
BSTR titleBSTR;
if (FAILED(m_element->get_accName(self(), &titleBSTR)) || !titleBSTR)
return JSStringCreateWithCharacters(0, 0);
wstring title(titleBSTR, SysStringLen(titleBSTR));
::SysFreeString(titleBSTR);
return JSStringCreateWithCharacters(title.data(), title.length());
}
JSStringRef AccessibilityUIElement::description()
{
BSTR descriptionBSTR;
if (FAILED(m_element->get_accDescription(self(), &descriptionBSTR)) || !descriptionBSTR)
return JSStringCreateWithCharacters(0, 0);
wstring description(descriptionBSTR, SysStringLen(descriptionBSTR));
::SysFreeString(descriptionBSTR);
return JSStringCreateWithCharacters(description.data(), description.length());
}
JSStringRef AccessibilityUIElement::stringValue()
{
return JSStringCreateWithCharacters(0, 0);
}
JSStringRef AccessibilityUIElement::language()
{
return JSStringCreateWithCharacters(0, 0);
}
JSStringRef AccessibilityUIElement::helpText() const
{
return 0;
}
double AccessibilityUIElement::x()
{
long x, y, width, height;
if (FAILED(m_element->accLocation(&x, &y, &width, &height, self())))
return 0;
return x;
}
double AccessibilityUIElement::y()
{
long x, y, width, height;
if (FAILED(m_element->accLocation(&x, &y, &width, &height, self())))
return 0;
return y;
}
double AccessibilityUIElement::width()
{
long x, y, width, height;
if (FAILED(m_element->accLocation(&x, &y, &width, &height, self())))
return 0;
return width;
}
double AccessibilityUIElement::height()
{
long x, y, width, height;
if (FAILED(m_element->accLocation(&x, &y, &width, &height, self())))
return 0;
return height;
}
double AccessibilityUIElement::clickPointX()
{
return 0;
}
double AccessibilityUIElement::clickPointY()
{
return 0;
}
JSStringRef AccessibilityUIElement::valueDescription()
{
return 0;
}
static DWORD accessibilityState(COMPtr<IAccessible> element)
{
VARIANT state;
element->get_accState(self(), &state);
ASSERT(V_VT(&state) == VT_I4);
DWORD result = state.lVal;
VariantClear(&state);
return result;
}
bool AccessibilityUIElement::isFocused() const
{
// FIXME: implement
return false;
}
bool AccessibilityUIElement::isSelected() const
{
DWORD state = accessibilityState(m_element);
return (state & STATE_SYSTEM_SELECTED) == STATE_SYSTEM_SELECTED;
}
int AccessibilityUIElement::hierarchicalLevel() const
{
return 0;
}
bool AccessibilityUIElement::ariaIsGrabbed() const
{
return false;
}
JSStringRef AccessibilityUIElement::ariaDropEffects() const
{
return 0;
}
bool AccessibilityUIElement::isExpanded() const
{
return false;
}
bool AccessibilityUIElement::isChecked() const
{
VARIANT vState;
if (FAILED(m_element->get_accState(self(), &vState)))
return false;
return vState.lVal & STATE_SYSTEM_CHECKED;
}
JSStringRef AccessibilityUIElement::orientation() const
{
return 0;
}
double AccessibilityUIElement::intValue() const
{
BSTR valueBSTR;
if (FAILED(m_element->get_accValue(self(), &valueBSTR)) || !valueBSTR)
return 0;
wstring value(valueBSTR, SysStringLen(valueBSTR));
::SysFreeString(valueBSTR);
TCHAR* ignored;
return _tcstod(value.data(), &ignored);
}
double AccessibilityUIElement::minValue()
{
return 0;
}
double AccessibilityUIElement::maxValue()
{
return 0;
}
bool AccessibilityUIElement::isActionSupported(JSStringRef action)
{
return false;
}
bool AccessibilityUIElement::isEnabled()
{
DWORD state = accessibilityState(m_element);
return (state & STATE_SYSTEM_UNAVAILABLE) != STATE_SYSTEM_UNAVAILABLE;
}
bool AccessibilityUIElement::isRequired() const
{
return false;
}
int AccessibilityUIElement::insertionPointLineNumber()
{
return 0;
}
JSStringRef AccessibilityUIElement::attributesOfColumnHeaders()
{
return JSStringCreateWithCharacters(0, 0);
}
JSStringRef AccessibilityUIElement::attributesOfRowHeaders()
{
return JSStringCreateWithCharacters(0, 0);
}
JSStringRef AccessibilityUIElement::attributesOfColumns()
{
return JSStringCreateWithCharacters(0, 0);
}
JSStringRef AccessibilityUIElement::attributesOfRows()
{
return JSStringCreateWithCharacters(0, 0);
}
JSStringRef AccessibilityUIElement::attributesOfVisibleCells()
{
return JSStringCreateWithCharacters(0, 0);
}
JSStringRef AccessibilityUIElement::attributesOfHeader()
{
return JSStringCreateWithCharacters(0, 0);
}
int AccessibilityUIElement::indexInTable()
{
return 0;
}
JSStringRef AccessibilityUIElement::rowIndexRange()
{
return JSStringCreateWithCharacters(0, 0);
}
JSStringRef AccessibilityUIElement::columnIndexRange()
{
return JSStringCreateWithCharacters(0, 0);
}
int AccessibilityUIElement::lineForIndex(int)
{
return 0;
}
JSStringRef AccessibilityUIElement::boundsForRange(unsigned location, unsigned length)
{
return JSStringCreateWithCharacters(0, 0);
}
JSStringRef AccessibilityUIElement::stringForRange(unsigned, unsigned)
{
return JSStringCreateWithCharacters(0, 0);
}
JSStringRef AccessibilityUIElement::attributedStringForRange(unsigned, unsigned)
{
return JSStringCreateWithCharacters(0, 0);
}
bool AccessibilityUIElement::attributedStringRangeIsMisspelled(unsigned, unsigned)
{
return false;
}
AccessibilityUIElement AccessibilityUIElement::cellForColumnAndRow(unsigned column, unsigned row)
{
return 0;
}
JSStringRef AccessibilityUIElement::selectedTextRange()
{
return JSStringCreateWithCharacters(0, 0);
}
void AccessibilityUIElement::setSelectedTextRange(unsigned location, unsigned length)
{
}
JSStringRef AccessibilityUIElement::stringAttributeValue(JSStringRef attribute)
{
// FIXME: implement
return JSStringCreateWithCharacters(0, 0);
}
bool AccessibilityUIElement::boolAttributeValue(JSStringRef attribute)
{
// FIXME: implement
return false;
}
bool AccessibilityUIElement::isAttributeSettable(JSStringRef attribute)
{
return false;
}
bool AccessibilityUIElement::isAttributeSupported(JSStringRef attribute)
{
return false;
}
void AccessibilityUIElement::increment()
{
}
void AccessibilityUIElement::decrement()
{
}
void AccessibilityUIElement::showMenu()
{
ASSERT(hasPopup());
m_element->accDoDefaultAction(self());
}
void AccessibilityUIElement::press()
{
// FIXME: implement
}
AccessibilityUIElement AccessibilityUIElement::disclosedRowAtIndex(unsigned index)
{
return 0;
}
AccessibilityUIElement AccessibilityUIElement::ariaOwnsElementAtIndex(unsigned index)
{
return 0;
}
AccessibilityUIElement AccessibilityUIElement::ariaFlowToElementAtIndex(unsigned index)
{
return 0;
}
AccessibilityUIElement AccessibilityUIElement::selectedRowAtIndex(unsigned index)
{
return 0;
}
AccessibilityUIElement AccessibilityUIElement::disclosedByRow()
{
return 0;
}
JSStringRef AccessibilityUIElement::accessibilityValue() const
{
BSTR valueBSTR;
if (FAILED(m_element->get_accValue(self(), &valueBSTR)) || !valueBSTR)
return JSStringCreateWithCharacters(0, 0);
wstring value(valueBSTR, SysStringLen(valueBSTR));
::SysFreeString(valueBSTR);
return JSStringCreateWithCharacters(value.data(), value.length());
}
JSStringRef AccessibilityUIElement::documentEncoding()
{
return JSStringCreateWithCharacters(0, 0);
}
JSStringRef AccessibilityUIElement::documentURI()
{
return JSStringCreateWithCharacters(0, 0);
}
JSStringRef AccessibilityUIElement::url()
{
// FIXME: implement
return JSStringCreateWithCharacters(0, 0);
}
bool AccessibilityUIElement::addNotificationListener(JSObjectRef functionCallback)
{
if (!functionCallback)
return false;
sharedFrameLoadDelegate->accessibilityController()->addNotificationListener(m_element, functionCallback);
return true;
}
void AccessibilityUIElement::removeNotificationListener()
{
// FIXME: implement
}
bool AccessibilityUIElement::isFocusable() const
{
// FIXME: implement
return false;
}
bool AccessibilityUIElement::isSelectable() const
{
DWORD state = accessibilityState(m_element);
return (state & STATE_SYSTEM_SELECTABLE) == STATE_SYSTEM_SELECTABLE;
}
bool AccessibilityUIElement::isMultiSelectable() const
{
DWORD multiSelectable = STATE_SYSTEM_EXTSELECTABLE | STATE_SYSTEM_MULTISELECTABLE;
DWORD state = accessibilityState(m_element);
return (state & multiSelectable) == multiSelectable;
}
bool AccessibilityUIElement::isVisible() const
{
DWORD state = accessibilityState(m_element);
return (state & STATE_SYSTEM_INVISIBLE) != STATE_SYSTEM_INVISIBLE;
}
bool AccessibilityUIElement::isOffScreen() const
{
DWORD state = accessibilityState(m_element);
return (state & STATE_SYSTEM_OFFSCREEN) == STATE_SYSTEM_OFFSCREEN;
}
bool AccessibilityUIElement::isCollapsed() const
{
DWORD state = accessibilityState(m_element);
return (state & STATE_SYSTEM_COLLAPSED) == STATE_SYSTEM_COLLAPSED;
}
bool AccessibilityUIElement::isIgnored() const
{
// FIXME: implement
return false;
}
bool AccessibilityUIElement::hasPopup() const
{
DWORD state = accessibilityState(m_element);
return (state & STATE_SYSTEM_HASPOPUP) == STATE_SYSTEM_HASPOPUP;
}
void AccessibilityUIElement::takeFocus()
{
m_element->accSelect(SELFLAG_TAKEFOCUS, self());
}
void AccessibilityUIElement::takeSelection()
{
m_element->accSelect(SELFLAG_TAKESELECTION, self());
}
void AccessibilityUIElement::addSelection()
{
m_element->accSelect(SELFLAG_ADDSELECTION, self());
}
void AccessibilityUIElement::removeSelection()
{
m_element->accSelect(SELFLAG_REMOVESELECTION, self());
}