Home | History | Annotate | Download | only in accessibility
      1 /*
      2  * Copyright (C) 2008, 2009 Apple Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  *
      8  * 1.  Redistributions of source code must retain the above copyright
      9  *     notice, this list of conditions and the following disclaimer.
     10  * 2.  Redistributions in binary form must reproduce the above copyright
     11  *     notice, this list of conditions and the following disclaimer in the
     12  *     documentation and/or other materials provided with the distribution.
     13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
     14  *     its contributors may be used to endorse or promote products derived
     15  *     from this software without specific prior written permission.
     16  *
     17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
     18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
     21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
     24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 #include "config.h"
     30 #include "AccessibilityObject.h"
     31 
     32 #include "AXObjectCache.h"
     33 #include "AccessibilityRenderObject.h"
     34 #include "CharacterNames.h"
     35 #include "FloatRect.h"
     36 #include "FocusController.h"
     37 #include "Frame.h"
     38 #include "FrameLoader.h"
     39 #include "LocalizedStrings.h"
     40 #include "NodeList.h"
     41 #include "NotImplemented.h"
     42 #include "Page.h"
     43 #include "RenderImage.h"
     44 #include "RenderListItem.h"
     45 #include "RenderListMarker.h"
     46 #include "RenderMenuList.h"
     47 #include "RenderTextControl.h"
     48 #include "RenderTheme.h"
     49 #include "RenderView.h"
     50 #include "RenderWidget.h"
     51 #include "SelectionController.h"
     52 #include "TextIterator.h"
     53 #include "htmlediting.h"
     54 #include "visible_units.h"
     55 #include <wtf/StdLibExtras.h>
     56 
     57 using namespace std;
     58 
     59 namespace WebCore {
     60 
     61 using namespace HTMLNames;
     62 
     63 AccessibilityObject::AccessibilityObject()
     64     : m_id(0)
     65     , m_haveChildren(false)
     66     , m_role(UnknownRole)
     67 #if PLATFORM(GTK)
     68     , m_wrapper(0)
     69 #endif
     70 {
     71 }
     72 
     73 AccessibilityObject::~AccessibilityObject()
     74 {
     75     ASSERT(isDetached());
     76 }
     77 
     78 void AccessibilityObject::detach()
     79 {
     80 #if HAVE(ACCESSIBILITY)
     81     setWrapper(0);
     82 #endif
     83 }
     84 
     85 AccessibilityObject* AccessibilityObject::parentObjectUnignored() const
     86 {
     87     AccessibilityObject* parent;
     88     for (parent = parentObject(); parent && parent->accessibilityIsIgnored(); parent = parent->parentObject()) {
     89     }
     90 
     91     return parent;
     92 }
     93 
     94 AccessibilityObject* AccessibilityObject::firstAccessibleObjectFromNode(const Node* node)
     95 {
     96     ASSERT(AXObjectCache::accessibilityEnabled());
     97 
     98     if (!node)
     99         return 0;
    100 
    101     Document* document = node->document();
    102     if (!document)
    103         return 0;
    104 
    105     AXObjectCache* cache = document->axObjectCache();
    106 
    107     AccessibilityObject* accessibleObject = cache->getOrCreate(node->renderer());
    108     while (accessibleObject && accessibleObject->accessibilityIsIgnored()) {
    109         node = node->traverseNextNode();
    110 
    111         while (node && !node->renderer())
    112             node = node->traverseNextSibling();
    113 
    114         if (!node)
    115             return 0;
    116 
    117         accessibleObject = cache->getOrCreate(node->renderer());
    118     }
    119 
    120     return accessibleObject;
    121 }
    122 
    123 bool AccessibilityObject::isARIAInput(AccessibilityRole ariaRole)
    124 {
    125     return ariaRole == RadioButtonRole || ariaRole == CheckBoxRole || ariaRole == TextFieldRole;
    126 }
    127 
    128 bool AccessibilityObject::isARIAControl(AccessibilityRole ariaRole)
    129 {
    130     return isARIAInput(ariaRole) || ariaRole == TextAreaRole || ariaRole == ButtonRole
    131     || ariaRole == ComboBoxRole || ariaRole == SliderRole;
    132 }
    133 
    134 IntPoint AccessibilityObject::clickPoint() const
    135 {
    136     IntRect rect = elementRect();
    137     return IntPoint(rect.x() + rect.width() / 2, rect.y() + rect.height() / 2);
    138 }
    139 
    140 bool AccessibilityObject::press() const
    141 {
    142     Element* actionElem = actionElement();
    143     if (!actionElem)
    144         return false;
    145     if (Frame* f = actionElem->document()->frame())
    146         f->loader()->resetMultipleFormSubmissionProtection();
    147     actionElem->accessKeyAction(true);
    148     return true;
    149 }
    150 
    151 String AccessibilityObject::language() const
    152 {
    153     AccessibilityObject* parent = parentObject();
    154 
    155     // as a last resort, fall back to the content language specified in the meta tag
    156     if (!parent) {
    157         Document* doc = document();
    158         if (doc)
    159             return doc->contentLanguage();
    160         return String();
    161     }
    162 
    163     return parent->language();
    164 }
    165 
    166 VisiblePositionRange AccessibilityObject::visiblePositionRangeForUnorderedPositions(const VisiblePosition& visiblePos1, const VisiblePosition& visiblePos2) const
    167 {
    168     if (visiblePos1.isNull() || visiblePos2.isNull())
    169         return VisiblePositionRange();
    170 
    171     VisiblePosition startPos;
    172     VisiblePosition endPos;
    173     bool alreadyInOrder;
    174 
    175     // upstream is ordered before downstream for the same position
    176     if (visiblePos1 == visiblePos2 && visiblePos2.affinity() == UPSTREAM)
    177         alreadyInOrder = false;
    178 
    179     // use selection order to see if the positions are in order
    180     else
    181         alreadyInOrder = VisibleSelection(visiblePos1, visiblePos2).isBaseFirst();
    182 
    183     if (alreadyInOrder) {
    184         startPos = visiblePos1;
    185         endPos = visiblePos2;
    186     } else {
    187         startPos = visiblePos2;
    188         endPos = visiblePos1;
    189     }
    190 
    191     return VisiblePositionRange(startPos, endPos);
    192 }
    193 
    194 VisiblePositionRange AccessibilityObject::positionOfLeftWord(const VisiblePosition& visiblePos) const
    195 {
    196     VisiblePosition startPosition = startOfWord(visiblePos, LeftWordIfOnBoundary);
    197     VisiblePosition endPosition = endOfWord(startPosition);
    198     return VisiblePositionRange(startPosition, endPosition);
    199 }
    200 
    201 VisiblePositionRange AccessibilityObject::positionOfRightWord(const VisiblePosition& visiblePos) const
    202 {
    203     VisiblePosition startPosition = startOfWord(visiblePos, RightWordIfOnBoundary);
    204     VisiblePosition endPosition = endOfWord(startPosition);
    205     return VisiblePositionRange(startPosition, endPosition);
    206 }
    207 
    208 static VisiblePosition updateAXLineStartForVisiblePosition(const VisiblePosition& visiblePosition)
    209 {
    210     // A line in the accessibility sense should include floating objects, such as aligned image, as part of a line.
    211     // So let's update the position to include that.
    212     VisiblePosition tempPosition;
    213     VisiblePosition startPosition = visiblePosition;
    214     Position p;
    215     RenderObject* renderer;
    216     while (true) {
    217         tempPosition = startPosition.previous();
    218         if (tempPosition.isNull())
    219             break;
    220         p = tempPosition.deepEquivalent();
    221         if (!p.node())
    222             break;
    223         renderer = p.node()->renderer();
    224         if (!renderer || (renderer->isRenderBlock() && !p.deprecatedEditingOffset()))
    225             break;
    226         InlineBox* box;
    227         int ignoredCaretOffset;
    228         p.getInlineBoxAndOffset(tempPosition.affinity(), box, ignoredCaretOffset);
    229         if (box)
    230             break;
    231         startPosition = tempPosition;
    232     }
    233 
    234     return startPosition;
    235 }
    236 
    237 VisiblePositionRange AccessibilityObject::leftLineVisiblePositionRange(const VisiblePosition& visiblePos) const
    238 {
    239     if (visiblePos.isNull())
    240         return VisiblePositionRange();
    241 
    242     // make a caret selection for the position before marker position (to make sure
    243     // we move off of a line start)
    244     VisiblePosition prevVisiblePos = visiblePos.previous();
    245     if (prevVisiblePos.isNull())
    246         return VisiblePositionRange();
    247 
    248     VisiblePosition startPosition = startOfLine(prevVisiblePos);
    249 
    250     // keep searching for a valid line start position.  Unless the VisiblePosition is at the very beginning, there should
    251     // always be a valid line range.  However, startOfLine will return null for position next to a floating object,
    252     // since floating object doesn't really belong to any line.
    253     // This check will reposition the marker before the floating object, to ensure we get a line start.
    254     if (startPosition.isNull()) {
    255         while (startPosition.isNull() && prevVisiblePos.isNotNull()) {
    256             prevVisiblePos = prevVisiblePos.previous();
    257             startPosition = startOfLine(prevVisiblePos);
    258         }
    259     } else
    260         startPosition = updateAXLineStartForVisiblePosition(startPosition);
    261 
    262     VisiblePosition endPosition = endOfLine(prevVisiblePos);
    263     return VisiblePositionRange(startPosition, endPosition);
    264 }
    265 
    266 VisiblePositionRange AccessibilityObject::rightLineVisiblePositionRange(const VisiblePosition& visiblePos) const
    267 {
    268     if (visiblePos.isNull())
    269         return VisiblePositionRange();
    270 
    271     // make sure we move off of a line end
    272     VisiblePosition nextVisiblePos = visiblePos.next();
    273     if (nextVisiblePos.isNull())
    274         return VisiblePositionRange();
    275 
    276     VisiblePosition startPosition = startOfLine(nextVisiblePos);
    277 
    278     // fetch for a valid line start position
    279     if (startPosition.isNull()) {
    280         startPosition = visiblePos;
    281         nextVisiblePos = nextVisiblePos.next();
    282     } else
    283         startPosition = updateAXLineStartForVisiblePosition(startPosition);
    284 
    285     VisiblePosition endPosition = endOfLine(nextVisiblePos);
    286 
    287     // as long as the position hasn't reached the end of the doc,  keep searching for a valid line end position
    288     // Unless the VisiblePosition is at the very end, there should always be a valid line range.  However, endOfLine will
    289     // return null for position by a floating object, since floating object doesn't really belong to any line.
    290     // This check will reposition the marker after the floating object, to ensure we get a line end.
    291     while (endPosition.isNull() && nextVisiblePos.isNotNull()) {
    292         nextVisiblePos = nextVisiblePos.next();
    293         endPosition = endOfLine(nextVisiblePos);
    294     }
    295 
    296     return VisiblePositionRange(startPosition, endPosition);
    297 }
    298 
    299 VisiblePositionRange AccessibilityObject::sentenceForPosition(const VisiblePosition& visiblePos) const
    300 {
    301     // FIXME: FO 2 IMPLEMENT (currently returns incorrect answer)
    302     // Related? <rdar://problem/3927736> Text selection broken in 8A336
    303     VisiblePosition startPosition = startOfSentence(visiblePos);
    304     VisiblePosition endPosition = endOfSentence(startPosition);
    305     return VisiblePositionRange(startPosition, endPosition);
    306 }
    307 
    308 VisiblePositionRange AccessibilityObject::paragraphForPosition(const VisiblePosition& visiblePos) const
    309 {
    310     VisiblePosition startPosition = startOfParagraph(visiblePos);
    311     VisiblePosition endPosition = endOfParagraph(startPosition);
    312     return VisiblePositionRange(startPosition, endPosition);
    313 }
    314 
    315 static VisiblePosition startOfStyleRange(const VisiblePosition visiblePos)
    316 {
    317     RenderObject* renderer = visiblePos.deepEquivalent().node()->renderer();
    318     RenderObject* startRenderer = renderer;
    319     RenderStyle* style = renderer->style();
    320 
    321     // traverse backward by renderer to look for style change
    322     for (RenderObject* r = renderer->previousInPreOrder(); r; r = r->previousInPreOrder()) {
    323         // skip non-leaf nodes
    324         if (r->firstChild())
    325             continue;
    326 
    327         // stop at style change
    328         if (r->style() != style)
    329             break;
    330 
    331         // remember match
    332         startRenderer = r;
    333     }
    334 
    335     return VisiblePosition(startRenderer->node(), 0, VP_DEFAULT_AFFINITY);
    336 }
    337 
    338 static VisiblePosition endOfStyleRange(const VisiblePosition& visiblePos)
    339 {
    340     RenderObject* renderer = visiblePos.deepEquivalent().node()->renderer();
    341     RenderObject* endRenderer = renderer;
    342     RenderStyle* style = renderer->style();
    343 
    344     // traverse forward by renderer to look for style change
    345     for (RenderObject* r = renderer->nextInPreOrder(); r; r = r->nextInPreOrder()) {
    346         // skip non-leaf nodes
    347         if (r->firstChild())
    348             continue;
    349 
    350         // stop at style change
    351         if (r->style() != style)
    352             break;
    353 
    354         // remember match
    355         endRenderer = r;
    356     }
    357 
    358     return lastDeepEditingPositionForNode(endRenderer->node());
    359 }
    360 
    361 VisiblePositionRange AccessibilityObject::styleRangeForPosition(const VisiblePosition& visiblePos) const
    362 {
    363     if (visiblePos.isNull())
    364         return VisiblePositionRange();
    365 
    366     return VisiblePositionRange(startOfStyleRange(visiblePos), endOfStyleRange(visiblePos));
    367 }
    368 
    369 // NOTE: Consider providing this utility method as AX API
    370 VisiblePositionRange AccessibilityObject::visiblePositionRangeForRange(const PlainTextRange& range) const
    371 {
    372     if (range.start + range.length > text().length())
    373         return VisiblePositionRange();
    374 
    375     VisiblePosition startPosition = visiblePositionForIndex(range.start);
    376     startPosition.setAffinity(DOWNSTREAM);
    377     VisiblePosition endPosition = visiblePositionForIndex(range.start + range.length);
    378     return VisiblePositionRange(startPosition, endPosition);
    379 }
    380 
    381 static bool replacedNodeNeedsCharacter(Node* replacedNode)
    382 {
    383     // we should always be given a rendered node and a replaced node, but be safe
    384     // replaced nodes are either attachments (widgets) or images
    385     if (!replacedNode || !replacedNode->renderer() || !replacedNode->renderer()->isReplaced() || replacedNode->isTextNode())
    386         return false;
    387 
    388     // create an AX object, but skip it if it is not supposed to be seen
    389     AccessibilityObject* object = replacedNode->renderer()->document()->axObjectCache()->getOrCreate(replacedNode->renderer());
    390     if (object->accessibilityIsIgnored())
    391         return false;
    392 
    393     return true;
    394 }
    395 
    396 // Finds a RenderListItem parent give a node.
    397 RenderListItem* AccessibilityObject::renderListItemContainerForNode(Node* node) const
    398 {
    399     for (Node* stringNode = node; stringNode; stringNode = stringNode->parent()) {
    400         RenderObject* renderObject = stringNode->renderer();
    401         if (!renderObject || !renderObject->isListItem())
    402             continue;
    403 
    404         return toRenderListItem(renderObject);
    405     }
    406 
    407     return 0;
    408 }
    409 
    410 // Returns the text associated with a list marker if this node is contained within a list item.
    411 String AccessibilityObject::listMarkerTextForNodeAndPosition(Node* node, const VisiblePosition& visiblePositionStart) const
    412 {
    413     // If the range does not contain the start of the line, the list marker text should not be included.
    414     if (!isStartOfLine(visiblePositionStart))
    415         return String();
    416 
    417     RenderListItem* listItem = renderListItemContainerForNode(node);
    418     if (!listItem)
    419         return String();
    420 
    421     // If this is in a list item, we need to manually add the text for the list marker
    422     // because a RenderListMarker does not have a Node equivalent and thus does not appear
    423     // when iterating text.
    424     const String& markerText = listItem->markerText();
    425     if (markerText.isEmpty())
    426         return String();
    427 
    428     // Append text, plus the period that follows the text.
    429     // FIXME: Not all list marker styles are followed by a period, but this
    430     // sounds much better when there is a synthesized pause because of a period.
    431     Vector<UChar> resultVector;
    432     resultVector.append(markerText.characters(), markerText.length());
    433     resultVector.append('.');
    434     resultVector.append(' ');
    435 
    436     return String::adopt(resultVector);
    437 }
    438 
    439 String AccessibilityObject::stringForVisiblePositionRange(const VisiblePositionRange& visiblePositionRange) const
    440 {
    441     if (visiblePositionRange.isNull())
    442         return String();
    443 
    444     Vector<UChar> resultVector;
    445     RefPtr<Range> range = makeRange(visiblePositionRange.start, visiblePositionRange.end);
    446     for (TextIterator it(range.get()); !it.atEnd(); it.advance()) {
    447         // non-zero length means textual node, zero length means replaced node (AKA "attachments" in AX)
    448         if (it.length()) {
    449             // Add a textual representation for list marker text
    450             String listMarkerText = listMarkerTextForNodeAndPosition(it.node(), visiblePositionRange.start);
    451             if (!listMarkerText.isEmpty())
    452                 resultVector.append(listMarkerText.characters(), listMarkerText.length());
    453 
    454             resultVector.append(it.characters(), it.length());
    455         } else {
    456             // locate the node and starting offset for this replaced range
    457             int exception = 0;
    458             Node* node = it.range()->startContainer(exception);
    459             ASSERT(node == it.range()->endContainer(exception));
    460             int offset = it.range()->startOffset(exception);
    461 
    462             if (replacedNodeNeedsCharacter(node->childNode(offset)))
    463                 resultVector.append(objectReplacementCharacter);
    464         }
    465     }
    466 
    467     return String::adopt(resultVector);
    468 }
    469 
    470 int AccessibilityObject::lengthForVisiblePositionRange(const VisiblePositionRange& visiblePositionRange) const
    471 {
    472     // FIXME: Multi-byte support
    473     if (visiblePositionRange.isNull())
    474         return -1;
    475 
    476     int length = 0;
    477     RefPtr<Range> range = makeRange(visiblePositionRange.start, visiblePositionRange.end);
    478     for (TextIterator it(range.get()); !it.atEnd(); it.advance()) {
    479         // non-zero length means textual node, zero length means replaced node (AKA "attachments" in AX)
    480         if (it.length())
    481             length += it.length();
    482         else {
    483             // locate the node and starting offset for this replaced range
    484             int exception = 0;
    485             Node* node = it.range()->startContainer(exception);
    486             ASSERT(node == it.range()->endContainer(exception));
    487             int offset = it.range()->startOffset(exception);
    488 
    489             if (replacedNodeNeedsCharacter(node->childNode(offset)))
    490                 length++;
    491         }
    492     }
    493 
    494     return length;
    495 }
    496 
    497 VisiblePosition AccessibilityObject::nextWordEnd(const VisiblePosition& visiblePos) const
    498 {
    499     if (visiblePos.isNull())
    500         return VisiblePosition();
    501 
    502     // make sure we move off of a word end
    503     VisiblePosition nextVisiblePos = visiblePos.next();
    504     if (nextVisiblePos.isNull())
    505         return VisiblePosition();
    506 
    507     return endOfWord(nextVisiblePos, LeftWordIfOnBoundary);
    508 }
    509 
    510 VisiblePosition AccessibilityObject::previousWordStart(const VisiblePosition& visiblePos) const
    511 {
    512     if (visiblePos.isNull())
    513         return VisiblePosition();
    514 
    515     // make sure we move off of a word start
    516     VisiblePosition prevVisiblePos = visiblePos.previous();
    517     if (prevVisiblePos.isNull())
    518         return VisiblePosition();
    519 
    520     return startOfWord(prevVisiblePos, RightWordIfOnBoundary);
    521 }
    522 
    523 VisiblePosition AccessibilityObject::nextLineEndPosition(const VisiblePosition& visiblePos) const
    524 {
    525     if (visiblePos.isNull())
    526         return VisiblePosition();
    527 
    528     // to make sure we move off of a line end
    529     VisiblePosition nextVisiblePos = visiblePos.next();
    530     if (nextVisiblePos.isNull())
    531         return VisiblePosition();
    532 
    533     VisiblePosition endPosition = endOfLine(nextVisiblePos);
    534 
    535     // as long as the position hasn't reached the end of the doc,  keep searching for a valid line end position
    536     // 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.
    537     while (endPosition.isNull() && nextVisiblePos.isNotNull()) {
    538         nextVisiblePos = nextVisiblePos.next();
    539         endPosition = endOfLine(nextVisiblePos);
    540     }
    541 
    542     return endPosition;
    543 }
    544 
    545 VisiblePosition AccessibilityObject::previousLineStartPosition(const VisiblePosition& visiblePos) const
    546 {
    547     if (visiblePos.isNull())
    548         return VisiblePosition();
    549 
    550     // make sure we move off of a line start
    551     VisiblePosition prevVisiblePos = visiblePos.previous();
    552     if (prevVisiblePos.isNull())
    553         return VisiblePosition();
    554 
    555     VisiblePosition startPosition = startOfLine(prevVisiblePos);
    556 
    557     // as long as the position hasn't reached the beginning of the doc,  keep searching for a valid line start position
    558     // 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.
    559     if (startPosition.isNull()) {
    560         while (startPosition.isNull() && prevVisiblePos.isNotNull()) {
    561             prevVisiblePos = prevVisiblePos.previous();
    562             startPosition = startOfLine(prevVisiblePos);
    563         }
    564     } else
    565         startPosition = updateAXLineStartForVisiblePosition(startPosition);
    566 
    567     return startPosition;
    568 }
    569 
    570 VisiblePosition AccessibilityObject::nextSentenceEndPosition(const VisiblePosition& visiblePos) const
    571 {
    572     // FIXME: FO 2 IMPLEMENT (currently returns incorrect answer)
    573     // Related? <rdar://problem/3927736> Text selection broken in 8A336
    574     if (visiblePos.isNull())
    575         return VisiblePosition();
    576 
    577     // make sure we move off of a sentence end
    578     VisiblePosition nextVisiblePos = visiblePos.next();
    579     if (nextVisiblePos.isNull())
    580         return VisiblePosition();
    581 
    582     // an empty line is considered a sentence. If it's skipped, then the sentence parser will not
    583     // see this empty line.  Instead, return the end position of the empty line.
    584     VisiblePosition endPosition;
    585 
    586     String lineString = plainText(makeRange(startOfLine(nextVisiblePos), endOfLine(nextVisiblePos)).get());
    587     if (lineString.isEmpty())
    588         endPosition = nextVisiblePos;
    589     else
    590         endPosition = endOfSentence(nextVisiblePos);
    591 
    592     return endPosition;
    593 }
    594 
    595 VisiblePosition AccessibilityObject::previousSentenceStartPosition(const VisiblePosition& visiblePos) const
    596 {
    597     // FIXME: FO 2 IMPLEMENT (currently returns incorrect answer)
    598     // Related? <rdar://problem/3927736> Text selection broken in 8A336
    599     if (visiblePos.isNull())
    600         return VisiblePosition();
    601 
    602     // make sure we move off of a sentence start
    603     VisiblePosition previousVisiblePos = visiblePos.previous();
    604     if (previousVisiblePos.isNull())
    605         return VisiblePosition();
    606 
    607     // treat empty line as a separate sentence.
    608     VisiblePosition startPosition;
    609 
    610     String lineString = plainText(makeRange(startOfLine(previousVisiblePos), endOfLine(previousVisiblePos)).get());
    611     if (lineString.isEmpty())
    612         startPosition = previousVisiblePos;
    613     else
    614         startPosition = startOfSentence(previousVisiblePos);
    615 
    616     return startPosition;
    617 }
    618 
    619 VisiblePosition AccessibilityObject::nextParagraphEndPosition(const VisiblePosition& visiblePos) const
    620 {
    621     if (visiblePos.isNull())
    622         return VisiblePosition();
    623 
    624     // make sure we move off of a paragraph end
    625     VisiblePosition nextPos = visiblePos.next();
    626     if (nextPos.isNull())
    627         return VisiblePosition();
    628 
    629     return endOfParagraph(nextPos);
    630 }
    631 
    632 VisiblePosition AccessibilityObject::previousParagraphStartPosition(const VisiblePosition& visiblePos) const
    633 {
    634     if (visiblePos.isNull())
    635         return VisiblePosition();
    636 
    637     // make sure we move off of a paragraph start
    638     VisiblePosition previousPos = visiblePos.previous();
    639     if (previousPos.isNull())
    640         return VisiblePosition();
    641 
    642     return startOfParagraph(previousPos);
    643 }
    644 
    645 AccessibilityObject* AccessibilityObject::accessibilityObjectForPosition(const VisiblePosition& visiblePos) const
    646 {
    647     if (visiblePos.isNull())
    648         return 0;
    649 
    650     RenderObject* obj = visiblePos.deepEquivalent().node()->renderer();
    651     if (!obj)
    652         return 0;
    653 
    654     return obj->document()->axObjectCache()->getOrCreate(obj);
    655 }
    656 
    657 int AccessibilityObject::lineForPosition(const VisiblePosition& visiblePos) const
    658 {
    659     if (visiblePos.isNull())
    660         return 0;
    661 
    662     unsigned lineCount = 0;
    663     VisiblePosition currentVisiblePos = visiblePos;
    664     VisiblePosition savedVisiblePos;
    665 
    666     // move up until we get to the top
    667     // FIXME: This only takes us to the top of the rootEditableElement, not the top of the
    668     // top document.
    669     while (currentVisiblePos.isNotNull() && !(inSameLine(currentVisiblePos, savedVisiblePos))) {
    670         ++lineCount;
    671         savedVisiblePos = currentVisiblePos;
    672         VisiblePosition prevVisiblePos = previousLinePosition(currentVisiblePos, 0);
    673         currentVisiblePos = prevVisiblePos;
    674     }
    675 
    676     return lineCount - 1;
    677 }
    678 
    679 // NOTE: Consider providing this utility method as AX API
    680 PlainTextRange AccessibilityObject::plainTextRangeForVisiblePositionRange(const VisiblePositionRange& positionRange) const
    681 {
    682     int index1 = index(positionRange.start);
    683     int index2 = index(positionRange.end);
    684     if (index1 < 0 || index2 < 0 || index1 > index2)
    685         return PlainTextRange();
    686 
    687     return PlainTextRange(index1, index2 - index1);
    688 }
    689 
    690 // The composed character range in the text associated with this accessibility object that
    691 // is specified by the given screen coordinates. This parameterized attribute returns the
    692 // complete range of characters (including surrogate pairs of multi-byte glyphs) at the given
    693 // screen coordinates.
    694 // NOTE: This varies from AppKit when the point is below the last line. AppKit returns an
    695 // an error in that case. We return textControl->text().length(), 1. Does this matter?
    696 PlainTextRange AccessibilityObject::doAXRangeForPosition(const IntPoint& point) const
    697 {
    698     int i = index(visiblePositionForPoint(point));
    699     if (i < 0)
    700         return PlainTextRange();
    701 
    702     return PlainTextRange(i, 1);
    703 }
    704 
    705 // Given a character index, the range of text associated with this accessibility object
    706 // over which the style in effect at that character index applies.
    707 PlainTextRange AccessibilityObject::doAXStyleRangeForIndex(unsigned index) const
    708 {
    709     VisiblePositionRange range = styleRangeForPosition(visiblePositionForIndex(index, false));
    710     return plainTextRangeForVisiblePositionRange(range);
    711 }
    712 
    713 // Given an indexed character, the line number of the text associated with this accessibility
    714 // object that contains the character.
    715 unsigned AccessibilityObject::doAXLineForIndex(unsigned index)
    716 {
    717     return lineForPosition(visiblePositionForIndex(index, false));
    718 }
    719 
    720 FrameView* AccessibilityObject::documentFrameView() const
    721 {
    722     const AccessibilityObject* object = this;
    723     while (object && !object->isAccessibilityRenderObject())
    724         object = object->parentObject();
    725 
    726     if (!object)
    727         return 0;
    728 
    729     return object->documentFrameView();
    730 }
    731 
    732 void AccessibilityObject::clearChildren()
    733 {
    734     m_haveChildren = false;
    735     m_children.clear();
    736 }
    737 
    738 AccessibilityObject* AccessibilityObject::anchorElementForNode(Node* node)
    739 {
    740     RenderObject* obj = node->renderer();
    741     if (!obj)
    742         return 0;
    743 
    744     RefPtr<AccessibilityObject> axObj = obj->document()->axObjectCache()->getOrCreate(obj);
    745     Element* anchor = axObj->anchorElement();
    746     if (!anchor)
    747         return 0;
    748 
    749     RenderObject* anchorRenderer = anchor->renderer();
    750     if (!anchorRenderer)
    751         return 0;
    752 
    753     return anchorRenderer->document()->axObjectCache()->getOrCreate(anchorRenderer);
    754 }
    755 
    756 void AccessibilityObject::ariaTreeRows(AccessibilityChildrenVector& result)
    757 {
    758     AccessibilityChildrenVector axChildren = children();
    759     unsigned count = axChildren.size();
    760     for (unsigned k = 0; k < count; ++k) {
    761         AccessibilityObject* obj = axChildren[k].get();
    762 
    763         // Add tree items as the rows.
    764         if (obj->roleValue() == TreeItemRole)
    765             result.append(obj);
    766 
    767         // Now see if this item also has rows hiding inside of it.
    768         obj->ariaTreeRows(result);
    769     }
    770 }
    771 
    772 void AccessibilityObject::ariaTreeItemContent(AccessibilityChildrenVector& result)
    773 {
    774     // The ARIA tree item content are the item that are not other tree items or their containing groups.
    775     AccessibilityChildrenVector axChildren = children();
    776     unsigned count = axChildren.size();
    777     for (unsigned k = 0; k < count; ++k) {
    778         AccessibilityObject* obj = axChildren[k].get();
    779         AccessibilityRole role = obj->roleValue();
    780         if (role == TreeItemRole || role == GroupRole)
    781             continue;
    782 
    783         result.append(obj);
    784     }
    785 }
    786 
    787 void AccessibilityObject::ariaTreeItemDisclosedRows(AccessibilityChildrenVector& result)
    788 {
    789     AccessibilityChildrenVector axChildren = children();
    790     unsigned count = axChildren.size();
    791     for (unsigned k = 0; k < count; ++k) {
    792         AccessibilityObject* obj = axChildren[k].get();
    793 
    794         // Add tree items as the rows.
    795         if (obj->roleValue() == TreeItemRole)
    796             result.append(obj);
    797         // If it's not a tree item, then descend into the group to find more tree items.
    798         else
    799             obj->ariaTreeRows(result);
    800     }
    801 }
    802 
    803 const String& AccessibilityObject::actionVerb() const
    804 {
    805     // FIXME: Need to add verbs for select elements.
    806     DEFINE_STATIC_LOCAL(const String, buttonAction, (AXButtonActionVerb()));
    807     DEFINE_STATIC_LOCAL(const String, textFieldAction, (AXTextFieldActionVerb()));
    808     DEFINE_STATIC_LOCAL(const String, radioButtonAction, (AXRadioButtonActionVerb()));
    809     DEFINE_STATIC_LOCAL(const String, checkedCheckBoxAction, (AXCheckedCheckBoxActionVerb()));
    810     DEFINE_STATIC_LOCAL(const String, uncheckedCheckBoxAction, (AXUncheckedCheckBoxActionVerb()));
    811     DEFINE_STATIC_LOCAL(const String, linkAction, (AXLinkActionVerb()));
    812     DEFINE_STATIC_LOCAL(const String, menuListAction, (AXMenuListActionVerb()));
    813     DEFINE_STATIC_LOCAL(const String, menuListPopupAction, (AXMenuListPopupActionVerb()));
    814     DEFINE_STATIC_LOCAL(const String, noAction, ());
    815 
    816     switch (roleValue()) {
    817     case ButtonRole:
    818         return buttonAction;
    819     case TextFieldRole:
    820     case TextAreaRole:
    821         return textFieldAction;
    822     case RadioButtonRole:
    823         return radioButtonAction;
    824     case CheckBoxRole:
    825         return isChecked() ? checkedCheckBoxAction : uncheckedCheckBoxAction;
    826     case LinkRole:
    827     case WebCoreLinkRole:
    828         return linkAction;
    829     case PopUpButtonRole:
    830         return menuListAction;
    831     case MenuListPopupRole:
    832         return menuListPopupAction;
    833     default:
    834         return noAction;
    835     }
    836 }
    837 
    838 // Lacking concrete evidence of orientation, horizontal means width > height. vertical is height > width;
    839 AccessibilityOrientation AccessibilityObject::orientation() const
    840 {
    841     IntRect bounds = elementRect();
    842     if (bounds.size().width() > bounds.size().height())
    843         return AccessibilityOrientationHorizontal;
    844     if (bounds.size().height() > bounds.size().width())
    845         return AccessibilityOrientationVertical;
    846 
    847     // A tie goes to horizontal.
    848     return AccessibilityOrientationHorizontal;
    849 }
    850 
    851 typedef HashMap<String, AccessibilityRole, CaseFoldingHash> ARIARoleMap;
    852 
    853 struct RoleEntry {
    854     String ariaRole;
    855     AccessibilityRole webcoreRole;
    856 };
    857 
    858 static ARIARoleMap* createARIARoleMap()
    859 {
    860     const RoleEntry roles[] = {
    861         { "alert", ApplicationAlertRole },
    862         { "alertdialog", ApplicationAlertDialogRole },
    863         { "application", LandmarkApplicationRole },
    864         { "article", DocumentArticleRole },
    865         { "banner", LandmarkBannerRole },
    866         { "button", ButtonRole },
    867         { "checkbox", CheckBoxRole },
    868         { "complementary", LandmarkComplementaryRole },
    869         { "contentinfo", LandmarkContentInfoRole },
    870         { "dialog", ApplicationDialogRole },
    871         { "directory", DirectoryRole },
    872         { "grid", TableRole },
    873         { "gridcell", CellRole },
    874         { "columnheader", ColumnHeaderRole },
    875         { "combobox", ComboBoxRole },
    876         { "definition", DefinitionListDefinitionRole },
    877         { "document", DocumentRole },
    878         { "rowheader", RowHeaderRole },
    879         { "group", GroupRole },
    880         { "heading", HeadingRole },
    881         { "img", ImageRole },
    882         { "link", WebCoreLinkRole },
    883         { "list", ListRole },
    884         { "listitem", GroupRole },
    885         { "listbox", ListBoxRole },
    886         { "log", ApplicationLogRole },
    887         // "option" isn't here because it may map to different roles depending on the parent element's role
    888         { "main", LandmarkMainRole },
    889         { "marquee", ApplicationMarqueeRole },
    890         { "math", DocumentMathRole },
    891         { "menu", MenuRole },
    892         { "menubar", GroupRole },
    893         // "menuitem" isn't here because it may map to different roles depending on the parent element's role
    894         { "menuitemcheckbox", MenuItemRole },
    895         { "menuitemradio", MenuItemRole },
    896         { "note", DocumentNoteRole },
    897         { "navigation", LandmarkNavigationRole },
    898         { "option", ListBoxOptionRole },
    899         { "presentation", IgnoredRole },
    900         { "progressbar", ProgressIndicatorRole },
    901         { "radio", RadioButtonRole },
    902         { "radiogroup", RadioGroupRole },
    903         { "region", DocumentRegionRole },
    904         { "row", RowRole },
    905         { "range", SliderRole },
    906         { "scrollbar", ScrollBarRole },
    907         { "search", LandmarkSearchRole },
    908         { "separator", SplitterRole },
    909         { "slider", SliderRole },
    910         { "spinbutton", ProgressIndicatorRole },
    911         { "status", ApplicationStatusRole },
    912         { "tab", TabRole },
    913         { "tablist", TabListRole },
    914         { "tabpanel", TabPanelRole },
    915         { "text", StaticTextRole },
    916         { "textbox", TextAreaRole },
    917         { "timer", ApplicationTimerRole },
    918         { "toolbar", ToolbarRole },
    919         { "tooltip", UserInterfaceTooltipRole },
    920         { "tree", TreeRole },
    921         { "treegrid", TreeGridRole },
    922         { "treeitem", TreeItemRole }
    923     };
    924     ARIARoleMap* roleMap = new ARIARoleMap;
    925 
    926     const unsigned numRoles = sizeof(roles) / sizeof(roles[0]);
    927     for (unsigned i = 0; i < numRoles; ++i)
    928         roleMap->set(roles[i].ariaRole, roles[i].webcoreRole);
    929     return roleMap;
    930 }
    931 
    932 AccessibilityRole AccessibilityObject::ariaRoleToWebCoreRole(const String& value)
    933 {
    934     ASSERT(!value.isEmpty());
    935     static const ARIARoleMap* roleMap = createARIARoleMap();
    936     return roleMap->get(value);
    937 }
    938 
    939 bool AccessibilityObject::isInsideARIALiveRegion() const
    940 {
    941     if (supportsARIALiveRegion())
    942         return true;
    943 
    944     for (AccessibilityObject* axParent = parentObject(); axParent; axParent = axParent->parentObject()) {
    945         if (axParent->supportsARIALiveRegion())
    946             return true;
    947     }
    948 
    949     return false;
    950 }
    951 
    952 bool AccessibilityObject::supportsARIALiveRegion() const
    953 {
    954     const AtomicString& liveRegion = ariaLiveRegionStatus();
    955     return equalIgnoringCase(liveRegion, "polite") || equalIgnoringCase(liveRegion, "assertive");
    956 }
    957 
    958 
    959 } // namespace WebCore
    960