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