WebCore/accessibility/AccessibilityObject.cpp
changeset 0 4f2f89ce4247
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebCore/accessibility/AccessibilityObject.cpp	Fri Sep 17 09:02:29 2010 +0300
@@ -0,0 +1,1007 @@
+/*
+ * Copyright (C) 2008, 2009 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 "AXObjectCache.h"
+#include "AccessibilityRenderObject.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 "RenderListItem.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)
+    , m_role(UnknownRole)
+#if PLATFORM(GTK)
+    , m_wrapper(0)
+#endif
+{
+}
+
+AccessibilityObject::~AccessibilityObject()
+{
+    ASSERT(isDetached());
+}
+
+void AccessibilityObject::detach()
+{
+#if HAVE(ACCESSIBILITY)
+    setWrapper(0);
+#endif    
+}
+
+AccessibilityObject* AccessibilityObject::parentObjectUnignored() const
+{
+    AccessibilityObject* parent;
+    for (parent = parentObject(); parent && parent->accessibilityIsIgnored(); parent = parent->parentObject()) {
+    }
+    
+    return parent;
+}
+
+AccessibilityObject* AccessibilityObject::firstAccessibleObjectFromNode(const Node* node)
+{
+    ASSERT(AXObjectCache::accessibilityEnabled());
+
+    if (!node)
+        return 0;
+
+    Document* document = node->document();
+    if (!document)
+        return 0;
+
+    AXObjectCache* cache = document->axObjectCache();
+
+    AccessibilityObject* accessibleObject = cache->getOrCreate(node->renderer());
+    while (accessibleObject && accessibleObject->accessibilityIsIgnored()) {
+        node = node->traverseNextNode();
+
+        while (node && !node->renderer())
+            node = node->traverseNextSibling();
+
+        if (!node)
+            return 0;
+
+        accessibleObject = cache->getOrCreate(node->renderer());
+    }
+
+    return accessibleObject;
+}
+
+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; 
+}
+
+IntPoint AccessibilityObject::clickPoint() const
+{
+    IntRect rect = elementRect();
+    return IntPoint(rect.x() + rect.width() / 2, rect.y() + rect.height() / 2);
+}
+
+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;
+}
+    
+String AccessibilityObject::language() const
+{
+    const AtomicString& lang = getAttribute(langAttr);
+    if (!lang.isEmpty())
+        return lang;
+
+    AccessibilityObject* parent = parentObject();
+    
+    // as a last resort, fall back to the content language specified in the meta tag
+    if (!parent) {
+        Document* doc = document();
+        if (doc)
+            return doc->contentLanguage();
+        return nullAtom;
+    }
+    
+    return parent->language();
+}
+    
+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 = VisibleSelection(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.deprecatedEditingOffset()))
+            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 lastDeepEditingPositionForNode(endRenderer->node());
+}
+
+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()->getOrCreate(replacedNode->renderer());
+    if (object->accessibilityIsIgnored())
+        return false;
+
+    return true;
+}
+
+// Finds a RenderListItem parent give a node.
+static RenderListItem* renderListItemContainerForNode(Node* node)
+{
+    for (Node* stringNode = node; stringNode; stringNode = stringNode->parent()) {
+        RenderObject* renderObject = stringNode->renderer();
+        if (!renderObject || !renderObject->isListItem())
+            continue;
+        
+        return toRenderListItem(renderObject);
+    }
+    
+    return 0;
+}
+    
+// Returns the text associated with a list marker if this node is contained within a list item.
+String AccessibilityObject::listMarkerTextForNodeAndPosition(Node* node, const VisiblePosition& visiblePositionStart) const
+{
+    // If the range does not contain the start of the line, the list marker text should not be included.
+    if (!isStartOfLine(visiblePositionStart))
+        return String();
+
+    RenderListItem* listItem = renderListItemContainerForNode(node);
+    if (!listItem)
+        return String();
+        
+    // If this is in a list item, we need to manually add the text for the list marker 
+    // because a RenderListMarker does not have a Node equivalent and thus does not appear
+    // when iterating text.
+    const String& markerText = listItem->markerText();
+    if (markerText.isEmpty())
+        return String();
+                
+    // Append text, plus the period that follows the text.
+    // FIXME: Not all list marker styles are followed by a period, but this
+    // sounds much better when there is a synthesized pause because of a period.
+    Vector<UChar> resultVector;
+    resultVector.append(markerText.characters(), markerText.length());
+    resultVector.append('.');
+    resultVector.append(' ');
+    
+    return String::adopt(resultVector);
+}
+    
+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()) {
+            // Add a textual representation for list marker text
+            String listMarkerText = listMarkerTextForNodeAndPosition(it.node(), visiblePositionRange.start);
+            if (!listMarkerText.isEmpty())
+                resultVector.append(listMarkerText.characters(), listMarkerText.length());
+                
+            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);
+}
+
+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())
+            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;
+}
+
+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);
+}
+
+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()->getOrCreate(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);
+}
+
+// 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);
+}
+
+// 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);
+}
+
+// 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();
+}
+
+void AccessibilityObject::clearChildren()
+{
+    m_children.clear();
+    m_haveChildren = false;
+}
+
+AccessibilityObject* AccessibilityObject::anchorElementForNode(Node* node)
+{
+    RenderObject* obj = node->renderer();
+    if (!obj)
+        return 0;
+    
+    RefPtr<AccessibilityObject> axObj = obj->document()->axObjectCache()->getOrCreate(obj);
+    Element* anchor = axObj->anchorElement();
+    if (!anchor)
+        return 0;
+    
+    RenderObject* anchorRenderer = anchor->renderer();
+    if (!anchorRenderer)
+        return 0;
+    
+    return anchorRenderer->document()->axObjectCache()->getOrCreate(anchorRenderer);
+}
+    
+void AccessibilityObject::ariaTreeRows(AccessibilityChildrenVector& result)
+{
+    AccessibilityChildrenVector axChildren = children();
+    unsigned count = axChildren.size();
+    for (unsigned k = 0; k < count; ++k) {
+        AccessibilityObject* obj = axChildren[k].get();
+        
+        // Add tree items as the rows.
+        if (obj->roleValue() == TreeItemRole) 
+            result.append(obj);
+
+        // Now see if this item also has rows hiding inside of it.
+        obj->ariaTreeRows(result);
+    }
+}
+    
+void AccessibilityObject::ariaTreeItemContent(AccessibilityChildrenVector& result)
+{
+    // The ARIA tree item content are the item that are not other tree items or their containing groups.
+    AccessibilityChildrenVector axChildren = children();
+    unsigned count = axChildren.size();
+    for (unsigned k = 0; k < count; ++k) {
+        AccessibilityObject* obj = axChildren[k].get();
+        AccessibilityRole role = obj->roleValue();
+        if (role == TreeItemRole || role == GroupRole)
+            continue;
+        
+        result.append(obj);
+    }
+}
+
+void AccessibilityObject::ariaTreeItemDisclosedRows(AccessibilityChildrenVector& result)
+{
+    AccessibilityChildrenVector axChildren = children();
+    unsigned count = axChildren.size();
+    for (unsigned k = 0; k < count; ++k) {
+        AccessibilityObject* obj = axChildren[k].get();
+        
+        // Add tree items as the rows.
+        if (obj->roleValue() == TreeItemRole)
+            result.append(obj);
+        // If it's not a tree item, then descend into the group to find more tree items.
+        else 
+            obj->ariaTreeRows(result);
+    }    
+}
+    
+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, menuListAction, (AXMenuListActionVerb()));
+    DEFINE_STATIC_LOCAL(const String, menuListPopupAction, (AXMenuListPopupActionVerb()));
+    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;
+    case PopUpButtonRole:
+        return menuListAction;
+    case MenuListPopupRole:
+        return menuListPopupAction;
+    default:
+        return noAction;
+    }
+}
+ 
+const AtomicString& AccessibilityObject::getAttribute(const QualifiedName& attribute) const
+{
+    Node* elementNode = node();
+    if (!elementNode)
+        return nullAtom;
+    
+    if (!elementNode->isElementNode())
+        return nullAtom;
+    
+    Element* element = static_cast<Element*>(elementNode);
+    return element->getAttribute(attribute);
+}
+    
+// Lacking concrete evidence of orientation, horizontal means width > height. vertical is height > width;
+AccessibilityOrientation AccessibilityObject::orientation() const
+{
+    IntRect bounds = elementRect();
+    if (bounds.size().width() > bounds.size().height())
+        return AccessibilityOrientationHorizontal;
+    if (bounds.size().height() > bounds.size().width())
+        return AccessibilityOrientationVertical;
+
+    // A tie goes to horizontal.
+    return AccessibilityOrientationHorizontal;
+}    
+
+typedef HashMap<String, AccessibilityRole, CaseFoldingHash> ARIARoleMap;
+
+struct RoleEntry {
+    String ariaRole;
+    AccessibilityRole webcoreRole;
+};
+
+static ARIARoleMap* createARIARoleMap()
+{
+    const RoleEntry roles[] = {
+        { "alert", ApplicationAlertRole },
+        { "alertdialog", ApplicationAlertDialogRole },
+        { "application", LandmarkApplicationRole },
+        { "article", DocumentArticleRole },
+        { "banner", LandmarkBannerRole },
+        { "button", ButtonRole },
+        { "checkbox", CheckBoxRole },
+        { "complementary", LandmarkComplementaryRole },
+        { "contentinfo", LandmarkContentInfoRole },
+        { "dialog", ApplicationDialogRole },
+        { "directory", DirectoryRole },
+        { "grid", TableRole },
+        { "gridcell", CellRole },
+        { "columnheader", ColumnHeaderRole },
+        { "combobox", ComboBoxRole },
+        { "definition", DefinitionListDefinitionRole },
+        { "document", DocumentRole },
+        { "rowheader", RowHeaderRole },
+        { "group", GroupRole },
+        { "heading", HeadingRole },
+        { "img", ImageRole },
+        { "link", WebCoreLinkRole },
+        { "list", ListRole },        
+        { "listitem", ListItemRole },        
+        { "listbox", ListBoxRole },
+        { "log", ApplicationLogRole },
+        // "option" isn't here because it may map to different roles depending on the parent element's role
+        { "main", LandmarkMainRole },
+        { "marquee", ApplicationMarqueeRole },
+        { "math", DocumentMathRole },
+        { "menu", MenuRole },
+        { "menubar", GroupRole },
+        // "menuitem" isn't here because it may map to different roles depending on the parent element's role
+        { "menuitemcheckbox", MenuItemRole },
+        { "menuitemradio", MenuItemRole },
+        { "note", DocumentNoteRole },
+        { "navigation", LandmarkNavigationRole },
+        { "option", ListBoxOptionRole },
+        { "presentation", PresentationalRole },
+        { "progressbar", ProgressIndicatorRole },
+        { "radio", RadioButtonRole },
+        { "radiogroup", RadioGroupRole },
+        { "region", DocumentRegionRole },
+        { "row", RowRole },
+        { "range", SliderRole },
+        { "scrollbar", ScrollBarRole },
+        { "search", LandmarkSearchRole },
+        { "separator", SplitterRole },
+        { "slider", SliderRole },
+        { "spinbutton", ProgressIndicatorRole },
+        { "status", ApplicationStatusRole },
+        { "tab", TabRole },
+        { "tablist", TabListRole },
+        { "tabpanel", TabPanelRole },
+        { "text", StaticTextRole },
+        { "textbox", TextAreaRole },
+        { "timer", ApplicationTimerRole },
+        { "toolbar", ToolbarRole },
+        { "tooltip", UserInterfaceTooltipRole },
+        { "tree", TreeRole },
+        { "treegrid", TreeGridRole },
+        { "treeitem", TreeItemRole }
+    };
+    ARIARoleMap* roleMap = new ARIARoleMap;
+    
+    const unsigned numRoles = sizeof(roles) / sizeof(roles[0]);
+    for (unsigned i = 0; i < numRoles; ++i)
+        roleMap->set(roles[i].ariaRole, roles[i].webcoreRole);
+    return roleMap;
+}
+
+AccessibilityRole AccessibilityObject::ariaRoleToWebCoreRole(const String& value)
+{
+    ASSERT(!value.isEmpty());
+    static const ARIARoleMap* roleMap = createARIARoleMap();
+    return roleMap->get(value);
+}
+
+bool AccessibilityObject::isInsideARIALiveRegion() const
+{
+    if (supportsARIALiveRegion())
+        return true;
+    
+    for (AccessibilityObject* axParent = parentObject(); axParent; axParent = axParent->parentObject()) {
+        if (axParent->supportsARIALiveRegion())
+            return true;
+    }
+    
+    return false;
+}
+
+bool AccessibilityObject::supportsARIAAttributes() const
+{
+    return supportsARIALiveRegion() || supportsARIADragging() || supportsARIADropping() || supportsARIAFlowTo() || supportsARIAOwns();
+}
+    
+bool AccessibilityObject::supportsARIALiveRegion() const
+{
+    const AtomicString& liveRegion = ariaLiveRegionStatus();
+    return equalIgnoringCase(liveRegion, "polite") || equalIgnoringCase(liveRegion, "assertive");
+}
+    
+int AccessibilityObject::intValue() const
+{
+    if (isPasswordField())
+        return 0;
+    
+    if (isHeading())
+        return headingLevel();
+    
+    // If this is a real checkbox or radio button, AccessibilityRenderObject will handle.
+    // If it's an ARIA checkbox or radio, the aria-checked attribute should be used.
+    if (isCheckboxOrRadio())
+        return equalIgnoringCase(getAttribute(aria_checkedAttr), "true");
+    
+    return 0;
+}
+
+bool AccessibilityObject::hasIntValue() const
+{
+    if (isHeading())
+        return true;
+    
+    if (isCheckboxOrRadio())
+        return true;
+    
+    return false;
+}
+    
+} // namespace WebCore