/*
* 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.
* 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "AccessibilityObject.h"
#include "AccessibilityRenderObject.h"
#include "AXObjectCache.h"
#include "CharacterNames.h"
#include "FloatRect.h"
#include "FocusController.h"
#include "Frame.h"
#include "FrameLoader.h"
#include "LocalizedStrings.h"
#include "NodeList.h"
#include "NotImplemented.h"
#include "Page.h"
#include "RenderImage.h"
#include "RenderListMarker.h"
#include "RenderMenuList.h"
#include "RenderTextControl.h"
#include "RenderTheme.h"
#include "RenderView.h"
#include "RenderWidget.h"
#include "SelectionController.h"
#include "TextIterator.h"
#include "htmlediting.h"
#include "visible_units.h"
#include <wtf/StdLibExtras.h>
using namespace std;
namespace WebCore {
using namespace HTMLNames;
AccessibilityObject::AccessibilityObject()
: m_id(0)
, m_haveChildren(false)
#if PLATFORM(GTK)
, m_wrapper(0)
#endif
{
}
AccessibilityObject::~AccessibilityObject()
{
ASSERT(isDetached());
}
void AccessibilityObject::detach()
{
removeAXObjectID();
#if HAVE(ACCESSIBILITY)
setWrapper(0);
#endif
}
AccessibilityObject* AccessibilityObject::firstChild() const
{
return 0;
}
AccessibilityObject* AccessibilityObject::lastChild() const
{
return 0;
}
AccessibilityObject* AccessibilityObject::previousSibling() const
{
return 0;
}
AccessibilityObject* AccessibilityObject::nextSibling() const
{
return 0;
}
AccessibilityObject* AccessibilityObject::parentObject() const
{
return 0;
}
AccessibilityObject* AccessibilityObject::parentObjectUnignored() const
{
AccessibilityObject* parent;
for (parent = parentObject(); parent && parent->accessibilityIsIgnored(); parent = parent->parentObject())
;
return parent;
}
int AccessibilityObject::layoutCount() const
{
return 0;
}
String AccessibilityObject::text() const
{
return String();
}
String AccessibilityObject::helpText() const
{
return String();
}
String AccessibilityObject::textUnderElement() const
{
return String();
}
bool AccessibilityObject::isARIAInput(AccessibilityRole ariaRole)
{
return ariaRole == RadioButtonRole || ariaRole == CheckBoxRole || ariaRole == TextFieldRole;
}
bool AccessibilityObject::isARIAControl(AccessibilityRole ariaRole)
{
return isARIAInput(ariaRole) || ariaRole == TextAreaRole || ariaRole == ButtonRole
|| ariaRole == ComboBoxRole || ariaRole == SliderRole;
}
int AccessibilityObject::intValue() const
{
return 0;
}
String AccessibilityObject::stringValue() const
{
return String();
}
String AccessibilityObject::ariaAccessiblityName(const String&) const
{
return String();
}
String AccessibilityObject::ariaLabeledByAttribute() const
{
return String();
}
String AccessibilityObject::title() const
{
return String();
}
String AccessibilityObject::ariaDescribedByAttribute() const
{
return String();
}
String AccessibilityObject::accessibilityDescription() const
{
return String();
}
IntRect AccessibilityObject::boundingBoxRect() const
{
return IntRect();
}
IntRect AccessibilityObject::elementRect() const
{
return IntRect();
}
IntSize AccessibilityObject::size() const
{
return IntSize();
}
IntPoint AccessibilityObject::clickPoint() const
{
IntRect rect = elementRect();
return IntPoint(rect.x() + rect.width() / 2, rect.y() + rect.height() / 2);
}
void AccessibilityObject::linkedUIElements(AccessibilityChildrenVector&) const
{
return;
}
AccessibilityObject* AccessibilityObject::titleUIElement() const
{
return 0;
}
int AccessibilityObject::textLength() const
{
return 0;
}
PassRefPtr<Range> AccessibilityObject::ariaSelectedTextDOMRange() const
{
return 0;
}
String AccessibilityObject::selectedText() const
{
return String();
}
const AtomicString& AccessibilityObject::accessKey() const
{
return nullAtom;
}
Selection AccessibilityObject::selection() const
{
return Selection();
}
PlainTextRange AccessibilityObject::selectedTextRange() const
{
return PlainTextRange();
}
unsigned AccessibilityObject::selectionStart() const
{
return selectedTextRange().start;
}
unsigned AccessibilityObject::selectionEnd() const
{
return selectedTextRange().length;
}
void AccessibilityObject::setSelectedText(const String&)
{
// TODO: set selected text (ReplaceSelectionCommand). <rdar://problem/4712125>
notImplemented();
}
void AccessibilityObject::setSelectedTextRange(const PlainTextRange&)
{
}
void AccessibilityObject::makeRangeVisible(const PlainTextRange&)
{
// TODO: make range visible (scrollRectToVisible). <rdar://problem/4712101>
notImplemented();
}
KURL AccessibilityObject::url() const
{
return KURL();
}
void AccessibilityObject::setFocused(bool)
{
}
void AccessibilityObject::setValue(const String&)
{
}
void AccessibilityObject::setSelected(bool)
{
}
bool AccessibilityObject::press() const
{
Element* actionElem = actionElement();
if (!actionElem)
return false;
if (Frame* f = actionElem->document()->frame())
f->loader()->resetMultipleFormSubmissionProtection();
actionElem->accessKeyAction(true);
return true;
}
AXObjectCache* AccessibilityObject::axObjectCache() const
{
return 0;
}
Widget* AccessibilityObject::widget() const
{
return 0;
}
Widget* AccessibilityObject::widgetForAttachmentView() const
{
return 0;
}
Element* AccessibilityObject::anchorElement() const
{
return 0;
}
Element* AccessibilityObject::actionElement() const
{
return 0;
}
// This function is like a cross-platform version of - (WebCoreTextMarkerRange*)textMarkerRange. It returns
// a Range that we can convert to a WebCoreRange in the Obj-C file
VisiblePositionRange AccessibilityObject::visiblePositionRange() const
{
return VisiblePositionRange();
}
VisiblePositionRange AccessibilityObject::visiblePositionRangeForLine(unsigned) const
{
return VisiblePositionRange();
}
VisiblePosition AccessibilityObject::visiblePositionForIndex(int) const
{
return VisiblePosition();
}
int AccessibilityObject::indexForVisiblePosition(const VisiblePosition&) const
{
return 0;
}
VisiblePositionRange AccessibilityObject::visiblePositionRangeForUnorderedPositions(const VisiblePosition& visiblePos1, const VisiblePosition& visiblePos2) const
{
if (visiblePos1.isNull() || visiblePos2.isNull())
return VisiblePositionRange();
VisiblePosition startPos;
VisiblePosition endPos;
bool alreadyInOrder;
// upstream is ordered before downstream for the same position
if (visiblePos1 == visiblePos2 && visiblePos2.affinity() == UPSTREAM)
alreadyInOrder = false;
// use selection order to see if the positions are in order
else
alreadyInOrder = Selection(visiblePos1, visiblePos2).isBaseFirst();
if (alreadyInOrder) {
startPos = visiblePos1;
endPos = visiblePos2;
} else {
startPos = visiblePos2;
endPos = visiblePos1;
}
return VisiblePositionRange(startPos, endPos);
}
VisiblePositionRange AccessibilityObject::positionOfLeftWord(const VisiblePosition& visiblePos) const
{
VisiblePosition startPosition = startOfWord(visiblePos, LeftWordIfOnBoundary);
VisiblePosition endPosition = endOfWord(startPosition);
return VisiblePositionRange(startPosition, endPosition);
}
VisiblePositionRange AccessibilityObject::positionOfRightWord(const VisiblePosition& visiblePos) const
{
VisiblePosition startPosition = startOfWord(visiblePos, RightWordIfOnBoundary);
VisiblePosition endPosition = endOfWord(startPosition);
return VisiblePositionRange(startPosition, endPosition);
}
static VisiblePosition updateAXLineStartForVisiblePosition(const VisiblePosition& visiblePosition)
{
// A line in the accessibility sense should include floating objects, such as aligned image, as part of a line.
// So let's update the position to include that.
VisiblePosition tempPosition;
VisiblePosition startPosition = visiblePosition;
Position p;
RenderObject* renderer;
while (true) {
tempPosition = startPosition.previous();
if (tempPosition.isNull())
break;
p = tempPosition.deepEquivalent();
if (!p.node())
break;
renderer = p.node()->renderer();
if (!renderer || renderer->isRenderBlock() && !p.offset())
break;
InlineBox* box;
int ignoredCaretOffset;
p.getInlineBoxAndOffset(tempPosition.affinity(), box, ignoredCaretOffset);
if (box)
break;
startPosition = tempPosition;
}
return startPosition;
}
VisiblePositionRange AccessibilityObject::leftLineVisiblePositionRange(const VisiblePosition& visiblePos) const
{
if (visiblePos.isNull())
return VisiblePositionRange();
// make a caret selection for the position before marker position (to make sure
// we move off of a line start)
VisiblePosition prevVisiblePos = visiblePos.previous();
if (prevVisiblePos.isNull())
return VisiblePositionRange();
VisiblePosition startPosition = startOfLine(prevVisiblePos);
// keep searching for a valid line start position. Unless the VisiblePosition is at the very beginning, there should
// always be a valid line range. However, startOfLine will return null for position next to a floating object,
// since floating object doesn't really belong to any line.
// This check will reposition the marker before the floating object, to ensure we get a line start.
if (startPosition.isNull()) {
while (startPosition.isNull() && prevVisiblePos.isNotNull()) {
prevVisiblePos = prevVisiblePos.previous();
startPosition = startOfLine(prevVisiblePos);
}
} else
startPosition = updateAXLineStartForVisiblePosition(startPosition);
VisiblePosition endPosition = endOfLine(prevVisiblePos);
return VisiblePositionRange(startPosition, endPosition);
}
VisiblePositionRange AccessibilityObject::rightLineVisiblePositionRange(const VisiblePosition& visiblePos) const
{
if (visiblePos.isNull())
return VisiblePositionRange();
// make sure we move off of a line end
VisiblePosition nextVisiblePos = visiblePos.next();
if (nextVisiblePos.isNull())
return VisiblePositionRange();
VisiblePosition startPosition = startOfLine(nextVisiblePos);
// fetch for a valid line start position
if (startPosition.isNull() ) {
startPosition = visiblePos;
nextVisiblePos = nextVisiblePos.next();
} else
startPosition = updateAXLineStartForVisiblePosition(startPosition);
VisiblePosition endPosition = endOfLine(nextVisiblePos);
// as long as the position hasn't reached the end of the doc, keep searching for a valid line end position
// Unless the VisiblePosition is at the very end, there should always be a valid line range. However, endOfLine will
// return null for position by a floating object, since floating object doesn't really belong to any line.
// This check will reposition the marker after the floating object, to ensure we get a line end.
while (endPosition.isNull() && nextVisiblePos.isNotNull()) {
nextVisiblePos = nextVisiblePos.next();
endPosition = endOfLine(nextVisiblePos);
}
return VisiblePositionRange(startPosition, endPosition);
}
VisiblePositionRange AccessibilityObject::sentenceForPosition(const VisiblePosition& visiblePos) const
{
// FIXME: FO 2 IMPLEMENT (currently returns incorrect answer)
// Related? <rdar://problem/3927736> Text selection broken in 8A336
VisiblePosition startPosition = startOfSentence(visiblePos);
VisiblePosition endPosition = endOfSentence(startPosition);
return VisiblePositionRange(startPosition, endPosition);
}
VisiblePositionRange AccessibilityObject::paragraphForPosition(const VisiblePosition& visiblePos) const
{
VisiblePosition startPosition = startOfParagraph(visiblePos);
VisiblePosition endPosition = endOfParagraph(startPosition);
return VisiblePositionRange(startPosition, endPosition);
}
static VisiblePosition startOfStyleRange(const VisiblePosition visiblePos)
{
RenderObject* renderer = visiblePos.deepEquivalent().node()->renderer();
RenderObject* startRenderer = renderer;
RenderStyle* style = renderer->style();
// traverse backward by renderer to look for style change
for (RenderObject* r = renderer->previousInPreOrder(); r; r = r->previousInPreOrder()) {
// skip non-leaf nodes
if (r->firstChild())
continue;
// stop at style change
if (r->style() != style)
break;
// remember match
startRenderer = r;
}
return VisiblePosition(startRenderer->node(), 0, VP_DEFAULT_AFFINITY);
}
static VisiblePosition endOfStyleRange(const VisiblePosition visiblePos)
{
RenderObject* renderer = visiblePos.deepEquivalent().node()->renderer();
RenderObject* endRenderer = renderer;
RenderStyle* style = renderer->style();
// traverse forward by renderer to look for style change
for (RenderObject* r = renderer->nextInPreOrder(); r; r = r->nextInPreOrder()) {
// skip non-leaf nodes
if (r->firstChild())
continue;
// stop at style change
if (r->style() != style)
break;
// remember match
endRenderer = r;
}
return VisiblePosition(endRenderer->node(), maxDeepOffset(endRenderer->node()), VP_DEFAULT_AFFINITY);
}
VisiblePositionRange AccessibilityObject::styleRangeForPosition(const VisiblePosition& visiblePos) const
{
if (visiblePos.isNull())
return VisiblePositionRange();
return VisiblePositionRange(startOfStyleRange(visiblePos), endOfStyleRange(visiblePos));
}
// NOTE: Consider providing this utility method as AX API
VisiblePositionRange AccessibilityObject::visiblePositionRangeForRange(const PlainTextRange& range) const
{
if (range.start + range.length > text().length())
return VisiblePositionRange();
VisiblePosition startPosition = visiblePositionForIndex(range.start);
startPosition.setAffinity(DOWNSTREAM);
VisiblePosition endPosition = visiblePositionForIndex(range.start + range.length);
return VisiblePositionRange(startPosition, endPosition);
}
static bool replacedNodeNeedsCharacter(Node* replacedNode)
{
// we should always be given a rendered node and a replaced node, but be safe
// replaced nodes are either attachments (widgets) or images
if (!replacedNode || !replacedNode->renderer() || !replacedNode->renderer()->isReplaced() || replacedNode->isTextNode()) {
return false;
}
// create an AX object, but skip it if it is not supposed to be seen
AccessibilityObject* object = replacedNode->renderer()->document()->axObjectCache()->get(replacedNode->renderer());
if (object->accessibilityIsIgnored())
return false;
return true;
}
String AccessibilityObject::stringForVisiblePositionRange(const VisiblePositionRange& visiblePositionRange) const
{
if (visiblePositionRange.isNull())
return String();
Vector<UChar> resultVector;
RefPtr<Range> range = makeRange(visiblePositionRange.start, visiblePositionRange.end);
for (TextIterator it(range.get()); !it.atEnd(); it.advance()) {
// non-zero length means textual node, zero length means replaced node (AKA "attachments" in AX)
if (it.length() != 0) {
resultVector.append(it.characters(), it.length());
} else {
// locate the node and starting offset for this replaced range
int exception = 0;
Node* node = it.range()->startContainer(exception);
ASSERT(node == it.range()->endContainer(exception));
int offset = it.range()->startOffset(exception);
if (replacedNodeNeedsCharacter(node->childNode(offset))) {
resultVector.append(objectReplacementCharacter);
}
}
}
return String::adopt(resultVector);
}
IntRect AccessibilityObject::boundsForVisiblePositionRange(const VisiblePositionRange&) const
{
return IntRect();
}
int AccessibilityObject::lengthForVisiblePositionRange(const VisiblePositionRange& visiblePositionRange) const
{
// FIXME: Multi-byte support
if (visiblePositionRange.isNull())
return -1;
int length = 0;
RefPtr<Range> range = makeRange(visiblePositionRange.start, visiblePositionRange.end);
for (TextIterator it(range.get()); !it.atEnd(); it.advance()) {
// non-zero length means textual node, zero length means replaced node (AKA "attachments" in AX)
if (it.length() != 0) {
length += it.length();
} else {
// locate the node and starting offset for this replaced range
int exception = 0;
Node* node = it.range()->startContainer(exception);
ASSERT(node == it.range()->endContainer(exception));
int offset = it.range()->startOffset(exception);
if (replacedNodeNeedsCharacter(node->childNode(offset)))
length++;
}
}
return length;
}
void AccessibilityObject::setSelectedVisiblePositionRange(const VisiblePositionRange&) const
{
}
VisiblePosition AccessibilityObject::visiblePositionForPoint(const IntPoint&) const
{
return VisiblePosition();
}
VisiblePosition AccessibilityObject::nextVisiblePosition(const VisiblePosition& visiblePos) const
{
return visiblePos.next();
}
VisiblePosition AccessibilityObject::previousVisiblePosition(const VisiblePosition& visiblePos) const
{
return visiblePos.previous();
}
VisiblePosition AccessibilityObject::nextWordEnd(const VisiblePosition& visiblePos) const
{
if (visiblePos.isNull())
return VisiblePosition();
// make sure we move off of a word end
VisiblePosition nextVisiblePos = visiblePos.next();
if (nextVisiblePos.isNull())
return VisiblePosition();
return endOfWord(nextVisiblePos, LeftWordIfOnBoundary);
}
VisiblePosition AccessibilityObject::previousWordStart(const VisiblePosition& visiblePos) const
{
if (visiblePos.isNull())
return VisiblePosition();
// make sure we move off of a word start
VisiblePosition prevVisiblePos = visiblePos.previous();
if (prevVisiblePos.isNull())
return VisiblePosition();
return startOfWord(prevVisiblePos, RightWordIfOnBoundary);
}
VisiblePosition AccessibilityObject::nextLineEndPosition(const VisiblePosition& visiblePos) const
{
if (visiblePos.isNull())
return VisiblePosition();
// to make sure we move off of a line end
VisiblePosition nextVisiblePos = visiblePos.next();
if (nextVisiblePos.isNull())
return VisiblePosition();
VisiblePosition endPosition = endOfLine(nextVisiblePos);
// as long as the position hasn't reached the end of the doc, keep searching for a valid line end position
// There are cases like when the position is next to a floating object that'll return null for end of line. This code will avoid returning null.
while (endPosition.isNull() && nextVisiblePos.isNotNull()) {
nextVisiblePos = nextVisiblePos.next();
endPosition = endOfLine(nextVisiblePos);
}
return endPosition;
}
VisiblePosition AccessibilityObject::previousLineStartPosition(const VisiblePosition& visiblePos) const
{
if (visiblePos.isNull())
return VisiblePosition();
// make sure we move off of a line start
VisiblePosition prevVisiblePos = visiblePos.previous();
if (prevVisiblePos.isNull())
return VisiblePosition();
VisiblePosition startPosition = startOfLine(prevVisiblePos);
// as long as the position hasn't reached the beginning of the doc, keep searching for a valid line start position
// There are cases like when the position is next to a floating object that'll return null for start of line. This code will avoid returning null.
if (startPosition.isNull()) {
while (startPosition.isNull() && prevVisiblePos.isNotNull()) {
prevVisiblePos = prevVisiblePos.previous();
startPosition = startOfLine(prevVisiblePos);
}
} else
startPosition = updateAXLineStartForVisiblePosition(startPosition);
return startPosition;
}
VisiblePosition AccessibilityObject::nextSentenceEndPosition(const VisiblePosition& visiblePos) const
{
// FIXME: FO 2 IMPLEMENT (currently returns incorrect answer)
// Related? <rdar://problem/3927736> Text selection broken in 8A336
if (visiblePos.isNull())
return VisiblePosition();
// make sure we move off of a sentence end
VisiblePosition nextVisiblePos = visiblePos.next();
if (nextVisiblePos.isNull())
return VisiblePosition();
// an empty line is considered a sentence. If it's skipped, then the sentence parser will not
// see this empty line. Instead, return the end position of the empty line.
VisiblePosition endPosition;
String lineString = plainText(makeRange(startOfLine(nextVisiblePos), endOfLine(nextVisiblePos)).get());
if (lineString.isEmpty())
endPosition = nextVisiblePos;
else
endPosition = endOfSentence(nextVisiblePos);
return endPosition;
}
VisiblePosition AccessibilityObject::previousSentenceStartPosition(const VisiblePosition& visiblePos) const
{
// FIXME: FO 2 IMPLEMENT (currently returns incorrect answer)
// Related? <rdar://problem/3927736> Text selection broken in 8A336
if (visiblePos.isNull())
return VisiblePosition();
// make sure we move off of a sentence start
VisiblePosition previousVisiblePos = visiblePos.previous();
if (previousVisiblePos.isNull())
return VisiblePosition();
// treat empty line as a separate sentence.
VisiblePosition startPosition;
String lineString = plainText(makeRange(startOfLine(previousVisiblePos), endOfLine(previousVisiblePos)).get());
if (lineString.isEmpty())
startPosition = previousVisiblePos;
else
startPosition = startOfSentence(previousVisiblePos);
return startPosition;
}
VisiblePosition AccessibilityObject::nextParagraphEndPosition(const VisiblePosition& visiblePos) const
{
if (visiblePos.isNull())
return VisiblePosition();
// make sure we move off of a paragraph end
VisiblePosition nextPos = visiblePos.next();
if (nextPos.isNull())
return VisiblePosition();
return endOfParagraph(nextPos);
}
VisiblePosition AccessibilityObject::previousParagraphStartPosition(const VisiblePosition& visiblePos) const
{
if (visiblePos.isNull())
return VisiblePosition();
// make sure we move off of a paragraph start
VisiblePosition previousPos = visiblePos.previous();
if (previousPos.isNull())
return VisiblePosition();
return startOfParagraph(previousPos);
}
// NOTE: Consider providing this utility method as AX API
VisiblePosition AccessibilityObject::visiblePositionForIndex(unsigned, bool) const
{
return VisiblePosition();
}
AccessibilityObject* AccessibilityObject::accessibilityObjectForPosition(const VisiblePosition& visiblePos) const
{
if (visiblePos.isNull())
return 0;
RenderObject* obj = visiblePos.deepEquivalent().node()->renderer();
if (!obj)
return 0;
return obj->document()->axObjectCache()->get(obj);
}
int AccessibilityObject::lineForPosition(const VisiblePosition& visiblePos) const
{
if (visiblePos.isNull())
return 0;
unsigned lineCount = 0;
VisiblePosition currentVisiblePos = visiblePos;
VisiblePosition savedVisiblePos;
// move up until we get to the top
// FIXME: This only takes us to the top of the rootEditableElement, not the top of the
// top document.
while (currentVisiblePos.isNotNull() && !(inSameLine(currentVisiblePos, savedVisiblePos))) {
++lineCount;
savedVisiblePos = currentVisiblePos;
VisiblePosition prevVisiblePos = previousLinePosition(currentVisiblePos, 0);
currentVisiblePos = prevVisiblePos;
}
return lineCount - 1;
}
// NOTE: Consider providing this utility method as AX API
PlainTextRange AccessibilityObject::plainTextRangeForVisiblePositionRange(const VisiblePositionRange& positionRange) const
{
int index1 = index(positionRange.start);
int index2 = index(positionRange.end);
if (index1 < 0 || index2 < 0 || index1 > index2)
return PlainTextRange();
return PlainTextRange(index1, index2 - index1);
}
// NOTE: Consider providing this utility method as AX API
int AccessibilityObject::index(const VisiblePosition&) const
{
return -1;
}
// Given a line number, the range of characters of the text associated with this accessibility
// object that contains the line number.
PlainTextRange AccessibilityObject::doAXRangeForLine(unsigned) const
{
return PlainTextRange();
}
// The composed character range in the text associated with this accessibility object that
// is specified by the given screen coordinates. This parameterized attribute returns the
// complete range of characters (including surrogate pairs of multi-byte glyphs) at the given
// screen coordinates.
// NOTE: This varies from AppKit when the point is below the last line. AppKit returns an
// an error in that case. We return textControl->text().length(), 1. Does this matter?
PlainTextRange AccessibilityObject::doAXRangeForPosition(const IntPoint& point) const
{
int i = index(visiblePositionForPoint(point));
if (i < 0)
return PlainTextRange();
return PlainTextRange(i, 1);
}
// The composed character range in the text associated with this accessibility object that
// is specified by the given index value. This parameterized attribute returns the complete
// range of characters (including surrogate pairs of multi-byte glyphs) at the given index.
PlainTextRange AccessibilityObject::doAXRangeForIndex(unsigned) const
{
return PlainTextRange();
}
// Given a character index, the range of text associated with this accessibility object
// over which the style in effect at that character index applies.
PlainTextRange AccessibilityObject::doAXStyleRangeForIndex(unsigned index) const
{
VisiblePositionRange range = styleRangeForPosition(visiblePositionForIndex(index, false));
return plainTextRangeForVisiblePositionRange(range);
}
// A substring of the text associated with this accessibility object that is
// specified by the given character range.
String AccessibilityObject::doAXStringForRange(const PlainTextRange&) const
{
return String();
}
// The bounding rectangle of the text associated with this accessibility object that is
// specified by the given range. This is the bounding rectangle a sighted user would see
// on the display screen, in pixels.
IntRect AccessibilityObject::doAXBoundsForRange(const PlainTextRange&) const
{
return IntRect();
}
// Given an indexed character, the line number of the text associated with this accessibility
// object that contains the character.
unsigned AccessibilityObject::doAXLineForIndex(unsigned index)
{
return lineForPosition(visiblePositionForIndex(index, false));
}
FrameView* AccessibilityObject::documentFrameView() const
{
const AccessibilityObject* object = this;
while (object && !object->isAccessibilityRenderObject())
object = object->parentObject();
if (!object)
return 0;
return object->documentFrameView();
}
AccessibilityObject* AccessibilityObject::doAccessibilityHitTest(const IntPoint&) const
{
return 0;
}
AccessibilityObject* AccessibilityObject::focusedUIElement() const
{
return 0;
}
AccessibilityObject* AccessibilityObject::observableObject() const
{
return 0;
}
AccessibilityRole AccessibilityObject::roleValue() const
{
return UnknownRole;
}
AccessibilityRole AccessibilityObject::ariaRoleAttribute() const
{
return UnknownRole;
}
bool AccessibilityObject::isPresentationalChildOfAriaRole() const
{
return false;
}
bool AccessibilityObject::ariaRoleHasPresentationalChildren() const
{
return false;
}
void AccessibilityObject::clearChildren()
{
m_haveChildren = false;
m_children.clear();
}
void AccessibilityObject::childrenChanged()
{
return;
}
void AccessibilityObject::addChildren()
{
}
void AccessibilityObject::selectedChildren(AccessibilityChildrenVector&)
{
}
void AccessibilityObject::visibleChildren(AccessibilityChildrenVector&)
{
}
unsigned AccessibilityObject::axObjectID() const
{
return m_id;
}
void AccessibilityObject::setAXObjectID(unsigned axObjectID)
{
m_id = axObjectID;
}
void AccessibilityObject::removeAXObjectID()
{
return;
}
const String& AccessibilityObject::actionVerb() const
{
// FIXME: Need to add verbs for select elements.
DEFINE_STATIC_LOCAL(const String, buttonAction, (AXButtonActionVerb()));
DEFINE_STATIC_LOCAL(const String, textFieldAction, (AXTextFieldActionVerb()));
DEFINE_STATIC_LOCAL(const String, radioButtonAction, (AXRadioButtonActionVerb()));
DEFINE_STATIC_LOCAL(const String, checkedCheckBoxAction, (AXCheckedCheckBoxActionVerb()));
DEFINE_STATIC_LOCAL(const String, uncheckedCheckBoxAction, (AXUncheckedCheckBoxActionVerb()));
DEFINE_STATIC_LOCAL(const String, linkAction, (AXLinkActionVerb()));
DEFINE_STATIC_LOCAL(const String, noAction, ());
switch (roleValue()) {
case ButtonRole:
return buttonAction;
case TextFieldRole:
case TextAreaRole:
return textFieldAction;
case RadioButtonRole:
return radioButtonAction;
case CheckBoxRole:
return isChecked() ? checkedCheckBoxAction : uncheckedCheckBoxAction;
case LinkRole:
case WebCoreLinkRole:
return linkAction;
default:
return noAction;
}
}
void AccessibilityObject::updateBackingStore()
{
}
} // namespace WebCore