• 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 "AccessibilityRenderObject.h"
31 
32 #include "AXObjectCache.h"
33 #include "AccessibilityListBox.h"
34 #include "AccessibilityImageMapLink.h"
35 #include "CharacterNames.h"
36 #include "EventNames.h"
37 #include "FloatRect.h"
38 #include "FocusController.h"
39 #include "Frame.h"
40 #include "FrameLoader.h"
41 #include "HTMLAreaElement.h"
42 #include "HTMLFrameElementBase.h"
43 #include "HTMLImageElement.h"
44 #include "HTMLInputElement.h"
45 #include "HTMLLabelElement.h"
46 #include "HTMLMapElement.h"
47 #include "HTMLOptGroupElement.h"
48 #include "HTMLOptionElement.h"
49 #include "HTMLOptionsCollection.h"
50 #include "HTMLSelectElement.h"
51 #include "HTMLTextAreaElement.h"
52 #include "HitTestRequest.h"
53 #include "HitTestResult.h"
54 #include "LocalizedStrings.h"
55 #include "NodeList.h"
56 #include "NotImplemented.h"
57 #include "Page.h"
58 #include "RenderFieldset.h"
59 #include "RenderFileUploadControl.h"
60 #include "RenderImage.h"
61 #include "RenderListBox.h"
62 #include "RenderListMarker.h"
63 #include "RenderMenuList.h"
64 #include "RenderText.h"
65 #include "RenderTextControl.h"
66 #include "RenderTheme.h"
67 #include "RenderView.h"
68 #include "RenderWidget.h"
69 #include "SelectionController.h"
70 #include "Text.h"
71 #include "TextIterator.h"
72 #include "htmlediting.h"
73 #include "visible_units.h"
74 #include <wtf/StdLibExtras.h>
75 
76 using namespace std;
77 
78 namespace WebCore {
79 
80 using namespace HTMLNames;
81 
AccessibilityRenderObject(RenderObject * renderer)82 AccessibilityRenderObject::AccessibilityRenderObject(RenderObject* renderer)
83     : m_renderer(renderer)
84     , m_ariaRole(UnknownRole)
85 {
86     setAriaRole();
87 #ifndef NDEBUG
88     m_renderer->setHasAXObject(true);
89 #endif
90 }
91 
~AccessibilityRenderObject()92 AccessibilityRenderObject::~AccessibilityRenderObject()
93 {
94     ASSERT(isDetached());
95 }
96 
create(RenderObject * renderer)97 PassRefPtr<AccessibilityRenderObject> AccessibilityRenderObject::create(RenderObject* renderer)
98 {
99     return adoptRef(new AccessibilityRenderObject(renderer));
100 }
101 
detach()102 void AccessibilityRenderObject::detach()
103 {
104     clearChildren();
105     AccessibilityObject::detach();
106 
107 #ifndef NDEBUG
108     if (m_renderer)
109         m_renderer->setHasAXObject(false);
110 #endif
111     m_renderer = 0;
112 }
113 
firstChild() const114 AccessibilityObject* AccessibilityRenderObject::firstChild() const
115 {
116     if (!m_renderer)
117         return 0;
118 
119     RenderObject* firstChild = m_renderer->firstChild();
120     if (!firstChild)
121         return 0;
122 
123     return m_renderer->document()->axObjectCache()->get(firstChild);
124 }
125 
lastChild() const126 AccessibilityObject* AccessibilityRenderObject::lastChild() const
127 {
128     if (!m_renderer)
129         return 0;
130 
131     RenderObject* lastChild = m_renderer->lastChild();
132     if (!lastChild)
133         return 0;
134 
135     return m_renderer->document()->axObjectCache()->get(lastChild);
136 }
137 
previousSibling() const138 AccessibilityObject* AccessibilityRenderObject::previousSibling() const
139 {
140     if (!m_renderer)
141         return 0;
142 
143     RenderObject* previousSibling = m_renderer->previousSibling();
144     if (!previousSibling)
145         return 0;
146 
147     return m_renderer->document()->axObjectCache()->get(previousSibling);
148 }
149 
nextSibling() const150 AccessibilityObject* AccessibilityRenderObject::nextSibling() const
151 {
152     if (!m_renderer)
153         return 0;
154 
155     RenderObject* nextSibling = m_renderer->nextSibling();
156     if (!nextSibling)
157         return 0;
158 
159     return m_renderer->document()->axObjectCache()->get(nextSibling);
160 }
161 
parentObject() const162 AccessibilityObject* AccessibilityRenderObject::parentObject() const
163 {
164     if (!m_renderer)
165         return 0;
166 
167     RenderObject *parent = m_renderer->parent();
168     if (!parent)
169         return 0;
170 
171     if (ariaRoleAttribute() == MenuBarRole)
172         return m_renderer->document()->axObjectCache()->get(parent);
173 
174     // menuButton and its corresponding menu are DOM siblings, but Accessibility needs them to be parent/child
175     if (ariaRoleAttribute() == MenuRole) {
176         AccessibilityObject* parent = menuButtonForMenu();
177         if (parent)
178             return parent;
179     }
180 
181     return m_renderer->document()->axObjectCache()->get(parent);
182 }
183 
isWebArea() const184 bool AccessibilityRenderObject::isWebArea() const
185 {
186     return roleValue() == WebAreaRole;
187 }
188 
isImageButton() const189 bool AccessibilityRenderObject::isImageButton() const
190 {
191     return isNativeImage() && roleValue() == ButtonRole;
192 }
193 
isAnchor() const194 bool AccessibilityRenderObject::isAnchor() const
195 {
196     return !isNativeImage() && isLink();
197 }
198 
isNativeTextControl() const199 bool AccessibilityRenderObject::isNativeTextControl() const
200 {
201     return m_renderer->isTextField() || m_renderer->isTextArea();
202 }
203 
isTextControl() const204 bool AccessibilityRenderObject::isTextControl() const
205 {
206     AccessibilityRole role = roleValue();
207     return role == TextAreaRole || role == TextFieldRole;
208 }
209 
isNativeImage() const210 bool AccessibilityRenderObject::isNativeImage() const
211 {
212     return m_renderer->isImage();
213 }
214 
isImage() const215 bool AccessibilityRenderObject::isImage() const
216 {
217     return roleValue() == ImageRole;
218 }
219 
isAttachment() const220 bool AccessibilityRenderObject::isAttachment() const
221 {
222     // Widgets are the replaced elements that we represent to AX as attachments
223     bool isWidget = m_renderer && m_renderer->isWidget();
224     ASSERT(!isWidget || (m_renderer->isReplaced() && !isImage()));
225     return isWidget && ariaRoleAttribute() == UnknownRole;
226 }
227 
isPasswordField() const228 bool AccessibilityRenderObject::isPasswordField() const
229 {
230     ASSERT(m_renderer);
231     if (!m_renderer->element() || !m_renderer->element()->isHTMLElement())
232         return false;
233     return static_cast<HTMLElement*>(m_renderer->element())->isPasswordField() && ariaRoleAttribute() == UnknownRole;
234 }
235 
isCheckboxOrRadio() const236 bool AccessibilityRenderObject::isCheckboxOrRadio() const
237 {
238     AccessibilityRole role = roleValue();
239     return role == RadioButtonRole || role == CheckBoxRole;
240 }
241 
isFileUploadButton() const242 bool AccessibilityRenderObject::isFileUploadButton() const
243 {
244     if (m_renderer && m_renderer->element() && m_renderer->element()->hasTagName(inputTag)) {
245         HTMLInputElement* input = static_cast<HTMLInputElement*>(m_renderer->element());
246         return input->inputType() == HTMLInputElement::FILE;
247     }
248 
249     return false;
250 }
251 
isInputImage() const252 bool AccessibilityRenderObject::isInputImage() const
253 {
254     if (m_renderer && m_renderer->element() && m_renderer->element()->hasTagName(inputTag)) {
255         HTMLInputElement* input = static_cast<HTMLInputElement*>(m_renderer->element());
256         return input->inputType() == HTMLInputElement::IMAGE;
257     }
258 
259     return false;
260 }
261 
isProgressIndicator() const262 bool AccessibilityRenderObject::isProgressIndicator() const
263 {
264     return roleValue() == ProgressIndicatorRole;
265 }
266 
isSlider() const267 bool AccessibilityRenderObject::isSlider() const
268 {
269     return roleValue() == SliderRole;
270 }
271 
isMenuRelated() const272 bool AccessibilityRenderObject::isMenuRelated() const
273 {
274     AccessibilityRole role = roleValue();
275     return  role == MenuRole ||
276             role == MenuBarRole ||
277             role == MenuButtonRole ||
278             role == MenuItemRole;
279 }
280 
isMenu() const281 bool AccessibilityRenderObject::isMenu() const
282 {
283     return roleValue() == MenuRole;
284 }
285 
isMenuBar() const286 bool AccessibilityRenderObject::isMenuBar() const
287 {
288     return roleValue() == MenuBarRole;
289 }
290 
isMenuButton() const291 bool AccessibilityRenderObject::isMenuButton() const
292 {
293     return roleValue() == MenuButtonRole;
294 }
295 
isMenuItem() const296 bool AccessibilityRenderObject::isMenuItem() const
297 {
298     return roleValue() == MenuItemRole;
299 }
300 
isPressed() const301 bool AccessibilityRenderObject::isPressed() const
302 {
303     ASSERT(m_renderer);
304     if (roleValue() != ButtonRole)
305         return false;
306 
307     Node* node = m_renderer->node();
308     if (!node)
309         return false;
310 
311     // If this is an ARIA button, check the aria-pressed attribute rather than node()->active()
312     if (ariaRoleAttribute() == ButtonRole) {
313         if (equalIgnoringCase(getAttribute(aria_pressedAttr).string(), "true"))
314             return true;
315         return false;
316     }
317 
318     return node->active();
319 }
320 
isIndeterminate() const321 bool AccessibilityRenderObject::isIndeterminate() const
322 {
323     ASSERT(m_renderer);
324     return m_renderer->node() && m_renderer->node()->isIndeterminate();
325 }
326 
isChecked() const327 bool AccessibilityRenderObject::isChecked() const
328 {
329     ASSERT(m_renderer);
330     return m_renderer->node() && m_renderer->node()->isChecked();
331 }
332 
isHovered() const333 bool AccessibilityRenderObject::isHovered() const
334 {
335     ASSERT(m_renderer);
336     return m_renderer->node() && m_renderer->node()->hovered();
337 }
338 
isMultiSelect() const339 bool AccessibilityRenderObject::isMultiSelect() const
340 {
341     ASSERT(m_renderer);
342     if (!m_renderer->isListBox())
343         return false;
344     return m_renderer->element() && static_cast<HTMLSelectElement*>(m_renderer->element())->multiple();
345 }
346 
isReadOnly() const347 bool AccessibilityRenderObject::isReadOnly() const
348 {
349     ASSERT(m_renderer);
350 
351     if (isWebArea()) {
352         Document* document = m_renderer->document();
353         if (!document)
354             return true;
355 
356         HTMLElement* body = document->body();
357         if (body && body->isContentEditable())
358             return false;
359 
360         Frame* frame = document->frame();
361         if (!frame)
362             return true;
363 
364         return !frame->isContentEditable();
365     }
366 
367     return !m_renderer->node() || !m_renderer->node()->isContentEditable();
368 }
369 
isOffScreen() const370 bool AccessibilityRenderObject::isOffScreen() const
371 {
372     ASSERT(m_renderer);
373     IntRect contentRect = m_renderer->absoluteClippedOverflowRect();
374     FrameView* view = m_renderer->document()->frame()->view();
375     FloatRect viewRect = view->visibleContentRect();
376     viewRect.intersect(contentRect);
377     return viewRect.isEmpty();
378 }
379 
headingLevel(Node * node)380 int AccessibilityRenderObject::headingLevel(Node* node)
381 {
382     // headings can be in block flow and non-block flow
383     if (!node)
384         return 0;
385 
386     if (RenderObject* renderer = node->renderer()) {
387         AccessibilityObject* axObjectForNode = node->document()->axObjectCache()->get(renderer);
388         if (axObjectForNode->ariaRoleAttribute() == HeadingRole) {
389             if (!node->isElementNode())
390                 return 0;
391             Element* element = static_cast<Element*>(node);
392             return element->getAttribute(aria_levelAttr).toInt();
393         }
394     }
395 
396 
397     if (node->hasTagName(h1Tag))
398         return 1;
399 
400     if (node->hasTagName(h2Tag))
401         return 2;
402 
403     if (node->hasTagName(h3Tag))
404         return 3;
405 
406     if (node->hasTagName(h4Tag))
407         return 4;
408 
409     if (node->hasTagName(h5Tag))
410         return 5;
411 
412     if (node->hasTagName(h6Tag))
413         return 6;
414 
415     return 0;
416 }
417 
isHeading() const418 bool AccessibilityRenderObject::isHeading() const
419 {
420     return roleValue() == HeadingRole;
421 }
422 
isLink() const423 bool AccessibilityRenderObject::isLink() const
424 {
425     return roleValue() == WebCoreLinkRole;
426 }
427 
isControl() const428 bool AccessibilityRenderObject::isControl() const
429 {
430     if (!m_renderer)
431         return false;
432 
433     Node* node = m_renderer->element();
434     return node && (node->isControl() || AccessibilityObject::isARIAControl(ariaRoleAttribute()));
435 }
436 
isFieldset() const437 bool AccessibilityRenderObject::isFieldset() const
438 {
439     if (!m_renderer)
440         return false;
441 
442     return m_renderer->isFieldset();
443 }
444 
isGroup() const445 bool AccessibilityRenderObject::isGroup() const
446 {
447     return roleValue() == GroupRole;
448 }
449 
getAttribute(const QualifiedName & attribute) const450 const AtomicString& AccessibilityRenderObject::getAttribute(const QualifiedName& attribute) const
451 {
452     Node* node = m_renderer->element();
453     if (!node)
454         return nullAtom;
455 
456     if (!node->isElementNode())
457         return nullAtom;
458 
459     Element* element = static_cast<Element*>(node);
460     return element->getAttribute(attribute);
461 }
462 
anchorElement() const463 Element* AccessibilityRenderObject::anchorElement() const
464 {
465     if (!m_renderer)
466         return 0;
467 
468     AXObjectCache* cache = axObjectCache();
469     RenderObject* currRenderer;
470 
471     // Search up the render tree for a RenderObject with a DOM node.  Defer to an earlier continuation, though.
472     for (currRenderer = m_renderer; currRenderer && !currRenderer->element(); currRenderer = currRenderer->parent()) {
473         RenderFlow* continuation = currRenderer->virtualContinuation();
474         if (continuation)
475             return cache->get(continuation)->anchorElement();
476     }
477 
478     // bail if none found
479     if (!currRenderer)
480         return 0;
481 
482     // search up the DOM tree for an anchor element
483     // NOTE: this assumes that any non-image with an anchor is an HTMLAnchorElement
484     Node* node = currRenderer->node();
485     for ( ; node; node = node->parentNode()) {
486         if (node->hasTagName(aTag) || (node->renderer() && cache->get(node->renderer())->isAnchor()))
487             return static_cast<Element*>(node);
488     }
489 
490     return 0;
491 }
492 
actionElement() const493 Element* AccessibilityRenderObject::actionElement() const
494 {
495     if (!m_renderer)
496         return 0;
497 
498     Node* node = m_renderer->element();
499     if (node) {
500         if (node->hasTagName(inputTag)) {
501             HTMLInputElement* input = static_cast<HTMLInputElement*>(node);
502             if (!input->disabled() && (isCheckboxOrRadio() || input->isTextButton()))
503                 return input;
504         } else if (node->hasTagName(buttonTag))
505             return static_cast<Element*>(node);
506     }
507 
508     if (isFileUploadButton())
509         return static_cast<Element*>(m_renderer->element());
510 
511     if (AccessibilityObject::isARIAInput(ariaRoleAttribute()))
512         return static_cast<Element*>(m_renderer->element());
513 
514     if (isImageButton())
515         return static_cast<Element*>(m_renderer->element());
516 
517     if (m_renderer->isMenuList())
518         return static_cast<RenderMenuList*>(m_renderer)->selectElement();
519 
520     Element* elt = anchorElement();
521     if (!elt)
522         elt = mouseButtonListener();
523     return elt;
524 }
525 
mouseButtonListener() const526 Element* AccessibilityRenderObject::mouseButtonListener() const
527 {
528     Node* node = m_renderer->element();
529     if (!node)
530         return 0;
531     if (!node->isEventTargetNode())
532         return 0;
533 
534     // FIXME: Do the continuation search like anchorElement does
535     for (EventTargetNode* elt = static_cast<EventTargetNode*>(node); elt; elt = static_cast<EventTargetNode*>(elt->parentNode())) {
536         if (elt->inlineEventListenerForType(eventNames().clickEvent) || elt->inlineEventListenerForType(eventNames().mousedownEvent) || elt->inlineEventListenerForType(eventNames().mouseupEvent))
537             return static_cast<Element*>(elt);
538     }
539 
540     return 0;
541 }
542 
siblingWithAriaRole(String role,Node * node)543 static Element* siblingWithAriaRole(String role, Node* node)
544 {
545     Node* sibling = node->parent()->firstChild();
546     while (sibling) {
547         if (sibling->isElementNode()) {
548             String siblingAriaRole = static_cast<Element*>(sibling)->getAttribute(roleAttr).string();
549             if (equalIgnoringCase(siblingAriaRole, role))
550                 return static_cast<Element*>(sibling);
551         }
552         sibling = sibling->nextSibling();
553     }
554 
555     return 0;
556 }
557 
menuElementForMenuButton() const558 Element* AccessibilityRenderObject::menuElementForMenuButton() const
559 {
560     if (ariaRoleAttribute() != MenuButtonRole)
561         return 0;
562 
563     return siblingWithAriaRole("menu", renderer()->node());
564 }
565 
menuForMenuButton() const566 AccessibilityObject* AccessibilityRenderObject::menuForMenuButton() const
567 {
568     Element* menu = menuElementForMenuButton();
569     if (menu && menu->renderer())
570         return m_renderer->document()->axObjectCache()->get(menu->renderer());
571     return 0;
572 }
573 
menuItemElementForMenu() const574 Element* AccessibilityRenderObject::menuItemElementForMenu() const
575 {
576     if (ariaRoleAttribute() != MenuRole)
577         return 0;
578 
579     return siblingWithAriaRole("menuitem", renderer()->node());
580 }
581 
menuButtonForMenu() const582 AccessibilityObject* AccessibilityRenderObject::menuButtonForMenu() const
583 {
584     Element* menuItem = menuItemElementForMenu();
585 
586     if (menuItem && menuItem->renderer()) {
587         // ARIA just has generic menu items.  AppKit needs to know if this is a top level items like MenuBarButton or MenuBarItem
588         AccessibilityObject* menuItemAX = m_renderer->document()->axObjectCache()->get(menuItem->renderer());
589         if (menuItemAX->isMenuButton())
590             return menuItemAX;
591     }
592     return 0;
593 }
594 
helpText() const595 String AccessibilityRenderObject::helpText() const
596 {
597     if (!m_renderer)
598         return String();
599 
600     for (RenderObject* curr = m_renderer; curr; curr = curr->parent()) {
601         if (curr->element() && curr->element()->isHTMLElement()) {
602             const AtomicString& summary = static_cast<Element*>(curr->element())->getAttribute(summaryAttr);
603             if (!summary.isEmpty())
604                 return summary;
605             const AtomicString& title = static_cast<Element*>(curr->element())->getAttribute(titleAttr);
606             if (!title.isEmpty())
607                 return title;
608         }
609     }
610 
611     return String();
612 }
613 
textUnderElement() const614 String AccessibilityRenderObject::textUnderElement() const
615 {
616     if (!m_renderer)
617         return String();
618 
619     if (isFileUploadButton()) {
620         RenderFileUploadControl* uploadControl = static_cast<RenderFileUploadControl*>(m_renderer);
621         return uploadControl->buttonValue();
622     }
623 
624     Node* node = m_renderer->element();
625     if (node) {
626         if (Frame* frame = node->document()->frame()) {
627             // catch stale WebCoreAXObject (see <rdar://problem/3960196>)
628             if (frame->document() != node->document())
629                 return String();
630             return plainText(rangeOfContents(node).get());
631         }
632     }
633 
634     // return the null string for anonymous text because it is non-trivial to get
635     // the actual text and, so far, that is not needed
636     return String();
637 }
638 
hasIntValue() const639 bool AccessibilityRenderObject::hasIntValue() const
640 {
641     if (isHeading())
642         return true;
643 
644     if (m_renderer->element() && isCheckboxOrRadio())
645         return true;
646 
647     return false;
648 }
649 
intValue() const650 int AccessibilityRenderObject::intValue() const
651 {
652     if (!m_renderer || isPasswordField())
653         return 0;
654 
655     if (isHeading())
656         return headingLevel(m_renderer->element());
657 
658     Node* node = m_renderer->element();
659     if (!node || !isCheckboxOrRadio())
660         return 0;
661 
662     // If this is an ARIA checkbox or radio, check the aria-checked attribute rather than node()->checked()
663     AccessibilityRole ariaRole = ariaRoleAttribute();
664     if (ariaRole == RadioButtonRole || ariaRole == CheckBoxRole) {
665         if (equalIgnoringCase(getAttribute(aria_checkedAttr).string(), "true"))
666             return true;
667         return false;
668     }
669 
670     return static_cast<HTMLInputElement*>(node)->checked();
671 }
672 
valueForRange() const673 float AccessibilityRenderObject::valueForRange() const
674 {
675     if (!isProgressIndicator() && !isSlider())
676         return 0.0f;
677 
678     return getAttribute(aria_valuenowAttr).toFloat();
679 }
680 
maxValueForRange() const681 float AccessibilityRenderObject::maxValueForRange() const
682 {
683     if (!isProgressIndicator() && !isSlider())
684         return 0.0f;
685 
686     return getAttribute(aria_valuemaxAttr).toFloat();
687 }
688 
minValueForRange() const689 float AccessibilityRenderObject::minValueForRange() const
690 {
691     if (!isProgressIndicator() && !isSlider())
692         return 0.0f;
693 
694     return getAttribute(aria_valueminAttr).toFloat();
695 }
696 
stringValue() const697 String AccessibilityRenderObject::stringValue() const
698 {
699     if (!m_renderer || isPasswordField())
700         return String();
701 
702     if (m_renderer->isText())
703         return textUnderElement();
704 
705     if (m_renderer->isMenuList())
706         return static_cast<RenderMenuList*>(m_renderer)->text();
707 
708     if (m_renderer->isListMarker())
709         return static_cast<RenderListMarker*>(m_renderer)->text();
710 
711     if (isWebArea()) {
712         if (m_renderer->document()->frame())
713             return String();
714 
715         // FIXME: should use startOfDocument and endOfDocument (or rangeForDocument?) here
716         VisiblePosition startVisiblePosition = m_renderer->positionForCoordinates(0, 0);
717         VisiblePosition endVisiblePosition = m_renderer->positionForCoordinates(INT_MAX, INT_MAX);
718         if (startVisiblePosition.isNull() || endVisiblePosition.isNull())
719             return String();
720 
721         return plainText(makeRange(startVisiblePosition, endVisiblePosition).get());
722     }
723 
724     if (isTextControl())
725         return text();
726 
727     if (isFileUploadButton()) {
728         RenderFileUploadControl* uploadControl = static_cast<RenderFileUploadControl*>(m_renderer);
729         return uploadControl->fileTextValue();
730     }
731 
732     // FIXME: We might need to implement a value here for more types
733     // FIXME: It would be better not to advertise a value at all for the types for which we don't implement one;
734     // this would require subclassing or making accessibilityAttributeNames do something other than return a
735     // single static array.
736     return String();
737 }
738 
739 // This function implements the ARIA accessible name as described by the Mozilla
740 // ARIA Implementer's Guide.
accessibleNameForNode(Node * node)741 static String accessibleNameForNode(Node* node)
742 {
743     if (node->isTextNode())
744         return static_cast<Text*>(node)->data();
745 
746     if (node->hasTagName(inputTag))
747         return static_cast<HTMLInputElement*>(node)->value();
748 
749     if (node->isHTMLElement()) {
750         const AtomicString& alt = static_cast<HTMLElement*>(node)->getAttribute(altAttr);
751         if (!alt.isEmpty())
752             return alt;
753     }
754 
755     return String();
756 }
757 
ariaAccessiblityName(const String & s) const758 String AccessibilityRenderObject::ariaAccessiblityName(const String& s) const
759 {
760     Document* document = m_renderer->document();
761     if (!document)
762         return String();
763 
764     String idList = s;
765     idList.replace('\n', ' ');
766     Vector<String> idVector;
767     idList.split(' ', idVector);
768 
769     Vector<UChar> ariaLabel;
770     unsigned size = idVector.size();
771     for (unsigned i = 0; i < size; ++i) {
772         String idName = idVector[i];
773         Element* idElement = document->getElementById(idName);
774         if (idElement) {
775             String nameFragment = accessibleNameForNode(idElement);
776             ariaLabel.append(nameFragment.characters(), nameFragment.length());
777             for (Node* n = idElement->firstChild(); n; n = n->traverseNextNode(idElement->nextSibling())) {
778                 nameFragment = accessibleNameForNode(n);
779                 ariaLabel.append(nameFragment.characters(), nameFragment.length());
780             }
781             ariaLabel.append(' ');
782         }
783     }
784     return String::adopt(ariaLabel);
785 }
786 
ariaLabeledByAttribute() const787 String AccessibilityRenderObject::ariaLabeledByAttribute() const
788 {
789     Node* node = m_renderer->node();
790     if (!node)
791         return String();
792 
793     if (!node->isElementNode())
794         return String();
795 
796     // The ARIA spec uses the British spelling: "labelled." It seems prudent to support the American
797     // spelling ("labeled") as well.
798     String idList = getAttribute(aria_labeledbyAttr).string();
799     if (idList.isEmpty()) {
800         idList = getAttribute(aria_labelledbyAttr).string();
801         if (idList.isEmpty())
802             return String();
803     }
804 
805     return ariaAccessiblityName(idList);
806 }
807 
labelForElement(Element * element)808 static HTMLLabelElement* labelForElement(Element* element)
809 {
810     RefPtr<NodeList> list = element->document()->getElementsByTagName("label");
811     unsigned len = list->length();
812     for (unsigned i = 0; i < len; i++) {
813         if (list->item(i)->hasTagName(labelTag)) {
814             HTMLLabelElement* label = static_cast<HTMLLabelElement*>(list->item(i));
815             if (label->correspondingControl() == element)
816                 return label;
817         }
818     }
819 
820     return 0;
821 }
822 
labelElementContainer() const823 HTMLLabelElement* AccessibilityRenderObject::labelElementContainer() const
824 {
825     if (!m_renderer)
826         return false;
827 
828     // the control element should not be considered part of the label
829     if (isControl())
830         return false;
831 
832     // find if this has a parent that is a label
833     for (Node* parentNode = m_renderer->element(); parentNode; parentNode = parentNode->parentNode()) {
834         if (parentNode->hasTagName(labelTag))
835             return static_cast<HTMLLabelElement*>(parentNode);
836     }
837 
838     return 0;
839 }
840 
title() const841 String AccessibilityRenderObject::title() const
842 {
843     AccessibilityRole ariaRole = ariaRoleAttribute();
844 
845     if (!m_renderer)
846         return String();
847 
848     Node* node = m_renderer->element();
849     if (!node)
850         return String();
851 
852     String ariaLabel = ariaLabeledByAttribute();
853     if (!ariaLabel.isEmpty())
854         return ariaLabel;
855 
856     const AtomicString& title = getAttribute(titleAttr);
857     if (!title.isEmpty())
858         return title;
859 
860     bool isInputTag = node->hasTagName(inputTag);
861     if (isInputTag) {
862         HTMLInputElement* input = static_cast<HTMLInputElement*>(node);
863         if (input->isTextButton())
864             return input->value();
865     }
866 
867     if (isInputTag || AccessibilityObject::isARIAInput(ariaRole) || isControl()) {
868         HTMLLabelElement* label = labelForElement(static_cast<Element*>(node));
869         if (label && !titleUIElement())
870             return label->innerText();
871     }
872 
873     if (roleValue() == ButtonRole
874         || ariaRole == ListBoxOptionRole
875         || ariaRole == MenuItemRole
876         || ariaRole == MenuButtonRole
877         || isHeading())
878         return textUnderElement();
879 
880     if (isLink())
881         return textUnderElement();
882 
883     return String();
884 }
885 
ariaDescribedByAttribute() const886 String AccessibilityRenderObject::ariaDescribedByAttribute() const
887 {
888     String idList = getAttribute(aria_describedbyAttr).string();
889     if (idList.isEmpty())
890         return String();
891 
892     return ariaAccessiblityName(idList);
893 }
894 
accessibilityDescription() const895 String AccessibilityRenderObject::accessibilityDescription() const
896 {
897     if (!m_renderer)
898         return String();
899 
900     String ariaDescription = ariaDescribedByAttribute();
901     if (!ariaDescription.isEmpty())
902         return ariaDescription;
903 
904     if (isImage()) {
905         if (m_renderer->element() && m_renderer->element()->isHTMLElement()) {
906             const AtomicString& alt = static_cast<HTMLElement*>(m_renderer->element())->getAttribute(altAttr);
907             if (alt.isEmpty())
908                 return String();
909             return alt;
910         }
911     }
912 
913     if (isWebArea()) {
914         Document *document = m_renderer->document();
915         Node* owner = document->ownerElement();
916         if (owner) {
917             if (owner->hasTagName(frameTag) || owner->hasTagName(iframeTag)) {
918                 const AtomicString& title = static_cast<HTMLFrameElementBase*>(owner)->getAttribute(titleAttr);
919                 if (!title.isEmpty())
920                     return title;
921                 return static_cast<HTMLFrameElementBase*>(owner)->name();
922             }
923             if (owner->isHTMLElement())
924                 return static_cast<HTMLElement*>(owner)->getAttribute(nameAttr);
925         }
926         owner = document->body();
927         if (owner && owner->isHTMLElement())
928             return static_cast<HTMLElement*>(owner)->getAttribute(nameAttr);
929     }
930 
931     if (roleValue() == DefinitionListTermRole)
932         return AXDefinitionListTermText();
933     if (roleValue() == DefinitionListDefinitionRole)
934         return AXDefinitionListDefinitionText();
935 
936     return String();
937 }
938 
boundingBoxRect() const939 IntRect AccessibilityRenderObject::boundingBoxRect() const
940 {
941     IntRect rect;
942     RenderObject* obj = m_renderer;
943 
944     if (!obj)
945         return IntRect();
946 
947     if (obj->isInlineContinuation())
948         obj = obj->element()->renderer();
949 
950     // FIXME: This doesn't work correctly with transforms.
951     Vector<IntRect> rects;
952     FloatPoint absPos = obj->localToAbsolute();
953     obj->absoluteRects(rects, absPos.x(), absPos.y());
954     const size_t n = rects.size();
955     for (size_t i = 0; i < n; ++i) {
956         IntRect r = rects[i];
957         if (!r.isEmpty()) {
958             if (obj->style()->hasAppearance())
959                 theme()->adjustRepaintRect(obj, r);
960             rect.unite(r);
961         }
962     }
963     return rect;
964 }
965 
checkboxOrRadioRect() const966 IntRect AccessibilityRenderObject::checkboxOrRadioRect() const
967 {
968     if (!m_renderer)
969         return IntRect();
970 
971     HTMLLabelElement* label = labelForElement(static_cast<Element*>(m_renderer->element()));
972     if (!label || !label->renderer())
973         return boundingBoxRect();
974 
975     IntRect labelRect = axObjectCache()->get(label->renderer())->elementRect();
976     labelRect.unite(boundingBoxRect());
977     return labelRect;
978 }
979 
elementRect() const980 IntRect AccessibilityRenderObject::elementRect() const
981 {
982     // a checkbox or radio button should encompass its label
983     if (isCheckboxOrRadio())
984         return checkboxOrRadioRect();
985 
986     return boundingBoxRect();
987 }
988 
size() const989 IntSize AccessibilityRenderObject::size() const
990 {
991     IntRect rect = elementRect();
992     return rect.size();
993 }
994 
internalLinkElement() const995 AccessibilityObject* AccessibilityRenderObject::internalLinkElement() const
996 {
997     Element* element = anchorElement();
998     if (!element)
999         return 0;
1000 
1001     // Right now, we do not support ARIA links as internal link elements
1002     if (!element->hasTagName(aTag))
1003         return 0;
1004     HTMLAnchorElement* anchor = static_cast<HTMLAnchorElement*>(element);
1005 
1006     KURL linkURL = anchor->href();
1007     String ref = linkURL.ref();
1008     if (ref.isEmpty())
1009         return 0;
1010 
1011     // check if URL is the same as current URL
1012     linkURL.removeRef();
1013     if (m_renderer->document()->url() != linkURL)
1014         return 0;
1015 
1016     Node* linkedNode = m_renderer->document()->findAnchor(ref);
1017     if (!linkedNode)
1018         return 0;
1019 
1020     // the element we find may not be accessible, keep searching until we find a good one
1021     AccessibilityObject* linkedAXElement = m_renderer->document()->axObjectCache()->get(linkedNode->renderer());
1022     while (linkedAXElement && linkedAXElement->accessibilityIsIgnored()) {
1023         linkedNode = linkedNode->traverseNextNode();
1024 
1025         while (linkedNode && !linkedNode->renderer())
1026             linkedNode = linkedNode->traverseNextSibling();
1027 
1028         if (!linkedNode)
1029             return 0;
1030         linkedAXElement = m_renderer->document()->axObjectCache()->get(linkedNode->renderer());
1031     }
1032 
1033     return linkedAXElement;
1034 }
1035 
addRadioButtonGroupMembers(AccessibilityChildrenVector & linkedUIElements) const1036 void AccessibilityRenderObject::addRadioButtonGroupMembers(AccessibilityChildrenVector& linkedUIElements) const
1037 {
1038     if (!m_renderer || roleValue() != RadioButtonRole)
1039         return;
1040 
1041     Node* node = m_renderer->node();
1042     if (!node || !node->hasTagName(inputTag))
1043         return;
1044 
1045     HTMLInputElement* input = static_cast<HTMLInputElement*>(node);
1046     // if there's a form, then this is easy
1047     if (input->form()) {
1048         Vector<RefPtr<Node> > formElements;
1049         input->form()->getNamedElements(input->name(), formElements);
1050 
1051         unsigned len = formElements.size();
1052         for (unsigned i = 0; i < len; ++i) {
1053             Node* associateElement = formElements[i].get();
1054             if (AccessibilityObject* object = m_renderer->document()->axObjectCache()->get(associateElement->renderer()))
1055                 linkedUIElements.append(object);
1056         }
1057     } else {
1058         RefPtr<NodeList> list = node->document()->getElementsByTagName("input");
1059         unsigned len = list->length();
1060         for (unsigned i = 0; i < len; ++i) {
1061             if (list->item(i)->hasTagName(inputTag)) {
1062                 HTMLInputElement* associateElement = static_cast<HTMLInputElement*>(list->item(i));
1063                 if (associateElement->isRadioButton() && associateElement->name() == input->name()) {
1064                     if (AccessibilityObject* object = m_renderer->document()->axObjectCache()->get(associateElement->renderer()))
1065                         linkedUIElements.append(object);
1066                 }
1067             }
1068         }
1069     }
1070 }
1071 
1072 // linked ui elements could be all the related radio buttons in a group
1073 // or an internal anchor connection
linkedUIElements(AccessibilityChildrenVector & linkedUIElements) const1074 void AccessibilityRenderObject::linkedUIElements(AccessibilityChildrenVector& linkedUIElements) const
1075 {
1076     if (isAnchor()) {
1077         AccessibilityObject* linkedAXElement = internalLinkElement();
1078         if (linkedAXElement)
1079             linkedUIElements.append(linkedAXElement);
1080     }
1081 
1082     if (roleValue() == RadioButtonRole)
1083         addRadioButtonGroupMembers(linkedUIElements);
1084 }
1085 
titleUIElement() const1086 AccessibilityObject* AccessibilityRenderObject::titleUIElement() const
1087 {
1088     if (!m_renderer)
1089         return 0;
1090 
1091     // if isFieldset is true, the renderer is guaranteed to be a RenderFieldset
1092     if (isFieldset())
1093         return axObjectCache()->get(static_cast<RenderFieldset*>(m_renderer)->findLegend());
1094 
1095     // checkbox and radio hide their labels. Only controls get titleUIElements for now
1096     if (isCheckboxOrRadio() || !isControl())
1097         return 0;
1098 
1099     Node* element = m_renderer->element();
1100     HTMLLabelElement* label = labelForElement(static_cast<Element*>(element));
1101     if (label && label->renderer())
1102         return axObjectCache()->get(label->renderer());
1103 
1104     return 0;
1105 }
1106 
accessibilityIsIgnored() const1107 bool AccessibilityRenderObject::accessibilityIsIgnored() const
1108 {
1109     // ignore invisible element
1110     if (!m_renderer || m_renderer->style()->visibility() != VISIBLE)
1111         return true;
1112 
1113     if (isPresentationalChildOfAriaRole())
1114         return true;
1115 
1116     // ignore popup menu items because AppKit does
1117     for (RenderObject* parent = m_renderer->parent(); parent; parent = parent->parent()) {
1118         if (parent->isMenuList())
1119             return true;
1120     }
1121 
1122     // find out if this element is inside of a label element.
1123     // if so, it may be ignored because it's the label for a checkbox or radio button
1124     HTMLLabelElement* labelElement = labelElementContainer();
1125     if (labelElement) {
1126         HTMLElement* correspondingControl = labelElement->correspondingControl();
1127         if (correspondingControl && correspondingControl->renderer()) {
1128             AccessibilityObject* controlObject = axObjectCache()->get(correspondingControl->renderer());
1129             if (controlObject->isCheckboxOrRadio())
1130                 return true;
1131         }
1132     }
1133 
1134     AccessibilityRole ariaRole = ariaRoleAttribute();
1135     if (ariaRole == TextAreaRole || ariaRole == StaticTextRole) {
1136         String ariaText = text();
1137         return ariaText.isNull() || ariaText.isEmpty();
1138     }
1139 
1140     // NOTE: BRs always have text boxes now, so the text box check here can be removed
1141     if (m_renderer->isText()) {
1142         // static text beneath MenuItems and MenuButtons are just reported along with the menu item, so it's ignored on an individual level
1143         if (parentObjectUnignored()->ariaRoleAttribute() == MenuItemRole ||
1144             parentObjectUnignored()->ariaRoleAttribute() == MenuButtonRole)
1145             return true;
1146          return m_renderer->isBR() || !toRenderText(m_renderer)->firstTextBox();
1147     }
1148 
1149     if (isHeading())
1150         return false;
1151 
1152     if (isLink())
1153         return false;
1154 
1155     // all controls are accessible
1156     if (isControl())
1157         return false;
1158 
1159     // don't ignore labels, because they serve as TitleUIElements
1160     Node* node = m_renderer->element();
1161     if (node && node->hasTagName(labelTag))
1162         return false;
1163 
1164     if (m_renderer->isBlockFlow() && m_renderer->childrenInline())
1165         return !static_cast<RenderBlock*>(m_renderer)->firstLineBox() && !mouseButtonListener();
1166 
1167     // ignore images seemingly used as spacers
1168     if (isImage()) {
1169         if (node && node->isElementNode()) {
1170             Element* elt = static_cast<Element*>(node);
1171             const AtomicString& alt = elt->getAttribute(altAttr);
1172             // don't ignore an image that has an alt tag
1173             if (!alt.isEmpty())
1174                 return false;
1175             // informal standard is to ignore images with zero-length alt strings
1176             if (!alt.isNull())
1177                 return true;
1178         }
1179 
1180         // check for one-dimensional image
1181         RenderImage* image = static_cast<RenderImage*>(m_renderer);
1182         if (image->height() <= 1 || image->width() <= 1)
1183             return true;
1184 
1185         // check whether rendered image was stretched from one-dimensional file image
1186         if (isNativeImage()) {
1187             if (image->cachedImage()) {
1188                 IntSize imageSize = image->cachedImage()->imageSize(image->view()->zoomFactor());
1189                 return imageSize.height() <= 1 || imageSize.width() <= 1;
1190             }
1191         }
1192         return false;
1193     }
1194 
1195     if (ariaRole != UnknownRole)
1196         return false;
1197 
1198     // make a platform-specific decision
1199     if (isAttachment())
1200         return accessibilityIgnoreAttachment();
1201 
1202     return !m_renderer->isListMarker() && !isWebArea();
1203 }
1204 
isLoaded() const1205 bool AccessibilityRenderObject::isLoaded() const
1206 {
1207     return !m_renderer->document()->tokenizer();
1208 }
1209 
layoutCount() const1210 int AccessibilityRenderObject::layoutCount() const
1211 {
1212     if (!m_renderer->isRenderView())
1213         return 0;
1214     return static_cast<RenderView*>(m_renderer)->frameView()->layoutCount();
1215 }
1216 
text() const1217 String AccessibilityRenderObject::text() const
1218 {
1219     if (!isTextControl() || isPasswordField())
1220         return String();
1221 
1222     if (isNativeTextControl())
1223         return static_cast<RenderTextControl*>(m_renderer)->text();
1224 
1225     Node* node = m_renderer->element();
1226     if (!node)
1227         return String();
1228     if (!node->isElementNode())
1229         return String();
1230 
1231     return static_cast<Element*>(node)->innerText();
1232 }
1233 
textLength() const1234 int AccessibilityRenderObject::textLength() const
1235 {
1236     ASSERT(isTextControl());
1237 
1238     if (isPasswordField())
1239         return -1; // need to return something distinct from 0
1240 
1241     return text().length();
1242 }
1243 
ariaSelectedTextDOMRange() const1244 PassRefPtr<Range> AccessibilityRenderObject::ariaSelectedTextDOMRange() const
1245 {
1246     Node* node = m_renderer->element();
1247     if (!node)
1248         return 0;
1249 
1250     RefPtr<Range> currentSelectionRange = selection().toRange();
1251     if (!currentSelectionRange)
1252         return 0;
1253 
1254     ExceptionCode ec = 0;
1255     if (!currentSelectionRange->intersectsNode(node, ec))
1256         return Range::create(currentSelectionRange->ownerDocument());
1257 
1258     RefPtr<Range> ariaRange = rangeOfContents(node);
1259     Position startPosition, endPosition;
1260 
1261     // Find intersection of currentSelectionRange and ariaRange
1262     if (ariaRange->startOffset() > currentSelectionRange->startOffset())
1263         startPosition = ariaRange->startPosition();
1264     else
1265         startPosition = currentSelectionRange->startPosition();
1266 
1267     if (ariaRange->endOffset() < currentSelectionRange->endOffset())
1268         endPosition = ariaRange->endPosition();
1269     else
1270         endPosition = currentSelectionRange->endPosition();
1271 
1272     return Range::create(ariaRange->ownerDocument(), startPosition, endPosition);
1273 }
1274 
selectedText() const1275 String AccessibilityRenderObject::selectedText() const
1276 {
1277     ASSERT(isTextControl());
1278 
1279     if (isPasswordField())
1280         return String(); // need to return something distinct from empty string
1281 
1282     if (isNativeTextControl()) {
1283         RenderTextControl* textControl = static_cast<RenderTextControl*>(m_renderer);
1284         return textControl->text().substring(textControl->selectionStart(), textControl->selectionEnd() - textControl->selectionStart());
1285     }
1286 
1287     if (ariaRoleAttribute() == UnknownRole)
1288         return String();
1289 
1290     RefPtr<Range> ariaRange = ariaSelectedTextDOMRange();
1291     if (!ariaRange)
1292         return String();
1293     return ariaRange->text();
1294 }
1295 
accessKey() const1296 const AtomicString& AccessibilityRenderObject::accessKey() const
1297 {
1298     Node* node = m_renderer->element();
1299     if (!node)
1300         return nullAtom;
1301     if (!node->isElementNode())
1302         return nullAtom;
1303     return static_cast<Element*>(node)->getAttribute(accesskeyAttr);
1304 }
1305 
selection() const1306 Selection AccessibilityRenderObject::selection() const
1307 {
1308     return m_renderer->document()->frame()->selection()->selection();
1309 }
1310 
selectedTextRange() const1311 PlainTextRange AccessibilityRenderObject::selectedTextRange() const
1312 {
1313     ASSERT(isTextControl());
1314 
1315     if (isPasswordField())
1316         return PlainTextRange();
1317 
1318     AccessibilityRole ariaRole = ariaRoleAttribute();
1319     if (isNativeTextControl() && ariaRole == UnknownRole) {
1320         RenderTextControl* textControl = static_cast<RenderTextControl*>(m_renderer);
1321         return PlainTextRange(textControl->selectionStart(), textControl->selectionEnd() - textControl->selectionStart());
1322     }
1323 
1324     if (ariaRole == UnknownRole)
1325         return PlainTextRange();
1326 
1327     RefPtr<Range> ariaRange = ariaSelectedTextDOMRange();
1328     if (!ariaRange)
1329         return PlainTextRange();
1330     return PlainTextRange(ariaRange->startOffset(), ariaRange->endOffset());
1331 }
1332 
setSelectedTextRange(const PlainTextRange & range)1333 void AccessibilityRenderObject::setSelectedTextRange(const PlainTextRange& range)
1334 {
1335     if (isNativeTextControl()) {
1336         RenderTextControl* textControl = static_cast<RenderTextControl*>(m_renderer);
1337         textControl->setSelectionRange(range.start, range.start + range.length);
1338         return;
1339     }
1340 
1341     Document* document = m_renderer->document();
1342     if (!document)
1343         return;
1344     Frame* frame = document->frame();
1345     if (!frame)
1346         return;
1347     Node* node = m_renderer->element();
1348     frame->selection()->setSelection(Selection(Position(node, range.start),
1349         Position(node, range.start + range.length), DOWNSTREAM));
1350 }
1351 
url() const1352 KURL AccessibilityRenderObject::url() const
1353 {
1354     if (isAnchor() && m_renderer->element()->hasTagName(aTag)) {
1355         if (HTMLAnchorElement* anchor = static_cast<HTMLAnchorElement*>(anchorElement()))
1356             return anchor->href();
1357     }
1358 
1359     if (isWebArea())
1360         return m_renderer->document()->url();
1361 
1362     if (isImage() && m_renderer->element() && m_renderer->element()->hasTagName(imgTag))
1363         return static_cast<HTMLImageElement*>(m_renderer->element())->src();
1364 
1365     if (isInputImage())
1366         return static_cast<HTMLInputElement*>(m_renderer->element())->src();
1367 
1368     return KURL();
1369 }
1370 
isVisited() const1371 bool AccessibilityRenderObject::isVisited() const
1372 {
1373     return m_renderer->style()->pseudoState() == PseudoVisited;
1374 }
1375 
isSelected() const1376 bool AccessibilityRenderObject::isSelected() const
1377 {
1378     if (!m_renderer)
1379         return false;
1380 
1381     Node* node = m_renderer->node();
1382     if (!node)
1383         return false;
1384 
1385     return false;
1386 }
1387 
isFocused() const1388 bool AccessibilityRenderObject::isFocused() const
1389 {
1390     if (!m_renderer)
1391         return false;
1392 
1393     Document* document = m_renderer->document();
1394     if (!document)
1395         return false;
1396 
1397     Node* focusedNode = document->focusedNode();
1398     if (!focusedNode)
1399         return false;
1400 
1401     // A web area is represented by the Document node in the DOM tree, which isn't focusable.
1402     // Check instead if the frame's selection controller is focused
1403     if (focusedNode == m_renderer->element() ||
1404         (roleValue() == WebAreaRole && document->frame()->selection()->isFocusedAndActive()))
1405         return true;
1406 
1407     return false;
1408 }
1409 
setFocused(bool on)1410 void AccessibilityRenderObject::setFocused(bool on)
1411 {
1412     if (!canSetFocusAttribute())
1413         return;
1414 
1415     if (!on)
1416         m_renderer->document()->setFocusedNode(0);
1417     else {
1418         if (m_renderer->element()->isElementNode())
1419             static_cast<Element*>(m_renderer->element())->focus();
1420         else
1421             m_renderer->document()->setFocusedNode(m_renderer->element());
1422     }
1423 }
1424 
setValue(const String & string)1425 void AccessibilityRenderObject::setValue(const String& string)
1426 {
1427     // FIXME: Do we want to do anything here for ARIA textboxes?
1428     if (m_renderer->isTextField()) {
1429         HTMLInputElement* input = static_cast<HTMLInputElement*>(m_renderer->element());
1430         input->setValue(string);
1431     } else if (m_renderer->isTextArea()) {
1432         HTMLTextAreaElement* textArea = static_cast<HTMLTextAreaElement*>(m_renderer->element());
1433         textArea->setValue(string);
1434     }
1435 }
1436 
isEnabled() const1437 bool AccessibilityRenderObject::isEnabled() const
1438 {
1439     return m_renderer->element() ? m_renderer->element()->isEnabled() : true;
1440 }
1441 
topRenderer() const1442 RenderView* AccessibilityRenderObject::topRenderer() const
1443 {
1444     return m_renderer->document()->topDocument()->renderView();
1445 }
1446 
document() const1447 Document* AccessibilityRenderObject::document() const
1448 {
1449     return m_renderer->document();
1450 }
1451 
topDocumentFrameView() const1452 FrameView* AccessibilityRenderObject::topDocumentFrameView() const
1453 {
1454     return topRenderer()->view()->frameView();
1455 }
1456 
widget() const1457 Widget* AccessibilityRenderObject::widget() const
1458 {
1459     if (!m_renderer->isWidget())
1460         return 0;
1461 
1462     return static_cast<RenderWidget*>(m_renderer)->widget();
1463 }
1464 
axObjectCache() const1465 AXObjectCache* AccessibilityRenderObject::axObjectCache() const
1466 {
1467     return m_renderer->document()->axObjectCache();
1468 }
1469 
accessibilityParentForImageMap(HTMLMapElement * map) const1470 AccessibilityObject* AccessibilityRenderObject::accessibilityParentForImageMap(HTMLMapElement* map) const
1471 {
1472     // find an image that is using this map
1473     if (!m_renderer || !map)
1474         return 0;
1475 
1476     RefPtr<HTMLCollection> coll = m_renderer->document()->images();
1477     for (Node* curr = coll->firstItem(); curr; curr = coll->nextItem()) {
1478         RenderObject* obj = curr->renderer();
1479         if (!obj || !curr->hasTagName(imgTag))
1480             continue;
1481 
1482         // The HTMLImageElement's useMap() value includes the '#' symbol at the beginning,
1483         // which has to be stripped off
1484         if (static_cast<HTMLImageElement*>(curr)->useMap().substring(1) == map->getName())
1485             return axObjectCache()->get(obj);
1486     }
1487 
1488     return 0;
1489 }
1490 
getDocumentLinks(AccessibilityChildrenVector & result)1491 void AccessibilityRenderObject::getDocumentLinks(AccessibilityChildrenVector& result)
1492 {
1493     Document* document = m_renderer->document();
1494     RefPtr<HTMLCollection> coll = document->links();
1495     Node* curr = coll->firstItem();
1496     while (curr) {
1497         RenderObject* obj = curr->renderer();
1498         if (obj) {
1499             RefPtr<AccessibilityObject> axobj = document->axObjectCache()->get(obj);
1500             ASSERT(axobj);
1501             ASSERT(axobj->roleValue() == WebCoreLinkRole);
1502             if (!axobj->accessibilityIsIgnored())
1503                 result.append(axobj);
1504         } else {
1505             Node* parent = curr->parent();
1506             if (parent && curr->hasTagName(areaTag) && parent->hasTagName(mapTag)) {
1507                 AccessibilityImageMapLink* areaObject = static_cast<AccessibilityImageMapLink*>(axObjectCache()->get(ImageMapLinkRole));
1508                 areaObject->setHTMLAreaElement(static_cast<HTMLAreaElement*>(curr));
1509                 areaObject->setHTMLMapElement(static_cast<HTMLMapElement*>(parent));
1510                 areaObject->setParent(accessibilityParentForImageMap(static_cast<HTMLMapElement*>(parent)));
1511 
1512                 result.append(areaObject);
1513             }
1514         }
1515         curr = coll->nextItem();
1516     }
1517 }
1518 
documentFrameView() const1519 FrameView* AccessibilityRenderObject::documentFrameView() const
1520 {
1521     if (!m_renderer || !m_renderer->document())
1522         return 0;
1523 
1524     // this is the RenderObject's Document's Frame's FrameView
1525     return m_renderer->document()->view();
1526 }
1527 
widgetForAttachmentView() const1528 Widget* AccessibilityRenderObject::widgetForAttachmentView() const
1529 {
1530     if (!isAttachment())
1531         return 0;
1532     return static_cast<RenderWidget*>(m_renderer)->widget();
1533 }
1534 
frameViewIfRenderView() const1535 FrameView* AccessibilityRenderObject::frameViewIfRenderView() const
1536 {
1537     if (!m_renderer->isRenderView())
1538         return 0;
1539     // this is the RenderObject's Document's renderer's FrameView
1540     return m_renderer->view()->frameView();
1541 }
1542 
1543 // This function is like a cross-platform version of - (WebCoreTextMarkerRange*)textMarkerRange. It returns
1544 // a Range that we can convert to a WebCoreTextMarkerRange in the Obj-C file
visiblePositionRange() const1545 VisiblePositionRange AccessibilityRenderObject::visiblePositionRange() const
1546 {
1547     if (!m_renderer)
1548         return VisiblePositionRange();
1549 
1550     // construct VisiblePositions for start and end
1551     Node* node = m_renderer->element();
1552     if (!node)
1553         return VisiblePositionRange();
1554 
1555     VisiblePosition startPos = VisiblePosition(node, 0, VP_DEFAULT_AFFINITY);
1556     VisiblePosition endPos = VisiblePosition(node, maxDeepOffset(node), VP_DEFAULT_AFFINITY);
1557 
1558     // the VisiblePositions are equal for nodes like buttons, so adjust for that
1559     if (startPos == endPos) {
1560         endPos = endPos.next();
1561         if (endPos.isNull())
1562             endPos = startPos;
1563     }
1564 
1565     return VisiblePositionRange(startPos, endPos);
1566 }
1567 
visiblePositionRangeForLine(unsigned lineCount) const1568 VisiblePositionRange AccessibilityRenderObject::visiblePositionRangeForLine(unsigned lineCount) const
1569 {
1570     if (lineCount == 0 || !m_renderer)
1571         return VisiblePositionRange();
1572 
1573     // iterate over the lines
1574     // FIXME: this is wrong when lineNumber is lineCount+1,  because nextLinePosition takes you to the
1575     // last offset of the last line
1576     VisiblePosition visiblePos = m_renderer->document()->renderer()->positionForCoordinates(0, 0);
1577     VisiblePosition savedVisiblePos;
1578     while (--lineCount != 0) {
1579         savedVisiblePos = visiblePos;
1580         visiblePos = nextLinePosition(visiblePos, 0);
1581         if (visiblePos.isNull() || visiblePos == savedVisiblePos)
1582             return VisiblePositionRange();
1583     }
1584 
1585     // make a caret selection for the marker position, then extend it to the line
1586     // NOTE: ignores results of sel.modify because it returns false when
1587     // starting at an empty line.  The resulting selection in that case
1588     // will be a caret at visiblePos.
1589     SelectionController selection;
1590     selection.setSelection(Selection(visiblePos));
1591     selection.modify(SelectionController::EXTEND, SelectionController::RIGHT, LineBoundary);
1592 
1593     return VisiblePositionRange(selection.selection().visibleStart(), selection.selection().visibleEnd());
1594 }
1595 
visiblePositionForIndex(int index) const1596 VisiblePosition AccessibilityRenderObject::visiblePositionForIndex(int index) const
1597 {
1598     if (!m_renderer)
1599         return VisiblePosition();
1600 
1601     if (isNativeTextControl())
1602         return static_cast<RenderTextControl*>(m_renderer)->visiblePositionForIndex(index);
1603 
1604     if (!isTextControl() && !m_renderer->isText())
1605         return VisiblePosition();
1606 
1607     Node* node = m_renderer->node();
1608     if (!node)
1609         return VisiblePosition();
1610 
1611     if (index <= 0)
1612         return VisiblePosition(node, 0, DOWNSTREAM);
1613 
1614     ExceptionCode ec = 0;
1615     RefPtr<Range> range = Range::create(m_renderer->document());
1616     range->selectNodeContents(node, ec);
1617     CharacterIterator it(range.get());
1618     it.advance(index - 1);
1619     return VisiblePosition(it.range()->endContainer(ec), it.range()->endOffset(ec), UPSTREAM);
1620 }
1621 
indexForVisiblePosition(const VisiblePosition & pos) const1622 int AccessibilityRenderObject::indexForVisiblePosition(const VisiblePosition& pos) const
1623 {
1624     if (isNativeTextControl())
1625         return static_cast<RenderTextControl*>(m_renderer)->indexForVisiblePosition(pos);
1626 
1627     if (!isTextControl())
1628         return 0;
1629 
1630     Node* node = m_renderer->node();
1631     if (!node)
1632         return 0;
1633 
1634     Position indexPosition = pos.deepEquivalent();
1635     if (!indexPosition.node() || indexPosition.node()->rootEditableElement() != node)
1636         return 0;
1637 
1638     ExceptionCode ec = 0;
1639     RefPtr<Range> range = Range::create(m_renderer->document());
1640     range->setStart(node, 0, ec);
1641     range->setEnd(indexPosition.node(), indexPosition.offset(), ec);
1642     return TextIterator::rangeLength(range.get());
1643 }
1644 
boundsForVisiblePositionRange(const VisiblePositionRange & visiblePositionRange) const1645 IntRect AccessibilityRenderObject::boundsForVisiblePositionRange(const VisiblePositionRange& visiblePositionRange) const
1646 {
1647     if (visiblePositionRange.isNull())
1648         return IntRect();
1649 
1650     // Create a mutable VisiblePositionRange.
1651     VisiblePositionRange range(visiblePositionRange);
1652     IntRect rect1 = range.start.absoluteCaretBounds();
1653     IntRect rect2 = range.end.absoluteCaretBounds();
1654 
1655     // readjust for position at the edge of a line.  This is to exclude line rect that doesn't need to be accounted in the range bounds
1656     if (rect2.y() != rect1.y()) {
1657         VisiblePosition endOfFirstLine = endOfLine(range.start);
1658         if (range.start == endOfFirstLine) {
1659             range.start.setAffinity(DOWNSTREAM);
1660             rect1 = range.start.absoluteCaretBounds();
1661         }
1662         if (range.end == endOfFirstLine) {
1663             range.end.setAffinity(UPSTREAM);
1664             rect2 = range.end.absoluteCaretBounds();
1665         }
1666     }
1667 
1668     IntRect ourrect = rect1;
1669     ourrect.unite(rect2);
1670 
1671     // if the rectangle spans lines and contains multiple text chars, use the range's bounding box intead
1672     if (rect1.bottom() != rect2.bottom()) {
1673         RefPtr<Range> dataRange = makeRange(range.start, range.end);
1674         IntRect boundingBox = dataRange->boundingBox();
1675         String rangeString = plainText(dataRange.get());
1676         if (rangeString.length() > 1 && !boundingBox.isEmpty())
1677             ourrect = boundingBox;
1678     }
1679 
1680 #if PLATFORM(MAC)
1681     return m_renderer->document()->view()->contentsToScreen(ourrect);
1682 #else
1683     return ourrect;
1684 #endif
1685 }
1686 
setSelectedVisiblePositionRange(const VisiblePositionRange & range) const1687 void AccessibilityRenderObject::setSelectedVisiblePositionRange(const VisiblePositionRange& range) const
1688 {
1689     if (range.start.isNull() || range.end.isNull())
1690         return;
1691 
1692     // make selection and tell the document to use it. if it's zero length, then move to that position
1693     if (range.start == range.end) {
1694         m_renderer->document()->frame()->selection()->moveTo(range.start, true);
1695     }
1696     else {
1697         Selection newSelection = Selection(range.start, range.end);
1698         m_renderer->document()->frame()->selection()->setSelection(newSelection);
1699     }
1700 }
1701 
visiblePositionForPoint(const IntPoint & point) const1702 VisiblePosition AccessibilityRenderObject::visiblePositionForPoint(const IntPoint& point) const
1703 {
1704     // convert absolute point to view coordinates
1705     FrameView* frameView = m_renderer->document()->topDocument()->renderer()->view()->frameView();
1706     RenderView* renderView = topRenderer();
1707     Node* innerNode = 0;
1708 
1709     // locate the node containing the point
1710     IntPoint pointResult;
1711     while (1) {
1712         IntPoint ourpoint;
1713 #if PLATFORM(MAC)
1714         ourpoint = frameView->screenToContents(point);
1715 #else
1716         ourpoint = point;
1717 #endif
1718         HitTestRequest request(true, true);
1719         HitTestResult result(ourpoint);
1720         renderView->layer()->hitTest(request, result);
1721         innerNode = result.innerNode();
1722         if (!innerNode || !innerNode->renderer())
1723             return VisiblePosition();
1724 
1725         pointResult = result.localPoint();
1726 
1727         // done if hit something other than a widget
1728         RenderObject* renderer = innerNode->renderer();
1729         if (!renderer->isWidget())
1730             break;
1731 
1732         // descend into widget (FRAME, IFRAME, OBJECT...)
1733         Widget* widget = static_cast<RenderWidget*>(renderer)->widget();
1734         if (!widget || !widget->isFrameView())
1735             break;
1736         Frame* frame = static_cast<FrameView*>(widget)->frame();
1737         if (!frame)
1738             break;
1739         Document* document = frame->document();
1740         if (!document)
1741             break;
1742         renderView = document->renderView();
1743         frameView = static_cast<FrameView*>(widget);
1744     }
1745 
1746     return innerNode->renderer()->positionForPoint(pointResult);
1747 }
1748 
1749 // NOTE: Consider providing this utility method as AX API
visiblePositionForIndex(unsigned indexValue,bool lastIndexOK) const1750 VisiblePosition AccessibilityRenderObject::visiblePositionForIndex(unsigned indexValue, bool lastIndexOK) const
1751 {
1752     if (!isTextControl())
1753         return VisiblePosition();
1754 
1755     // lastIndexOK specifies whether the position after the last character is acceptable
1756     if (indexValue >= text().length()) {
1757         if (!lastIndexOK || indexValue > text().length())
1758             return VisiblePosition();
1759     }
1760     VisiblePosition position = visiblePositionForIndex(indexValue);
1761     position.setAffinity(DOWNSTREAM);
1762     return position;
1763 }
1764 
1765 // NOTE: Consider providing this utility method as AX API
index(const VisiblePosition & position) const1766 int AccessibilityRenderObject::index(const VisiblePosition& position) const
1767 {
1768     if (!isTextControl())
1769         return -1;
1770 
1771     Node* node = position.deepEquivalent().node();
1772     if (!node)
1773         return -1;
1774 
1775     for (RenderObject* renderer = node->renderer(); renderer && renderer->element(); renderer = renderer->parent()) {
1776         if (renderer == m_renderer)
1777             return indexForVisiblePosition(position);
1778     }
1779 
1780     return -1;
1781 }
1782 
1783 // Given a line number, the range of characters of the text associated with this accessibility
1784 // object that contains the line number.
doAXRangeForLine(unsigned lineNumber) const1785 PlainTextRange AccessibilityRenderObject::doAXRangeForLine(unsigned lineNumber) const
1786 {
1787     if (!isTextControl())
1788         return PlainTextRange();
1789 
1790     // iterate to the specified line
1791     VisiblePosition visiblePos = visiblePositionForIndex(0);
1792     VisiblePosition savedVisiblePos;
1793     for (unsigned lineCount = lineNumber; lineCount != 0; lineCount -= 1) {
1794         savedVisiblePos = visiblePos;
1795         visiblePos = nextLinePosition(visiblePos, 0);
1796         if (visiblePos.isNull() || visiblePos == savedVisiblePos)
1797             return PlainTextRange();
1798     }
1799 
1800     // make a caret selection for the marker position, then extend it to the line
1801     // NOTE: ignores results of selection.modify because it returns false when
1802     // starting at an empty line.  The resulting selection in that case
1803     // will be a caret at visiblePos.
1804     SelectionController selection;
1805     selection.setSelection(Selection(visiblePos));
1806     selection.modify(SelectionController::EXTEND, SelectionController::LEFT, LineBoundary);
1807     selection.modify(SelectionController::EXTEND, SelectionController::RIGHT, LineBoundary);
1808 
1809     // calculate the indices for the selection start and end
1810     VisiblePosition startPosition = selection.selection().visibleStart();
1811     VisiblePosition endPosition = selection.selection().visibleEnd();
1812     int index1 = indexForVisiblePosition(startPosition);
1813     int index2 = indexForVisiblePosition(endPosition);
1814 
1815     // add one to the end index for a line break not caused by soft line wrap (to match AppKit)
1816     if (endPosition.affinity() == DOWNSTREAM && endPosition.next().isNotNull())
1817         index2 += 1;
1818 
1819     // return nil rather than an zero-length range (to match AppKit)
1820     if (index1 == index2)
1821         return PlainTextRange();
1822 
1823     return PlainTextRange(index1, index2 - index1);
1824 }
1825 
1826 // The composed character range in the text associated with this accessibility object that
1827 // is specified by the given index value. This parameterized attribute returns the complete
1828 // range of characters (including surrogate pairs of multi-byte glyphs) at the given index.
doAXRangeForIndex(unsigned index) const1829 PlainTextRange AccessibilityRenderObject::doAXRangeForIndex(unsigned index) const
1830 {
1831     if (!isTextControl())
1832         return PlainTextRange();
1833 
1834     String elementText = text();
1835     if (!elementText.length() || index > elementText.length() - 1)
1836         return PlainTextRange();
1837 
1838     return PlainTextRange(index, 1);
1839 }
1840 
1841 // A substring of the text associated with this accessibility object that is
1842 // specified by the given character range.
doAXStringForRange(const PlainTextRange & range) const1843 String AccessibilityRenderObject::doAXStringForRange(const PlainTextRange& range) const
1844 {
1845     if (isPasswordField())
1846         return String();
1847 
1848     if (range.length == 0)
1849         return "";
1850 
1851     if (!isTextControl())
1852         return String();
1853 
1854     String elementText = text();
1855     if (range.start + range.length > elementText.length())
1856         return String();
1857 
1858     return elementText.substring(range.start, range.length);
1859 }
1860 
1861 // The bounding rectangle of the text associated with this accessibility object that is
1862 // specified by the given range. This is the bounding rectangle a sighted user would see
1863 // on the display screen, in pixels.
doAXBoundsForRange(const PlainTextRange & range) const1864 IntRect AccessibilityRenderObject::doAXBoundsForRange(const PlainTextRange& range) const
1865 {
1866     if (isTextControl())
1867         return boundsForVisiblePositionRange(visiblePositionRangeForRange(range));
1868     return IntRect();
1869 }
1870 
doAccessibilityHitTest(const IntPoint & point) const1871 AccessibilityObject* AccessibilityRenderObject::doAccessibilityHitTest(const IntPoint& point) const
1872 {
1873     if (!m_renderer || !m_renderer->hasLayer())
1874         return 0;
1875 
1876     RenderLayer* layer = toRenderBox(m_renderer)->layer();
1877 
1878     HitTestRequest request(true, true);
1879     HitTestResult hitTestResult = HitTestResult(point);
1880     layer->hitTest(request, hitTestResult);
1881     if (!hitTestResult.innerNode())
1882         return 0;
1883     Node* node = hitTestResult.innerNode()->shadowAncestorNode();
1884     RenderObject* obj = node->renderer();
1885     if (!obj)
1886         return 0;
1887 
1888     AccessibilityObject *result = obj->document()->axObjectCache()->get(obj);
1889 
1890     if (obj->isListBox())
1891         return static_cast<AccessibilityListBox*>(result)->doAccessibilityHitTest(point);
1892 
1893     if (result->accessibilityIsIgnored())
1894         result = result->parentObjectUnignored();
1895 
1896     return result;
1897 }
1898 
focusedUIElement() const1899 AccessibilityObject* AccessibilityRenderObject::focusedUIElement() const
1900 {
1901     // get the focused node in the page
1902     Page* page = m_renderer->document()->page();
1903     if (!page)
1904         return 0;
1905 
1906     Document* focusedDocument = page->focusController()->focusedOrMainFrame()->document();
1907     Node* focusedNode = focusedDocument->focusedNode();
1908     if (!focusedNode)
1909         focusedNode = focusedDocument;
1910 
1911     RenderObject* focusedNodeRenderer = focusedNode->renderer();
1912     if (!focusedNodeRenderer)
1913         return 0;
1914 
1915     AccessibilityObject* obj = focusedNodeRenderer->document()->axObjectCache()->get(focusedNodeRenderer);
1916 
1917     if (obj->shouldFocusActiveDescendant()) {
1918         if (AccessibilityObject* descendant = obj->activeDescendant())
1919             obj = descendant;
1920     }
1921 
1922     // the HTML element, for example, is focusable but has an AX object that is ignored
1923     if (obj->accessibilityIsIgnored())
1924         obj = obj->parentObjectUnignored();
1925 
1926     return obj;
1927 }
1928 
shouldFocusActiveDescendant() const1929 bool AccessibilityRenderObject::shouldFocusActiveDescendant() const
1930 {
1931     switch (ariaRoleAttribute()) {
1932     case GroupRole:
1933     case ComboBoxRole:
1934     case ListBoxRole:
1935     case MenuRole:
1936     case MenuBarRole:
1937     case RadioGroupRole:
1938     case RowRole:
1939     case PopUpButtonRole:
1940     case ProgressIndicatorRole:
1941     case ToolbarRole:
1942     case OutlineRole:
1943     /* FIXME: replace these with actual roles when they are added to AccessibilityRole
1944     composite
1945     alert
1946     alertdialog
1947     grid
1948     status
1949     timer
1950     tree
1951     */
1952         return true;
1953     default:
1954         return false;
1955     }
1956 }
1957 
activeDescendant() const1958 AccessibilityObject* AccessibilityRenderObject::activeDescendant() const
1959 {
1960     if (renderer()->element() && !renderer()->element()->isElementNode())
1961         return 0;
1962     Element* element = static_cast<Element*>(renderer()->element());
1963 
1964     String activeDescendantAttrStr = element->getAttribute(aria_activedescendantAttr).string();
1965     if (activeDescendantAttrStr.isNull() || activeDescendantAttrStr.isEmpty())
1966         return 0;
1967 
1968     Element* target = renderer()->document()->getElementById(activeDescendantAttrStr);
1969     if (!target)
1970         return 0;
1971 
1972     AccessibilityObject* obj = renderer()->document()->axObjectCache()->get(target->renderer());
1973     if (obj->isAccessibilityRenderObject())
1974     // an activedescendant is only useful if it has a renderer, because that's what's needed to post the notification
1975         return obj;
1976     return 0;
1977 }
1978 
1979 
handleActiveDescendantChanged()1980 void AccessibilityRenderObject::handleActiveDescendantChanged()
1981 {
1982     Element* element = static_cast<Element*>(renderer()->element());
1983     if (!element)
1984         return;
1985     Document* doc = renderer()->document();
1986     if (!doc->frame()->selection()->isFocusedAndActive() || doc->focusedNode() != element)
1987         return;
1988     AccessibilityRenderObject* activedescendant = static_cast<AccessibilityRenderObject*>(activeDescendant());
1989 
1990     if (activedescendant && shouldFocusActiveDescendant())
1991         doc->axObjectCache()->postNotificationToElement(activedescendant->renderer(), "AXFocusedUIElementChanged");
1992 }
1993 
1994 
observableObject() const1995 AccessibilityObject* AccessibilityRenderObject::observableObject() const
1996 {
1997     for (RenderObject* renderer = m_renderer; renderer && renderer->element(); renderer = renderer->parent()) {
1998         if (renderer->isTextField() || renderer->isTextArea())
1999             return renderer->document()->axObjectCache()->get(renderer);
2000     }
2001 
2002     return 0;
2003 }
2004 
2005 typedef HashMap<String, AccessibilityRole, CaseFoldingHash> ARIARoleMap;
2006 
createARIARoleMap()2007 static const ARIARoleMap& createARIARoleMap()
2008 {
2009     struct RoleEntry {
2010         String ariaRole;
2011         AccessibilityRole webcoreRole;
2012     };
2013 
2014     const RoleEntry roles[] = {
2015         { "button", ButtonRole },
2016         { "checkbox", CheckBoxRole },
2017         { "group", GroupRole },
2018         { "heading", HeadingRole },
2019         { "img", ImageRole },
2020         { "link", WebCoreLinkRole },
2021         { "listbox", ListBoxRole },
2022         // "option" isn't here because it may map to different roles depending on the parent element's role
2023         { "menu", MenuRole },
2024         { "menubar", GroupRole },
2025         // "menuitem" isn't here because it may map to different roles depending on the parent element's role
2026         { "menuitemcheckbox", MenuItemRole },
2027         { "menuitemradio", MenuItemRole },
2028         { "progressbar", ProgressIndicatorRole },
2029         { "radio", RadioButtonRole },
2030         { "range", SliderRole },
2031         { "slider", SliderRole },
2032         { "spinbutton", ProgressIndicatorRole },
2033         { "textbox", TextAreaRole }
2034     };
2035     ARIARoleMap& roleMap = *new ARIARoleMap;
2036 
2037     const unsigned numRoles = sizeof(roles) / sizeof(roles[0]);
2038     for (unsigned i = 0; i < numRoles; ++i)
2039         roleMap.set(roles[i].ariaRole, roles[i].webcoreRole);
2040     return roleMap;
2041 }
2042 
ariaRoleToWebCoreRole(String value)2043 static AccessibilityRole ariaRoleToWebCoreRole(String value)
2044 {
2045     ASSERT(!value.isEmpty() && !value.isNull());
2046     static const ARIARoleMap& roleMap = createARIARoleMap();
2047     return roleMap.get(value);
2048 }
2049 
determineAriaRoleAttribute() const2050 AccessibilityRole AccessibilityRenderObject::determineAriaRoleAttribute() const
2051 {
2052     String ariaRole = getAttribute(roleAttr).string();
2053     if (ariaRole.isNull() || ariaRole.isEmpty())
2054         return UnknownRole;
2055 
2056     AccessibilityRole role = ariaRoleToWebCoreRole(ariaRole);
2057     if (role)
2058         return role;
2059     // selects and listboxes both have options as child roles, but they map to different roles within WebCore
2060     if (equalIgnoringCase(ariaRole,"option")) {
2061         if (parentObjectUnignored()->ariaRoleAttribute() == MenuRole)
2062             return MenuItemRole;
2063         if (parentObjectUnignored()->ariaRoleAttribute() == ListBoxRole)
2064             return ListBoxOptionRole;
2065     }
2066     // an aria "menuitem" may map to MenuButton or MenuItem depending on its parent
2067     if (equalIgnoringCase(ariaRole,"menuitem")) {
2068         if (parentObjectUnignored()->ariaRoleAttribute() == GroupRole)
2069             return MenuButtonRole;
2070         if (parentObjectUnignored()->ariaRoleAttribute() == MenuRole)
2071             return MenuItemRole;
2072     }
2073 
2074     return UnknownRole;
2075 }
2076 
setAriaRole()2077 void AccessibilityRenderObject::setAriaRole()
2078 {
2079     m_ariaRole = determineAriaRoleAttribute();
2080 }
2081 
ariaRoleAttribute() const2082 AccessibilityRole AccessibilityRenderObject::ariaRoleAttribute() const
2083 {
2084     return m_ariaRole;
2085 }
2086 
roleValue() const2087 AccessibilityRole AccessibilityRenderObject::roleValue() const
2088 {
2089     if (!m_renderer)
2090         return UnknownRole;
2091 
2092     Node* node = m_renderer->element();
2093     AccessibilityRole ariaRole = ariaRoleAttribute();
2094     if (ariaRole != UnknownRole)
2095         return ariaRole;
2096 
2097     if (node && node->isLink()) {
2098         if (m_renderer->isImage())
2099             return ImageMapRole;
2100         return WebCoreLinkRole;
2101     }
2102     if (m_renderer->isListMarker())
2103         return ListMarkerRole;
2104     if (node && node->hasTagName(buttonTag))
2105         return ButtonRole;
2106     if (m_renderer->isText())
2107         return StaticTextRole;
2108     if (m_renderer->isImage()) {
2109         if (node && node->hasTagName(inputTag))
2110             return ButtonRole;
2111         return ImageRole;
2112     }
2113     if (m_renderer->isRenderView())
2114         return WebAreaRole;
2115 
2116     if (m_renderer->isTextField())
2117         return TextFieldRole;
2118 
2119     if (m_renderer->isTextArea())
2120         return TextAreaRole;
2121 
2122     if (node && node->hasTagName(inputTag)) {
2123         HTMLInputElement* input = static_cast<HTMLInputElement*>(node);
2124         if (input->inputType() == HTMLInputElement::CHECKBOX)
2125             return CheckBoxRole;
2126         if (input->inputType() == HTMLInputElement::RADIO)
2127             return RadioButtonRole;
2128         if (input->isTextButton())
2129             return ButtonRole;
2130     }
2131 
2132     if (node && node->hasTagName(buttonTag))
2133         return ButtonRole;
2134 
2135     if (isFileUploadButton())
2136         return ButtonRole;
2137 
2138     if (m_renderer->isMenuList())
2139         return PopUpButtonRole;
2140 
2141     if (headingLevel(m_renderer->element()) != 0)
2142         return HeadingRole;
2143 
2144     if (node && node->hasTagName(ddTag))
2145         return DefinitionListDefinitionRole;
2146 
2147     if (node && node->hasTagName(dtTag))
2148         return DefinitionListTermRole;
2149 
2150     if (m_renderer->isBlockFlow() || (node && node->hasTagName(labelTag)))
2151         return GroupRole;
2152 
2153     return UnknownRole;
2154 }
2155 
isPresentationalChildOfAriaRole() const2156 bool AccessibilityRenderObject::isPresentationalChildOfAriaRole() const
2157 {
2158     // Walk the parent chain looking for a parent that has presentational children
2159     AccessibilityObject* parent;
2160     for (parent = parentObject(); parent && !parent->ariaRoleHasPresentationalChildren(); parent = parent->parentObject())
2161         ;
2162     return parent;
2163 }
2164 
ariaRoleHasPresentationalChildren() const2165 bool AccessibilityRenderObject::ariaRoleHasPresentationalChildren() const
2166 {
2167     switch (m_ariaRole) {
2168     case ButtonRole:
2169     case SliderRole:
2170     case ImageRole:
2171     case ProgressIndicatorRole:
2172     //case SeparatorRole:
2173         return true;
2174     default:
2175         return false;
2176     }
2177 }
2178 
canSetFocusAttribute() const2179 bool AccessibilityRenderObject::canSetFocusAttribute() const
2180 {
2181     // NOTE: It would be more accurate to ask the document whether setFocusedNode() would
2182     // do anything.  For example, it setFocusedNode() will do nothing if the current focused
2183     // node will not relinquish the focus.
2184     if (!m_renderer->element() || !m_renderer->element()->isEnabled())
2185         return false;
2186 
2187     switch (roleValue()) {
2188         case WebCoreLinkRole:
2189         case ImageMapLinkRole:
2190         case TextFieldRole:
2191         case TextAreaRole:
2192         case ButtonRole:
2193         case PopUpButtonRole:
2194         case CheckBoxRole:
2195         case RadioButtonRole:
2196             return true;
2197         default:
2198             return false;
2199     }
2200 }
2201 
canSetValueAttribute() const2202 bool AccessibilityRenderObject::canSetValueAttribute() const
2203 {
2204     if (isWebArea())
2205         return !isReadOnly();
2206 
2207     return isTextControl() || isProgressIndicator() || isSlider();
2208 }
2209 
canSetTextRangeAttributes() const2210 bool AccessibilityRenderObject::canSetTextRangeAttributes() const
2211 {
2212     return isTextControl();
2213 }
2214 
childrenChanged()2215 void AccessibilityRenderObject::childrenChanged()
2216 {
2217     // this method is meant as a quick way of marking dirty
2218     // a portion of the accessibility tree
2219 
2220     markChildrenDirty();
2221 
2222     // this object may not be accessible (and thus may not appear
2223     // in the hierarchy), which means we need to go up the parent
2224     // chain and mark the parent's dirty. Ideally, we would want
2225     // to only access the next object that is not ignored, but
2226     // asking an element if it's ignored can lead to an examination of the
2227     // render tree which is dangerous.
2228     for (AccessibilityObject* parent = parentObject(); parent; parent = parent->parentObject()) {
2229         if (parent->isAccessibilityRenderObject())
2230             static_cast<AccessibilityRenderObject *>(parent)->markChildrenDirty();
2231     }
2232 }
2233 
canHaveChildren() const2234 bool AccessibilityRenderObject::canHaveChildren() const
2235 {
2236     if (!m_renderer)
2237         return false;
2238 
2239     // Elements that should not have children
2240     switch (roleValue()) {
2241         case ImageRole:
2242         case ButtonRole:
2243         case PopUpButtonRole:
2244         case CheckBoxRole:
2245         case RadioButtonRole:
2246             return false;
2247         default:
2248             return true;
2249     }
2250 }
2251 
children()2252 const AccessibilityObject::AccessibilityChildrenVector& AccessibilityRenderObject::children()
2253 {
2254     if (m_childrenDirty) {
2255         clearChildren();
2256         m_childrenDirty = false;
2257     }
2258 
2259     if (!m_haveChildren)
2260         addChildren();
2261     return m_children;
2262 }
2263 
addChildren()2264 void AccessibilityRenderObject::addChildren()
2265 {
2266     // If the need to add more children in addition to existing children arises,
2267     // childrenChanged should have been called, leaving the object with no children.
2268     ASSERT(!m_haveChildren);
2269 
2270     // nothing to add if there is no RenderObject
2271     if (!m_renderer)
2272         return;
2273 
2274     m_haveChildren = true;
2275 
2276     if (!canHaveChildren())
2277         return;
2278 
2279     // add all unignored acc children
2280     for (RefPtr<AccessibilityObject> obj = firstChild(); obj; obj = obj->nextSibling()) {
2281         if (obj->accessibilityIsIgnored()) {
2282             if (!obj->hasChildren())
2283                 obj->addChildren();
2284             AccessibilityChildrenVector children = obj->children();
2285             unsigned length = children.size();
2286             for (unsigned i = 0; i < length; ++i)
2287                 m_children.append(children[i]);
2288         } else
2289             m_children.append(obj);
2290     }
2291 
2292     // for a RenderImage, add the <area> elements as individual accessibility objects
2293     if (m_renderer->isRenderImage()) {
2294         HTMLMapElement* map = static_cast<RenderImage*>(m_renderer)->imageMap();
2295         if (map) {
2296             for (Node* current = map->firstChild(); current; current = current->traverseNextNode(map)) {
2297 
2298                 // add an <area> element for this child if it has a link
2299                 if (current->isLink()) {
2300                     AccessibilityImageMapLink* areaObject = static_cast<AccessibilityImageMapLink*>(m_renderer->document()->axObjectCache()->get(ImageMapLinkRole));
2301                     areaObject->setHTMLAreaElement(static_cast<HTMLAreaElement*>(current));
2302                     areaObject->setHTMLMapElement(map);
2303                     areaObject->setParent(this);
2304 
2305                     m_children.append(areaObject);
2306                 }
2307             }
2308         }
2309     }
2310 }
2311 
ariaListboxSelectedChildren(AccessibilityChildrenVector & result)2312 void AccessibilityRenderObject::ariaListboxSelectedChildren(AccessibilityChildrenVector& result)
2313 {
2314     AccessibilityObject* child = firstChild();
2315     bool isMultiselectable = false;
2316 
2317     Element* element = static_cast<Element*>(renderer()->element());
2318     if (!element || !element->isElementNode()) // do this check to ensure safety of static_cast above
2319         return;
2320 
2321     String multiselectablePropertyStr = element->getAttribute("aria-multiselectable").string();
2322     isMultiselectable = equalIgnoringCase(multiselectablePropertyStr, "true");
2323 
2324     while (child) {
2325         // every child should have aria-role option, and if so, check for selected attribute/state
2326         AccessibilityRole ariaRole = child->ariaRoleAttribute();
2327         RenderObject* childRenderer = 0;
2328         if (child->isAccessibilityRenderObject())
2329             childRenderer = static_cast<AccessibilityRenderObject*>(child)->renderer();
2330         if (childRenderer && ariaRole == ListBoxOptionRole) {
2331             Element* childElement = static_cast<Element*>(childRenderer->element());
2332             if (childElement && childElement->isElementNode()) { // do this check to ensure safety of static_cast above
2333                 String selectedAttrString = childElement->getAttribute("aria-selected").string();
2334                 if (equalIgnoringCase(selectedAttrString, "true")) {
2335                     result.append(child);
2336                     if (isMultiselectable)
2337                         return;
2338                 }
2339             }
2340         }
2341         child = child->nextSibling();
2342     }
2343 }
2344 
selectedChildren(AccessibilityChildrenVector & result)2345 void AccessibilityRenderObject::selectedChildren(AccessibilityChildrenVector& result)
2346 {
2347     ASSERT(result.isEmpty());
2348 
2349     // only listboxes should be asked for their selected children.
2350     if (ariaRoleAttribute() != ListBoxRole) { // native list boxes would be AccessibilityListBoxes, so only check for aria list boxes
2351         ASSERT_NOT_REACHED();
2352         return;
2353     }
2354     return ariaListboxSelectedChildren(result);
2355 }
2356 
ariaListboxVisibleChildren(AccessibilityChildrenVector & result)2357 void AccessibilityRenderObject::ariaListboxVisibleChildren(AccessibilityChildrenVector& result)
2358 {
2359     if (!hasChildren())
2360         addChildren();
2361 
2362     unsigned length = m_children.size();
2363     for (unsigned i = 0; i < length; i++) {
2364         if (!m_children[i]->isOffScreen())
2365             result.append(m_children[i]);
2366     }
2367 }
2368 
visibleChildren(AccessibilityChildrenVector & result)2369 void AccessibilityRenderObject::visibleChildren(AccessibilityChildrenVector& result)
2370 {
2371     ASSERT(result.isEmpty());
2372 
2373     // only listboxes are asked for their visible children.
2374     if (ariaRoleAttribute() != ListBoxRole) { // native list boxes would be AccessibilityListBoxes, so only check for aria list boxes
2375         ASSERT_NOT_REACHED();
2376         return;
2377     }
2378     return ariaListboxVisibleChildren(result);
2379 }
2380 
removeAXObjectID()2381 void AccessibilityRenderObject::removeAXObjectID()
2382 {
2383     if (!m_id)
2384         return;
2385 #if PLATFORM(MAC)
2386     m_renderer->document()->axObjectCache()->removeAXID(this);
2387 #endif
2388 }
2389 
actionVerb() const2390 const String& AccessibilityRenderObject::actionVerb() const
2391 {
2392     // FIXME: Need to add verbs for select elements.
2393     DEFINE_STATIC_LOCAL(const String, buttonAction, (AXButtonActionVerb()));
2394     DEFINE_STATIC_LOCAL(const String, textFieldAction, (AXTextFieldActionVerb()));
2395     DEFINE_STATIC_LOCAL(const String, radioButtonAction, (AXRadioButtonActionVerb()));
2396     DEFINE_STATIC_LOCAL(const String, checkedCheckBoxAction, (AXCheckedCheckBoxActionVerb()));
2397     DEFINE_STATIC_LOCAL(const String, uncheckedCheckBoxAction, (AXUncheckedCheckBoxActionVerb()));
2398     DEFINE_STATIC_LOCAL(const String, linkAction, (AXLinkActionVerb()));
2399     DEFINE_STATIC_LOCAL(const String, noAction, ());
2400 
2401     switch (roleValue()) {
2402         case ButtonRole:
2403             return buttonAction;
2404         case TextFieldRole:
2405         case TextAreaRole:
2406             return textFieldAction;
2407         case RadioButtonRole:
2408             return radioButtonAction;
2409         case CheckBoxRole:
2410             return isChecked() ? checkedCheckBoxAction : uncheckedCheckBoxAction;
2411         case LinkRole:
2412         case WebCoreLinkRole:
2413             return linkAction;
2414         default:
2415             return noAction;
2416     }
2417 }
2418 
updateBackingStore()2419 void AccessibilityRenderObject::updateBackingStore()
2420 {
2421     if (!m_renderer)
2422         return;
2423     m_renderer->view()->layoutIfNeeded();
2424 }
2425 
2426 } // namespace WebCore
2427