• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 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 "AccessibilityRenderObject.h"
33 #include "AXObjectCache.h"
34 #include "CharacterNames.h"
35 #include "FloatRect.h"
36 #include "FocusController.h"
37 #include "Frame.h"
38 #include "FrameLoader.h"
39 #include "LocalizedStrings.h"
40 #include "NodeList.h"
41 #include "NotImplemented.h"
42 #include "Page.h"
43 #include "RenderImage.h"
44 #include "RenderListMarker.h"
45 #include "RenderMenuList.h"
46 #include "RenderTextControl.h"
47 #include "RenderTheme.h"
48 #include "RenderView.h"
49 #include "RenderWidget.h"
50 #include "SelectionController.h"
51 #include "TextIterator.h"
52 #include "htmlediting.h"
53 #include "visible_units.h"
54 #include <wtf/StdLibExtras.h>
55 
56 using namespace std;
57 
58 namespace WebCore {
59 
60 using namespace HTMLNames;
61 
AccessibilityObject()62 AccessibilityObject::AccessibilityObject()
63     : m_id(0)
64     , m_haveChildren(false)
65     , m_role(UnknownRole)
66 #if PLATFORM(GTK)
67     , m_wrapper(0)
68 #endif
69 {
70 }
71 
~AccessibilityObject()72 AccessibilityObject::~AccessibilityObject()
73 {
74     ASSERT(isDetached());
75 }
76 
detach()77 void AccessibilityObject::detach()
78 {
79 #if HAVE(ACCESSIBILITY)
80     setWrapper(0);
81 #endif
82 }
83 
parentObjectUnignored() const84 AccessibilityObject* AccessibilityObject::parentObjectUnignored() const
85 {
86     AccessibilityObject* parent;
87     for (parent = parentObject(); parent && parent->accessibilityIsIgnored(); parent = parent->parentObject())
88         ;
89     return parent;
90 }
91 
isARIAInput(AccessibilityRole ariaRole)92 bool AccessibilityObject::isARIAInput(AccessibilityRole ariaRole)
93 {
94     return ariaRole == RadioButtonRole || ariaRole == CheckBoxRole || ariaRole == TextFieldRole;
95 }
96 
isARIAControl(AccessibilityRole ariaRole)97 bool AccessibilityObject::isARIAControl(AccessibilityRole ariaRole)
98 {
99     return isARIAInput(ariaRole) || ariaRole == TextAreaRole || ariaRole == ButtonRole
100     || ariaRole == ComboBoxRole || ariaRole == SliderRole;
101 }
102 
clickPoint() const103 IntPoint AccessibilityObject::clickPoint() const
104 {
105     IntRect rect = elementRect();
106     return IntPoint(rect.x() + rect.width() / 2, rect.y() + rect.height() / 2);
107 }
108 
press() const109 bool AccessibilityObject::press() const
110 {
111     Element* actionElem = actionElement();
112     if (!actionElem)
113         return false;
114     if (Frame* f = actionElem->document()->frame())
115         f->loader()->resetMultipleFormSubmissionProtection();
116     actionElem->accessKeyAction(true);
117     return true;
118 }
119 
language() const120 String AccessibilityObject::language() const
121 {
122     AccessibilityObject* parent = parentObject();
123 
124     // as a last resort, fall back to the content language specified in the meta tag
125     if (!parent) {
126         Document* doc = document();
127         if (doc)
128             return doc->contentLanguage();
129         return String();
130     }
131 
132     return parent->language();
133 }
134 
visiblePositionRangeForUnorderedPositions(const VisiblePosition & visiblePos1,const VisiblePosition & visiblePos2) const135 VisiblePositionRange AccessibilityObject::visiblePositionRangeForUnorderedPositions(const VisiblePosition& visiblePos1, const VisiblePosition& visiblePos2) const
136 {
137     if (visiblePos1.isNull() || visiblePos2.isNull())
138         return VisiblePositionRange();
139 
140     VisiblePosition startPos;
141     VisiblePosition endPos;
142     bool alreadyInOrder;
143 
144     // upstream is ordered before downstream for the same position
145     if (visiblePos1 == visiblePos2 && visiblePos2.affinity() == UPSTREAM)
146         alreadyInOrder = false;
147 
148     // use selection order to see if the positions are in order
149     else
150         alreadyInOrder = VisibleSelection(visiblePos1, visiblePos2).isBaseFirst();
151 
152     if (alreadyInOrder) {
153         startPos = visiblePos1;
154         endPos = visiblePos2;
155     } else {
156         startPos = visiblePos2;
157         endPos = visiblePos1;
158     }
159 
160     return VisiblePositionRange(startPos, endPos);
161 }
162 
positionOfLeftWord(const VisiblePosition & visiblePos) const163 VisiblePositionRange AccessibilityObject::positionOfLeftWord(const VisiblePosition& visiblePos) const
164 {
165     VisiblePosition startPosition = startOfWord(visiblePos, LeftWordIfOnBoundary);
166     VisiblePosition endPosition = endOfWord(startPosition);
167     return VisiblePositionRange(startPosition, endPosition);
168 }
169 
positionOfRightWord(const VisiblePosition & visiblePos) const170 VisiblePositionRange AccessibilityObject::positionOfRightWord(const VisiblePosition& visiblePos) const
171 {
172     VisiblePosition startPosition = startOfWord(visiblePos, RightWordIfOnBoundary);
173     VisiblePosition endPosition = endOfWord(startPosition);
174     return VisiblePositionRange(startPosition, endPosition);
175 }
176 
updateAXLineStartForVisiblePosition(const VisiblePosition & visiblePosition)177 static VisiblePosition updateAXLineStartForVisiblePosition(const VisiblePosition& visiblePosition)
178 {
179     // A line in the accessibility sense should include floating objects, such as aligned image, as part of a line.
180     // So let's update the position to include that.
181     VisiblePosition tempPosition;
182     VisiblePosition startPosition = visiblePosition;
183     Position p;
184     RenderObject* renderer;
185     while (true) {
186         tempPosition = startPosition.previous();
187         if (tempPosition.isNull())
188             break;
189         p = tempPosition.deepEquivalent();
190         if (!p.node())
191             break;
192         renderer = p.node()->renderer();
193         if (!renderer || (renderer->isRenderBlock() && !p.deprecatedEditingOffset()))
194             break;
195         InlineBox* box;
196         int ignoredCaretOffset;
197         p.getInlineBoxAndOffset(tempPosition.affinity(), box, ignoredCaretOffset);
198         if (box)
199             break;
200         startPosition = tempPosition;
201     }
202 
203     return startPosition;
204 }
205 
leftLineVisiblePositionRange(const VisiblePosition & visiblePos) const206 VisiblePositionRange AccessibilityObject::leftLineVisiblePositionRange(const VisiblePosition& visiblePos) const
207 {
208     if (visiblePos.isNull())
209         return VisiblePositionRange();
210 
211     // make a caret selection for the position before marker position (to make sure
212     // we move off of a line start)
213     VisiblePosition prevVisiblePos = visiblePos.previous();
214     if (prevVisiblePos.isNull())
215         return VisiblePositionRange();
216 
217     VisiblePosition startPosition = startOfLine(prevVisiblePos);
218 
219     // keep searching for a valid line start position.  Unless the VisiblePosition is at the very beginning, there should
220     // always be a valid line range.  However, startOfLine will return null for position next to a floating object,
221     // since floating object doesn't really belong to any line.
222     // This check will reposition the marker before the floating object, to ensure we get a line start.
223     if (startPosition.isNull()) {
224         while (startPosition.isNull() && prevVisiblePos.isNotNull()) {
225             prevVisiblePos = prevVisiblePos.previous();
226             startPosition = startOfLine(prevVisiblePos);
227         }
228     } else
229         startPosition = updateAXLineStartForVisiblePosition(startPosition);
230 
231     VisiblePosition endPosition = endOfLine(prevVisiblePos);
232     return VisiblePositionRange(startPosition, endPosition);
233 }
234 
rightLineVisiblePositionRange(const VisiblePosition & visiblePos) const235 VisiblePositionRange AccessibilityObject::rightLineVisiblePositionRange(const VisiblePosition& visiblePos) const
236 {
237     if (visiblePos.isNull())
238         return VisiblePositionRange();
239 
240     // make sure we move off of a line end
241     VisiblePosition nextVisiblePos = visiblePos.next();
242     if (nextVisiblePos.isNull())
243         return VisiblePositionRange();
244 
245     VisiblePosition startPosition = startOfLine(nextVisiblePos);
246 
247     // fetch for a valid line start position
248     if (startPosition.isNull() ) {
249         startPosition = visiblePos;
250         nextVisiblePos = nextVisiblePos.next();
251     } else
252         startPosition = updateAXLineStartForVisiblePosition(startPosition);
253 
254     VisiblePosition endPosition = endOfLine(nextVisiblePos);
255 
256     // as long as the position hasn't reached the end of the doc,  keep searching for a valid line end position
257     // Unless the VisiblePosition is at the very end, there should always be a valid line range.  However, endOfLine will
258     // return null for position by a floating object, since floating object doesn't really belong to any line.
259     // This check will reposition the marker after the floating object, to ensure we get a line end.
260     while (endPosition.isNull() && nextVisiblePos.isNotNull()) {
261         nextVisiblePos = nextVisiblePos.next();
262         endPosition = endOfLine(nextVisiblePos);
263     }
264 
265     return VisiblePositionRange(startPosition, endPosition);
266 }
267 
sentenceForPosition(const VisiblePosition & visiblePos) const268 VisiblePositionRange AccessibilityObject::sentenceForPosition(const VisiblePosition& visiblePos) const
269 {
270     // FIXME: FO 2 IMPLEMENT (currently returns incorrect answer)
271     // Related? <rdar://problem/3927736> Text selection broken in 8A336
272     VisiblePosition startPosition = startOfSentence(visiblePos);
273     VisiblePosition endPosition = endOfSentence(startPosition);
274     return VisiblePositionRange(startPosition, endPosition);
275 }
276 
paragraphForPosition(const VisiblePosition & visiblePos) const277 VisiblePositionRange AccessibilityObject::paragraphForPosition(const VisiblePosition& visiblePos) const
278 {
279     VisiblePosition startPosition = startOfParagraph(visiblePos);
280     VisiblePosition endPosition = endOfParagraph(startPosition);
281     return VisiblePositionRange(startPosition, endPosition);
282 }
283 
startOfStyleRange(const VisiblePosition visiblePos)284 static VisiblePosition startOfStyleRange(const VisiblePosition visiblePos)
285 {
286     RenderObject* renderer = visiblePos.deepEquivalent().node()->renderer();
287     RenderObject* startRenderer = renderer;
288     RenderStyle* style = renderer->style();
289 
290     // traverse backward by renderer to look for style change
291     for (RenderObject* r = renderer->previousInPreOrder(); r; r = r->previousInPreOrder()) {
292         // skip non-leaf nodes
293         if (r->firstChild())
294             continue;
295 
296         // stop at style change
297         if (r->style() != style)
298             break;
299 
300         // remember match
301         startRenderer = r;
302     }
303 
304     return VisiblePosition(startRenderer->node(), 0, VP_DEFAULT_AFFINITY);
305 }
306 
endOfStyleRange(const VisiblePosition & visiblePos)307 static VisiblePosition endOfStyleRange(const VisiblePosition& visiblePos)
308 {
309     RenderObject* renderer = visiblePos.deepEquivalent().node()->renderer();
310     RenderObject* endRenderer = renderer;
311     RenderStyle* style = renderer->style();
312 
313     // traverse forward by renderer to look for style change
314     for (RenderObject* r = renderer->nextInPreOrder(); r; r = r->nextInPreOrder()) {
315         // skip non-leaf nodes
316         if (r->firstChild())
317             continue;
318 
319         // stop at style change
320         if (r->style() != style)
321             break;
322 
323         // remember match
324         endRenderer = r;
325     }
326 
327     return lastDeepEditingPositionForNode(endRenderer->node());
328 }
329 
styleRangeForPosition(const VisiblePosition & visiblePos) const330 VisiblePositionRange AccessibilityObject::styleRangeForPosition(const VisiblePosition& visiblePos) const
331 {
332     if (visiblePos.isNull())
333         return VisiblePositionRange();
334 
335     return VisiblePositionRange(startOfStyleRange(visiblePos), endOfStyleRange(visiblePos));
336 }
337 
338 // NOTE: Consider providing this utility method as AX API
visiblePositionRangeForRange(const PlainTextRange & range) const339 VisiblePositionRange AccessibilityObject::visiblePositionRangeForRange(const PlainTextRange& range) const
340 {
341     if (range.start + range.length > text().length())
342         return VisiblePositionRange();
343 
344     VisiblePosition startPosition = visiblePositionForIndex(range.start);
345     startPosition.setAffinity(DOWNSTREAM);
346     VisiblePosition endPosition = visiblePositionForIndex(range.start + range.length);
347     return VisiblePositionRange(startPosition, endPosition);
348 }
349 
replacedNodeNeedsCharacter(Node * replacedNode)350 static bool replacedNodeNeedsCharacter(Node* replacedNode)
351 {
352     // we should always be given a rendered node and a replaced node, but be safe
353     // replaced nodes are either attachments (widgets) or images
354     if (!replacedNode || !replacedNode->renderer() || !replacedNode->renderer()->isReplaced() || replacedNode->isTextNode()) {
355         return false;
356     }
357 
358     // create an AX object, but skip it if it is not supposed to be seen
359     AccessibilityObject* object = replacedNode->renderer()->document()->axObjectCache()->getOrCreate(replacedNode->renderer());
360     if (object->accessibilityIsIgnored())
361         return false;
362 
363     return true;
364 }
365 
stringForVisiblePositionRange(const VisiblePositionRange & visiblePositionRange) const366 String AccessibilityObject::stringForVisiblePositionRange(const VisiblePositionRange& visiblePositionRange) const
367 {
368     if (visiblePositionRange.isNull())
369         return String();
370 
371     Vector<UChar> resultVector;
372     RefPtr<Range> range = makeRange(visiblePositionRange.start, visiblePositionRange.end);
373     for (TextIterator it(range.get()); !it.atEnd(); it.advance()) {
374         // non-zero length means textual node, zero length means replaced node (AKA "attachments" in AX)
375         if (it.length() != 0) {
376             resultVector.append(it.characters(), it.length());
377         } else {
378             // locate the node and starting offset for this replaced range
379             int exception = 0;
380             Node* node = it.range()->startContainer(exception);
381             ASSERT(node == it.range()->endContainer(exception));
382             int offset = it.range()->startOffset(exception);
383 
384             if (replacedNodeNeedsCharacter(node->childNode(offset))) {
385                 resultVector.append(objectReplacementCharacter);
386             }
387         }
388     }
389 
390     return String::adopt(resultVector);
391 }
392 
lengthForVisiblePositionRange(const VisiblePositionRange & visiblePositionRange) const393 int AccessibilityObject::lengthForVisiblePositionRange(const VisiblePositionRange& visiblePositionRange) const
394 {
395     // FIXME: Multi-byte support
396     if (visiblePositionRange.isNull())
397         return -1;
398 
399     int length = 0;
400     RefPtr<Range> range = makeRange(visiblePositionRange.start, visiblePositionRange.end);
401     for (TextIterator it(range.get()); !it.atEnd(); it.advance()) {
402         // non-zero length means textual node, zero length means replaced node (AKA "attachments" in AX)
403         if (it.length() != 0) {
404             length += it.length();
405         } else {
406             // locate the node and starting offset for this replaced range
407             int exception = 0;
408             Node* node = it.range()->startContainer(exception);
409             ASSERT(node == it.range()->endContainer(exception));
410             int offset = it.range()->startOffset(exception);
411 
412             if (replacedNodeNeedsCharacter(node->childNode(offset)))
413                 length++;
414         }
415     }
416 
417     return length;
418 }
419 
nextWordEnd(const VisiblePosition & visiblePos) const420 VisiblePosition AccessibilityObject::nextWordEnd(const VisiblePosition& visiblePos) const
421 {
422     if (visiblePos.isNull())
423         return VisiblePosition();
424 
425     // make sure we move off of a word end
426     VisiblePosition nextVisiblePos = visiblePos.next();
427     if (nextVisiblePos.isNull())
428         return VisiblePosition();
429 
430     return endOfWord(nextVisiblePos, LeftWordIfOnBoundary);
431 }
432 
previousWordStart(const VisiblePosition & visiblePos) const433 VisiblePosition AccessibilityObject::previousWordStart(const VisiblePosition& visiblePos) const
434 {
435     if (visiblePos.isNull())
436         return VisiblePosition();
437 
438     // make sure we move off of a word start
439     VisiblePosition prevVisiblePos = visiblePos.previous();
440     if (prevVisiblePos.isNull())
441         return VisiblePosition();
442 
443     return startOfWord(prevVisiblePos, RightWordIfOnBoundary);
444 }
445 
nextLineEndPosition(const VisiblePosition & visiblePos) const446 VisiblePosition AccessibilityObject::nextLineEndPosition(const VisiblePosition& visiblePos) const
447 {
448     if (visiblePos.isNull())
449         return VisiblePosition();
450 
451     // to make sure we move off of a line end
452     VisiblePosition nextVisiblePos = visiblePos.next();
453     if (nextVisiblePos.isNull())
454         return VisiblePosition();
455 
456     VisiblePosition endPosition = endOfLine(nextVisiblePos);
457 
458     // as long as the position hasn't reached the end of the doc,  keep searching for a valid line end position
459     // 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.
460     while (endPosition.isNull() && nextVisiblePos.isNotNull()) {
461         nextVisiblePos = nextVisiblePos.next();
462         endPosition = endOfLine(nextVisiblePos);
463     }
464 
465     return endPosition;
466 }
467 
previousLineStartPosition(const VisiblePosition & visiblePos) const468 VisiblePosition AccessibilityObject::previousLineStartPosition(const VisiblePosition& visiblePos) const
469 {
470     if (visiblePos.isNull())
471         return VisiblePosition();
472 
473     // make sure we move off of a line start
474     VisiblePosition prevVisiblePos = visiblePos.previous();
475     if (prevVisiblePos.isNull())
476         return VisiblePosition();
477 
478     VisiblePosition startPosition = startOfLine(prevVisiblePos);
479 
480     // as long as the position hasn't reached the beginning of the doc,  keep searching for a valid line start position
481     // 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.
482     if (startPosition.isNull()) {
483         while (startPosition.isNull() && prevVisiblePos.isNotNull()) {
484             prevVisiblePos = prevVisiblePos.previous();
485             startPosition = startOfLine(prevVisiblePos);
486         }
487     } else
488         startPosition = updateAXLineStartForVisiblePosition(startPosition);
489 
490     return startPosition;
491 }
492 
nextSentenceEndPosition(const VisiblePosition & visiblePos) const493 VisiblePosition AccessibilityObject::nextSentenceEndPosition(const VisiblePosition& visiblePos) const
494 {
495     // FIXME: FO 2 IMPLEMENT (currently returns incorrect answer)
496     // Related? <rdar://problem/3927736> Text selection broken in 8A336
497     if (visiblePos.isNull())
498         return VisiblePosition();
499 
500     // make sure we move off of a sentence end
501     VisiblePosition nextVisiblePos = visiblePos.next();
502     if (nextVisiblePos.isNull())
503         return VisiblePosition();
504 
505     // an empty line is considered a sentence. If it's skipped, then the sentence parser will not
506     // see this empty line.  Instead, return the end position of the empty line.
507     VisiblePosition endPosition;
508 
509     String lineString = plainText(makeRange(startOfLine(nextVisiblePos), endOfLine(nextVisiblePos)).get());
510     if (lineString.isEmpty())
511         endPosition = nextVisiblePos;
512     else
513         endPosition = endOfSentence(nextVisiblePos);
514 
515     return endPosition;
516 }
517 
previousSentenceStartPosition(const VisiblePosition & visiblePos) const518 VisiblePosition AccessibilityObject::previousSentenceStartPosition(const VisiblePosition& visiblePos) const
519 {
520     // FIXME: FO 2 IMPLEMENT (currently returns incorrect answer)
521     // Related? <rdar://problem/3927736> Text selection broken in 8A336
522     if (visiblePos.isNull())
523         return VisiblePosition();
524 
525     // make sure we move off of a sentence start
526     VisiblePosition previousVisiblePos = visiblePos.previous();
527     if (previousVisiblePos.isNull())
528         return VisiblePosition();
529 
530     // treat empty line as a separate sentence.
531     VisiblePosition startPosition;
532 
533     String lineString = plainText(makeRange(startOfLine(previousVisiblePos), endOfLine(previousVisiblePos)).get());
534     if (lineString.isEmpty())
535         startPosition = previousVisiblePos;
536     else
537         startPosition = startOfSentence(previousVisiblePos);
538 
539     return startPosition;
540 }
541 
nextParagraphEndPosition(const VisiblePosition & visiblePos) const542 VisiblePosition AccessibilityObject::nextParagraphEndPosition(const VisiblePosition& visiblePos) const
543 {
544     if (visiblePos.isNull())
545         return VisiblePosition();
546 
547     // make sure we move off of a paragraph end
548     VisiblePosition nextPos = visiblePos.next();
549     if (nextPos.isNull())
550         return VisiblePosition();
551 
552     return endOfParagraph(nextPos);
553 }
554 
previousParagraphStartPosition(const VisiblePosition & visiblePos) const555 VisiblePosition AccessibilityObject::previousParagraphStartPosition(const VisiblePosition& visiblePos) const
556 {
557     if (visiblePos.isNull())
558         return VisiblePosition();
559 
560     // make sure we move off of a paragraph start
561     VisiblePosition previousPos = visiblePos.previous();
562     if (previousPos.isNull())
563         return VisiblePosition();
564 
565     return startOfParagraph(previousPos);
566 }
567 
accessibilityObjectForPosition(const VisiblePosition & visiblePos) const568 AccessibilityObject* AccessibilityObject::accessibilityObjectForPosition(const VisiblePosition& visiblePos) const
569 {
570     if (visiblePos.isNull())
571         return 0;
572 
573     RenderObject* obj = visiblePos.deepEquivalent().node()->renderer();
574     if (!obj)
575         return 0;
576 
577     return obj->document()->axObjectCache()->getOrCreate(obj);
578 }
579 
lineForPosition(const VisiblePosition & visiblePos) const580 int AccessibilityObject::lineForPosition(const VisiblePosition& visiblePos) const
581 {
582     if (visiblePos.isNull())
583         return 0;
584 
585     unsigned lineCount = 0;
586     VisiblePosition currentVisiblePos = visiblePos;
587     VisiblePosition savedVisiblePos;
588 
589     // move up until we get to the top
590     // FIXME: This only takes us to the top of the rootEditableElement, not the top of the
591     // top document.
592     while (currentVisiblePos.isNotNull() && !(inSameLine(currentVisiblePos, savedVisiblePos))) {
593         ++lineCount;
594         savedVisiblePos = currentVisiblePos;
595         VisiblePosition prevVisiblePos = previousLinePosition(currentVisiblePos, 0);
596         currentVisiblePos = prevVisiblePos;
597     }
598 
599     return lineCount - 1;
600 }
601 
602 // NOTE: Consider providing this utility method as AX API
plainTextRangeForVisiblePositionRange(const VisiblePositionRange & positionRange) const603 PlainTextRange AccessibilityObject::plainTextRangeForVisiblePositionRange(const VisiblePositionRange& positionRange) const
604 {
605     int index1 = index(positionRange.start);
606     int index2 = index(positionRange.end);
607     if (index1 < 0 || index2 < 0 || index1 > index2)
608         return PlainTextRange();
609 
610     return PlainTextRange(index1, index2 - index1);
611 }
612 
613 // The composed character range in the text associated with this accessibility object that
614 // is specified by the given screen coordinates. This parameterized attribute returns the
615 // complete range of characters (including surrogate pairs of multi-byte glyphs) at the given
616 // screen coordinates.
617 // NOTE: This varies from AppKit when the point is below the last line. AppKit returns an
618 // an error in that case. We return textControl->text().length(), 1. Does this matter?
doAXRangeForPosition(const IntPoint & point) const619 PlainTextRange AccessibilityObject::doAXRangeForPosition(const IntPoint& point) const
620 {
621     int i = index(visiblePositionForPoint(point));
622     if (i < 0)
623         return PlainTextRange();
624 
625     return PlainTextRange(i, 1);
626 }
627 
628 // Given a character index, the range of text associated with this accessibility object
629 // over which the style in effect at that character index applies.
doAXStyleRangeForIndex(unsigned index) const630 PlainTextRange AccessibilityObject::doAXStyleRangeForIndex(unsigned index) const
631 {
632     VisiblePositionRange range = styleRangeForPosition(visiblePositionForIndex(index, false));
633     return plainTextRangeForVisiblePositionRange(range);
634 }
635 
636 // Given an indexed character, the line number of the text associated with this accessibility
637 // object that contains the character.
doAXLineForIndex(unsigned index)638 unsigned AccessibilityObject::doAXLineForIndex(unsigned index)
639 {
640     return lineForPosition(visiblePositionForIndex(index, false));
641 }
642 
documentFrameView() const643 FrameView* AccessibilityObject::documentFrameView() const
644 {
645     const AccessibilityObject* object = this;
646     while (object && !object->isAccessibilityRenderObject())
647         object = object->parentObject();
648 
649     if (!object)
650         return 0;
651 
652     return object->documentFrameView();
653 }
654 
clearChildren()655 void AccessibilityObject::clearChildren()
656 {
657     m_haveChildren = false;
658     m_children.clear();
659 }
660 
anchorElementForNode(Node * node)661 AccessibilityObject* AccessibilityObject::anchorElementForNode(Node* node)
662 {
663     RenderObject* obj = node->renderer();
664     if (!obj)
665         return 0;
666 
667     RefPtr<AccessibilityObject> axObj = obj->document()->axObjectCache()->getOrCreate(obj);
668     Element* anchor = axObj->anchorElement();
669     if (!anchor)
670         return 0;
671 
672     RenderObject* anchorRenderer = anchor->renderer();
673     if (!anchorRenderer)
674         return 0;
675 
676     return anchorRenderer->document()->axObjectCache()->getOrCreate(anchorRenderer);
677 }
678 
actionVerb() const679 const String& AccessibilityObject::actionVerb() const
680 {
681     // FIXME: Need to add verbs for select elements.
682     DEFINE_STATIC_LOCAL(const String, buttonAction, (AXButtonActionVerb()));
683     DEFINE_STATIC_LOCAL(const String, textFieldAction, (AXTextFieldActionVerb()));
684     DEFINE_STATIC_LOCAL(const String, radioButtonAction, (AXRadioButtonActionVerb()));
685     DEFINE_STATIC_LOCAL(const String, checkedCheckBoxAction, (AXCheckedCheckBoxActionVerb()));
686     DEFINE_STATIC_LOCAL(const String, uncheckedCheckBoxAction, (AXUncheckedCheckBoxActionVerb()));
687     DEFINE_STATIC_LOCAL(const String, linkAction, (AXLinkActionVerb()));
688     DEFINE_STATIC_LOCAL(const String, noAction, ());
689 
690     switch (roleValue()) {
691         case ButtonRole:
692             return buttonAction;
693         case TextFieldRole:
694         case TextAreaRole:
695             return textFieldAction;
696         case RadioButtonRole:
697             return radioButtonAction;
698         case CheckBoxRole:
699             return isChecked() ? checkedCheckBoxAction : uncheckedCheckBoxAction;
700         case LinkRole:
701         case WebCoreLinkRole:
702             return linkAction;
703         default:
704             return noAction;
705     }
706 }
707 
708 // Lacking concrete evidence of orientation, horizontal means width > height. vertical is height > width;
orientation() const709 AccessibilityOrientation AccessibilityObject::orientation() const
710 {
711     IntRect bounds = elementRect();
712     if (bounds.size().width() > bounds.size().height())
713         return AccessibilityOrientationHorizontal;
714     if (bounds.size().height() > bounds.size().width())
715         return AccessibilityOrientationVertical;
716 
717     // A tie goes to horizontal.
718     return AccessibilityOrientationHorizontal;
719 }
720 
721 } // namespace WebCore
722