Bug 24143: Crash occurs at WebCore::AccessibilityTable::isTableExposableThroughAccess...
[WebKit-https.git] / WebCore / page / AccessibilityObject.cpp
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
62 AccessibilityObject::AccessibilityObject()
63     : m_id(0)
64     , m_haveChildren(false)
65 #if PLATFORM(GTK)
66     , m_wrapper(0)
67 #endif
68 {
69 }
70
71 AccessibilityObject::~AccessibilityObject()
72 {
73     ASSERT(isDetached());
74 }
75
76 void AccessibilityObject::detach()
77 {
78 #if HAVE(ACCESSIBILITY)
79     setWrapper(0);
80 #endif    
81 }
82
83 AccessibilityObject* AccessibilityObject::firstChild() const
84 {
85     return 0;
86 }
87
88 AccessibilityObject* AccessibilityObject::lastChild() const
89 {
90     return 0;
91 }
92
93 AccessibilityObject* AccessibilityObject::previousSibling() const
94 {
95     return 0;
96 }
97
98 AccessibilityObject* AccessibilityObject::nextSibling() const
99 {
100     return 0;
101 }
102
103 AccessibilityObject* AccessibilityObject::parentObject() const
104 {
105     return 0;
106 }
107
108 AccessibilityObject* AccessibilityObject::parentObjectUnignored() const
109 {
110     AccessibilityObject* parent;
111     for (parent = parentObject(); parent && parent->accessibilityIsIgnored(); parent = parent->parentObject())
112         ;
113     return parent;
114 }
115
116 AccessibilityObject* AccessibilityObject::parentObjectIfExists() const
117 {
118     return 0;
119 }
120     
121 int AccessibilityObject::layoutCount() const
122 {
123     return 0;
124 }
125     
126 String AccessibilityObject::text() const
127 {
128     return String();
129 }
130     
131 String AccessibilityObject::helpText() const
132 {
133     return String();   
134 }
135
136 String AccessibilityObject::textUnderElement() const
137 {
138     return String();
139 }
140
141 bool AccessibilityObject::isARIAInput(AccessibilityRole ariaRole)
142 {
143     return ariaRole == RadioButtonRole || ariaRole == CheckBoxRole || ariaRole == TextFieldRole;
144 }    
145     
146 bool AccessibilityObject::isARIAControl(AccessibilityRole ariaRole)
147 {
148     return isARIAInput(ariaRole) || ariaRole == TextAreaRole || ariaRole == ButtonRole 
149     || ariaRole == ComboBoxRole || ariaRole == SliderRole; 
150 }
151     
152 int AccessibilityObject::intValue() const
153 {
154     return 0;
155 }
156
157 String AccessibilityObject::stringValue() const
158 {
159     return String();
160 }
161
162 String AccessibilityObject::ariaAccessiblityName(const String&) const
163 {
164     return String();
165 }
166
167 String AccessibilityObject::ariaLabeledByAttribute() const
168 {
169     return String();
170 }
171
172 String AccessibilityObject::title() const
173 {
174     return String();
175 }
176
177 String AccessibilityObject::ariaDescribedByAttribute() const
178 {
179     return String();
180 }
181
182 String AccessibilityObject::accessibilityDescription() const
183 {
184     return String();
185 }
186
187 IntRect AccessibilityObject::boundingBoxRect() const
188 {
189     return IntRect();
190 }
191
192 IntRect AccessibilityObject::elementRect() const
193 {
194     return IntRect();
195 }
196
197 IntSize AccessibilityObject::size() const
198 {
199     return IntSize();
200 }
201
202 IntPoint AccessibilityObject::clickPoint() const
203 {
204     IntRect rect = elementRect();
205     return IntPoint(rect.x() + rect.width() / 2, rect.y() + rect.height() / 2);
206 }
207     
208 void AccessibilityObject::linkedUIElements(AccessibilityChildrenVector&) const
209 {
210     return;
211 }
212     
213 AccessibilityObject* AccessibilityObject::titleUIElement() const
214 {
215      return 0;   
216 }
217
218 int AccessibilityObject::textLength() const
219 {
220     return 0;
221 }
222
223 PassRefPtr<Range> AccessibilityObject::ariaSelectedTextDOMRange() const
224 {
225     return 0;
226 }
227
228 String AccessibilityObject::selectedText() const
229 {
230     return String();
231 }
232
233 const AtomicString& AccessibilityObject::accessKey() const
234 {
235     return nullAtom;
236 }
237
238 VisibleSelection AccessibilityObject::selection() const
239 {
240     return VisibleSelection();
241 }
242
243 PlainTextRange AccessibilityObject::selectedTextRange() const
244 {
245     return PlainTextRange();
246 }
247
248 unsigned AccessibilityObject::selectionStart() const
249 {
250     return selectedTextRange().start;
251 }
252
253 unsigned AccessibilityObject::selectionEnd() const
254 {
255     return selectedTextRange().length;
256 }
257
258 void AccessibilityObject::setSelectedText(const String&)
259 {
260     // TODO: set selected text (ReplaceSelectionCommand). <rdar://problem/4712125>
261     notImplemented();
262 }
263
264 void AccessibilityObject::setSelectedTextRange(const PlainTextRange&)
265 {
266 }
267
268 void AccessibilityObject::makeRangeVisible(const PlainTextRange&)
269 {
270     // TODO: make range visible (scrollRectToVisible).  <rdar://problem/4712101>
271     notImplemented();
272 }
273
274 KURL AccessibilityObject::url() const
275 {
276     return KURL();
277 }
278
279 void AccessibilityObject::setFocused(bool)
280 {
281 }
282
283 void AccessibilityObject::setValue(const String&)
284 {
285 }
286
287 void AccessibilityObject::setSelected(bool)
288 {
289 }
290
291 bool AccessibilityObject::press() const
292 {
293     Element* actionElem = actionElement();
294     if (!actionElem)
295         return false;
296     if (Frame* f = actionElem->document()->frame())
297         f->loader()->resetMultipleFormSubmissionProtection();
298     actionElem->accessKeyAction(true);
299     return true;
300 }
301
302 AXObjectCache* AccessibilityObject::axObjectCache() const
303 {
304     return 0;
305 }
306
307 Widget* AccessibilityObject::widget() const
308 {
309     return 0;
310 }
311
312 Widget* AccessibilityObject::widgetForAttachmentView() const
313 {
314     return 0;
315 }
316
317 Element* AccessibilityObject::anchorElement() const
318 {
319     return 0;   
320 }
321
322 Element* AccessibilityObject::actionElement() const
323 {
324     return 0;
325 }
326
327 // This function is like a cross-platform version of - (WebCoreTextMarkerRange*)textMarkerRange. It returns
328 // a Range that we can convert to a WebCoreRange in the Obj-C file
329 VisiblePositionRange AccessibilityObject::visiblePositionRange() const
330 {
331     return VisiblePositionRange();
332 }
333
334 VisiblePositionRange AccessibilityObject::visiblePositionRangeForLine(unsigned) const
335 {
336     return VisiblePositionRange();
337 }
338
339 VisiblePosition AccessibilityObject::visiblePositionForIndex(int) const
340 {
341     return VisiblePosition();
342 }
343     
344 int AccessibilityObject::indexForVisiblePosition(const VisiblePosition&) const
345 {
346     return 0;
347 }
348     
349 VisiblePositionRange AccessibilityObject::visiblePositionRangeForUnorderedPositions(const VisiblePosition& visiblePos1, const VisiblePosition& visiblePos2) const
350 {
351     if (visiblePos1.isNull() || visiblePos2.isNull())
352         return VisiblePositionRange();
353
354     VisiblePosition startPos;
355     VisiblePosition endPos;
356     bool alreadyInOrder;
357
358     // upstream is ordered before downstream for the same position
359     if (visiblePos1 == visiblePos2 && visiblePos2.affinity() == UPSTREAM)
360         alreadyInOrder = false;
361
362     // use selection order to see if the positions are in order
363     else
364         alreadyInOrder = VisibleSelection(visiblePos1, visiblePos2).isBaseFirst();
365
366     if (alreadyInOrder) {
367         startPos = visiblePos1;
368         endPos = visiblePos2;
369     } else {
370         startPos = visiblePos2;
371         endPos = visiblePos1;
372     }
373
374     return VisiblePositionRange(startPos, endPos);
375 }
376
377 VisiblePositionRange AccessibilityObject::positionOfLeftWord(const VisiblePosition& visiblePos) const
378 {
379     VisiblePosition startPosition = startOfWord(visiblePos, LeftWordIfOnBoundary);
380     VisiblePosition endPosition = endOfWord(startPosition);
381     return VisiblePositionRange(startPosition, endPosition);
382 }
383
384 VisiblePositionRange AccessibilityObject::positionOfRightWord(const VisiblePosition& visiblePos) const
385 {
386     VisiblePosition startPosition = startOfWord(visiblePos, RightWordIfOnBoundary);
387     VisiblePosition endPosition = endOfWord(startPosition);
388     return VisiblePositionRange(startPosition, endPosition);
389 }
390
391 static VisiblePosition updateAXLineStartForVisiblePosition(const VisiblePosition& visiblePosition)
392 {
393     // A line in the accessibility sense should include floating objects, such as aligned image, as part of a line.
394     // So let's update the position to include that.
395     VisiblePosition tempPosition;
396     VisiblePosition startPosition = visiblePosition;
397     Position p;
398     RenderObject* renderer;
399     while (true) {
400         tempPosition = startPosition.previous();
401         if (tempPosition.isNull())
402             break;
403         p = tempPosition.deepEquivalent();
404         if (!p.node())
405             break;
406         renderer = p.node()->renderer();
407         if (!renderer || renderer->isRenderBlock() && !p.offset())
408             break;
409         InlineBox* box;
410         int ignoredCaretOffset;
411         p.getInlineBoxAndOffset(tempPosition.affinity(), box, ignoredCaretOffset);
412         if (box)
413             break;
414         startPosition = tempPosition;
415     }
416
417     return startPosition;
418 }
419
420 VisiblePositionRange AccessibilityObject::leftLineVisiblePositionRange(const VisiblePosition& visiblePos) const
421 {
422     if (visiblePos.isNull())
423         return VisiblePositionRange();
424
425     // make a caret selection for the position before marker position (to make sure
426     // we move off of a line start)
427     VisiblePosition prevVisiblePos = visiblePos.previous();
428     if (prevVisiblePos.isNull())
429         return VisiblePositionRange();
430
431     VisiblePosition startPosition = startOfLine(prevVisiblePos);
432
433     // keep searching for a valid line start position.  Unless the VisiblePosition is at the very beginning, there should
434     // always be a valid line range.  However, startOfLine will return null for position next to a floating object,
435     // since floating object doesn't really belong to any line.
436     // This check will reposition the marker before the floating object, to ensure we get a line start.
437     if (startPosition.isNull()) {
438         while (startPosition.isNull() && prevVisiblePos.isNotNull()) {
439             prevVisiblePos = prevVisiblePos.previous();
440             startPosition = startOfLine(prevVisiblePos);
441         }
442     } else
443         startPosition = updateAXLineStartForVisiblePosition(startPosition);
444
445     VisiblePosition endPosition = endOfLine(prevVisiblePos);
446     return VisiblePositionRange(startPosition, endPosition);
447 }
448
449 VisiblePositionRange AccessibilityObject::rightLineVisiblePositionRange(const VisiblePosition& visiblePos) const
450 {
451     if (visiblePos.isNull())
452         return VisiblePositionRange();
453
454     // make sure we move off of a line end
455     VisiblePosition nextVisiblePos = visiblePos.next();
456     if (nextVisiblePos.isNull())
457         return VisiblePositionRange();
458
459     VisiblePosition startPosition = startOfLine(nextVisiblePos);
460
461     // fetch for a valid line start position
462     if (startPosition.isNull() ) {
463         startPosition = visiblePos;
464         nextVisiblePos = nextVisiblePos.next();
465     } else
466         startPosition = updateAXLineStartForVisiblePosition(startPosition);
467
468     VisiblePosition endPosition = endOfLine(nextVisiblePos);
469
470     // as long as the position hasn't reached the end of the doc,  keep searching for a valid line end position
471     // Unless the VisiblePosition is at the very end, there should always be a valid line range.  However, endOfLine will
472     // return null for position by a floating object, since floating object doesn't really belong to any line.
473     // This check will reposition the marker after the floating object, to ensure we get a line end.
474     while (endPosition.isNull() && nextVisiblePos.isNotNull()) {
475         nextVisiblePos = nextVisiblePos.next();
476         endPosition = endOfLine(nextVisiblePos);
477     }
478
479     return VisiblePositionRange(startPosition, endPosition);
480 }
481
482 VisiblePositionRange AccessibilityObject::sentenceForPosition(const VisiblePosition& visiblePos) const
483 {
484     // FIXME: FO 2 IMPLEMENT (currently returns incorrect answer)
485     // Related? <rdar://problem/3927736> Text selection broken in 8A336
486     VisiblePosition startPosition = startOfSentence(visiblePos);
487     VisiblePosition endPosition = endOfSentence(startPosition);
488     return VisiblePositionRange(startPosition, endPosition);
489 }
490
491 VisiblePositionRange AccessibilityObject::paragraphForPosition(const VisiblePosition& visiblePos) const
492 {
493     VisiblePosition startPosition = startOfParagraph(visiblePos);
494     VisiblePosition endPosition = endOfParagraph(startPosition);
495     return VisiblePositionRange(startPosition, endPosition);
496 }
497
498 static VisiblePosition startOfStyleRange(const VisiblePosition visiblePos)
499 {
500     RenderObject* renderer = visiblePos.deepEquivalent().node()->renderer();
501     RenderObject* startRenderer = renderer;
502     RenderStyle* style = renderer->style();
503
504     // traverse backward by renderer to look for style change
505     for (RenderObject* r = renderer->previousInPreOrder(); r; r = r->previousInPreOrder()) {
506         // skip non-leaf nodes
507         if (r->firstChild())
508             continue;
509
510         // stop at style change
511         if (r->style() != style)
512             break;
513
514         // remember match
515         startRenderer = r;
516     }
517
518     return VisiblePosition(startRenderer->node(), 0, VP_DEFAULT_AFFINITY);
519 }
520
521 static VisiblePosition endOfStyleRange(const VisiblePosition visiblePos)
522 {
523     RenderObject* renderer = visiblePos.deepEquivalent().node()->renderer();
524     RenderObject* endRenderer = renderer;
525     RenderStyle* style = renderer->style();
526
527     // traverse forward by renderer to look for style change
528     for (RenderObject* r = renderer->nextInPreOrder(); r; r = r->nextInPreOrder()) {
529         // skip non-leaf nodes
530         if (r->firstChild())
531             continue;
532
533         // stop at style change
534         if (r->style() != style)
535             break;
536
537         // remember match
538         endRenderer = r;
539     }
540
541     return VisiblePosition(endRenderer->node(), maxDeepOffset(endRenderer->node()), VP_DEFAULT_AFFINITY);
542 }
543
544 VisiblePositionRange AccessibilityObject::styleRangeForPosition(const VisiblePosition& visiblePos) const
545 {
546     if (visiblePos.isNull())
547         return VisiblePositionRange();
548
549     return VisiblePositionRange(startOfStyleRange(visiblePos), endOfStyleRange(visiblePos));
550 }
551
552 // NOTE: Consider providing this utility method as AX API
553 VisiblePositionRange AccessibilityObject::visiblePositionRangeForRange(const PlainTextRange& range) const
554 {
555     if (range.start + range.length > text().length())
556         return VisiblePositionRange();
557
558     VisiblePosition startPosition = visiblePositionForIndex(range.start);
559     startPosition.setAffinity(DOWNSTREAM);
560     VisiblePosition endPosition = visiblePositionForIndex(range.start + range.length);
561     return VisiblePositionRange(startPosition, endPosition);
562 }
563
564 static bool replacedNodeNeedsCharacter(Node* replacedNode)
565 {
566     // we should always be given a rendered node and a replaced node, but be safe
567     // replaced nodes are either attachments (widgets) or images
568     if (!replacedNode || !replacedNode->renderer() || !replacedNode->renderer()->isReplaced() || replacedNode->isTextNode()) {
569         return false;
570     }
571
572     // create an AX object, but skip it if it is not supposed to be seen
573     AccessibilityObject* object = replacedNode->renderer()->document()->axObjectCache()->getOrCreate(replacedNode->renderer());
574     if (object->accessibilityIsIgnored())
575         return false;
576
577     return true;
578 }
579
580 String AccessibilityObject::stringForVisiblePositionRange(const VisiblePositionRange& visiblePositionRange) const
581 {
582     if (visiblePositionRange.isNull())
583         return String();
584
585     Vector<UChar> resultVector;
586     RefPtr<Range> range = makeRange(visiblePositionRange.start, visiblePositionRange.end);
587     for (TextIterator it(range.get()); !it.atEnd(); it.advance()) {
588         // non-zero length means textual node, zero length means replaced node (AKA "attachments" in AX)
589         if (it.length() != 0) {
590             resultVector.append(it.characters(), it.length());
591         } else {
592             // locate the node and starting offset for this replaced range
593             int exception = 0;
594             Node* node = it.range()->startContainer(exception);
595             ASSERT(node == it.range()->endContainer(exception));
596             int offset = it.range()->startOffset(exception);
597
598             if (replacedNodeNeedsCharacter(node->childNode(offset))) {
599                 resultVector.append(objectReplacementCharacter);
600             }
601         }
602     }
603
604     return String::adopt(resultVector);
605 }
606
607 IntRect AccessibilityObject::boundsForVisiblePositionRange(const VisiblePositionRange&) const
608 {
609     return IntRect();
610 }
611
612 int AccessibilityObject::lengthForVisiblePositionRange(const VisiblePositionRange& visiblePositionRange) const
613 {
614     // FIXME: Multi-byte support
615     if (visiblePositionRange.isNull())
616         return -1;
617     
618     int length = 0;
619     RefPtr<Range> range = makeRange(visiblePositionRange.start, visiblePositionRange.end);
620     for (TextIterator it(range.get()); !it.atEnd(); it.advance()) {
621         // non-zero length means textual node, zero length means replaced node (AKA "attachments" in AX)
622         if (it.length() != 0) {
623             length += it.length();
624         } else {
625             // locate the node and starting offset for this replaced range
626             int exception = 0;
627             Node* node = it.range()->startContainer(exception);
628             ASSERT(node == it.range()->endContainer(exception));
629             int offset = it.range()->startOffset(exception);
630
631             if (replacedNodeNeedsCharacter(node->childNode(offset)))
632                 length++;
633         }
634     }
635     
636     return length;
637 }
638
639 void AccessibilityObject::setSelectedVisiblePositionRange(const VisiblePositionRange&) const
640 {
641 }
642
643 VisiblePosition AccessibilityObject::visiblePositionForPoint(const IntPoint&) const
644 {
645     return VisiblePosition();
646 }
647
648 VisiblePosition AccessibilityObject::nextVisiblePosition(const VisiblePosition& visiblePos) const
649 {
650     return visiblePos.next();
651 }
652
653 VisiblePosition AccessibilityObject::previousVisiblePosition(const VisiblePosition& visiblePos) const
654 {
655     return visiblePos.previous();
656 }
657
658 VisiblePosition AccessibilityObject::nextWordEnd(const VisiblePosition& visiblePos) const
659 {
660     if (visiblePos.isNull())
661         return VisiblePosition();
662
663     // make sure we move off of a word end
664     VisiblePosition nextVisiblePos = visiblePos.next();
665     if (nextVisiblePos.isNull())
666         return VisiblePosition();
667
668     return endOfWord(nextVisiblePos, LeftWordIfOnBoundary);
669 }
670
671 VisiblePosition AccessibilityObject::previousWordStart(const VisiblePosition& visiblePos) const
672 {
673     if (visiblePos.isNull())
674         return VisiblePosition();
675
676     // make sure we move off of a word start
677     VisiblePosition prevVisiblePos = visiblePos.previous();
678     if (prevVisiblePos.isNull())
679         return VisiblePosition();
680
681     return startOfWord(prevVisiblePos, RightWordIfOnBoundary);
682 }
683
684 VisiblePosition AccessibilityObject::nextLineEndPosition(const VisiblePosition& visiblePos) const
685 {
686     if (visiblePos.isNull())
687         return VisiblePosition();
688
689     // to make sure we move off of a line end
690     VisiblePosition nextVisiblePos = visiblePos.next();
691     if (nextVisiblePos.isNull())
692         return VisiblePosition();
693
694     VisiblePosition endPosition = endOfLine(nextVisiblePos);
695
696     // as long as the position hasn't reached the end of the doc,  keep searching for a valid line end position
697     // 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.
698     while (endPosition.isNull() && nextVisiblePos.isNotNull()) {
699         nextVisiblePos = nextVisiblePos.next();
700         endPosition = endOfLine(nextVisiblePos);
701     }
702
703     return endPosition;
704 }
705
706 VisiblePosition AccessibilityObject::previousLineStartPosition(const VisiblePosition& visiblePos) const
707 {
708     if (visiblePos.isNull())
709         return VisiblePosition();
710
711     // make sure we move off of a line start
712     VisiblePosition prevVisiblePos = visiblePos.previous();
713     if (prevVisiblePos.isNull())
714         return VisiblePosition();
715
716     VisiblePosition startPosition = startOfLine(prevVisiblePos);
717
718     // as long as the position hasn't reached the beginning of the doc,  keep searching for a valid line start position
719     // 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.
720     if (startPosition.isNull()) {
721         while (startPosition.isNull() && prevVisiblePos.isNotNull()) {
722             prevVisiblePos = prevVisiblePos.previous();
723             startPosition = startOfLine(prevVisiblePos);
724         }
725     } else
726         startPosition = updateAXLineStartForVisiblePosition(startPosition);
727
728     return startPosition;
729 }
730
731 VisiblePosition AccessibilityObject::nextSentenceEndPosition(const VisiblePosition& visiblePos) const
732 {
733     // FIXME: FO 2 IMPLEMENT (currently returns incorrect answer)
734     // Related? <rdar://problem/3927736> Text selection broken in 8A336
735     if (visiblePos.isNull())
736         return VisiblePosition();
737
738     // make sure we move off of a sentence end
739     VisiblePosition nextVisiblePos = visiblePos.next();
740     if (nextVisiblePos.isNull())
741         return VisiblePosition();
742
743     // an empty line is considered a sentence. If it's skipped, then the sentence parser will not
744     // see this empty line.  Instead, return the end position of the empty line.
745     VisiblePosition endPosition;
746     
747     String lineString = plainText(makeRange(startOfLine(nextVisiblePos), endOfLine(nextVisiblePos)).get());
748     if (lineString.isEmpty())
749         endPosition = nextVisiblePos;
750     else
751         endPosition = endOfSentence(nextVisiblePos);
752
753     return endPosition;
754 }
755
756 VisiblePosition AccessibilityObject::previousSentenceStartPosition(const VisiblePosition& visiblePos) const
757 {
758     // FIXME: FO 2 IMPLEMENT (currently returns incorrect answer)
759     // Related? <rdar://problem/3927736> Text selection broken in 8A336
760     if (visiblePos.isNull())
761         return VisiblePosition();
762
763     // make sure we move off of a sentence start
764     VisiblePosition previousVisiblePos = visiblePos.previous();
765     if (previousVisiblePos.isNull())
766         return VisiblePosition();
767
768     // treat empty line as a separate sentence.
769     VisiblePosition startPosition;
770     
771     String lineString = plainText(makeRange(startOfLine(previousVisiblePos), endOfLine(previousVisiblePos)).get());
772     if (lineString.isEmpty())
773         startPosition = previousVisiblePos;
774     else
775         startPosition = startOfSentence(previousVisiblePos);
776
777     return startPosition;
778 }
779
780 VisiblePosition AccessibilityObject::nextParagraphEndPosition(const VisiblePosition& visiblePos) const
781 {
782     if (visiblePos.isNull())
783         return VisiblePosition();
784
785     // make sure we move off of a paragraph end
786     VisiblePosition nextPos = visiblePos.next();
787     if (nextPos.isNull())
788         return VisiblePosition();
789
790     return endOfParagraph(nextPos);
791 }
792
793 VisiblePosition AccessibilityObject::previousParagraphStartPosition(const VisiblePosition& visiblePos) const
794 {
795     if (visiblePos.isNull())
796         return VisiblePosition();
797
798     // make sure we move off of a paragraph start
799     VisiblePosition previousPos = visiblePos.previous();
800     if (previousPos.isNull())
801         return VisiblePosition();
802
803     return startOfParagraph(previousPos);
804 }
805
806 // NOTE: Consider providing this utility method as AX API
807 VisiblePosition AccessibilityObject::visiblePositionForIndex(unsigned, bool) const
808 {
809     return VisiblePosition();
810 }
811
812 AccessibilityObject* AccessibilityObject::accessibilityObjectForPosition(const VisiblePosition& visiblePos) const
813 {
814     if (visiblePos.isNull())
815         return 0;
816
817     RenderObject* obj = visiblePos.deepEquivalent().node()->renderer();
818     if (!obj)
819         return 0;
820
821     return obj->document()->axObjectCache()->getOrCreate(obj);
822 }
823
824 int AccessibilityObject::lineForPosition(const VisiblePosition& visiblePos) const
825 {
826     if (visiblePos.isNull())
827         return 0;
828
829     unsigned lineCount = 0;
830     VisiblePosition currentVisiblePos = visiblePos;
831     VisiblePosition savedVisiblePos;
832
833     // move up until we get to the top
834     // FIXME: This only takes us to the top of the rootEditableElement, not the top of the
835     // top document.
836     while (currentVisiblePos.isNotNull() && !(inSameLine(currentVisiblePos, savedVisiblePos))) {
837         ++lineCount;
838         savedVisiblePos = currentVisiblePos;
839         VisiblePosition prevVisiblePos = previousLinePosition(currentVisiblePos, 0);
840         currentVisiblePos = prevVisiblePos;
841     }
842
843     return lineCount - 1;
844 }
845
846 // NOTE: Consider providing this utility method as AX API
847 PlainTextRange AccessibilityObject::plainTextRangeForVisiblePositionRange(const VisiblePositionRange& positionRange) const
848 {
849     int index1 = index(positionRange.start);
850     int index2 = index(positionRange.end);
851     if (index1 < 0 || index2 < 0 || index1 > index2)
852         return PlainTextRange();
853
854     return PlainTextRange(index1, index2 - index1);
855 }
856
857 // NOTE: Consider providing this utility method as AX API
858 int AccessibilityObject::index(const VisiblePosition&) const
859 {
860     return -1;
861 }
862
863 // Given a line number, the range of characters of the text associated with this accessibility
864 // object that contains the line number.
865 PlainTextRange AccessibilityObject::doAXRangeForLine(unsigned) const
866 {
867     return PlainTextRange();
868 }
869
870 // The composed character range in the text associated with this accessibility object that
871 // is specified by the given screen coordinates. This parameterized attribute returns the
872 // complete range of characters (including surrogate pairs of multi-byte glyphs) at the given
873 // screen coordinates.
874 // NOTE: This varies from AppKit when the point is below the last line. AppKit returns an
875 // an error in that case. We return textControl->text().length(), 1. Does this matter?
876 PlainTextRange AccessibilityObject::doAXRangeForPosition(const IntPoint& point) const
877 {
878     int i = index(visiblePositionForPoint(point));
879     if (i < 0)
880         return PlainTextRange();
881
882     return PlainTextRange(i, 1);
883 }
884
885 // The composed character range in the text associated with this accessibility object that
886 // is specified by the given index value. This parameterized attribute returns the complete
887 // range of characters (including surrogate pairs of multi-byte glyphs) at the given index.
888 PlainTextRange AccessibilityObject::doAXRangeForIndex(unsigned) const
889 {
890     return PlainTextRange();
891 }
892
893 // Given a character index, the range of text associated with this accessibility object
894 // over which the style in effect at that character index applies.
895 PlainTextRange AccessibilityObject::doAXStyleRangeForIndex(unsigned index) const
896 {
897     VisiblePositionRange range = styleRangeForPosition(visiblePositionForIndex(index, false));
898     return plainTextRangeForVisiblePositionRange(range);
899 }
900
901 // A substring of the text associated with this accessibility object that is
902 // specified by the given character range.
903 String AccessibilityObject::doAXStringForRange(const PlainTextRange&) const
904 {
905     return String();
906 }
907
908 // The bounding rectangle of the text associated with this accessibility object that is
909 // specified by the given range. This is the bounding rectangle a sighted user would see
910 // on the display screen, in pixels.
911 IntRect AccessibilityObject::doAXBoundsForRange(const PlainTextRange&) const
912 {
913     return IntRect();
914 }
915
916 // Given an indexed character, the line number of the text associated with this accessibility
917 // object that contains the character.
918 unsigned AccessibilityObject::doAXLineForIndex(unsigned index)
919 {
920     return lineForPosition(visiblePositionForIndex(index, false));
921 }
922
923 FrameView* AccessibilityObject::documentFrameView() const 
924
925     const AccessibilityObject* object = this;
926     while (object && !object->isAccessibilityRenderObject()) 
927         object = object->parentObject();
928         
929     if (!object)
930         return 0;
931
932     return object->documentFrameView();
933 }    
934
935 AccessibilityObject* AccessibilityObject::doAccessibilityHitTest(const IntPoint&) const
936 {
937     return 0;
938 }
939
940 AccessibilityObject* AccessibilityObject::focusedUIElement() const
941 {
942     return 0;
943 }
944
945 AccessibilityObject* AccessibilityObject::observableObject() const
946 {
947     return 0;
948 }
949
950 AccessibilityRole AccessibilityObject::roleValue() const
951 {
952     return UnknownRole;
953 }
954     
955 AccessibilityRole AccessibilityObject::ariaRoleAttribute() const
956 {
957     return UnknownRole;
958 }
959
960 bool AccessibilityObject::isPresentationalChildOfAriaRole() const
961 {
962     return false;
963 }
964
965 bool AccessibilityObject::ariaRoleHasPresentationalChildren() const
966 {
967     return false;
968 }
969
970 void AccessibilityObject::clearChildren()
971 {
972     m_haveChildren = false;
973     m_children.clear();
974 }
975
976 void AccessibilityObject::childrenChanged()
977 {
978     return;
979 }
980
981 void AccessibilityObject::addChildren()
982 {
983 }
984
985 void AccessibilityObject::selectedChildren(AccessibilityChildrenVector&)
986 {
987 }
988
989 void AccessibilityObject::visibleChildren(AccessibilityChildrenVector&)
990 {
991 }
992     
993 unsigned AccessibilityObject::axObjectID() const
994 {
995     return m_id;
996 }
997
998 void AccessibilityObject::setAXObjectID(unsigned axObjectID)
999 {
1000     m_id = axObjectID;
1001 }
1002
1003 const String& AccessibilityObject::actionVerb() const
1004 {
1005     // FIXME: Need to add verbs for select elements.
1006     DEFINE_STATIC_LOCAL(const String, buttonAction, (AXButtonActionVerb()));
1007     DEFINE_STATIC_LOCAL(const String, textFieldAction, (AXTextFieldActionVerb()));
1008     DEFINE_STATIC_LOCAL(const String, radioButtonAction, (AXRadioButtonActionVerb()));
1009     DEFINE_STATIC_LOCAL(const String, checkedCheckBoxAction, (AXCheckedCheckBoxActionVerb()));
1010     DEFINE_STATIC_LOCAL(const String, uncheckedCheckBoxAction, (AXUncheckedCheckBoxActionVerb()));
1011     DEFINE_STATIC_LOCAL(const String, linkAction, (AXLinkActionVerb()));
1012     DEFINE_STATIC_LOCAL(const String, noAction, ());
1013
1014     switch (roleValue()) {
1015         case ButtonRole:
1016             return buttonAction;
1017         case TextFieldRole:
1018         case TextAreaRole:
1019             return textFieldAction;
1020         case RadioButtonRole:
1021             return radioButtonAction;
1022         case CheckBoxRole:
1023             return isChecked() ? checkedCheckBoxAction : uncheckedCheckBoxAction;
1024         case LinkRole:
1025         case WebCoreLinkRole:
1026             return linkAction;
1027         default:
1028             return noAction;
1029     }
1030 }
1031
1032 void AccessibilityObject::updateBackingStore()
1033 {
1034 }
1035     
1036 } // namespace WebCore