2 * Copyright (C) 2008, 2009 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
30 #include "AccessibilityObject.h"
32 #include "AXObjectCache.h"
33 #include "AccessibilityRenderObject.h"
34 #include "CharacterNames.h"
35 #include "FloatRect.h"
36 #include "FocusController.h"
38 #include "FrameLoader.h"
39 #include "LocalizedStrings.h"
41 #include "NotImplemented.h"
43 #include "RenderImage.h"
44 #include "RenderListItem.h"
45 #include "RenderListMarker.h"
46 #include "RenderMenuList.h"
47 #include "RenderTextControl.h"
48 #include "RenderTheme.h"
49 #include "RenderView.h"
50 #include "RenderWidget.h"
51 #include "SelectionController.h"
52 #include "TextIterator.h"
53 #include "htmlediting.h"
54 #include "visible_units.h"
55 #include <wtf/StdLibExtras.h>
61 using namespace HTMLNames
;
63 AccessibilityObject::AccessibilityObject()
65 , m_haveChildren(false)
73 AccessibilityObject::~AccessibilityObject()
78 void AccessibilityObject::detach()
80 #if HAVE(ACCESSIBILITY)
85 AccessibilityObject
* AccessibilityObject::parentObjectUnignored() const
87 AccessibilityObject
* parent
;
88 for (parent
= parentObject(); parent
&& parent
->accessibilityIsIgnored(); parent
= parent
->parentObject()) {
94 AccessibilityObject
* AccessibilityObject::firstAccessibleObjectFromNode(const Node
* node
)
96 ASSERT(AXObjectCache::accessibilityEnabled());
101 Document
* document
= node
->document();
105 AXObjectCache
* cache
= document
->axObjectCache();
107 AccessibilityObject
* accessibleObject
= cache
->getOrCreate(node
->renderer());
108 while (accessibleObject
&& accessibleObject
->accessibilityIsIgnored()) {
109 node
= node
->traverseNextNode();
111 while (node
&& !node
->renderer())
112 node
= node
->traverseNextSibling();
117 accessibleObject
= cache
->getOrCreate(node
->renderer());
120 return accessibleObject
;
123 bool AccessibilityObject::isARIAInput(AccessibilityRole ariaRole
)
125 return ariaRole
== RadioButtonRole
|| ariaRole
== CheckBoxRole
|| ariaRole
== TextFieldRole
;
128 bool AccessibilityObject::isARIAControl(AccessibilityRole ariaRole
)
130 return isARIAInput(ariaRole
) || ariaRole
== TextAreaRole
|| ariaRole
== ButtonRole
131 || ariaRole
== ComboBoxRole
|| ariaRole
== SliderRole
;
134 IntPoint
AccessibilityObject::clickPoint() const
136 IntRect rect
= elementRect();
137 return IntPoint(rect
.x() + rect
.width() / 2, rect
.y() + rect
.height() / 2);
140 bool AccessibilityObject::press() const
142 Element
* actionElem
= actionElement();
145 if (Frame
* f
= actionElem
->document()->frame())
146 f
->loader()->resetMultipleFormSubmissionProtection();
147 actionElem
->accessKeyAction(true);
151 String
AccessibilityObject::language() const
153 AccessibilityObject
* parent
= parentObject();
155 // as a last resort, fall back to the content language specified in the meta tag
157 Document
* doc
= document();
159 return doc
->contentLanguage();
163 return parent
->language();
166 VisiblePositionRange
AccessibilityObject::visiblePositionRangeForUnorderedPositions(const VisiblePosition
& visiblePos1
, const VisiblePosition
& visiblePos2
) const
168 if (visiblePos1
.isNull() || visiblePos2
.isNull())
169 return VisiblePositionRange();
171 VisiblePosition startPos
;
172 VisiblePosition endPos
;
175 // upstream is ordered before downstream for the same position
176 if (visiblePos1
== visiblePos2
&& visiblePos2
.affinity() == UPSTREAM
)
177 alreadyInOrder
= false;
179 // use selection order to see if the positions are in order
181 alreadyInOrder
= VisibleSelection(visiblePos1
, visiblePos2
).isBaseFirst();
183 if (alreadyInOrder
) {
184 startPos
= visiblePos1
;
185 endPos
= visiblePos2
;
187 startPos
= visiblePos2
;
188 endPos
= visiblePos1
;
191 return VisiblePositionRange(startPos
, endPos
);
194 VisiblePositionRange
AccessibilityObject::positionOfLeftWord(const VisiblePosition
& visiblePos
) const
196 VisiblePosition startPosition
= startOfWord(visiblePos
, LeftWordIfOnBoundary
);
197 VisiblePosition endPosition
= endOfWord(startPosition
);
198 return VisiblePositionRange(startPosition
, endPosition
);
201 VisiblePositionRange
AccessibilityObject::positionOfRightWord(const VisiblePosition
& visiblePos
) const
203 VisiblePosition startPosition
= startOfWord(visiblePos
, RightWordIfOnBoundary
);
204 VisiblePosition endPosition
= endOfWord(startPosition
);
205 return VisiblePositionRange(startPosition
, endPosition
);
208 static VisiblePosition
updateAXLineStartForVisiblePosition(const VisiblePosition
& visiblePosition
)
210 // A line in the accessibility sense should include floating objects, such as aligned image, as part of a line.
211 // So let's update the position to include that.
212 VisiblePosition tempPosition
;
213 VisiblePosition startPosition
= visiblePosition
;
215 RenderObject
* renderer
;
217 tempPosition
= startPosition
.previous();
218 if (tempPosition
.isNull())
220 p
= tempPosition
.deepEquivalent();
223 renderer
= p
.node()->renderer();
224 if (!renderer
|| (renderer
->isRenderBlock() && !p
.deprecatedEditingOffset()))
227 int ignoredCaretOffset
;
228 p
.getInlineBoxAndOffset(tempPosition
.affinity(), box
, ignoredCaretOffset
);
231 startPosition
= tempPosition
;
234 return startPosition
;
237 VisiblePositionRange
AccessibilityObject::leftLineVisiblePositionRange(const VisiblePosition
& visiblePos
) const
239 if (visiblePos
.isNull())
240 return VisiblePositionRange();
242 // make a caret selection for the position before marker position (to make sure
243 // we move off of a line start)
244 VisiblePosition prevVisiblePos
= visiblePos
.previous();
245 if (prevVisiblePos
.isNull())
246 return VisiblePositionRange();
248 VisiblePosition startPosition
= startOfLine(prevVisiblePos
);
250 // keep searching for a valid line start position. Unless the VisiblePosition is at the very beginning, there should
251 // always be a valid line range. However, startOfLine will return null for position next to a floating object,
252 // since floating object doesn't really belong to any line.
253 // This check will reposition the marker before the floating object, to ensure we get a line start.
254 if (startPosition
.isNull()) {
255 while (startPosition
.isNull() && prevVisiblePos
.isNotNull()) {
256 prevVisiblePos
= prevVisiblePos
.previous();
257 startPosition
= startOfLine(prevVisiblePos
);
260 startPosition
= updateAXLineStartForVisiblePosition(startPosition
);
262 VisiblePosition endPosition
= endOfLine(prevVisiblePos
);
263 return VisiblePositionRange(startPosition
, endPosition
);
266 VisiblePositionRange
AccessibilityObject::rightLineVisiblePositionRange(const VisiblePosition
& visiblePos
) const
268 if (visiblePos
.isNull())
269 return VisiblePositionRange();
271 // make sure we move off of a line end
272 VisiblePosition nextVisiblePos
= visiblePos
.next();
273 if (nextVisiblePos
.isNull())
274 return VisiblePositionRange();
276 VisiblePosition startPosition
= startOfLine(nextVisiblePos
);
278 // fetch for a valid line start position
279 if (startPosition
.isNull()) {
280 startPosition
= visiblePos
;
281 nextVisiblePos
= nextVisiblePos
.next();
283 startPosition
= updateAXLineStartForVisiblePosition(startPosition
);
285 VisiblePosition endPosition
= endOfLine(nextVisiblePos
);
287 // as long as the position hasn't reached the end of the doc, keep searching for a valid line end position
288 // Unless the VisiblePosition is at the very end, there should always be a valid line range. However, endOfLine will
289 // return null for position by a floating object, since floating object doesn't really belong to any line.
290 // This check will reposition the marker after the floating object, to ensure we get a line end.
291 while (endPosition
.isNull() && nextVisiblePos
.isNotNull()) {
292 nextVisiblePos
= nextVisiblePos
.next();
293 endPosition
= endOfLine(nextVisiblePos
);
296 return VisiblePositionRange(startPosition
, endPosition
);
299 VisiblePositionRange
AccessibilityObject::sentenceForPosition(const VisiblePosition
& visiblePos
) const
301 // FIXME: FO 2 IMPLEMENT (currently returns incorrect answer)
302 // Related? <rdar://problem/3927736> Text selection broken in 8A336
303 VisiblePosition startPosition
= startOfSentence(visiblePos
);
304 VisiblePosition endPosition
= endOfSentence(startPosition
);
305 return VisiblePositionRange(startPosition
, endPosition
);
308 VisiblePositionRange
AccessibilityObject::paragraphForPosition(const VisiblePosition
& visiblePos
) const
310 VisiblePosition startPosition
= startOfParagraph(visiblePos
);
311 VisiblePosition endPosition
= endOfParagraph(startPosition
);
312 return VisiblePositionRange(startPosition
, endPosition
);
315 static VisiblePosition
startOfStyleRange(const VisiblePosition visiblePos
)
317 RenderObject
* renderer
= visiblePos
.deepEquivalent().node()->renderer();
318 RenderObject
* startRenderer
= renderer
;
319 RenderStyle
* style
= renderer
->style();
321 // traverse backward by renderer to look for style change
322 for (RenderObject
* r
= renderer
->previousInPreOrder(); r
; r
= r
->previousInPreOrder()) {
323 // skip non-leaf nodes
327 // stop at style change
328 if (r
->style() != style
)
335 return VisiblePosition(startRenderer
->node(), 0, VP_DEFAULT_AFFINITY
);
338 static VisiblePosition
endOfStyleRange(const VisiblePosition
& visiblePos
)
340 RenderObject
* renderer
= visiblePos
.deepEquivalent().node()->renderer();
341 RenderObject
* endRenderer
= renderer
;
342 RenderStyle
* style
= renderer
->style();
344 // traverse forward by renderer to look for style change
345 for (RenderObject
* r
= renderer
->nextInPreOrder(); r
; r
= r
->nextInPreOrder()) {
346 // skip non-leaf nodes
350 // stop at style change
351 if (r
->style() != style
)
358 return lastDeepEditingPositionForNode(endRenderer
->node());
361 VisiblePositionRange
AccessibilityObject::styleRangeForPosition(const VisiblePosition
& visiblePos
) const
363 if (visiblePos
.isNull())
364 return VisiblePositionRange();
366 return VisiblePositionRange(startOfStyleRange(visiblePos
), endOfStyleRange(visiblePos
));
369 // NOTE: Consider providing this utility method as AX API
370 VisiblePositionRange
AccessibilityObject::visiblePositionRangeForRange(const PlainTextRange
& range
) const
372 if (range
.start
+ range
.length
> text().length())
373 return VisiblePositionRange();
375 VisiblePosition startPosition
= visiblePositionForIndex(range
.start
);
376 startPosition
.setAffinity(DOWNSTREAM
);
377 VisiblePosition endPosition
= visiblePositionForIndex(range
.start
+ range
.length
);
378 return VisiblePositionRange(startPosition
, endPosition
);
381 static bool replacedNodeNeedsCharacter(Node
* replacedNode
)
383 // we should always be given a rendered node and a replaced node, but be safe
384 // replaced nodes are either attachments (widgets) or images
385 if (!replacedNode
|| !replacedNode
->renderer() || !replacedNode
->renderer()->isReplaced() || replacedNode
->isTextNode())
388 // create an AX object, but skip it if it is not supposed to be seen
389 AccessibilityObject
* object
= replacedNode
->renderer()->document()->axObjectCache()->getOrCreate(replacedNode
->renderer());
390 if (object
->accessibilityIsIgnored())
396 // Finds a RenderListItem parent give a node.
397 RenderListItem
* AccessibilityObject::renderListItemContainerForNode(Node
* node
) const
399 for (Node
* stringNode
= node
; stringNode
; stringNode
= stringNode
->parent()) {
400 RenderObject
* renderObject
= stringNode
->renderer();
401 if (!renderObject
|| !renderObject
->isListItem())
404 return toRenderListItem(renderObject
);
410 // Returns the text associated with a list marker if this node is contained within a list item.
411 String
AccessibilityObject::listMarkerTextForNodeAndPosition(Node
* node
, const VisiblePosition
& visiblePositionStart
) const
413 // If the range does not contain the start of the line, the list marker text should not be included.
414 if (!isStartOfLine(visiblePositionStart
))
417 RenderListItem
* listItem
= renderListItemContainerForNode(node
);
421 // If this is in a list item, we need to manually add the text for the list marker
422 // because a RenderListMarker does not have a Node equivalent and thus does not appear
423 // when iterating text.
424 const String
& markerText
= listItem
->markerText();
425 if (markerText
.isEmpty())
428 // Append text, plus the period that follows the text.
429 // FIXME: Not all list marker styles are followed by a period, but this
430 // sounds much better when there is a synthesized pause because of a period.
431 Vector
<UChar
> resultVector
;
432 resultVector
.append(markerText
.characters(), markerText
.length());
433 resultVector
.append('.');
434 resultVector
.append(' ');
436 return String::adopt(resultVector
);
439 String
AccessibilityObject::stringForVisiblePositionRange(const VisiblePositionRange
& visiblePositionRange
) const
441 if (visiblePositionRange
.isNull())
444 Vector
<UChar
> resultVector
;
445 RefPtr
<Range
> range
= makeRange(visiblePositionRange
.start
, visiblePositionRange
.end
);
446 for (TextIterator
it(range
.get()); !it
.atEnd(); it
.advance()) {
447 // non-zero length means textual node, zero length means replaced node (AKA "attachments" in AX)
449 // Add a textual representation for list marker text
450 String listMarkerText
= listMarkerTextForNodeAndPosition(it
.node(), visiblePositionRange
.start
);
451 if (!listMarkerText
.isEmpty())
452 resultVector
.append(listMarkerText
.characters(), listMarkerText
.length());
454 resultVector
.append(it
.characters(), it
.length());
456 // locate the node and starting offset for this replaced range
458 Node
* node
= it
.range()->startContainer(exception
);
459 ASSERT(node
== it
.range()->endContainer(exception
));
460 int offset
= it
.range()->startOffset(exception
);
462 if (replacedNodeNeedsCharacter(node
->childNode(offset
)))
463 resultVector
.append(objectReplacementCharacter
);
467 return String::adopt(resultVector
);
470 int AccessibilityObject::lengthForVisiblePositionRange(const VisiblePositionRange
& visiblePositionRange
) const
472 // FIXME: Multi-byte support
473 if (visiblePositionRange
.isNull())
477 RefPtr
<Range
> range
= makeRange(visiblePositionRange
.start
, visiblePositionRange
.end
);
478 for (TextIterator
it(range
.get()); !it
.atEnd(); it
.advance()) {
479 // non-zero length means textual node, zero length means replaced node (AKA "attachments" in AX)
481 length
+= it
.length();
483 // locate the node and starting offset for this replaced range
485 Node
* node
= it
.range()->startContainer(exception
);
486 ASSERT(node
== it
.range()->endContainer(exception
));
487 int offset
= it
.range()->startOffset(exception
);
489 if (replacedNodeNeedsCharacter(node
->childNode(offset
)))
497 VisiblePosition
AccessibilityObject::nextWordEnd(const VisiblePosition
& visiblePos
) const
499 if (visiblePos
.isNull())
500 return VisiblePosition();
502 // make sure we move off of a word end
503 VisiblePosition nextVisiblePos
= visiblePos
.next();
504 if (nextVisiblePos
.isNull())
505 return VisiblePosition();
507 return endOfWord(nextVisiblePos
, LeftWordIfOnBoundary
);
510 VisiblePosition
AccessibilityObject::previousWordStart(const VisiblePosition
& visiblePos
) const
512 if (visiblePos
.isNull())
513 return VisiblePosition();
515 // make sure we move off of a word start
516 VisiblePosition prevVisiblePos
= visiblePos
.previous();
517 if (prevVisiblePos
.isNull())
518 return VisiblePosition();
520 return startOfWord(prevVisiblePos
, RightWordIfOnBoundary
);
523 VisiblePosition
AccessibilityObject::nextLineEndPosition(const VisiblePosition
& visiblePos
) const
525 if (visiblePos
.isNull())
526 return VisiblePosition();
528 // to make sure we move off of a line end
529 VisiblePosition nextVisiblePos
= visiblePos
.next();
530 if (nextVisiblePos
.isNull())
531 return VisiblePosition();
533 VisiblePosition endPosition
= endOfLine(nextVisiblePos
);
535 // as long as the position hasn't reached the end of the doc, keep searching for a valid line end position
536 // 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.
537 while (endPosition
.isNull() && nextVisiblePos
.isNotNull()) {
538 nextVisiblePos
= nextVisiblePos
.next();
539 endPosition
= endOfLine(nextVisiblePos
);
545 VisiblePosition
AccessibilityObject::previousLineStartPosition(const VisiblePosition
& visiblePos
) const
547 if (visiblePos
.isNull())
548 return VisiblePosition();
550 // make sure we move off of a line start
551 VisiblePosition prevVisiblePos
= visiblePos
.previous();
552 if (prevVisiblePos
.isNull())
553 return VisiblePosition();
555 VisiblePosition startPosition
= startOfLine(prevVisiblePos
);
557 // as long as the position hasn't reached the beginning of the doc, keep searching for a valid line start position
558 // 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.
559 if (startPosition
.isNull()) {
560 while (startPosition
.isNull() && prevVisiblePos
.isNotNull()) {
561 prevVisiblePos
= prevVisiblePos
.previous();
562 startPosition
= startOfLine(prevVisiblePos
);
565 startPosition
= updateAXLineStartForVisiblePosition(startPosition
);
567 return startPosition
;
570 VisiblePosition
AccessibilityObject::nextSentenceEndPosition(const VisiblePosition
& visiblePos
) const
572 // FIXME: FO 2 IMPLEMENT (currently returns incorrect answer)
573 // Related? <rdar://problem/3927736> Text selection broken in 8A336
574 if (visiblePos
.isNull())
575 return VisiblePosition();
577 // make sure we move off of a sentence end
578 VisiblePosition nextVisiblePos
= visiblePos
.next();
579 if (nextVisiblePos
.isNull())
580 return VisiblePosition();
582 // an empty line is considered a sentence. If it's skipped, then the sentence parser will not
583 // see this empty line. Instead, return the end position of the empty line.
584 VisiblePosition endPosition
;
586 String lineString
= plainText(makeRange(startOfLine(nextVisiblePos
), endOfLine(nextVisiblePos
)).get());
587 if (lineString
.isEmpty())
588 endPosition
= nextVisiblePos
;
590 endPosition
= endOfSentence(nextVisiblePos
);
595 VisiblePosition
AccessibilityObject::previousSentenceStartPosition(const VisiblePosition
& visiblePos
) const
597 // FIXME: FO 2 IMPLEMENT (currently returns incorrect answer)
598 // Related? <rdar://problem/3927736> Text selection broken in 8A336
599 if (visiblePos
.isNull())
600 return VisiblePosition();
602 // make sure we move off of a sentence start
603 VisiblePosition previousVisiblePos
= visiblePos
.previous();
604 if (previousVisiblePos
.isNull())
605 return VisiblePosition();
607 // treat empty line as a separate sentence.
608 VisiblePosition startPosition
;
610 String lineString
= plainText(makeRange(startOfLine(previousVisiblePos
), endOfLine(previousVisiblePos
)).get());
611 if (lineString
.isEmpty())
612 startPosition
= previousVisiblePos
;
614 startPosition
= startOfSentence(previousVisiblePos
);
616 return startPosition
;
619 VisiblePosition
AccessibilityObject::nextParagraphEndPosition(const VisiblePosition
& visiblePos
) const
621 if (visiblePos
.isNull())
622 return VisiblePosition();
624 // make sure we move off of a paragraph end
625 VisiblePosition nextPos
= visiblePos
.next();
626 if (nextPos
.isNull())
627 return VisiblePosition();
629 return endOfParagraph(nextPos
);
632 VisiblePosition
AccessibilityObject::previousParagraphStartPosition(const VisiblePosition
& visiblePos
) const
634 if (visiblePos
.isNull())
635 return VisiblePosition();
637 // make sure we move off of a paragraph start
638 VisiblePosition previousPos
= visiblePos
.previous();
639 if (previousPos
.isNull())
640 return VisiblePosition();
642 return startOfParagraph(previousPos
);
645 AccessibilityObject
* AccessibilityObject::accessibilityObjectForPosition(const VisiblePosition
& visiblePos
) const
647 if (visiblePos
.isNull())
650 RenderObject
* obj
= visiblePos
.deepEquivalent().node()->renderer();
654 return obj
->document()->axObjectCache()->getOrCreate(obj
);
657 int AccessibilityObject::lineForPosition(const VisiblePosition
& visiblePos
) const
659 if (visiblePos
.isNull())
662 unsigned lineCount
= 0;
663 VisiblePosition currentVisiblePos
= visiblePos
;
664 VisiblePosition savedVisiblePos
;
666 // move up until we get to the top
667 // FIXME: This only takes us to the top of the rootEditableElement, not the top of the
669 while (currentVisiblePos
.isNotNull() && !(inSameLine(currentVisiblePos
, savedVisiblePos
))) {
671 savedVisiblePos
= currentVisiblePos
;
672 VisiblePosition prevVisiblePos
= previousLinePosition(currentVisiblePos
, 0);
673 currentVisiblePos
= prevVisiblePos
;
676 return lineCount
- 1;
679 // NOTE: Consider providing this utility method as AX API
680 PlainTextRange
AccessibilityObject::plainTextRangeForVisiblePositionRange(const VisiblePositionRange
& positionRange
) const
682 int index1
= index(positionRange
.start
);
683 int index2
= index(positionRange
.end
);
684 if (index1
< 0 || index2
< 0 || index1
> index2
)
685 return PlainTextRange();
687 return PlainTextRange(index1
, index2
- index1
);
690 // The composed character range in the text associated with this accessibility object that
691 // is specified by the given screen coordinates. This parameterized attribute returns the
692 // complete range of characters (including surrogate pairs of multi-byte glyphs) at the given
693 // screen coordinates.
694 // NOTE: This varies from AppKit when the point is below the last line. AppKit returns an
695 // an error in that case. We return textControl->text().length(), 1. Does this matter?
696 PlainTextRange
AccessibilityObject::doAXRangeForPosition(const IntPoint
& point
) const
698 int i
= index(visiblePositionForPoint(point
));
700 return PlainTextRange();
702 return PlainTextRange(i
, 1);
705 // Given a character index, the range of text associated with this accessibility object
706 // over which the style in effect at that character index applies.
707 PlainTextRange
AccessibilityObject::doAXStyleRangeForIndex(unsigned index
) const
709 VisiblePositionRange range
= styleRangeForPosition(visiblePositionForIndex(index
, false));
710 return plainTextRangeForVisiblePositionRange(range
);
713 // Given an indexed character, the line number of the text associated with this accessibility
714 // object that contains the character.
715 unsigned AccessibilityObject::doAXLineForIndex(unsigned index
)
717 return lineForPosition(visiblePositionForIndex(index
, false));
720 FrameView
* AccessibilityObject::documentFrameView() const
722 const AccessibilityObject
* object
= this;
723 while (object
&& !object
->isAccessibilityRenderObject())
724 object
= object
->parentObject();
729 return object
->documentFrameView();
732 void AccessibilityObject::clearChildren()
734 m_haveChildren
= false;
738 AccessibilityObject
* AccessibilityObject::anchorElementForNode(Node
* node
)
740 RenderObject
* obj
= node
->renderer();
744 RefPtr
<AccessibilityObject
> axObj
= obj
->document()->axObjectCache()->getOrCreate(obj
);
745 Element
* anchor
= axObj
->anchorElement();
749 RenderObject
* anchorRenderer
= anchor
->renderer();
753 return anchorRenderer
->document()->axObjectCache()->getOrCreate(anchorRenderer
);
756 void AccessibilityObject::ariaTreeRows(AccessibilityChildrenVector
& result
)
758 AccessibilityChildrenVector axChildren
= children();
759 unsigned count
= axChildren
.size();
760 for (unsigned k
= 0; k
< count
; ++k
) {
761 AccessibilityObject
* obj
= axChildren
[k
].get();
763 // Add tree items as the rows.
764 if (obj
->roleValue() == TreeItemRole
)
767 // Now see if this item also has rows hiding inside of it.
768 obj
->ariaTreeRows(result
);
772 void AccessibilityObject::ariaTreeItemContent(AccessibilityChildrenVector
& result
)
774 // The ARIA tree item content are the item that are not other tree items or their containing groups.
775 AccessibilityChildrenVector axChildren
= children();
776 unsigned count
= axChildren
.size();
777 for (unsigned k
= 0; k
< count
; ++k
) {
778 AccessibilityObject
* obj
= axChildren
[k
].get();
779 AccessibilityRole role
= obj
->roleValue();
780 if (role
== TreeItemRole
|| role
== GroupRole
)
787 void AccessibilityObject::ariaTreeItemDisclosedRows(AccessibilityChildrenVector
& result
)
789 AccessibilityChildrenVector axChildren
= children();
790 unsigned count
= axChildren
.size();
791 for (unsigned k
= 0; k
< count
; ++k
) {
792 AccessibilityObject
* obj
= axChildren
[k
].get();
794 // Add tree items as the rows.
795 if (obj
->roleValue() == TreeItemRole
)
797 // If it's not a tree item, then descend into the group to find more tree items.
799 obj
->ariaTreeRows(result
);
803 const String
& AccessibilityObject::actionVerb() const
805 // FIXME: Need to add verbs for select elements.
806 DEFINE_STATIC_LOCAL(const String
, buttonAction
, (AXButtonActionVerb()));
807 DEFINE_STATIC_LOCAL(const String
, textFieldAction
, (AXTextFieldActionVerb()));
808 DEFINE_STATIC_LOCAL(const String
, radioButtonAction
, (AXRadioButtonActionVerb()));
809 DEFINE_STATIC_LOCAL(const String
, checkedCheckBoxAction
, (AXCheckedCheckBoxActionVerb()));
810 DEFINE_STATIC_LOCAL(const String
, uncheckedCheckBoxAction
, (AXUncheckedCheckBoxActionVerb()));
811 DEFINE_STATIC_LOCAL(const String
, linkAction
, (AXLinkActionVerb()));
812 DEFINE_STATIC_LOCAL(const String
, noAction
, ());
814 switch (roleValue()) {
819 return textFieldAction
;
820 case RadioButtonRole
:
821 return radioButtonAction
;
823 return isChecked() ? checkedCheckBoxAction
: uncheckedCheckBoxAction
;
825 case WebCoreLinkRole
:
832 // Lacking concrete evidence of orientation, horizontal means width > height. vertical is height > width;
833 AccessibilityOrientation
AccessibilityObject::orientation() const
835 IntRect bounds
= elementRect();
836 if (bounds
.size().width() > bounds
.size().height())
837 return AccessibilityOrientationHorizontal
;
838 if (bounds
.size().height() > bounds
.size().width())
839 return AccessibilityOrientationVertical
;
841 // A tie goes to horizontal.
842 return AccessibilityOrientationHorizontal
;
845 typedef HashMap
<String
, AccessibilityRole
, CaseFoldingHash
> ARIARoleMap
;
849 AccessibilityRole webcoreRole
;
852 static ARIARoleMap
* createARIARoleMap()
854 const RoleEntry roles
[] = {
855 { "alert", ApplicationAlertRole
},
856 { "alertdialog", ApplicationAlertDialogRole
},
857 { "application", LandmarkApplicationRole
},
858 { "article", DocumentArticleRole
},
859 { "banner", LandmarkBannerRole
},
860 { "button", ButtonRole
},
861 { "checkbox", CheckBoxRole
},
862 { "complementary", LandmarkComplementaryRole
},
863 { "contentinfo", LandmarkContentInfoRole
},
864 { "dialog", ApplicationDialogRole
},
865 { "directory", DirectoryRole
},
866 { "grid", TableRole
},
867 { "gridcell", CellRole
},
868 { "columnheader", ColumnHeaderRole
},
869 { "combobox", ComboBoxRole
},
870 { "definition", DefinitionListDefinitionRole
},
871 { "document", DocumentRole
},
872 { "rowheader", RowHeaderRole
},
873 { "group", GroupRole
},
874 { "heading", HeadingRole
},
875 { "img", ImageRole
},
876 { "link", WebCoreLinkRole
},
877 { "list", ListRole
},
878 { "listitem", GroupRole
},
879 { "listbox", ListBoxRole
},
880 { "log", ApplicationLogRole
},
881 // "option" isn't here because it may map to different roles depending on the parent element's role
882 { "main", LandmarkMainRole
},
883 { "marquee", ApplicationMarqueeRole
},
884 { "math", DocumentMathRole
},
885 { "menu", MenuRole
},
886 { "menubar", GroupRole
},
887 // "menuitem" isn't here because it may map to different roles depending on the parent element's role
888 { "menuitemcheckbox", MenuItemRole
},
889 { "menuitemradio", MenuItemRole
},
890 { "note", DocumentNoteRole
},
891 { "navigation", LandmarkNavigationRole
},
892 { "option", ListBoxOptionRole
},
893 { "presentation", IgnoredRole
},
894 { "progressbar", ProgressIndicatorRole
},
895 { "radio", RadioButtonRole
},
896 { "radiogroup", RadioGroupRole
},
897 { "region", DocumentRegionRole
},
899 { "range", SliderRole
},
900 { "scrollbar", ScrollBarRole
},
901 { "search", LandmarkSearchRole
},
902 { "separator", SplitterRole
},
903 { "slider", SliderRole
},
904 { "spinbutton", ProgressIndicatorRole
},
905 { "status", ApplicationStatusRole
},
907 { "tablist", TabListRole
},
908 { "tabpanel", TabPanelRole
},
909 { "text", StaticTextRole
},
910 { "textbox", TextAreaRole
},
911 { "timer", ApplicationTimerRole
},
912 { "toolbar", ToolbarRole
},
913 { "tooltip", UserInterfaceTooltipRole
},
914 { "tree", TreeRole
},
915 { "treeitem", TreeItemRole
}
917 ARIARoleMap
* roleMap
= new ARIARoleMap
;
919 const unsigned numRoles
= sizeof(roles
) / sizeof(roles
[0]);
920 for (unsigned i
= 0; i
< numRoles
; ++i
)
921 roleMap
->set(roles
[i
].ariaRole
, roles
[i
].webcoreRole
);
925 AccessibilityRole
AccessibilityObject::ariaRoleToWebCoreRole(const String
& value
)
927 ASSERT(!value
.isEmpty());
928 static const ARIARoleMap
* roleMap
= createARIARoleMap();
929 return roleMap
->get(value
);
932 } // namespace WebCore