AX: Notification should be sent when accessibilityIsIgnored changes
[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 "AccessibilityTable.h"
35 #include "FloatRect.h"
36 #include "FocusController.h"
37 #include "Frame.h"
38 #include "FrameLoader.h"
39 #include "FrameSelection.h"
40 #include "HTMLNames.h"
41 #include "LocalizedStrings.h"
42 #include "NodeList.h"
43 #include "NotImplemented.h"
44 #include "Page.h"
45 #include "RenderImage.h"
46 #include "RenderListItem.h"
47 #include "RenderListMarker.h"
48 #include "RenderMenuList.h"
49 #include "RenderTextControl.h"
50 #include "RenderTheme.h"
51 #include "RenderView.h"
52 #include "RenderWidget.h"
53 #include "RenderedPosition.h"
54 #include "Settings.h"
55 #include "TextCheckerClient.h"
56 #include "TextCheckingHelper.h"
57 #include "TextIterator.h"
58 #include "htmlediting.h"
59 #include "visible_units.h"
60 #include <wtf/StdLibExtras.h>
61 #include <wtf/text/StringBuilder.h>
62 #include <wtf/text/WTFString.h>
63 #include <wtf/unicode/CharacterNames.h>
64
65 using namespace std;
66
67 namespace WebCore {
68
69 using namespace HTMLNames;
70
71 AccessibilityObject::AccessibilityObject()
72     : m_id(0)
73     , m_haveChildren(false)
74     , m_role(UnknownRole)
75     , m_cachedIsIgnoredValue(DefaultBehavior)
76 #if PLATFORM(GTK)
77     , m_wrapper(0)
78 #elif PLATFORM(CHROMIUM)
79     , m_detached(false)
80 #endif
81 {
82 }
83
84 AccessibilityObject::~AccessibilityObject()
85 {
86     ASSERT(isDetached());
87 }
88
89 void AccessibilityObject::detach()
90 {
91 #if HAVE(ACCESSIBILITY) && PLATFORM(CHROMIUM)
92     m_detached = true;
93 #elif HAVE(ACCESSIBILITY)
94     setWrapper(0);
95 #endif
96 }
97
98 bool AccessibilityObject::isDetached() const
99 {
100 #if HAVE(ACCESSIBILITY) && PLATFORM(CHROMIUM)
101     return m_detached;
102 #elif HAVE(ACCESSIBILITY)
103     return !wrapper();
104 #else
105     return true;
106 #endif
107 }
108
109 bool AccessibilityObject::isAccessibilityObjectSearchMatch(AccessibilityObject* axObject, AccessibilitySearchCriteria* criteria)
110 {
111     if (!axObject || !criteria)
112         return false;
113     
114     switch (criteria->searchKey) {
115     // The AnyTypeSearchKey matches any non-null AccessibilityObject.
116     case AnyTypeSearchKey:
117         return true;
118         
119     case BlockquoteSameLevelSearchKey:
120         return criteria->startObject
121             && axObject->isBlockquote()
122             && axObject->blockquoteLevel() == criteria->startObject->blockquoteLevel();
123         
124     case BlockquoteSearchKey:
125         return axObject->isBlockquote();
126         
127     case BoldFontSearchKey:
128         return axObject->hasBoldFont();
129         
130     case ButtonSearchKey:
131         return axObject->isButton();
132         
133     case CheckBoxSearchKey:
134         return axObject->isCheckbox();
135         
136     case ControlSearchKey:
137         return axObject->isControl();
138         
139     case DifferentTypeSearchKey:
140         return criteria->startObject
141             && axObject->roleValue() != criteria->startObject->roleValue();
142         
143     case FontChangeSearchKey:
144         return criteria->startObject
145             && !axObject->hasSameFont(criteria->startObject->renderer());
146         
147     case FontColorChangeSearchKey:
148         return criteria->startObject
149             && !axObject->hasSameFontColor(criteria->startObject->renderer());
150         
151     case FrameSearchKey:
152         return axObject->isWebArea();
153         
154     case GraphicSearchKey:
155         return axObject->isImage();
156         
157     case HeadingLevel1SearchKey:
158         return axObject->headingLevel() == 1;
159         
160     case HeadingLevel2SearchKey:
161         return axObject->headingLevel() == 2;
162         
163     case HeadingLevel3SearchKey:
164         return axObject->headingLevel() == 3;
165         
166     case HeadingLevel4SearchKey:
167         return axObject->headingLevel() == 4;
168         
169     case HeadingLevel5SearchKey:
170         return axObject->headingLevel() == 5;
171         
172     case HeadingLevel6SearchKey:
173         return axObject->headingLevel() == 6;
174         
175     case HeadingSameLevelSearchKey:
176         return criteria->startObject
177             && axObject->isHeading()
178             && axObject->headingLevel() == criteria->startObject->headingLevel();
179         
180     case HeadingSearchKey:
181         return axObject->isHeading();
182     
183     case HighlightedSearchKey:
184         return axObject->hasHighlighting();
185             
186     case ItalicFontSearchKey:
187         return axObject->hasItalicFont();
188         
189     case LandmarkSearchKey:
190         return axObject->isLandmark();
191         
192     case LinkSearchKey:
193         return axObject->isLink();
194         
195     case ListSearchKey:
196         return axObject->isList();
197         
198     case LiveRegionSearchKey:
199         return axObject->supportsARIALiveRegion();
200         
201     case MisspelledWordSearchKey:
202         return axObject->hasMisspelling();
203         
204     case PlainTextSearchKey:
205         return axObject->hasPlainText();
206         
207     case RadioGroupSearchKey:
208         return axObject->isRadioGroup();
209         
210     case SameTypeSearchKey:
211         return criteria->startObject
212             && axObject->roleValue() == criteria->startObject->roleValue();
213         
214     case StaticTextSearchKey:
215         return axObject->hasStaticText();
216         
217     case StyleChangeSearchKey:
218         return criteria->startObject
219             && !axObject->hasSameStyle(criteria->startObject->renderer());
220         
221     case TableSameLevelSearchKey:
222         return criteria->startObject
223             && axObject->isAccessibilityTable()
224             && axObject->tableLevel() == criteria->startObject->tableLevel();
225         
226     case TableSearchKey:
227         return axObject->isAccessibilityTable();
228         
229     case TextFieldSearchKey:
230         return axObject->isTextControl();
231         
232     case UnderlineSearchKey:
233         return axObject->hasUnderline();
234         
235     case UnvisitedLinkSearchKey:
236         return axObject->isUnvisited();
237         
238     case VisitedLinkSearchKey:
239         return axObject->isVisited();
240         
241     default:
242         return false;
243     }
244 }
245
246 bool AccessibilityObject::isAccessibilityTextSearchMatch(AccessibilityObject* axObject, AccessibilitySearchCriteria* criteria)
247 {
248     if (!axObject || !criteria)
249         return false;
250     
251     return axObject->accessibilityObjectContainsText(criteria->searchText);
252 }
253
254 bool AccessibilityObject::accessibilityObjectContainsText(String* text) const
255 {
256     // If text is null or empty we return true.
257     return !text
258         || text->isEmpty()
259         || title().contains(*text, false)
260         || accessibilityDescription().contains(*text, false)
261         || stringValue().contains(*text, false);
262 }
263
264 bool AccessibilityObject::isBlockquote() const
265 {
266     return node() && node()->hasTagName(blockquoteTag);
267 }
268
269 bool AccessibilityObject::isARIATextControl() const
270 {
271     return ariaRoleAttribute() == TextAreaRole || ariaRoleAttribute() == TextFieldRole;
272 }
273
274 bool AccessibilityObject::isLandmark() const
275 {
276     AccessibilityRole role = roleValue();
277     
278     return role == LandmarkApplicationRole
279         || role == LandmarkBannerRole
280         || role == LandmarkComplementaryRole
281         || role == LandmarkContentInfoRole
282         || role == LandmarkMainRole
283         || role == LandmarkNavigationRole
284         || role == LandmarkSearchRole;
285 }
286
287 bool AccessibilityObject::hasMisspelling() const
288 {
289     if (!node())
290         return false;
291     
292     Document* document = node()->document();
293     if (!document)
294         return false;
295     
296     Frame* frame = document->frame();
297     if (!frame)
298         return false;
299     
300     Editor* editor = frame->editor();
301     if (!editor)
302         return false;
303     
304     TextCheckerClient* textChecker = editor->textChecker();
305     if (!textChecker)
306         return false;
307     
308     const UChar* chars = stringValue().characters();
309     int charsLength = stringValue().length();
310     bool isMisspelled = false;
311
312     if (unifiedTextCheckerEnabled(frame)) {
313         Vector<TextCheckingResult> results;
314         checkTextOfParagraph(textChecker, chars, charsLength, TextCheckingTypeSpelling, results);
315         if (!results.isEmpty())
316             isMisspelled = true;
317         return isMisspelled;
318     }
319
320     int misspellingLength = 0;
321     int misspellingLocation = -1;
322     textChecker->checkSpellingOfString(chars, charsLength, &misspellingLocation, &misspellingLength);
323     if (misspellingLength || misspellingLocation != -1)
324         isMisspelled = true;
325     
326     return isMisspelled;
327 }
328
329 int AccessibilityObject::blockquoteLevel() const
330 {
331     int level = 0;
332     for (Node* elementNode = node(); elementNode; elementNode = elementNode->parentNode()) {
333         if (elementNode->hasTagName(blockquoteTag))
334             ++level;
335     }
336     
337     return level;
338 }
339
340 AccessibilityObject* AccessibilityObject::parentObjectUnignored() const
341 {
342     AccessibilityObject* parent;
343     for (parent = parentObject(); parent && parent->accessibilityIsIgnored(); parent = parent->parentObject()) {
344     }
345     
346     return parent;
347 }
348
349 AccessibilityObject* AccessibilityObject::firstAccessibleObjectFromNode(const Node* node)
350 {
351     if (!node)
352         return 0;
353
354     Document* document = node->document();
355     if (!document)
356         return 0;
357
358     AXObjectCache* cache = document->axObjectCache();
359
360     AccessibilityObject* accessibleObject = cache->getOrCreate(node->renderer());
361     while (accessibleObject && accessibleObject->accessibilityIsIgnored()) {
362         node = node->traverseNextNode();
363
364         while (node && !node->renderer())
365             node = node->traverseNextSibling();
366
367         if (!node)
368             return 0;
369
370         accessibleObject = cache->getOrCreate(node->renderer());
371     }
372
373     return accessibleObject;
374 }
375
376 static void appendAccessibilityObject(AccessibilityObject* object, AccessibilityObject::AccessibilityChildrenVector& results)
377 {
378     // Find the next descendant of this attachment object so search can continue through frames.
379     if (object->isAttachment()) {
380         Widget* widget = object->widgetForAttachmentView();
381         if (!widget || !widget->isFrameView())
382             return;
383         
384         Document* doc = static_cast<FrameView*>(widget)->frame()->document();
385         if (!doc || !doc->renderer())
386             return;
387         
388         object = object->axObjectCache()->getOrCreate(doc);
389     }
390
391     if (object)
392         results.append(object);
393 }
394     
395 static void appendChildrenToArray(AccessibilityObject* object, bool isForward, AccessibilityObject* startObject, AccessibilityObject::AccessibilityChildrenVector& results)
396 {
397     AccessibilityObject::AccessibilityChildrenVector searchChildren;
398     // A table's children includes elements whose own children are also the table's children (due to the way the Mac exposes tables).
399     // The rows from the table should be queried, since those are direct descendants of the table, and they contain content.
400     if (object->isAccessibilityTable())
401         searchChildren = toAccessibilityTable(object)->rows();
402     else
403         searchChildren = object->children();
404
405     size_t childrenSize = searchChildren.size();
406
407     size_t startIndex = isForward ? childrenSize : 0;
408     size_t endIndex = isForward ? 0 : childrenSize;
409
410     size_t searchPosition = startObject ? searchChildren.find(startObject) : WTF::notFound;
411     if (searchPosition != WTF::notFound) {
412         if (isForward)
413             endIndex = searchPosition + 1;
414         else
415             endIndex = searchPosition;
416     }
417
418     // This is broken into two statements so that it's easier read.
419     if (isForward) {
420         for (size_t i = startIndex; i > endIndex; i--)
421             appendAccessibilityObject(searchChildren.at(i - 1).get(), results);
422     } else {
423         for (size_t i = startIndex; i < endIndex; i++)
424             appendAccessibilityObject(searchChildren.at(i).get(), results);
425     }
426 }
427
428 // Returns true if the number of results is now >= the number of results desired.
429 bool AccessibilityObject::objectMatchesSearchCriteriaWithResultLimit(AccessibilityObject* object, AccessibilitySearchCriteria* criteria, AccessibilityChildrenVector& results)
430 {
431     if (isAccessibilityObjectSearchMatch(object, criteria) && isAccessibilityTextSearchMatch(object, criteria)) {
432         results.append(object);
433         
434         // Enough results were found to stop searching.
435         if (results.size() >= criteria->resultsLimit)
436             return true;
437     }
438     
439     return false;
440 }
441
442 void AccessibilityObject::findMatchingObjects(AccessibilitySearchCriteria* criteria, AccessibilityChildrenVector& results)
443 {
444     ASSERT(criteria);
445     
446     if (!criteria)
447         return;
448
449     // This search mechanism only searches the elements before/after the starting object.
450     // It does this by stepping up the parent chain and at each level doing a DFS.
451     
452     // If there's no start object, it means we want to search everything.
453     AccessibilityObject* startObject = criteria->startObject;
454     if (!startObject)
455         startObject = this;
456     
457     bool isForward = criteria->searchDirection == SearchDirectionNext;
458     
459     // In the first iteration of the loop, it will examine the children of the start object for matches.
460     // However, when going backwards, those children should not be considered, so the loop is skipped ahead.
461     AccessibilityObject* previousObject = 0;
462     if (!isForward) {
463         previousObject = startObject;
464         startObject = startObject->parentObjectUnignored();
465     }
466     
467     // The outer loop steps up the parent chain each time (unignored is important here because otherwise elements would be searched twice)
468     for (AccessibilityObject* stopSearchElement = parentObjectUnignored(); startObject != stopSearchElement; startObject = startObject->parentObjectUnignored()) {
469
470         // Only append the children after/before the previous element, so that the search does not check elements that are 
471         // already behind/ahead of start element.
472         AccessibilityChildrenVector searchStack;
473         appendChildrenToArray(startObject, isForward, previousObject, searchStack);
474
475         // This now does a DFS at the current level of the parent.
476         while (!searchStack.isEmpty()) {
477             AccessibilityObject* searchObject = searchStack.last().get();
478             searchStack.removeLast();
479             
480             if (objectMatchesSearchCriteriaWithResultLimit(searchObject, criteria, results))
481                 break;
482             
483             appendChildrenToArray(searchObject, isForward, 0, searchStack);
484         }
485         
486         if (results.size() >= criteria->resultsLimit)
487             break;
488
489         // When moving backwards, the parent object needs to be checked, because technically it's "before" the starting element.
490         if (!isForward && objectMatchesSearchCriteriaWithResultLimit(startObject, criteria, results))
491             break;
492
493         previousObject = startObject;
494     }
495 }
496
497 bool AccessibilityObject::isARIAInput(AccessibilityRole ariaRole)
498 {
499     return ariaRole == RadioButtonRole || ariaRole == CheckBoxRole || ariaRole == TextFieldRole;
500 }    
501     
502 bool AccessibilityObject::isARIAControl(AccessibilityRole ariaRole)
503 {
504     return isARIAInput(ariaRole) || ariaRole == TextAreaRole || ariaRole == ButtonRole 
505     || ariaRole == ComboBoxRole || ariaRole == SliderRole; 
506 }
507
508 IntPoint AccessibilityObject::clickPoint()
509 {
510     LayoutRect rect = elementRect();
511     return roundedIntPoint(LayoutPoint(rect.x() + rect.width() / 2, rect.y() + rect.height() / 2));
512 }
513
514 IntRect AccessibilityObject::boundingBoxForQuads(RenderObject* obj, const Vector<FloatQuad>& quads)
515 {
516     ASSERT(obj);
517     if (!obj)
518         return IntRect();
519     
520     size_t count = quads.size();
521     if (!count)
522         return IntRect();
523     
524     IntRect result;
525     for (size_t i = 0; i < count; ++i) {
526         IntRect r = quads[i].enclosingBoundingBox();
527         if (!r.isEmpty()) {
528             if (obj->style()->hasAppearance())
529                 obj->theme()->adjustRepaintRect(obj, r);
530             result.unite(r);
531         }
532     }
533     return result;
534 }
535     
536 bool AccessibilityObject::press() const
537 {
538     Element* actionElem = actionElement();
539     if (!actionElem)
540         return false;
541     if (Frame* f = actionElem->document()->frame())
542         f->loader()->resetMultipleFormSubmissionProtection();
543     actionElem->accessKeyAction(true);
544     return true;
545 }
546     
547 String AccessibilityObject::language() const
548 {
549     const AtomicString& lang = getAttribute(langAttr);
550     if (!lang.isEmpty())
551         return lang;
552
553     AccessibilityObject* parent = parentObject();
554     
555     // as a last resort, fall back to the content language specified in the meta tag
556     if (!parent) {
557         Document* doc = document();
558         if (doc)
559             return doc->contentLanguage();
560         return nullAtom;
561     }
562     
563     return parent->language();
564 }
565     
566 VisiblePositionRange AccessibilityObject::visiblePositionRangeForUnorderedPositions(const VisiblePosition& visiblePos1, const VisiblePosition& visiblePos2) const
567 {
568     if (visiblePos1.isNull() || visiblePos2.isNull())
569         return VisiblePositionRange();
570
571     VisiblePosition startPos;
572     VisiblePosition endPos;
573     bool alreadyInOrder;
574
575     // upstream is ordered before downstream for the same position
576     if (visiblePos1 == visiblePos2 && visiblePos2.affinity() == UPSTREAM)
577         alreadyInOrder = false;
578
579     // use selection order to see if the positions are in order
580     else
581         alreadyInOrder = VisibleSelection(visiblePos1, visiblePos2).isBaseFirst();
582
583     if (alreadyInOrder) {
584         startPos = visiblePos1;
585         endPos = visiblePos2;
586     } else {
587         startPos = visiblePos2;
588         endPos = visiblePos1;
589     }
590
591     return VisiblePositionRange(startPos, endPos);
592 }
593
594 VisiblePositionRange AccessibilityObject::positionOfLeftWord(const VisiblePosition& visiblePos) const
595 {
596     VisiblePosition startPosition = startOfWord(visiblePos, LeftWordIfOnBoundary);
597     VisiblePosition endPosition = endOfWord(startPosition);
598     return VisiblePositionRange(startPosition, endPosition);
599 }
600
601 VisiblePositionRange AccessibilityObject::positionOfRightWord(const VisiblePosition& visiblePos) const
602 {
603     VisiblePosition startPosition = startOfWord(visiblePos, RightWordIfOnBoundary);
604     VisiblePosition endPosition = endOfWord(startPosition);
605     return VisiblePositionRange(startPosition, endPosition);
606 }
607
608 static VisiblePosition updateAXLineStartForVisiblePosition(const VisiblePosition& visiblePosition)
609 {
610     // A line in the accessibility sense should include floating objects, such as aligned image, as part of a line.
611     // So let's update the position to include that.
612     VisiblePosition tempPosition;
613     VisiblePosition startPosition = visiblePosition;
614     while (true) {
615         tempPosition = startPosition.previous();
616         if (tempPosition.isNull())
617             break;
618         Position p = tempPosition.deepEquivalent();
619         RenderObject* renderer = p.deprecatedNode()->renderer();
620         if (!renderer || (renderer->isRenderBlock() && !p.deprecatedEditingOffset()))
621             break;
622         if (!RenderedPosition(tempPosition).isNull())
623             break;
624         startPosition = tempPosition;
625     }
626
627     return startPosition;
628 }
629
630 VisiblePositionRange AccessibilityObject::leftLineVisiblePositionRange(const VisiblePosition& visiblePos) const
631 {
632     if (visiblePos.isNull())
633         return VisiblePositionRange();
634
635     // make a caret selection for the position before marker position (to make sure
636     // we move off of a line start)
637     VisiblePosition prevVisiblePos = visiblePos.previous();
638     if (prevVisiblePos.isNull())
639         return VisiblePositionRange();
640
641     VisiblePosition startPosition = startOfLine(prevVisiblePos);
642
643     // keep searching for a valid line start position.  Unless the VisiblePosition is at the very beginning, there should
644     // always be a valid line range.  However, startOfLine will return null for position next to a floating object,
645     // since floating object doesn't really belong to any line.
646     // This check will reposition the marker before the floating object, to ensure we get a line start.
647     if (startPosition.isNull()) {
648         while (startPosition.isNull() && prevVisiblePos.isNotNull()) {
649             prevVisiblePos = prevVisiblePos.previous();
650             startPosition = startOfLine(prevVisiblePos);
651         }
652     } else
653         startPosition = updateAXLineStartForVisiblePosition(startPosition);
654
655     VisiblePosition endPosition = endOfLine(prevVisiblePos);
656     return VisiblePositionRange(startPosition, endPosition);
657 }
658
659 VisiblePositionRange AccessibilityObject::rightLineVisiblePositionRange(const VisiblePosition& visiblePos) const
660 {
661     if (visiblePos.isNull())
662         return VisiblePositionRange();
663
664     // make sure we move off of a line end
665     VisiblePosition nextVisiblePos = visiblePos.next();
666     if (nextVisiblePos.isNull())
667         return VisiblePositionRange();
668
669     VisiblePosition startPosition = startOfLine(nextVisiblePos);
670
671     // fetch for a valid line start position
672     if (startPosition.isNull()) {
673         startPosition = visiblePos;
674         nextVisiblePos = nextVisiblePos.next();
675     } else
676         startPosition = updateAXLineStartForVisiblePosition(startPosition);
677
678     VisiblePosition endPosition = endOfLine(nextVisiblePos);
679
680     // as long as the position hasn't reached the end of the doc,  keep searching for a valid line end position
681     // Unless the VisiblePosition is at the very end, there should always be a valid line range.  However, endOfLine will
682     // return null for position by a floating object, since floating object doesn't really belong to any line.
683     // This check will reposition the marker after the floating object, to ensure we get a line end.
684     while (endPosition.isNull() && nextVisiblePos.isNotNull()) {
685         nextVisiblePos = nextVisiblePos.next();
686         endPosition = endOfLine(nextVisiblePos);
687     }
688
689     return VisiblePositionRange(startPosition, endPosition);
690 }
691
692 VisiblePositionRange AccessibilityObject::sentenceForPosition(const VisiblePosition& visiblePos) const
693 {
694     // FIXME: FO 2 IMPLEMENT (currently returns incorrect answer)
695     // Related? <rdar://problem/3927736> Text selection broken in 8A336
696     VisiblePosition startPosition = startOfSentence(visiblePos);
697     VisiblePosition endPosition = endOfSentence(startPosition);
698     return VisiblePositionRange(startPosition, endPosition);
699 }
700
701 VisiblePositionRange AccessibilityObject::paragraphForPosition(const VisiblePosition& visiblePos) const
702 {
703     VisiblePosition startPosition = startOfParagraph(visiblePos);
704     VisiblePosition endPosition = endOfParagraph(startPosition);
705     return VisiblePositionRange(startPosition, endPosition);
706 }
707
708 static VisiblePosition startOfStyleRange(const VisiblePosition visiblePos)
709 {
710     RenderObject* renderer = visiblePos.deepEquivalent().deprecatedNode()->renderer();
711     RenderObject* startRenderer = renderer;
712     RenderStyle* style = renderer->style();
713
714     // traverse backward by renderer to look for style change
715     for (RenderObject* r = renderer->previousInPreOrder(); r; r = r->previousInPreOrder()) {
716         // skip non-leaf nodes
717         if (r->firstChild())
718             continue;
719
720         // stop at style change
721         if (r->style() != style)
722             break;
723
724         // remember match
725         startRenderer = r;
726     }
727
728     return firstPositionInOrBeforeNode(startRenderer->node());
729 }
730
731 static VisiblePosition endOfStyleRange(const VisiblePosition& visiblePos)
732 {
733     RenderObject* renderer = visiblePos.deepEquivalent().deprecatedNode()->renderer();
734     RenderObject* endRenderer = renderer;
735     RenderStyle* style = renderer->style();
736
737     // traverse forward by renderer to look for style change
738     for (RenderObject* r = renderer->nextInPreOrder(); r; r = r->nextInPreOrder()) {
739         // skip non-leaf nodes
740         if (r->firstChild())
741             continue;
742
743         // stop at style change
744         if (r->style() != style)
745             break;
746
747         // remember match
748         endRenderer = r;
749     }
750
751     return lastPositionInOrAfterNode(endRenderer->node());
752 }
753
754 VisiblePositionRange AccessibilityObject::styleRangeForPosition(const VisiblePosition& visiblePos) const
755 {
756     if (visiblePos.isNull())
757         return VisiblePositionRange();
758
759     return VisiblePositionRange(startOfStyleRange(visiblePos), endOfStyleRange(visiblePos));
760 }
761
762 // NOTE: Consider providing this utility method as AX API
763 VisiblePositionRange AccessibilityObject::visiblePositionRangeForRange(const PlainTextRange& range) const
764 {
765     unsigned textLength = getLengthForTextRange();
766     if (range.start + range.length > textLength)
767         return VisiblePositionRange();
768
769     VisiblePosition startPosition = visiblePositionForIndex(range.start);
770     startPosition.setAffinity(DOWNSTREAM);
771     VisiblePosition endPosition = visiblePositionForIndex(range.start + range.length);
772     return VisiblePositionRange(startPosition, endPosition);
773 }
774
775 static bool replacedNodeNeedsCharacter(Node* replacedNode)
776 {
777     // we should always be given a rendered node and a replaced node, but be safe
778     // replaced nodes are either attachments (widgets) or images
779     if (!replacedNode || !replacedNode->renderer() || !replacedNode->renderer()->isReplaced() || replacedNode->isTextNode())
780         return false;
781
782     // create an AX object, but skip it if it is not supposed to be seen
783     AccessibilityObject* object = replacedNode->renderer()->document()->axObjectCache()->getOrCreate(replacedNode);
784     if (object->accessibilityIsIgnored())
785         return false;
786
787     return true;
788 }
789
790 // Finds a RenderListItem parent give a node.
791 static RenderListItem* renderListItemContainerForNode(Node* node)
792 {
793     for (; node; node = node->parentNode()) {
794         RenderBoxModelObject* renderer = node->renderBoxModelObject();
795         if (renderer && renderer->isListItem())
796             return toRenderListItem(renderer);
797     }
798     return 0;
799 }
800     
801 // Returns the text associated with a list marker if this node is contained within a list item.
802 String AccessibilityObject::listMarkerTextForNodeAndPosition(Node* node, const VisiblePosition& visiblePositionStart) const
803 {
804     // If the range does not contain the start of the line, the list marker text should not be included.
805     if (!isStartOfLine(visiblePositionStart))
806         return String();
807
808     RenderListItem* listItem = renderListItemContainerForNode(node);
809     if (!listItem)
810         return String();
811         
812     // If this is in a list item, we need to manually add the text for the list marker 
813     // because a RenderListMarker does not have a Node equivalent and thus does not appear
814     // when iterating text.
815     const String& markerText = listItem->markerText();
816     if (markerText.isEmpty())
817         return String();
818                 
819     // Append text, plus the period that follows the text.
820     // FIXME: Not all list marker styles are followed by a period, but this
821     // sounds much better when there is a synthesized pause because of a period.
822     return markerText + ". ";
823 }
824     
825 String AccessibilityObject::stringForVisiblePositionRange(const VisiblePositionRange& visiblePositionRange) const
826 {
827     if (visiblePositionRange.isNull())
828         return String();
829
830     StringBuilder builder;
831     RefPtr<Range> range = makeRange(visiblePositionRange.start, visiblePositionRange.end);
832     for (TextIterator it(range.get()); !it.atEnd(); it.advance()) {
833         // non-zero length means textual node, zero length means replaced node (AKA "attachments" in AX)
834         if (it.length()) {
835             // Add a textual representation for list marker text
836             String listMarkerText = listMarkerTextForNodeAndPosition(it.node(), visiblePositionRange.start);
837             if (!listMarkerText.isEmpty())
838                 builder.append(listMarkerText);
839
840             builder.append(it.characters(), it.length());
841         } else {
842             // locate the node and starting offset for this replaced range
843             int exception = 0;
844             Node* node = it.range()->startContainer(exception);
845             ASSERT(node == it.range()->endContainer(exception));
846             int offset = it.range()->startOffset(exception);
847
848             if (replacedNodeNeedsCharacter(node->childNode(offset)))
849                 builder.append(objectReplacementCharacter);
850         }
851     }
852
853     return builder.toString();
854 }
855
856 int AccessibilityObject::lengthForVisiblePositionRange(const VisiblePositionRange& visiblePositionRange) const
857 {
858     // FIXME: Multi-byte support
859     if (visiblePositionRange.isNull())
860         return -1;
861     
862     int length = 0;
863     RefPtr<Range> range = makeRange(visiblePositionRange.start, visiblePositionRange.end);
864     for (TextIterator it(range.get()); !it.atEnd(); it.advance()) {
865         // non-zero length means textual node, zero length means replaced node (AKA "attachments" in AX)
866         if (it.length())
867             length += it.length();
868         else {
869             // locate the node and starting offset for this replaced range
870             int exception = 0;
871             Node* node = it.range()->startContainer(exception);
872             ASSERT(node == it.range()->endContainer(exception));
873             int offset = it.range()->startOffset(exception);
874
875             if (replacedNodeNeedsCharacter(node->childNode(offset)))
876                 length++;
877         }
878     }
879     
880     return length;
881 }
882
883 VisiblePosition AccessibilityObject::nextWordEnd(const VisiblePosition& visiblePos) const
884 {
885     if (visiblePos.isNull())
886         return VisiblePosition();
887
888     // make sure we move off of a word end
889     VisiblePosition nextVisiblePos = visiblePos.next();
890     if (nextVisiblePos.isNull())
891         return VisiblePosition();
892
893     return endOfWord(nextVisiblePos, LeftWordIfOnBoundary);
894 }
895
896 VisiblePosition AccessibilityObject::previousWordStart(const VisiblePosition& visiblePos) const
897 {
898     if (visiblePos.isNull())
899         return VisiblePosition();
900
901     // make sure we move off of a word start
902     VisiblePosition prevVisiblePos = visiblePos.previous();
903     if (prevVisiblePos.isNull())
904         return VisiblePosition();
905
906     return startOfWord(prevVisiblePos, RightWordIfOnBoundary);
907 }
908
909 VisiblePosition AccessibilityObject::nextLineEndPosition(const VisiblePosition& visiblePos) const
910 {
911     if (visiblePos.isNull())
912         return VisiblePosition();
913
914     // to make sure we move off of a line end
915     VisiblePosition nextVisiblePos = visiblePos.next();
916     if (nextVisiblePos.isNull())
917         return VisiblePosition();
918
919     VisiblePosition endPosition = endOfLine(nextVisiblePos);
920
921     // as long as the position hasn't reached the end of the doc,  keep searching for a valid line end position
922     // 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.
923     while (endPosition.isNull() && nextVisiblePos.isNotNull()) {
924         nextVisiblePos = nextVisiblePos.next();
925         endPosition = endOfLine(nextVisiblePos);
926     }
927
928     return endPosition;
929 }
930
931 VisiblePosition AccessibilityObject::previousLineStartPosition(const VisiblePosition& visiblePos) const
932 {
933     if (visiblePos.isNull())
934         return VisiblePosition();
935
936     // make sure we move off of a line start
937     VisiblePosition prevVisiblePos = visiblePos.previous();
938     if (prevVisiblePos.isNull())
939         return VisiblePosition();
940
941     VisiblePosition startPosition = startOfLine(prevVisiblePos);
942
943     // as long as the position hasn't reached the beginning of the doc,  keep searching for a valid line start position
944     // 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.
945     if (startPosition.isNull()) {
946         while (startPosition.isNull() && prevVisiblePos.isNotNull()) {
947             prevVisiblePos = prevVisiblePos.previous();
948             startPosition = startOfLine(prevVisiblePos);
949         }
950     } else
951         startPosition = updateAXLineStartForVisiblePosition(startPosition);
952
953     return startPosition;
954 }
955
956 VisiblePosition AccessibilityObject::nextSentenceEndPosition(const VisiblePosition& visiblePos) const
957 {
958     // FIXME: FO 2 IMPLEMENT (currently returns incorrect answer)
959     // Related? <rdar://problem/3927736> Text selection broken in 8A336
960     if (visiblePos.isNull())
961         return VisiblePosition();
962
963     // make sure we move off of a sentence end
964     VisiblePosition nextVisiblePos = visiblePos.next();
965     if (nextVisiblePos.isNull())
966         return VisiblePosition();
967
968     // an empty line is considered a sentence. If it's skipped, then the sentence parser will not
969     // see this empty line.  Instead, return the end position of the empty line.
970     VisiblePosition endPosition;
971     
972     String lineString = plainText(makeRange(startOfLine(nextVisiblePos), endOfLine(nextVisiblePos)).get());
973     if (lineString.isEmpty())
974         endPosition = nextVisiblePos;
975     else
976         endPosition = endOfSentence(nextVisiblePos);
977
978     return endPosition;
979 }
980
981 VisiblePosition AccessibilityObject::previousSentenceStartPosition(const VisiblePosition& visiblePos) const
982 {
983     // FIXME: FO 2 IMPLEMENT (currently returns incorrect answer)
984     // Related? <rdar://problem/3927736> Text selection broken in 8A336
985     if (visiblePos.isNull())
986         return VisiblePosition();
987
988     // make sure we move off of a sentence start
989     VisiblePosition previousVisiblePos = visiblePos.previous();
990     if (previousVisiblePos.isNull())
991         return VisiblePosition();
992
993     // treat empty line as a separate sentence.
994     VisiblePosition startPosition;
995     
996     String lineString = plainText(makeRange(startOfLine(previousVisiblePos), endOfLine(previousVisiblePos)).get());
997     if (lineString.isEmpty())
998         startPosition = previousVisiblePos;
999     else
1000         startPosition = startOfSentence(previousVisiblePos);
1001
1002     return startPosition;
1003 }
1004
1005 VisiblePosition AccessibilityObject::nextParagraphEndPosition(const VisiblePosition& visiblePos) const
1006 {
1007     if (visiblePos.isNull())
1008         return VisiblePosition();
1009
1010     // make sure we move off of a paragraph end
1011     VisiblePosition nextPos = visiblePos.next();
1012     if (nextPos.isNull())
1013         return VisiblePosition();
1014
1015     return endOfParagraph(nextPos);
1016 }
1017
1018 VisiblePosition AccessibilityObject::previousParagraphStartPosition(const VisiblePosition& visiblePos) const
1019 {
1020     if (visiblePos.isNull())
1021         return VisiblePosition();
1022
1023     // make sure we move off of a paragraph start
1024     VisiblePosition previousPos = visiblePos.previous();
1025     if (previousPos.isNull())
1026         return VisiblePosition();
1027
1028     return startOfParagraph(previousPos);
1029 }
1030
1031 AccessibilityObject* AccessibilityObject::accessibilityObjectForPosition(const VisiblePosition& visiblePos) const
1032 {
1033     if (visiblePos.isNull())
1034         return 0;
1035
1036     RenderObject* obj = visiblePos.deepEquivalent().deprecatedNode()->renderer();
1037     if (!obj)
1038         return 0;
1039
1040     return obj->document()->axObjectCache()->getOrCreate(obj);
1041 }
1042
1043 #if HAVE(ACCESSIBILITY)
1044 int AccessibilityObject::lineForPosition(const VisiblePosition& visiblePos) const
1045 {
1046     if (visiblePos.isNull() || !node())
1047         return -1;
1048
1049     // If the position is not in the same editable region as this AX object, return -1.
1050     Node* containerNode = visiblePos.deepEquivalent().containerNode();
1051     if (!containerNode->containsIncludingShadowDOM(node()) && !node()->containsIncludingShadowDOM(containerNode))
1052         return -1;
1053
1054     int lineCount = -1;
1055     VisiblePosition currentVisiblePos = visiblePos;
1056     VisiblePosition savedVisiblePos;
1057
1058     // move up until we get to the top
1059     // FIXME: This only takes us to the top of the rootEditableElement, not the top of the
1060     // top document.
1061     do {
1062         savedVisiblePos = currentVisiblePos;
1063         VisiblePosition prevVisiblePos = previousLinePosition(currentVisiblePos, 0, HasEditableAXRole);
1064         currentVisiblePos = prevVisiblePos;
1065         ++lineCount;
1066     }  while (currentVisiblePos.isNotNull() && !(inSameLine(currentVisiblePos, savedVisiblePos)));
1067
1068     return lineCount;
1069 }
1070 #endif
1071
1072 // NOTE: Consider providing this utility method as AX API
1073 PlainTextRange AccessibilityObject::plainTextRangeForVisiblePositionRange(const VisiblePositionRange& positionRange) const
1074 {
1075     int index1 = index(positionRange.start);
1076     int index2 = index(positionRange.end);
1077     if (index1 < 0 || index2 < 0 || index1 > index2)
1078         return PlainTextRange();
1079
1080     return PlainTextRange(index1, index2 - index1);
1081 }
1082
1083 // The composed character range in the text associated with this accessibility object that
1084 // is specified by the given screen coordinates. This parameterized attribute returns the
1085 // complete range of characters (including surrogate pairs of multi-byte glyphs) at the given
1086 // screen coordinates.
1087 // NOTE: This varies from AppKit when the point is below the last line. AppKit returns an
1088 // an error in that case. We return textControl->text().length(), 1. Does this matter?
1089 PlainTextRange AccessibilityObject::doAXRangeForPosition(const IntPoint& point) const
1090 {
1091     int i = index(visiblePositionForPoint(point));
1092     if (i < 0)
1093         return PlainTextRange();
1094
1095     return PlainTextRange(i, 1);
1096 }
1097
1098 // Given a character index, the range of text associated with this accessibility object
1099 // over which the style in effect at that character index applies.
1100 PlainTextRange AccessibilityObject::doAXStyleRangeForIndex(unsigned index) const
1101 {
1102     VisiblePositionRange range = styleRangeForPosition(visiblePositionForIndex(index, false));
1103     return plainTextRangeForVisiblePositionRange(range);
1104 }
1105
1106 // Given an indexed character, the line number of the text associated with this accessibility
1107 // object that contains the character.
1108 unsigned AccessibilityObject::doAXLineForIndex(unsigned index)
1109 {
1110     return lineForPosition(visiblePositionForIndex(index, false));
1111 }
1112
1113 #if HAVE(ACCESSIBILITY)
1114 void AccessibilityObject::updateBackingStore()
1115 {
1116     // Updating the layout may delete this object.
1117     if (Document* document = this->document())
1118         document->updateLayoutIgnorePendingStylesheets();
1119 }
1120 #endif
1121
1122 Document* AccessibilityObject::document() const
1123 {
1124     FrameView* frameView = documentFrameView();
1125     if (!frameView)
1126         return 0;
1127     
1128     return frameView->frame()->document();
1129 }
1130     
1131 Page* AccessibilityObject::page() const
1132 {
1133     Document* document = this->document();
1134     if (!document)
1135         return 0;
1136     return document->page();
1137 }
1138
1139 FrameView* AccessibilityObject::documentFrameView() const 
1140
1141     const AccessibilityObject* object = this;
1142     while (object && !object->isAccessibilityRenderObject()) 
1143         object = object->parentObject();
1144         
1145     if (!object)
1146         return 0;
1147
1148     return object->documentFrameView();
1149 }
1150
1151 #if HAVE(ACCESSIBILITY)
1152 const AccessibilityObject::AccessibilityChildrenVector& AccessibilityObject::children()
1153 {
1154     updateChildrenIfNecessary();
1155
1156     return m_children;
1157 }
1158 #endif
1159
1160 void AccessibilityObject::updateChildrenIfNecessary()
1161 {
1162     if (!hasChildren())
1163         addChildren();    
1164 }
1165     
1166 void AccessibilityObject::clearChildren()
1167 {
1168     // Some objects have weak pointers to their parents and those associations need to be detached.
1169     size_t length = m_children.size();
1170     for (size_t i = 0; i < length; i++)
1171         m_children[i]->detachFromParent();
1172     
1173     m_children.clear();
1174     m_haveChildren = false;
1175 }
1176
1177 AccessibilityObject* AccessibilityObject::anchorElementForNode(Node* node)
1178 {
1179     RenderObject* obj = node->renderer();
1180     if (!obj)
1181         return 0;
1182     
1183     RefPtr<AccessibilityObject> axObj = obj->document()->axObjectCache()->getOrCreate(obj);
1184     Element* anchor = axObj->anchorElement();
1185     if (!anchor)
1186         return 0;
1187     
1188     RenderObject* anchorRenderer = anchor->renderer();
1189     if (!anchorRenderer)
1190         return 0;
1191     
1192     return anchorRenderer->document()->axObjectCache()->getOrCreate(anchorRenderer);
1193 }
1194     
1195 void AccessibilityObject::ariaTreeRows(AccessibilityChildrenVector& result)
1196 {
1197     AccessibilityChildrenVector axChildren = children();
1198     unsigned count = axChildren.size();
1199     for (unsigned k = 0; k < count; ++k) {
1200         AccessibilityObject* obj = axChildren[k].get();
1201         
1202         // Add tree items as the rows.
1203         if (obj->roleValue() == TreeItemRole) 
1204             result.append(obj);
1205
1206         // Now see if this item also has rows hiding inside of it.
1207         obj->ariaTreeRows(result);
1208     }
1209 }
1210     
1211 void AccessibilityObject::ariaTreeItemContent(AccessibilityChildrenVector& result)
1212 {
1213     // The ARIA tree item content are the item that are not other tree items or their containing groups.
1214     AccessibilityChildrenVector axChildren = children();
1215     unsigned count = axChildren.size();
1216     for (unsigned k = 0; k < count; ++k) {
1217         AccessibilityObject* obj = axChildren[k].get();
1218         AccessibilityRole role = obj->roleValue();
1219         if (role == TreeItemRole || role == GroupRole)
1220             continue;
1221         
1222         result.append(obj);
1223     }
1224 }
1225
1226 void AccessibilityObject::ariaTreeItemDisclosedRows(AccessibilityChildrenVector& result)
1227 {
1228     AccessibilityChildrenVector axChildren = children();
1229     unsigned count = axChildren.size();
1230     for (unsigned k = 0; k < count; ++k) {
1231         AccessibilityObject* obj = axChildren[k].get();
1232         
1233         // Add tree items as the rows.
1234         if (obj->roleValue() == TreeItemRole)
1235             result.append(obj);
1236         // If it's not a tree item, then descend into the group to find more tree items.
1237         else 
1238             obj->ariaTreeRows(result);
1239     }    
1240 }
1241
1242 #if HAVE(ACCESSIBILITY)
1243 const String& AccessibilityObject::actionVerb() const
1244 {
1245     // FIXME: Need to add verbs for select elements.
1246     DEFINE_STATIC_LOCAL(const String, buttonAction, (AXButtonActionVerb()));
1247     DEFINE_STATIC_LOCAL(const String, textFieldAction, (AXTextFieldActionVerb()));
1248     DEFINE_STATIC_LOCAL(const String, radioButtonAction, (AXRadioButtonActionVerb()));
1249     DEFINE_STATIC_LOCAL(const String, checkedCheckBoxAction, (AXCheckedCheckBoxActionVerb()));
1250     DEFINE_STATIC_LOCAL(const String, uncheckedCheckBoxAction, (AXUncheckedCheckBoxActionVerb()));
1251     DEFINE_STATIC_LOCAL(const String, linkAction, (AXLinkActionVerb()));
1252     DEFINE_STATIC_LOCAL(const String, menuListAction, (AXMenuListActionVerb()));
1253     DEFINE_STATIC_LOCAL(const String, menuListPopupAction, (AXMenuListPopupActionVerb()));
1254     DEFINE_STATIC_LOCAL(const String, noAction, ());
1255
1256     switch (roleValue()) {
1257     case ButtonRole:
1258     case ToggleButtonRole:
1259         return buttonAction;
1260     case TextFieldRole:
1261     case TextAreaRole:
1262         return textFieldAction;
1263     case RadioButtonRole:
1264         return radioButtonAction;
1265     case CheckBoxRole:
1266         return isChecked() ? checkedCheckBoxAction : uncheckedCheckBoxAction;
1267     case LinkRole:
1268     case WebCoreLinkRole:
1269         return linkAction;
1270     case PopUpButtonRole:
1271         return menuListAction;
1272     case MenuListPopupRole:
1273         return menuListPopupAction;
1274     default:
1275         return noAction;
1276     }
1277 }
1278 #endif
1279
1280 bool AccessibilityObject::ariaIsMultiline() const
1281 {
1282     return equalIgnoringCase(getAttribute(aria_multilineAttr), "true");
1283 }
1284
1285 const AtomicString& AccessibilityObject::invalidStatus() const
1286 {
1287     DEFINE_STATIC_LOCAL(const AtomicString, invalidStatusFalse, ("false"));
1288     
1289     // aria-invalid can return false (default), grammer, spelling, or true.
1290     const AtomicString& ariaInvalid = getAttribute(aria_invalidAttr);
1291     
1292     // If empty or not present, it should return false.
1293     if (ariaInvalid.isEmpty())
1294         return invalidStatusFalse;
1295     
1296     return ariaInvalid;
1297 }
1298  
1299 bool AccessibilityObject::hasAttribute(const QualifiedName& attribute) const
1300 {
1301     Node* elementNode = node();
1302     if (!elementNode)
1303         return false;
1304     
1305     if (!elementNode->isElementNode())
1306         return false;
1307     
1308     Element* element = static_cast<Element*>(elementNode);
1309     return element->fastHasAttribute(attribute);
1310 }
1311     
1312 const AtomicString& AccessibilityObject::getAttribute(const QualifiedName& attribute) const
1313 {
1314     Node* elementNode = node();
1315     if (!elementNode)
1316         return nullAtom;
1317     
1318     if (!elementNode->isElementNode())
1319         return nullAtom;
1320     
1321     Element* element = static_cast<Element*>(elementNode);
1322     return element->fastGetAttribute(attribute);
1323 }
1324     
1325 // Lacking concrete evidence of orientation, horizontal means width > height. vertical is height > width;
1326 AccessibilityOrientation AccessibilityObject::orientation() const
1327 {
1328     LayoutRect bounds = elementRect();
1329     if (bounds.size().width() > bounds.size().height())
1330         return AccessibilityOrientationHorizontal;
1331     if (bounds.size().height() > bounds.size().width())
1332         return AccessibilityOrientationVertical;
1333
1334     // A tie goes to horizontal.
1335     return AccessibilityOrientationHorizontal;
1336 }    
1337
1338 bool AccessibilityObject::isDescendantOfObject(const AccessibilityObject* axObject) const
1339 {
1340     if (!axObject || !axObject->hasChildren())
1341         return false;
1342
1343     for (const AccessibilityObject* parent = parentObject(); parent; parent = parent->parentObject()) {
1344         if (parent == axObject)
1345             return true;
1346     }
1347     return false;
1348 }
1349
1350 bool AccessibilityObject::isAncestorOfObject(const AccessibilityObject* axObject) const
1351 {
1352     if (!axObject)
1353         return false;
1354
1355     return this == axObject || axObject->isDescendantOfObject(this);
1356 }
1357
1358 AccessibilityObject* AccessibilityObject::firstAnonymousBlockChild() const
1359 {
1360     for (AccessibilityObject* child = firstChild(); child; child = child->nextSibling()) {
1361         if (child->renderer() && child->renderer()->isAnonymousBlock())
1362             return child;
1363     }
1364     return 0;
1365 }
1366
1367 typedef HashMap<String, AccessibilityRole, CaseFoldingHash> ARIARoleMap;
1368
1369 struct RoleEntry {
1370     String ariaRole;
1371     AccessibilityRole webcoreRole;
1372 };
1373
1374 static ARIARoleMap* createARIARoleMap()
1375 {
1376     const RoleEntry roles[] = {
1377         { "alert", ApplicationAlertRole },
1378         { "alertdialog", ApplicationAlertDialogRole },
1379         { "application", LandmarkApplicationRole },
1380         { "article", DocumentArticleRole },
1381         { "banner", LandmarkBannerRole },
1382         { "button", ButtonRole },
1383         { "checkbox", CheckBoxRole },
1384         { "complementary", LandmarkComplementaryRole },
1385         { "contentinfo", LandmarkContentInfoRole },
1386         { "dialog", ApplicationDialogRole },
1387         { "directory", DirectoryRole },
1388         { "grid", TableRole },
1389         { "gridcell", CellRole },
1390         { "columnheader", ColumnHeaderRole },
1391         { "combobox", ComboBoxRole },
1392         { "definition", DefinitionListDefinitionRole },
1393         { "document", DocumentRole },
1394         { "rowheader", RowHeaderRole },
1395         { "group", GroupRole },
1396         { "heading", HeadingRole },
1397         { "img", ImageRole },
1398         { "link", WebCoreLinkRole },
1399         { "list", ListRole },        
1400         { "listitem", ListItemRole },        
1401         { "listbox", ListBoxRole },
1402         { "log", ApplicationLogRole },
1403         // "option" isn't here because it may map to different roles depending on the parent element's role
1404         { "main", LandmarkMainRole },
1405         { "marquee", ApplicationMarqueeRole },
1406         { "math", DocumentMathRole },
1407         { "menu", MenuRole },
1408         { "menubar", MenuBarRole },
1409         { "menuitem", MenuItemRole },
1410         { "menuitemcheckbox", MenuItemRole },
1411         { "menuitemradio", MenuItemRole },
1412         { "note", DocumentNoteRole },
1413         { "navigation", LandmarkNavigationRole },
1414         { "option", ListBoxOptionRole },
1415         { "presentation", PresentationalRole },
1416         { "progressbar", ProgressIndicatorRole },
1417         { "radio", RadioButtonRole },
1418         { "radiogroup", RadioGroupRole },
1419         { "region", DocumentRegionRole },
1420         { "row", RowRole },
1421         { "scrollbar", ScrollBarRole },
1422         { "search", LandmarkSearchRole },
1423         { "separator", SplitterRole },
1424         { "slider", SliderRole },
1425         { "spinbutton", SpinButtonRole },
1426         { "status", ApplicationStatusRole },
1427         { "tab", TabRole },
1428         { "tablist", TabListRole },
1429         { "tabpanel", TabPanelRole },
1430         { "text", StaticTextRole },
1431         { "textbox", TextAreaRole },
1432         { "timer", ApplicationTimerRole },
1433         { "toolbar", ToolbarRole },
1434         { "tooltip", UserInterfaceTooltipRole },
1435         { "tree", TreeRole },
1436         { "treegrid", TreeGridRole },
1437         { "treeitem", TreeItemRole }
1438     };
1439     ARIARoleMap* roleMap = new ARIARoleMap;
1440
1441     for (size_t i = 0; i < WTF_ARRAY_LENGTH(roles); ++i)
1442         roleMap->set(roles[i].ariaRole, roles[i].webcoreRole);
1443     return roleMap;
1444 }
1445
1446 AccessibilityRole AccessibilityObject::ariaRoleToWebCoreRole(const String& value)
1447 {
1448     ASSERT(!value.isEmpty());
1449     
1450     static const ARIARoleMap* roleMap = createARIARoleMap();
1451
1452     Vector<String> roleVector;
1453     value.split(' ', roleVector);
1454     AccessibilityRole role = UnknownRole;
1455     unsigned size = roleVector.size();
1456     for (unsigned i = 0; i < size; ++i) {
1457         String roleName = roleVector[i];
1458         role = roleMap->get(roleName);
1459         if (role)
1460             return role;
1461     }
1462     
1463     return role;
1464 }
1465
1466 bool AccessibilityObject::hasHighlighting() const
1467 {
1468     for (Node* node = this->node(); node; node = node->parentNode()) {
1469         if (node->hasTagName(markTag))
1470             return true;
1471     }
1472     
1473     return false;
1474 }
1475
1476 const AtomicString& AccessibilityObject::placeholderValue() const
1477 {
1478     const AtomicString& placeholder = getAttribute(placeholderAttr);
1479     if (!placeholder.isEmpty())
1480         return placeholder;
1481     
1482     return nullAtom;
1483 }
1484     
1485 bool AccessibilityObject::isInsideARIALiveRegion() const
1486 {
1487     if (supportsARIALiveRegion())
1488         return true;
1489     
1490     for (AccessibilityObject* axParent = parentObject(); axParent; axParent = axParent->parentObject()) {
1491         if (axParent->supportsARIALiveRegion())
1492             return true;
1493     }
1494     
1495     return false;
1496 }
1497
1498 bool AccessibilityObject::supportsARIAAttributes() const
1499 {
1500     return supportsARIALiveRegion()
1501         || supportsARIADragging()
1502         || supportsARIADropping()
1503         || supportsARIAFlowTo()
1504         || supportsARIAOwns()
1505         || hasAttribute(aria_labelAttr);
1506 }
1507     
1508 bool AccessibilityObject::supportsARIALiveRegion() const
1509 {
1510     const AtomicString& liveRegion = ariaLiveRegionStatus();
1511     return equalIgnoringCase(liveRegion, "polite") || equalIgnoringCase(liveRegion, "assertive");
1512 }
1513
1514 AccessibilityObject* AccessibilityObject::elementAccessibilityHitTest(const IntPoint& point) const
1515
1516     // Send the hit test back into the sub-frame if necessary.
1517     if (isAttachment()) {
1518         Widget* widget = widgetForAttachmentView();
1519         // Normalize the point for the widget's bounds.
1520         if (widget && widget->isFrameView())
1521             return axObjectCache()->getOrCreate(widget)->accessibilityHitTest(toPoint(point - widget->frameRect().location()));
1522     }
1523     
1524     // Check if there are any mock elements that need to be handled.
1525     size_t count = m_children.size();
1526     for (size_t k = 0; k < count; k++) {
1527         if (m_children[k]->isMockObject() && m_children[k]->elementRect().contains(point))
1528             return m_children[k]->elementAccessibilityHitTest(point);
1529     }
1530
1531     return const_cast<AccessibilityObject*>(this); 
1532 }
1533     
1534 AXObjectCache* AccessibilityObject::axObjectCache() const
1535 {
1536     Document* doc = document();
1537     if (doc)
1538         return doc->axObjectCache();
1539     return 0;
1540 }
1541     
1542 AccessibilityObject* AccessibilityObject::focusedUIElement() const
1543 {
1544     Document* doc = document();
1545     if (!doc)
1546         return 0;
1547     
1548     Page* page = doc->page();
1549     if (!page)
1550         return 0;
1551     
1552     return AXObjectCache::focusedUIElementForPage(page);
1553 }
1554     
1555 AccessibilitySortDirection AccessibilityObject::sortDirection() const
1556 {
1557     const AtomicString& sortAttribute = getAttribute(aria_sortAttr);
1558     if (equalIgnoringCase(sortAttribute, "ascending"))
1559         return SortDirectionAscending;
1560     if (equalIgnoringCase(sortAttribute, "descending"))
1561         return SortDirectionDescending;
1562     
1563     return SortDirectionNone;
1564 }
1565
1566 bool AccessibilityObject::supportsRangeValue() const
1567 {
1568     return isProgressIndicator()
1569         || isSlider()
1570         || isScrollbar()
1571         || isSpinButton();
1572 }
1573     
1574 bool AccessibilityObject::supportsARIAExpanded() const
1575 {
1576     return !getAttribute(aria_expandedAttr).isEmpty();
1577 }
1578     
1579 bool AccessibilityObject::isExpanded() const
1580 {
1581     if (equalIgnoringCase(getAttribute(aria_expandedAttr), "true"))
1582         return true;
1583     
1584     return false;  
1585 }
1586     
1587 AccessibilityButtonState AccessibilityObject::checkboxOrRadioValue() const
1588 {
1589     // If this is a real checkbox or radio button, AccessibilityRenderObject will handle.
1590     // If it's an ARIA checkbox or radio, the aria-checked attribute should be used.
1591
1592     const AtomicString& result = getAttribute(aria_checkedAttr);
1593     if (equalIgnoringCase(result, "true"))
1594         return ButtonStateOn;
1595     if (equalIgnoringCase(result, "mixed"))
1596         return ButtonStateMixed;
1597     
1598     return ButtonStateOff;
1599 }
1600
1601 // This is a 1-dimensional scroll offset helper function that's applied
1602 // separately in the horizontal and vertical directions, because the
1603 // logic is the same. The goal is to compute the best scroll offset
1604 // in order to make an object visible within a viewport.
1605 //
1606 // In case the whole object cannot fit, you can specify a
1607 // subfocus - a smaller region within the object that should
1608 // be prioritized. If the whole object can fit, the subfocus is
1609 // ignored.
1610 //
1611 // Example: the viewport is scrolled to the right just enough
1612 // that the object is in view.
1613 //   Before:
1614 //   +----------Viewport---------+
1615 //                         +---Object---+
1616 //                         +--SubFocus--+
1617 //
1618 //   After:
1619 //          +----------Viewport---------+
1620 //                         +---Object---+
1621 //                         +--SubFocus--+
1622 //
1623 // When constraints cannot be fully satisfied, the min
1624 // (left/top) position takes precedence over the max (right/bottom).
1625 //
1626 // Note that the return value represents the ideal new scroll offset.
1627 // This may be out of range - the calling function should clip this
1628 // to the available range.
1629 static int computeBestScrollOffset(int currentScrollOffset,
1630                                    int subfocusMin, int subfocusMax,
1631                                    int objectMin, int objectMax,
1632                                    int viewportMin, int viewportMax) {
1633     int viewportSize = viewportMax - viewportMin;
1634
1635     // If the focus size is larger than the viewport size, shrink it in the
1636     // direction of subfocus.
1637     if (objectMax - objectMin > viewportSize) {
1638         // Subfocus must be within focus:
1639         subfocusMin = std::max(subfocusMin, objectMin);
1640         subfocusMax = std::min(subfocusMax, objectMax);
1641
1642         // Subfocus must be no larger than the viewport size; favor top/left.
1643         if (subfocusMax - subfocusMin > viewportSize)
1644             subfocusMax = subfocusMin + viewportSize;
1645
1646         if (subfocusMin + viewportSize > objectMax)
1647             objectMin = objectMax - viewportSize;
1648         else {
1649             objectMin = subfocusMin;
1650             objectMax = subfocusMin + viewportSize;
1651         }
1652     }
1653
1654     // Exit now if the focus is already within the viewport.
1655     if (objectMin - currentScrollOffset >= viewportMin
1656         && objectMax - currentScrollOffset <= viewportMax)
1657         return currentScrollOffset;
1658
1659     // Scroll left if we're too far to the right.
1660     if (objectMax - currentScrollOffset > viewportMax)
1661         return objectMax - viewportMax;
1662
1663     // Scroll right if we're too far to the left.
1664     if (objectMin - currentScrollOffset < viewportMin)
1665         return objectMin - viewportMin;
1666
1667     ASSERT_NOT_REACHED();
1668
1669     // This shouldn't happen.
1670     return currentScrollOffset;
1671 }
1672
1673 void AccessibilityObject::scrollToMakeVisible() const
1674 {
1675     IntRect objectRect = pixelSnappedIntRect(boundingBoxRect());
1676     objectRect.setLocation(IntPoint());
1677     scrollToMakeVisibleWithSubFocus(objectRect);
1678 }
1679
1680 void AccessibilityObject::scrollToMakeVisibleWithSubFocus(const IntRect& subfocus) const
1681 {
1682     // Search up the parent chain until we find the first one that's scrollable.
1683     AccessibilityObject* scrollParent = parentObject();
1684     ScrollableArea* scrollableArea;
1685     for (scrollableArea = 0;
1686          scrollParent && !(scrollableArea = scrollParent->getScrollableAreaIfScrollable());
1687          scrollParent = scrollParent->parentObject()) { }
1688     if (!scrollableArea)
1689         return;
1690
1691     LayoutRect objectRect = boundingBoxRect();
1692     IntPoint scrollPosition = scrollableArea->scrollPosition();
1693     IntRect scrollVisibleRect = scrollableArea->visibleContentRect();
1694
1695     int desiredX = computeBestScrollOffset(
1696         scrollPosition.x(),
1697         objectRect.x() + subfocus.x(), objectRect.x() + subfocus.maxX(),
1698         objectRect.x(), objectRect.maxX(),
1699         0, scrollVisibleRect.width());
1700     int desiredY = computeBestScrollOffset(
1701         scrollPosition.y(),
1702         objectRect.y() + subfocus.y(), objectRect.y() + subfocus.maxY(),
1703         objectRect.y(), objectRect.maxY(),
1704         0, scrollVisibleRect.height());
1705
1706     scrollParent->scrollTo(IntPoint(desiredX, desiredY));
1707
1708     // Recursively make sure the scroll parent itself is visible.
1709     if (scrollParent->parentObject())
1710         scrollParent->scrollToMakeVisible();
1711 }
1712
1713 void AccessibilityObject::scrollToGlobalPoint(const IntPoint& globalPoint) const
1714 {
1715     // Search up the parent chain and create a vector of all scrollable parent objects
1716     // and ending with this object itself.
1717     Vector<const AccessibilityObject*> objects;
1718     AccessibilityObject* parentObject;
1719     for (parentObject = this->parentObject(); parentObject; parentObject = parentObject->parentObject()) {
1720         if (parentObject->getScrollableAreaIfScrollable())
1721             objects.prepend(parentObject);
1722     }
1723     objects.append(this);
1724
1725     // Start with the outermost scrollable (the main window) and try to scroll the
1726     // next innermost object to the given point.
1727     int offsetX = 0, offsetY = 0;
1728     IntPoint point = globalPoint;
1729     size_t levels = objects.size() - 1;
1730     for (size_t i = 0; i < levels; i++) {
1731         const AccessibilityObject* outer = objects[i];
1732         const AccessibilityObject* inner = objects[i + 1];
1733
1734         ScrollableArea* scrollableArea = outer->getScrollableAreaIfScrollable();
1735
1736         LayoutRect innerRect = inner->isAccessibilityScrollView() ? inner->parentObject()->boundingBoxRect() : inner->boundingBoxRect();
1737         LayoutRect objectRect = innerRect;
1738         IntPoint scrollPosition = scrollableArea->scrollPosition();
1739
1740         // Convert the object rect into local coordinates.
1741         objectRect.move(offsetX, offsetY);
1742         if (!outer->isAccessibilityScrollView())
1743             objectRect.move(scrollPosition.x(), scrollPosition.y());
1744
1745         int desiredX = computeBestScrollOffset(
1746             0,
1747             objectRect.x(), objectRect.maxX(),
1748             objectRect.x(), objectRect.maxX(),
1749             point.x(), point.x());
1750         int desiredY = computeBestScrollOffset(
1751             0,
1752             objectRect.y(), objectRect.maxY(),
1753             objectRect.y(), objectRect.maxY(),
1754             point.y(), point.y());
1755         outer->scrollTo(IntPoint(desiredX, desiredY));
1756
1757         if (outer->isAccessibilityScrollView() && !inner->isAccessibilityScrollView()) {
1758             // If outer object we just scrolled is a scroll view (main window or iframe) but the
1759             // inner object is not, keep track of the coordinate transformation to apply to
1760             // future nested calculations.
1761             scrollPosition = scrollableArea->scrollPosition();
1762             offsetX -= (scrollPosition.x() + point.x());
1763             offsetY -= (scrollPosition.y() + point.y());
1764             point.move(scrollPosition.x() - innerRect.x(),
1765                        scrollPosition.y() - innerRect.y());
1766         } else if (inner->isAccessibilityScrollView()) {
1767             // Otherwise, if the inner object is a scroll view, reset the coordinate transformation.
1768             offsetX = 0;
1769             offsetY = 0;
1770         }
1771     }
1772 }
1773
1774 bool AccessibilityObject::cachedIsIgnoredValue()
1775 {
1776     if (m_cachedIsIgnoredValue == DefaultBehavior)
1777         m_cachedIsIgnoredValue = accessibilityIsIgnored() ? IgnoreObject : IncludeObject;
1778
1779     return m_cachedIsIgnoredValue == IgnoreObject;
1780 }
1781
1782 void AccessibilityObject::setCachedIsIgnoredValue(bool isIgnored)
1783 {
1784     m_cachedIsIgnoredValue = isIgnored ? IgnoreObject : IncludeObject;
1785 }
1786
1787 void AccessibilityObject::notifyIfIgnoredValueChanged()
1788 {
1789     bool isIgnored = accessibilityIsIgnored();
1790     if (cachedIsIgnoredValue() != isIgnored) {
1791         axObjectCache()->childrenChanged(parentObject());
1792         setCachedIsIgnoredValue(isIgnored);
1793     }
1794 }
1795
1796 bool AccessibilityObject::ariaPressedIsPresent() const
1797 {
1798     return !getAttribute(aria_pressedAttr).isEmpty();
1799 }
1800
1801 TextIteratorBehavior AccessibilityObject::textIteratorBehaviorForTextRange() const
1802 {
1803     TextIteratorBehavior behavior = TextIteratorIgnoresStyleVisibility;
1804     
1805 #if PLATFORM(GTK)
1806     // We need to emit replaced elements for GTK, and present
1807     // them with the 'object replacement character' (0xFFFC).
1808     behavior = static_cast<TextIteratorBehavior>(behavior | TextIteratorEmitsObjectReplacementCharacters);
1809 #endif
1810     
1811     return behavior;
1812 }
1813     
1814 AccessibilityRole AccessibilityObject::buttonRoleType() const
1815 {
1816     // If aria-pressed is present, then it should be exposed as a toggle button.
1817     // http://www.w3.org/TR/wai-aria/states_and_properties#aria-pressed
1818     if (ariaPressedIsPresent())
1819         return ToggleButtonRole;
1820     if (ariaHasPopup())
1821         return PopUpButtonRole;
1822     // We don't contemplate RadioButtonRole, as it depends on the input
1823     // type.
1824
1825     return ButtonRole;
1826 }
1827
1828 bool AccessibilityObject::isButton() const
1829 {
1830     AccessibilityRole role = roleValue();
1831
1832     return role == ButtonRole || role == PopUpButtonRole || role == ToggleButtonRole;
1833 }
1834
1835 } // namespace WebCore