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