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