Merge "[CherryPick] [WEBGL] Rename WEBKIT_WEBGL_lose_context to WEBGL_lose_context...
[framework/web/webkit-efl.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->renderer());
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->renderer());
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         return buttonAction;
1258     case TextFieldRole:
1259     case TextAreaRole:
1260         return textFieldAction;
1261     case RadioButtonRole:
1262         return radioButtonAction;
1263     case CheckBoxRole:
1264         return isChecked() ? checkedCheckBoxAction : uncheckedCheckBoxAction;
1265     case LinkRole:
1266     case WebCoreLinkRole:
1267         return linkAction;
1268     case PopUpButtonRole:
1269         return menuListAction;
1270     case MenuListPopupRole:
1271         return menuListPopupAction;
1272     default:
1273         return noAction;
1274     }
1275 }
1276 #endif
1277
1278 bool AccessibilityObject::ariaIsMultiline() const
1279 {
1280     return equalIgnoringCase(getAttribute(aria_multilineAttr), "true");
1281 }
1282
1283 const AtomicString& AccessibilityObject::invalidStatus() const
1284 {
1285     DEFINE_STATIC_LOCAL(const AtomicString, invalidStatusFalse, ("false"));
1286     
1287     // aria-invalid can return false (default), grammer, spelling, or true.
1288     const AtomicString& ariaInvalid = getAttribute(aria_invalidAttr);
1289     
1290     // If empty or not present, it should return false.
1291     if (ariaInvalid.isEmpty())
1292         return invalidStatusFalse;
1293     
1294     return ariaInvalid;
1295 }
1296  
1297 const AtomicString& AccessibilityObject::getAttribute(const QualifiedName& attribute) const
1298 {
1299     Node* elementNode = node();
1300     if (!elementNode)
1301         return nullAtom;
1302     
1303     if (!elementNode->isElementNode())
1304         return nullAtom;
1305     
1306     Element* element = static_cast<Element*>(elementNode);
1307     return element->fastGetAttribute(attribute);
1308 }
1309     
1310 // Lacking concrete evidence of orientation, horizontal means width > height. vertical is height > width;
1311 AccessibilityOrientation AccessibilityObject::orientation() const
1312 {
1313     LayoutRect bounds = elementRect();
1314     if (bounds.size().width() > bounds.size().height())
1315         return AccessibilityOrientationHorizontal;
1316     if (bounds.size().height() > bounds.size().width())
1317         return AccessibilityOrientationVertical;
1318
1319     // A tie goes to horizontal.
1320     return AccessibilityOrientationHorizontal;
1321 }    
1322
1323 bool AccessibilityObject::isDescendantOfObject(const AccessibilityObject* axObject) const
1324 {
1325     if (!axObject || !axObject->hasChildren())
1326         return false;
1327
1328     for (const AccessibilityObject* parent = parentObject(); parent; parent = parent->parentObject()) {
1329         if (parent == axObject)
1330             return true;
1331     }
1332     return false;
1333 }
1334
1335 bool AccessibilityObject::isAncestorOfObject(const AccessibilityObject* axObject) const
1336 {
1337     if (!axObject)
1338         return false;
1339
1340     return this == axObject || axObject->isDescendantOfObject(this);
1341 }
1342
1343 typedef HashMap<String, AccessibilityRole, CaseFoldingHash> ARIARoleMap;
1344
1345 struct RoleEntry {
1346     String ariaRole;
1347     AccessibilityRole webcoreRole;
1348 };
1349
1350 static ARIARoleMap* createARIARoleMap()
1351 {
1352     const RoleEntry roles[] = {
1353         { "alert", ApplicationAlertRole },
1354         { "alertdialog", ApplicationAlertDialogRole },
1355         { "application", LandmarkApplicationRole },
1356         { "article", DocumentArticleRole },
1357         { "banner", LandmarkBannerRole },
1358         { "button", ButtonRole },
1359         { "checkbox", CheckBoxRole },
1360         { "complementary", LandmarkComplementaryRole },
1361         { "contentinfo", LandmarkContentInfoRole },
1362         { "dialog", ApplicationDialogRole },
1363         { "directory", DirectoryRole },
1364         { "grid", TableRole },
1365         { "gridcell", CellRole },
1366         { "columnheader", ColumnHeaderRole },
1367         { "combobox", ComboBoxRole },
1368         { "definition", DefinitionListDefinitionRole },
1369         { "document", DocumentRole },
1370         { "rowheader", RowHeaderRole },
1371         { "group", GroupRole },
1372         { "heading", HeadingRole },
1373         { "img", ImageRole },
1374         { "link", WebCoreLinkRole },
1375         { "list", ListRole },        
1376         { "listitem", ListItemRole },        
1377         { "listbox", ListBoxRole },
1378         { "log", ApplicationLogRole },
1379         // "option" isn't here because it may map to different roles depending on the parent element's role
1380         { "main", LandmarkMainRole },
1381         { "marquee", ApplicationMarqueeRole },
1382         { "math", DocumentMathRole },
1383         { "menu", MenuRole },
1384         { "menubar", MenuBarRole },
1385         { "menuitem", MenuItemRole },
1386         { "menuitemcheckbox", MenuItemRole },
1387         { "menuitemradio", MenuItemRole },
1388         { "note", DocumentNoteRole },
1389         { "navigation", LandmarkNavigationRole },
1390         { "option", ListBoxOptionRole },
1391         { "presentation", PresentationalRole },
1392         { "progressbar", ProgressIndicatorRole },
1393         { "radio", RadioButtonRole },
1394         { "radiogroup", RadioGroupRole },
1395         { "region", DocumentRegionRole },
1396         { "row", RowRole },
1397         { "range", SliderRole },
1398         { "scrollbar", ScrollBarRole },
1399         { "search", LandmarkSearchRole },
1400         { "separator", SplitterRole },
1401         { "slider", SliderRole },
1402         { "spinbutton", SpinButtonRole },
1403         { "status", ApplicationStatusRole },
1404         { "tab", TabRole },
1405         { "tablist", TabListRole },
1406         { "tabpanel", TabPanelRole },
1407         { "text", StaticTextRole },
1408         { "textbox", TextAreaRole },
1409         { "timer", ApplicationTimerRole },
1410         { "toolbar", ToolbarRole },
1411         { "tooltip", UserInterfaceTooltipRole },
1412         { "tree", TreeRole },
1413         { "treegrid", TreeGridRole },
1414         { "treeitem", TreeItemRole }
1415     };
1416     ARIARoleMap* roleMap = new ARIARoleMap;
1417
1418     for (size_t i = 0; i < WTF_ARRAY_LENGTH(roles); ++i)
1419         roleMap->set(roles[i].ariaRole, roles[i].webcoreRole);
1420     return roleMap;
1421 }
1422
1423 AccessibilityRole AccessibilityObject::ariaRoleToWebCoreRole(const String& value)
1424 {
1425     ASSERT(!value.isEmpty());
1426     
1427     static const ARIARoleMap* roleMap = createARIARoleMap();
1428
1429     Vector<String> roleVector;
1430     value.split(' ', roleVector);
1431     AccessibilityRole role = UnknownRole;
1432     unsigned size = roleVector.size();
1433     for (unsigned i = 0; i < size; ++i) {
1434         String roleName = roleVector[i];
1435         role = roleMap->get(roleName);
1436         if (role)
1437             return role;
1438     }
1439     
1440     return role;
1441 }
1442
1443 bool AccessibilityObject::hasHighlighting() const
1444 {
1445     for (Node* node = this->node(); node; node = node->parentNode()) {
1446         if (node->hasTagName(markTag))
1447             return true;
1448     }
1449     
1450     return false;
1451 }
1452
1453 const AtomicString& AccessibilityObject::placeholderValue() const
1454 {
1455     const AtomicString& placeholder = getAttribute(placeholderAttr);
1456     if (!placeholder.isEmpty())
1457         return placeholder;
1458     
1459     return nullAtom;
1460 }
1461     
1462 bool AccessibilityObject::isInsideARIALiveRegion() const
1463 {
1464     if (supportsARIALiveRegion())
1465         return true;
1466     
1467     for (AccessibilityObject* axParent = parentObject(); axParent; axParent = axParent->parentObject()) {
1468         if (axParent->supportsARIALiveRegion())
1469             return true;
1470     }
1471     
1472     return false;
1473 }
1474
1475 bool AccessibilityObject::supportsARIAAttributes() const
1476 {
1477     return supportsARIALiveRegion() || supportsARIADragging() || supportsARIADropping() || supportsARIAFlowTo() || supportsARIAOwns();
1478 }
1479     
1480 bool AccessibilityObject::supportsARIALiveRegion() const
1481 {
1482     const AtomicString& liveRegion = ariaLiveRegionStatus();
1483     return equalIgnoringCase(liveRegion, "polite") || equalIgnoringCase(liveRegion, "assertive");
1484 }
1485
1486 AccessibilityObject* AccessibilityObject::elementAccessibilityHitTest(const IntPoint& point) const
1487
1488     // Send the hit test back into the sub-frame if necessary.
1489     if (isAttachment()) {
1490         Widget* widget = widgetForAttachmentView();
1491         // Normalize the point for the widget's bounds.
1492         if (widget && widget->isFrameView())
1493             return axObjectCache()->getOrCreate(widget)->accessibilityHitTest(toPoint(point - widget->frameRect().location()));
1494     }
1495     
1496     // Check if there are any mock elements that need to be handled.
1497     size_t count = m_children.size();
1498     for (size_t k = 0; k < count; k++) {
1499         if (m_children[k]->isMockObject() && m_children[k]->elementRect().contains(point))
1500             return m_children[k]->elementAccessibilityHitTest(point);
1501     }
1502
1503     return const_cast<AccessibilityObject*>(this); 
1504 }
1505     
1506 AXObjectCache* AccessibilityObject::axObjectCache() const
1507 {
1508     Document* doc = document();
1509     if (doc)
1510         return doc->axObjectCache();
1511     return 0;
1512 }
1513     
1514 AccessibilityObject* AccessibilityObject::focusedUIElement() const
1515 {
1516     Document* doc = document();
1517     if (!doc)
1518         return 0;
1519     
1520     Page* page = doc->page();
1521     if (!page)
1522         return 0;
1523     
1524     return AXObjectCache::focusedUIElementForPage(page);
1525 }
1526     
1527 AccessibilitySortDirection AccessibilityObject::sortDirection() const
1528 {
1529     const AtomicString& sortAttribute = getAttribute(aria_sortAttr);
1530     if (equalIgnoringCase(sortAttribute, "ascending"))
1531         return SortDirectionAscending;
1532     if (equalIgnoringCase(sortAttribute, "descending"))
1533         return SortDirectionDescending;
1534     
1535     return SortDirectionNone;
1536 }
1537     
1538 bool AccessibilityObject::supportsARIAExpanded() const
1539 {
1540     return !getAttribute(aria_expandedAttr).isEmpty();
1541 }
1542     
1543 bool AccessibilityObject::isExpanded() const
1544 {
1545     if (equalIgnoringCase(getAttribute(aria_expandedAttr), "true"))
1546         return true;
1547     
1548     return false;  
1549 }
1550     
1551 AccessibilityButtonState AccessibilityObject::checkboxOrRadioValue() const
1552 {
1553     // If this is a real checkbox or radio button, AccessibilityRenderObject will handle.
1554     // If it's an ARIA checkbox or radio, the aria-checked attribute should be used.
1555
1556     const AtomicString& result = getAttribute(aria_checkedAttr);
1557     if (equalIgnoringCase(result, "true"))
1558         return ButtonStateOn;
1559     if (equalIgnoringCase(result, "mixed"))
1560         return ButtonStateMixed;
1561     
1562     return ButtonStateOff;
1563 }
1564
1565 // This is a 1-dimensional scroll offset helper function that's applied
1566 // separately in the horizontal and vertical directions, because the
1567 // logic is the same. The goal is to compute the best scroll offset
1568 // in order to make an object visible within a viewport.
1569 //
1570 // In case the whole object cannot fit, you can specify a
1571 // subfocus - a smaller region within the object that should
1572 // be prioritized. If the whole object can fit, the subfocus is
1573 // ignored.
1574 //
1575 // Example: the viewport is scrolled to the right just enough
1576 // that the object is in view.
1577 //   Before:
1578 //   +----------Viewport---------+
1579 //                         +---Object---+
1580 //                         +--SubFocus--+
1581 //
1582 //   After:
1583 //          +----------Viewport---------+
1584 //                         +---Object---+
1585 //                         +--SubFocus--+
1586 //
1587 // When constraints cannot be fully satisfied, the min
1588 // (left/top) position takes precedence over the max (right/bottom).
1589 //
1590 // Note that the return value represents the ideal new scroll offset.
1591 // This may be out of range - the calling function should clip this
1592 // to the available range.
1593 static int computeBestScrollOffset(int currentScrollOffset,
1594                                    int subfocusMin, int subfocusMax,
1595                                    int objectMin, int objectMax,
1596                                    int viewportMin, int viewportMax) {
1597     int viewportSize = viewportMax - viewportMin;
1598
1599     // If the focus size is larger than the viewport size, shrink it in the
1600     // direction of subfocus.
1601     if (objectMax - objectMin > viewportSize) {
1602         // Subfocus must be within focus:
1603         subfocusMin = std::max(subfocusMin, objectMin);
1604         subfocusMax = std::min(subfocusMax, objectMax);
1605
1606         // Subfocus must be no larger than the viewport size; favor top/left.
1607         if (subfocusMax - subfocusMin > viewportSize)
1608             subfocusMax = subfocusMin + viewportSize;
1609
1610         if (subfocusMin + viewportSize > objectMax)
1611             objectMin = objectMax - viewportSize;
1612         else {
1613             objectMin = subfocusMin;
1614             objectMax = subfocusMin + viewportSize;
1615         }
1616     }
1617
1618     // Exit now if the focus is already within the viewport.
1619     if (objectMin - currentScrollOffset >= viewportMin
1620         && objectMax - currentScrollOffset <= viewportMax)
1621         return currentScrollOffset;
1622
1623     // Scroll left if we're too far to the right.
1624     if (objectMax - currentScrollOffset > viewportMax)
1625         return objectMax - viewportMax;
1626
1627     // Scroll right if we're too far to the left.
1628     if (objectMin - currentScrollOffset < viewportMin)
1629         return objectMin - viewportMin;
1630
1631     ASSERT_NOT_REACHED();
1632
1633     // This shouldn't happen.
1634     return currentScrollOffset;
1635 }
1636
1637 void AccessibilityObject::scrollToMakeVisible() const
1638 {
1639     IntRect objectRect = pixelSnappedIntRect(boundingBoxRect());
1640     objectRect.setLocation(IntPoint());
1641     scrollToMakeVisibleWithSubFocus(objectRect);
1642 }
1643
1644 void AccessibilityObject::scrollToMakeVisibleWithSubFocus(const IntRect& subfocus) const
1645 {
1646     // Search up the parent chain until we find the first one that's scrollable.
1647     AccessibilityObject* scrollParent = parentObject();
1648     ScrollableArea* scrollableArea;
1649     for (scrollableArea = 0;
1650          scrollParent && !(scrollableArea = scrollParent->getScrollableAreaIfScrollable());
1651          scrollParent = scrollParent->parentObject()) { }
1652     if (!scrollableArea)
1653         return;
1654
1655     LayoutRect objectRect = boundingBoxRect();
1656     IntPoint scrollPosition = scrollableArea->scrollPosition();
1657     IntRect scrollVisibleRect = scrollableArea->visibleContentRect();
1658
1659     int desiredX = computeBestScrollOffset(
1660         scrollPosition.x(),
1661         objectRect.x() + subfocus.x(), objectRect.x() + subfocus.maxX(),
1662         objectRect.x(), objectRect.maxX(),
1663         0, scrollVisibleRect.width());
1664     int desiredY = computeBestScrollOffset(
1665         scrollPosition.y(),
1666         objectRect.y() + subfocus.y(), objectRect.y() + subfocus.maxY(),
1667         objectRect.y(), objectRect.maxY(),
1668         0, scrollVisibleRect.height());
1669
1670     scrollParent->scrollTo(IntPoint(desiredX, desiredY));
1671
1672     // Recursively make sure the scroll parent itself is visible.
1673     if (scrollParent->parentObject())
1674         scrollParent->scrollToMakeVisible();
1675 }
1676
1677 void AccessibilityObject::scrollToGlobalPoint(const IntPoint& globalPoint) const
1678 {
1679     // Search up the parent chain and create a vector of all scrollable parent objects
1680     // and ending with this object itself.
1681     Vector<const AccessibilityObject*> objects;
1682     AccessibilityObject* parentObject;
1683     for (parentObject = this->parentObject(); parentObject; parentObject = parentObject->parentObject()) {
1684         if (parentObject->getScrollableAreaIfScrollable())
1685             objects.prepend(parentObject);
1686     }
1687     objects.append(this);
1688
1689     // Start with the outermost scrollable (the main window) and try to scroll the
1690     // next innermost object to the given point.
1691     int offsetX = 0, offsetY = 0;
1692     IntPoint point = globalPoint;
1693     size_t levels = objects.size() - 1;
1694     for (size_t i = 0; i < levels; i++) {
1695         const AccessibilityObject* outer = objects[i];
1696         const AccessibilityObject* inner = objects[i + 1];
1697
1698         ScrollableArea* scrollableArea = outer->getScrollableAreaIfScrollable();
1699
1700         LayoutRect innerRect = inner->isAccessibilityScrollView() ? inner->parentObject()->boundingBoxRect() : inner->boundingBoxRect();
1701         LayoutRect objectRect = innerRect;
1702         IntPoint scrollPosition = scrollableArea->scrollPosition();
1703
1704         // Convert the object rect into local coordinates.
1705         objectRect.move(offsetX, offsetY);
1706         if (!outer->isAccessibilityScrollView())
1707             objectRect.move(scrollPosition.x(), scrollPosition.y());
1708
1709         int desiredX = computeBestScrollOffset(
1710             0,
1711             objectRect.x(), objectRect.maxX(),
1712             objectRect.x(), objectRect.maxX(),
1713             point.x(), point.x());
1714         int desiredY = computeBestScrollOffset(
1715             0,
1716             objectRect.y(), objectRect.maxY(),
1717             objectRect.y(), objectRect.maxY(),
1718             point.y(), point.y());
1719         outer->scrollTo(IntPoint(desiredX, desiredY));
1720
1721         if (outer->isAccessibilityScrollView() && !inner->isAccessibilityScrollView()) {
1722             // If outer object we just scrolled is a scroll view (main window or iframe) but the
1723             // inner object is not, keep track of the coordinate transformation to apply to
1724             // future nested calculations.
1725             scrollPosition = scrollableArea->scrollPosition();
1726             offsetX -= (scrollPosition.x() + point.x());
1727             offsetY -= (scrollPosition.y() + point.y());
1728             point.move(scrollPosition.x() - innerRect.x(),
1729                        scrollPosition.y() - innerRect.y());
1730         } else if (inner->isAccessibilityScrollView()) {
1731             // Otherwise, if the inner object is a scroll view, reset the coordinate transformation.
1732             offsetX = 0;
1733             offsetY = 0;
1734         }
1735     }
1736 }
1737     
1738 } // namespace WebCore