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