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