• 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 "HTMLFormElement.h"
43 #include "HTMLFrameElementBase.h"
44 #include "HTMLImageElement.h"
45 #include "HTMLInputElement.h"
46 #include "HTMLLabelElement.h"
47 #include "HTMLMapElement.h"
48 #include "HTMLOptGroupElement.h"
49 #include "HTMLOptionElement.h"
50 #include "HTMLOptionsCollection.h"
51 #include "HTMLSelectElement.h"
52 #include "HTMLTextAreaElement.h"
53 #include "HitTestRequest.h"
54 #include "HitTestResult.h"
55 #include "LocalizedStrings.h"
56 #include "NodeList.h"
57 #include "Page.h"
58 #include "RenderButton.h"
59 #include "RenderFieldset.h"
60 #include "RenderFileUploadControl.h"
61 #include "RenderHTMLCanvas.h"
62 #include "RenderImage.h"
63 #include "RenderInline.h"
64 #include "RenderListBox.h"
65 #include "RenderListMarker.h"
66 #include "RenderMenuList.h"
67 #include "RenderText.h"
68 #include "RenderTextControl.h"
69 #include "RenderTheme.h"
70 #include "RenderView.h"
71 #include "RenderWidget.h"
72 #include "SelectionController.h"
73 #include "Text.h"
74 #include "TextIterator.h"
75 #include "htmlediting.h"
76 #include "visible_units.h"
77 #include <wtf/StdLibExtras.h>
78 
79 using namespace std;
80 
81 namespace WebCore {
82 
83 using namespace HTMLNames;
84 
AccessibilityRenderObject(RenderObject * renderer)85 AccessibilityRenderObject::AccessibilityRenderObject(RenderObject* renderer)
86     : AccessibilityObject()
87     , m_renderer(renderer)
88     , m_ariaRole(UnknownRole)
89 {
90     updateAccessibilityRole();
91 #ifndef NDEBUG
92     m_renderer->setHasAXObject(true);
93 #endif
94 }
95 
~AccessibilityRenderObject()96 AccessibilityRenderObject::~AccessibilityRenderObject()
97 {
98     ASSERT(isDetached());
99 }
100 
create(RenderObject * renderer)101 PassRefPtr<AccessibilityRenderObject> AccessibilityRenderObject::create(RenderObject* renderer)
102 {
103     return adoptRef(new AccessibilityRenderObject(renderer));
104 }
105 
detach()106 void AccessibilityRenderObject::detach()
107 {
108     clearChildren();
109     AccessibilityObject::detach();
110 
111 #ifndef NDEBUG
112     if (m_renderer)
113         m_renderer->setHasAXObject(false);
114 #endif
115     m_renderer = 0;
116 }
117 
firstChild() const118 AccessibilityObject* AccessibilityRenderObject::firstChild() const
119 {
120     if (!m_renderer)
121         return 0;
122 
123     RenderObject* firstChild = m_renderer->firstChild();
124     if (!firstChild)
125         return 0;
126 
127     return m_renderer->document()->axObjectCache()->getOrCreate(firstChild);
128 }
129 
lastChild() const130 AccessibilityObject* AccessibilityRenderObject::lastChild() const
131 {
132     if (!m_renderer)
133         return 0;
134 
135     RenderObject* lastChild = m_renderer->lastChild();
136     if (!lastChild)
137         return 0;
138 
139     return m_renderer->document()->axObjectCache()->getOrCreate(lastChild);
140 }
141 
previousSibling() const142 AccessibilityObject* AccessibilityRenderObject::previousSibling() const
143 {
144     if (!m_renderer)
145         return 0;
146 
147     RenderObject* previousSibling = m_renderer->previousSibling();
148     if (!previousSibling)
149         return 0;
150 
151     return m_renderer->document()->axObjectCache()->getOrCreate(previousSibling);
152 }
153 
nextSibling() const154 AccessibilityObject* AccessibilityRenderObject::nextSibling() const
155 {
156     if (!m_renderer)
157         return 0;
158 
159     RenderObject* nextSibling = m_renderer->nextSibling();
160     if (!nextSibling)
161         return 0;
162 
163     return m_renderer->document()->axObjectCache()->getOrCreate(nextSibling);
164 }
165 
parentObjectIfExists() const166 AccessibilityObject* AccessibilityRenderObject::parentObjectIfExists() const
167 {
168     if (!m_renderer)
169         return 0;
170 
171     RenderObject *parent = m_renderer->parent();
172     if (!parent)
173         return 0;
174 
175     return m_renderer->document()->axObjectCache()->get(parent);
176 }
177 
parentObject() const178 AccessibilityObject* AccessibilityRenderObject::parentObject() const
179 {
180     if (!m_renderer)
181         return 0;
182 
183     RenderObject *parent = m_renderer->parent();
184     if (!parent)
185         return 0;
186 
187     if (ariaRoleAttribute() == MenuBarRole)
188         return m_renderer->document()->axObjectCache()->getOrCreate(parent);
189 
190     // menuButton and its corresponding menu are DOM siblings, but Accessibility needs them to be parent/child
191     if (ariaRoleAttribute() == MenuRole) {
192         AccessibilityObject* parent = menuButtonForMenu();
193         if (parent)
194             return parent;
195     }
196 
197     return m_renderer->document()->axObjectCache()->getOrCreate(parent);
198 }
199 
isWebArea() const200 bool AccessibilityRenderObject::isWebArea() const
201 {
202     return roleValue() == WebAreaRole;
203 }
204 
isImageButton() const205 bool AccessibilityRenderObject::isImageButton() const
206 {
207     return isNativeImage() && roleValue() == ButtonRole;
208 }
209 
isAnchor() const210 bool AccessibilityRenderObject::isAnchor() const
211 {
212     return !isNativeImage() && isLink();
213 }
214 
isNativeTextControl() const215 bool AccessibilityRenderObject::isNativeTextControl() const
216 {
217     return m_renderer->isTextControl();
218 }
219 
isTextControl() const220 bool AccessibilityRenderObject::isTextControl() const
221 {
222     AccessibilityRole role = roleValue();
223     return role == TextAreaRole || role == TextFieldRole;
224 }
225 
isNativeImage() const226 bool AccessibilityRenderObject::isNativeImage() const
227 {
228     return m_renderer->isImage();
229 }
230 
isImage() const231 bool AccessibilityRenderObject::isImage() const
232 {
233     return roleValue() == ImageRole;
234 }
235 
isAttachment() const236 bool AccessibilityRenderObject::isAttachment() const
237 {
238     if (!m_renderer)
239         return false;
240 
241     // Widgets are the replaced elements that we represent to AX as attachments
242     bool isWidget = m_renderer && m_renderer->isWidget();
243     ASSERT(!isWidget || (m_renderer->isReplaced() && !isImage()));
244     return isWidget && ariaRoleAttribute() == UnknownRole;
245 }
246 
isPasswordField() const247 bool AccessibilityRenderObject::isPasswordField() const
248 {
249     ASSERT(m_renderer);
250     if (!m_renderer->node() || !m_renderer->node()->isHTMLElement())
251         return false;
252     if (ariaRoleAttribute() != UnknownRole)
253         return false;
254 
255     InputElement* inputElement = toInputElement(static_cast<Element*>(m_renderer->node()));
256     if (!inputElement)
257         return false;
258 
259     return inputElement->isPasswordField();
260 }
261 
isCheckboxOrRadio() const262 bool AccessibilityRenderObject::isCheckboxOrRadio() const
263 {
264     AccessibilityRole role = roleValue();
265     return role == RadioButtonRole || role == CheckBoxRole;
266 }
267 
isFileUploadButton() const268 bool AccessibilityRenderObject::isFileUploadButton() const
269 {
270     if (m_renderer && m_renderer->node() && m_renderer->node()->hasTagName(inputTag)) {
271         HTMLInputElement* input = static_cast<HTMLInputElement*>(m_renderer->node());
272         return input->inputType() == HTMLInputElement::FILE;
273     }
274 
275     return false;
276 }
277 
isInputImage() const278 bool AccessibilityRenderObject::isInputImage() const
279 {
280     if (m_renderer && m_renderer->node() && m_renderer->node()->hasTagName(inputTag)) {
281         HTMLInputElement* input = static_cast<HTMLInputElement*>(m_renderer->node());
282         return input->inputType() == HTMLInputElement::IMAGE;
283     }
284 
285     return false;
286 }
287 
isProgressIndicator() const288 bool AccessibilityRenderObject::isProgressIndicator() const
289 {
290     return roleValue() == ProgressIndicatorRole;
291 }
292 
isSlider() const293 bool AccessibilityRenderObject::isSlider() const
294 {
295     return roleValue() == SliderRole;
296 }
297 
isMenuRelated() const298 bool AccessibilityRenderObject::isMenuRelated() const
299 {
300     AccessibilityRole role = roleValue();
301     return  role == MenuRole ||
302             role == MenuBarRole ||
303             role == MenuButtonRole ||
304             role == MenuItemRole;
305 }
306 
isMenu() const307 bool AccessibilityRenderObject::isMenu() const
308 {
309     return roleValue() == MenuRole;
310 }
311 
isMenuBar() const312 bool AccessibilityRenderObject::isMenuBar() const
313 {
314     return roleValue() == MenuBarRole;
315 }
316 
isMenuButton() const317 bool AccessibilityRenderObject::isMenuButton() const
318 {
319     return roleValue() == MenuButtonRole;
320 }
321 
isMenuItem() const322 bool AccessibilityRenderObject::isMenuItem() const
323 {
324     return roleValue() == MenuItemRole;
325 }
326 
isPressed() const327 bool AccessibilityRenderObject::isPressed() const
328 {
329     ASSERT(m_renderer);
330     if (roleValue() != ButtonRole)
331         return false;
332 
333     Node* node = m_renderer->node();
334     if (!node)
335         return false;
336 
337     // If this is an ARIA button, check the aria-pressed attribute rather than node()->active()
338     if (ariaRoleAttribute() == ButtonRole) {
339         if (equalIgnoringCase(getAttribute(aria_pressedAttr).string(), "true"))
340             return true;
341         return false;
342     }
343 
344     return node->active();
345 }
346 
isIndeterminate() const347 bool AccessibilityRenderObject::isIndeterminate() const
348 {
349     ASSERT(m_renderer);
350     if (!m_renderer->node() || !m_renderer->node()->isElementNode())
351         return false;
352 
353     InputElement* inputElement = toInputElement(static_cast<Element*>(m_renderer->node()));
354     if (!inputElement)
355         return false;
356 
357     return inputElement->isIndeterminate();
358 }
359 
isChecked() const360 bool AccessibilityRenderObject::isChecked() const
361 {
362     ASSERT(m_renderer);
363     if (!m_renderer->node() || !m_renderer->node()->isElementNode())
364         return false;
365 
366     InputElement* inputElement = toInputElement(static_cast<Element*>(m_renderer->node()));
367     if (!inputElement)
368         return false;
369 
370     return inputElement->isChecked();
371 }
372 
isHovered() const373 bool AccessibilityRenderObject::isHovered() const
374 {
375     ASSERT(m_renderer);
376     return m_renderer->node() && m_renderer->node()->hovered();
377 }
378 
isMultiSelect() const379 bool AccessibilityRenderObject::isMultiSelect() const
380 {
381     ASSERT(m_renderer);
382     if (!m_renderer->isListBox())
383         return false;
384     return m_renderer->node() && static_cast<HTMLSelectElement*>(m_renderer->node())->multiple();
385 }
386 
isReadOnly() const387 bool AccessibilityRenderObject::isReadOnly() const
388 {
389     ASSERT(m_renderer);
390 
391     if (isWebArea()) {
392         Document* document = m_renderer->document();
393         if (!document)
394             return true;
395 
396         HTMLElement* body = document->body();
397         if (body && body->isContentEditable())
398             return false;
399 
400         Frame* frame = document->frame();
401         if (!frame)
402             return true;
403 
404         return !frame->isContentEditable();
405     }
406 
407     return !m_renderer->node() || !m_renderer->node()->isContentEditable();
408 }
409 
isOffScreen() const410 bool AccessibilityRenderObject::isOffScreen() const
411 {
412     ASSERT(m_renderer);
413     IntRect contentRect = m_renderer->absoluteClippedOverflowRect();
414     FrameView* view = m_renderer->document()->frame()->view();
415     FloatRect viewRect = view->visibleContentRect();
416     viewRect.intersect(contentRect);
417     return viewRect.isEmpty();
418 }
419 
headingLevel() const420 int AccessibilityRenderObject::headingLevel() const
421 {
422     // headings can be in block flow and non-block flow
423     if (!m_renderer)
424         return 0;
425 
426     Node* node = m_renderer->node();
427     if (!node)
428         return 0;
429 
430     if (ariaRoleAttribute() == HeadingRole)  {
431         if (!node->isElementNode())
432             return 0;
433         Element* element = static_cast<Element*>(node);
434         return element->getAttribute(aria_levelAttr).toInt();
435     }
436 
437     if (node->hasTagName(h1Tag))
438         return 1;
439 
440     if (node->hasTagName(h2Tag))
441         return 2;
442 
443     if (node->hasTagName(h3Tag))
444         return 3;
445 
446     if (node->hasTagName(h4Tag))
447         return 4;
448 
449     if (node->hasTagName(h5Tag))
450         return 5;
451 
452     if (node->hasTagName(h6Tag))
453         return 6;
454 
455     return 0;
456 }
457 
isHeading() const458 bool AccessibilityRenderObject::isHeading() const
459 {
460     return roleValue() == HeadingRole;
461 }
462 
isLink() const463 bool AccessibilityRenderObject::isLink() const
464 {
465     return roleValue() == WebCoreLinkRole;
466 }
467 
isControl() const468 bool AccessibilityRenderObject::isControl() const
469 {
470     if (!m_renderer)
471         return false;
472 
473     Node* node = m_renderer->node();
474     return node && ((node->isElementNode() && static_cast<Element*>(node)->isFormControlElement())
475                     || AccessibilityObject::isARIAControl(ariaRoleAttribute()));
476 }
477 
isFieldset() const478 bool AccessibilityRenderObject::isFieldset() const
479 {
480     if (!m_renderer)
481         return false;
482 
483     return m_renderer->isFieldset();
484 }
485 
isGroup() const486 bool AccessibilityRenderObject::isGroup() const
487 {
488     return roleValue() == GroupRole;
489 }
490 
selectedRadioButton()491 AccessibilityObject* AccessibilityRenderObject::selectedRadioButton()
492 {
493     if (!isRadioGroup())
494         return 0;
495 
496     // Find the child radio button that is selected (ie. the intValue == 1).
497     int count = m_children.size();
498     for (int i = 0; i < count; ++i) {
499         AccessibilityObject* object = m_children[i].get();
500         if (object->roleValue() == RadioButtonRole && object->intValue() == 1)
501             return object;
502     }
503     return 0;
504 }
505 
getAttribute(const QualifiedName & attribute) const506 const AtomicString& AccessibilityRenderObject::getAttribute(const QualifiedName& attribute) const
507 {
508     Node* node = m_renderer->node();
509     if (!node)
510         return nullAtom;
511 
512     if (!node->isElementNode())
513         return nullAtom;
514 
515     Element* element = static_cast<Element*>(node);
516     return element->getAttribute(attribute);
517 }
518 
anchorElement() const519 Element* AccessibilityRenderObject::anchorElement() const
520 {
521     if (!m_renderer)
522         return 0;
523 
524     AXObjectCache* cache = axObjectCache();
525     RenderObject* currRenderer;
526 
527     // Search up the render tree for a RenderObject with a DOM node.  Defer to an earlier continuation, though.
528     for (currRenderer = m_renderer; currRenderer && !currRenderer->node(); currRenderer = currRenderer->parent()) {
529         if (currRenderer->isRenderBlock()) {
530             RenderInline* continuation = toRenderBlock(currRenderer)->inlineContinuation();
531             if (continuation)
532                 return cache->getOrCreate(continuation)->anchorElement();
533         }
534     }
535 
536     // bail if none found
537     if (!currRenderer)
538         return 0;
539 
540     // search up the DOM tree for an anchor element
541     // NOTE: this assumes that any non-image with an anchor is an HTMLAnchorElement
542     Node* node = currRenderer->node();
543     for ( ; node; node = node->parentNode()) {
544         if (node->hasTagName(aTag) || (node->renderer() && cache->getOrCreate(node->renderer())->isAnchor()))
545             return static_cast<Element*>(node);
546     }
547 
548     return 0;
549 }
550 
actionElement() const551 Element* AccessibilityRenderObject::actionElement() const
552 {
553     if (!m_renderer)
554         return 0;
555 
556     Node* node = m_renderer->node();
557     if (node) {
558         if (node->hasTagName(inputTag)) {
559             HTMLInputElement* input = static_cast<HTMLInputElement*>(node);
560             if (!input->disabled() && (isCheckboxOrRadio() || input->isTextButton()))
561                 return input;
562         } else if (node->hasTagName(buttonTag))
563             return static_cast<Element*>(node);
564     }
565 
566     if (isFileUploadButton())
567         return static_cast<Element*>(m_renderer->node());
568 
569     if (AccessibilityObject::isARIAInput(ariaRoleAttribute()))
570         return static_cast<Element*>(m_renderer->node());
571 
572     if (isImageButton())
573         return static_cast<Element*>(m_renderer->node());
574 
575     if (m_renderer->isMenuList())
576         return static_cast<Element*>(m_renderer->node());
577 
578     Element* elt = anchorElement();
579     if (!elt)
580         elt = mouseButtonListener();
581     return elt;
582 }
583 
mouseButtonListener() const584 Element* AccessibilityRenderObject::mouseButtonListener() const
585 {
586     Node* node = m_renderer->node();
587     if (!node)
588         return 0;
589 
590     // check if our parent is a mouse button listener
591     while (node && !node->isElementNode())
592         node = node->parent();
593 
594     if (!node)
595         return 0;
596 
597     // FIXME: Do the continuation search like anchorElement does
598     for (Element* element = static_cast<Element*>(node); element; element = element->parentElement()) {
599         if (element->getAttributeEventListener(eventNames().clickEvent) || element->getAttributeEventListener(eventNames().mousedownEvent) || element->getAttributeEventListener(eventNames().mouseupEvent))
600             return element;
601     }
602 
603     return 0;
604 }
605 
siblingWithAriaRole(String role,Node * node)606 static Element* siblingWithAriaRole(String role, Node* node)
607 {
608     Node* sibling = node->parent()->firstChild();
609     while (sibling) {
610         if (sibling->isElementNode()) {
611             String siblingAriaRole = static_cast<Element*>(sibling)->getAttribute(roleAttr).string();
612             if (equalIgnoringCase(siblingAriaRole, role))
613                 return static_cast<Element*>(sibling);
614         }
615         sibling = sibling->nextSibling();
616     }
617 
618     return 0;
619 }
620 
menuElementForMenuButton() const621 Element* AccessibilityRenderObject::menuElementForMenuButton() const
622 {
623     if (ariaRoleAttribute() != MenuButtonRole)
624         return 0;
625 
626     return siblingWithAriaRole("menu", renderer()->node());
627 }
628 
menuForMenuButton() const629 AccessibilityObject* AccessibilityRenderObject::menuForMenuButton() const
630 {
631     Element* menu = menuElementForMenuButton();
632     if (menu && menu->renderer())
633         return m_renderer->document()->axObjectCache()->getOrCreate(menu->renderer());
634     return 0;
635 }
636 
menuItemElementForMenu() const637 Element* AccessibilityRenderObject::menuItemElementForMenu() const
638 {
639     if (ariaRoleAttribute() != MenuRole)
640         return 0;
641 
642     return siblingWithAriaRole("menuitem", renderer()->node());
643 }
644 
menuButtonForMenu() const645 AccessibilityObject* AccessibilityRenderObject::menuButtonForMenu() const
646 {
647     Element* menuItem = menuItemElementForMenu();
648 
649     if (menuItem && menuItem->renderer()) {
650         // ARIA just has generic menu items.  AppKit needs to know if this is a top level items like MenuBarButton or MenuBarItem
651         AccessibilityObject* menuItemAX = m_renderer->document()->axObjectCache()->getOrCreate(menuItem->renderer());
652         if (menuItemAX->isMenuButton())
653             return menuItemAX;
654     }
655     return 0;
656 }
657 
helpText() const658 String AccessibilityRenderObject::helpText() const
659 {
660     if (!m_renderer)
661         return String();
662 
663     for (RenderObject* curr = m_renderer; curr; curr = curr->parent()) {
664         if (curr->node() && curr->node()->isHTMLElement()) {
665             const AtomicString& summary = static_cast<Element*>(curr->node())->getAttribute(summaryAttr);
666             if (!summary.isEmpty())
667                 return summary;
668             const AtomicString& title = static_cast<Element*>(curr->node())->getAttribute(titleAttr);
669             if (!title.isEmpty())
670                 return title;
671         }
672     }
673 
674     return String();
675 }
676 
language() const677 String AccessibilityRenderObject::language() const
678 {
679     if (!m_renderer)
680         return String();
681 
682     // Defer to parent if this element doesn't have a language set
683     Node* node = m_renderer->node();
684     if (!node)
685         return AccessibilityObject::language();
686 
687     if (!node->isElementNode())
688         return AccessibilityObject::language();
689 
690     String language = static_cast<Element*>(node)->getAttribute(langAttr);
691     if (language.isEmpty())
692         return AccessibilityObject::language();
693     return language;
694 }
695 
textUnderElement() const696 String AccessibilityRenderObject::textUnderElement() const
697 {
698     if (!m_renderer)
699         return String();
700 
701     if (isFileUploadButton())
702         return toRenderFileUploadControl(m_renderer)->buttonValue();
703 
704     Node* node = m_renderer->node();
705     if (node) {
706         if (Frame* frame = node->document()->frame()) {
707             // catch stale WebCoreAXObject (see <rdar://problem/3960196>)
708             if (frame->document() != node->document())
709                 return String();
710             return plainText(rangeOfContents(node).get());
711         }
712     }
713 
714     // return the null string for anonymous text because it is non-trivial to get
715     // the actual text and, so far, that is not needed
716     return String();
717 }
718 
hasIntValue() const719 bool AccessibilityRenderObject::hasIntValue() const
720 {
721     if (isHeading())
722         return true;
723 
724     if (m_renderer->node() && isCheckboxOrRadio())
725         return true;
726 
727     return false;
728 }
729 
intValue() const730 int AccessibilityRenderObject::intValue() const
731 {
732     if (!m_renderer || isPasswordField())
733         return 0;
734 
735     if (isHeading())
736         return headingLevel();
737 
738     Node* node = m_renderer->node();
739     if (!node || !isCheckboxOrRadio())
740         return 0;
741 
742     // If this is an ARIA checkbox or radio, check the aria-checked attribute rather than node()->checked()
743     AccessibilityRole ariaRole = ariaRoleAttribute();
744     if (ariaRole == RadioButtonRole || ariaRole == CheckBoxRole) {
745         if (equalIgnoringCase(getAttribute(aria_checkedAttr).string(), "true"))
746             return true;
747         return false;
748     }
749 
750     return static_cast<HTMLInputElement*>(node)->checked();
751 }
752 
valueDescription() const753 String AccessibilityRenderObject::valueDescription() const
754 {
755     // Only sliders and progress bars support value descriptions currently.
756     if (!isProgressIndicator() && !isSlider())
757         return String();
758 
759     return getAttribute(aria_valuetextAttr).string();
760 }
761 
valueForRange() const762 float AccessibilityRenderObject::valueForRange() const
763 {
764     if (!isProgressIndicator() && !isSlider())
765         return 0.0f;
766 
767     return getAttribute(aria_valuenowAttr).toFloat();
768 }
769 
maxValueForRange() const770 float AccessibilityRenderObject::maxValueForRange() const
771 {
772     if (!isProgressIndicator() && !isSlider())
773         return 0.0f;
774 
775     return getAttribute(aria_valuemaxAttr).toFloat();
776 }
777 
minValueForRange() const778 float AccessibilityRenderObject::minValueForRange() const
779 {
780     if (!isProgressIndicator() && !isSlider())
781         return 0.0f;
782 
783     return getAttribute(aria_valueminAttr).toFloat();
784 }
785 
stringValue() const786 String AccessibilityRenderObject::stringValue() const
787 {
788     if (!m_renderer || isPasswordField())
789         return String();
790 
791     if (m_renderer->isText())
792         return textUnderElement();
793 
794     if (m_renderer->isMenuList())
795         return toRenderMenuList(m_renderer)->text();
796 
797     if (m_renderer->isListMarker())
798         return toRenderListMarker(m_renderer)->text();
799 
800     if (m_renderer->isRenderButton())
801         return toRenderButton(m_renderer)->text();
802 
803     if (isWebArea()) {
804         if (m_renderer->document()->frame())
805             return String();
806 
807         // FIXME: should use startOfDocument and endOfDocument (or rangeForDocument?) here
808         VisiblePosition startVisiblePosition = m_renderer->positionForCoordinates(0, 0);
809         VisiblePosition endVisiblePosition = m_renderer->positionForCoordinates(INT_MAX, INT_MAX);
810         if (startVisiblePosition.isNull() || endVisiblePosition.isNull())
811             return String();
812 
813         return plainText(makeRange(startVisiblePosition, endVisiblePosition).get());
814     }
815 
816     if (isTextControl())
817         return text();
818 
819     if (isFileUploadButton())
820         return toRenderFileUploadControl(m_renderer)->fileTextValue();
821 
822     // FIXME: We might need to implement a value here for more types
823     // FIXME: It would be better not to advertise a value at all for the types for which we don't implement one;
824     // this would require subclassing or making accessibilityAttributeNames do something other than return a
825     // single static array.
826     return String();
827 }
828 
829 // This function implements the ARIA accessible name as described by the Mozilla
830 // ARIA Implementer's Guide.
accessibleNameForNode(Node * node)831 static String accessibleNameForNode(Node* node)
832 {
833     if (node->isTextNode())
834         return static_cast<Text*>(node)->data();
835 
836     if (node->hasTagName(inputTag))
837         return static_cast<HTMLInputElement*>(node)->value();
838 
839     if (node->isHTMLElement()) {
840         const AtomicString& alt = static_cast<HTMLElement*>(node)->getAttribute(altAttr);
841         if (!alt.isEmpty())
842             return alt;
843     }
844 
845     return String();
846 }
847 
ariaAccessibilityName(const String & s) const848 String AccessibilityRenderObject::ariaAccessibilityName(const String& s) const
849 {
850     Document* document = m_renderer->document();
851     if (!document)
852         return String();
853 
854     String idList = s;
855     idList.replace('\n', ' ');
856     Vector<String> idVector;
857     idList.split(' ', idVector);
858 
859     Vector<UChar> ariaLabel;
860     unsigned size = idVector.size();
861     for (unsigned i = 0; i < size; ++i) {
862         String idName = idVector[i];
863         Element* idElement = document->getElementById(idName);
864         if (idElement) {
865             String nameFragment = accessibleNameForNode(idElement);
866             ariaLabel.append(nameFragment.characters(), nameFragment.length());
867             for (Node* n = idElement->firstChild(); n; n = n->traverseNextNode(idElement)) {
868                 nameFragment = accessibleNameForNode(n);
869                 ariaLabel.append(nameFragment.characters(), nameFragment.length());
870             }
871 
872             if (i != size - 1)
873                 ariaLabel.append(' ');
874         }
875     }
876     return String::adopt(ariaLabel);
877 }
878 
ariaLabeledByAttribute() const879 String AccessibilityRenderObject::ariaLabeledByAttribute() const
880 {
881     Node* node = m_renderer->node();
882     if (!node)
883         return String();
884 
885     if (!node->isElementNode())
886         return String();
887 
888     // The ARIA spec uses the British spelling: "labelled." It seems prudent to support the American
889     // spelling ("labeled") as well.
890     String idList = getAttribute(aria_labeledbyAttr).string();
891     if (idList.isEmpty()) {
892         idList = getAttribute(aria_labelledbyAttr).string();
893         if (idList.isEmpty())
894             return String();
895     }
896 
897     return ariaAccessibilityName(idList);
898 }
899 
labelForElement(Element * element)900 static HTMLLabelElement* labelForElement(Element* element)
901 {
902     RefPtr<NodeList> list = element->document()->getElementsByTagName("label");
903     unsigned len = list->length();
904     for (unsigned i = 0; i < len; i++) {
905         if (list->item(i)->hasTagName(labelTag)) {
906             HTMLLabelElement* label = static_cast<HTMLLabelElement*>(list->item(i));
907             if (label->correspondingControl() == element)
908                 return label;
909         }
910     }
911 
912     return 0;
913 }
914 
labelElementContainer() const915 HTMLLabelElement* AccessibilityRenderObject::labelElementContainer() const
916 {
917     if (!m_renderer)
918         return false;
919 
920     // the control element should not be considered part of the label
921     if (isControl())
922         return false;
923 
924     // find if this has a parent that is a label
925     for (Node* parentNode = m_renderer->node(); parentNode; parentNode = parentNode->parentNode()) {
926         if (parentNode->hasTagName(labelTag))
927             return static_cast<HTMLLabelElement*>(parentNode);
928     }
929 
930     return 0;
931 }
932 
title() const933 String AccessibilityRenderObject::title() const
934 {
935     AccessibilityRole ariaRole = ariaRoleAttribute();
936 
937     if (!m_renderer)
938         return String();
939 
940     Node* node = m_renderer->node();
941     if (!node)
942         return String();
943 
944     String ariaLabel = ariaLabeledByAttribute();
945     if (!ariaLabel.isEmpty())
946         return ariaLabel;
947 
948     const AtomicString& title = getAttribute(titleAttr);
949     if (!title.isEmpty())
950         return title;
951 
952     bool isInputTag = node->hasTagName(inputTag);
953     if (isInputTag) {
954         HTMLInputElement* input = static_cast<HTMLInputElement*>(node);
955         if (input->isTextButton())
956             return input->value();
957     }
958 
959     if (isInputTag || AccessibilityObject::isARIAInput(ariaRole) || isControl()) {
960         HTMLLabelElement* label = labelForElement(static_cast<Element*>(node));
961         if (label && !titleUIElement())
962             return label->innerText();
963 
964         const AtomicString& placeholder = getAttribute(placeholderAttr);
965         if (!placeholder.isEmpty())
966             return placeholder;
967     }
968 
969     if (roleValue() == ButtonRole
970         || ariaRole == ListBoxOptionRole
971         || ariaRole == MenuItemRole
972         || ariaRole == MenuButtonRole
973         || ariaRole == RadioButtonRole
974         || isHeading())
975         return textUnderElement();
976 
977     if (isLink())
978         return textUnderElement();
979 
980     return String();
981 }
982 
ariaDescribedByAttribute() const983 String AccessibilityRenderObject::ariaDescribedByAttribute() const
984 {
985     String idList = getAttribute(aria_describedbyAttr).string();
986     if (idList.isEmpty())
987         return String();
988 
989     return ariaAccessibilityName(idList);
990 }
991 
accessibilityDescription() const992 String AccessibilityRenderObject::accessibilityDescription() const
993 {
994     if (!m_renderer)
995         return String();
996 
997     String ariaLabel = getAttribute(aria_labelAttr).string();
998     if (!ariaLabel.isEmpty())
999         return ariaLabel;
1000 
1001     String ariaDescription = ariaDescribedByAttribute();
1002     if (!ariaDescription.isEmpty())
1003         return ariaDescription;
1004 
1005     if (isImage() || isInputImage() || isNativeImage()) {
1006         Node* node = m_renderer->node();
1007         if (node && node->isHTMLElement()) {
1008             const AtomicString& alt = static_cast<HTMLElement*>(node)->getAttribute(altAttr);
1009             if (alt.isEmpty())
1010                 return String();
1011             return alt;
1012         }
1013     }
1014 
1015     if (isWebArea()) {
1016         Document *document = m_renderer->document();
1017         Node* owner = document->ownerElement();
1018         if (owner) {
1019             if (owner->hasTagName(frameTag) || owner->hasTagName(iframeTag)) {
1020                 const AtomicString& title = static_cast<HTMLFrameElementBase*>(owner)->getAttribute(titleAttr);
1021                 if (!title.isEmpty())
1022                     return title;
1023                 return static_cast<HTMLFrameElementBase*>(owner)->name();
1024             }
1025             if (owner->isHTMLElement())
1026                 return static_cast<HTMLElement*>(owner)->getAttribute(nameAttr);
1027         }
1028         owner = document->body();
1029         if (owner && owner->isHTMLElement())
1030             return static_cast<HTMLElement*>(owner)->getAttribute(nameAttr);
1031     }
1032 
1033     if (roleValue() == DefinitionListTermRole)
1034         return AXDefinitionListTermText();
1035     if (roleValue() == DefinitionListDefinitionRole)
1036         return AXDefinitionListDefinitionText();
1037 
1038     return String();
1039 }
1040 
boundingBoxRect() const1041 IntRect AccessibilityRenderObject::boundingBoxRect() const
1042 {
1043     RenderObject* obj = m_renderer;
1044 
1045     if (!obj)
1046         return IntRect();
1047 
1048     if (obj->node()) // If we are a continuation, we want to make sure to use the primary renderer.
1049         obj = obj->node()->renderer();
1050 
1051     Vector<FloatQuad> quads;
1052     obj->absoluteQuads(quads);
1053     const size_t n = quads.size();
1054     if (!n)
1055         return IntRect();
1056 
1057     IntRect result;
1058     for (size_t i = 0; i < n; ++i) {
1059         IntRect r = quads[i].enclosingBoundingBox();
1060         if (!r.isEmpty()) {
1061             if (obj->style()->hasAppearance())
1062                 obj->theme()->adjustRepaintRect(obj, r);
1063             result.unite(r);
1064         }
1065     }
1066     return result;
1067 }
1068 
checkboxOrRadioRect() const1069 IntRect AccessibilityRenderObject::checkboxOrRadioRect() const
1070 {
1071     if (!m_renderer)
1072         return IntRect();
1073 
1074     HTMLLabelElement* label = labelForElement(static_cast<Element*>(m_renderer->node()));
1075     if (!label || !label->renderer())
1076         return boundingBoxRect();
1077 
1078     IntRect labelRect = axObjectCache()->getOrCreate(label->renderer())->elementRect();
1079     labelRect.unite(boundingBoxRect());
1080     return labelRect;
1081 }
1082 
elementRect() const1083 IntRect AccessibilityRenderObject::elementRect() const
1084 {
1085     // a checkbox or radio button should encompass its label
1086     if (isCheckboxOrRadio())
1087         return checkboxOrRadioRect();
1088 
1089     return boundingBoxRect();
1090 }
1091 
size() const1092 IntSize AccessibilityRenderObject::size() const
1093 {
1094     IntRect rect = elementRect();
1095     return rect.size();
1096 }
1097 
clickPoint() const1098 IntPoint AccessibilityRenderObject::clickPoint() const
1099 {
1100     // use the default position unless this is an editable web area, in which case we use the selection bounds.
1101     if (!isWebArea() || isReadOnly())
1102         return AccessibilityObject::clickPoint();
1103 
1104     VisibleSelection visSelection = selection();
1105     VisiblePositionRange range = VisiblePositionRange(visSelection.visibleStart(), visSelection.visibleEnd());
1106     IntRect bounds = boundsForVisiblePositionRange(range);
1107 #if PLATFORM(MAC)
1108     bounds.setLocation(m_renderer->document()->view()->screenToContents(bounds.location()));
1109 #endif
1110     return IntPoint(bounds.x() + (bounds.width() / 2), bounds.y() - (bounds.height() / 2));
1111 }
1112 
internalLinkElement() const1113 AccessibilityObject* AccessibilityRenderObject::internalLinkElement() const
1114 {
1115     Element* element = anchorElement();
1116     if (!element)
1117         return 0;
1118 
1119     // Right now, we do not support ARIA links as internal link elements
1120     if (!element->hasTagName(aTag))
1121         return 0;
1122     HTMLAnchorElement* anchor = static_cast<HTMLAnchorElement*>(element);
1123 
1124     KURL linkURL = anchor->href();
1125     String fragmentIdentifier = linkURL.fragmentIdentifier();
1126     if (fragmentIdentifier.isEmpty())
1127         return 0;
1128 
1129     // check if URL is the same as current URL
1130     linkURL.removeFragmentIdentifier();
1131     if (m_renderer->document()->url() != linkURL)
1132         return 0;
1133 
1134     Node* linkedNode = m_renderer->document()->findAnchor(fragmentIdentifier);
1135     if (!linkedNode)
1136         return 0;
1137 
1138     // the element we find may not be accessible, keep searching until we find a good one
1139     AccessibilityObject* linkedAXElement = m_renderer->document()->axObjectCache()->getOrCreate(linkedNode->renderer());
1140     while (linkedAXElement && linkedAXElement->accessibilityIsIgnored()) {
1141         linkedNode = linkedNode->traverseNextNode();
1142 
1143         while (linkedNode && !linkedNode->renderer())
1144             linkedNode = linkedNode->traverseNextSibling();
1145 
1146         if (!linkedNode)
1147             return 0;
1148         linkedAXElement = m_renderer->document()->axObjectCache()->getOrCreate(linkedNode->renderer());
1149     }
1150 
1151     return linkedAXElement;
1152 }
1153 
addRadioButtonGroupMembers(AccessibilityChildrenVector & linkedUIElements) const1154 void AccessibilityRenderObject::addRadioButtonGroupMembers(AccessibilityChildrenVector& linkedUIElements) const
1155 {
1156     if (!m_renderer || roleValue() != RadioButtonRole)
1157         return;
1158 
1159     Node* node = m_renderer->node();
1160     if (!node || !node->hasTagName(inputTag))
1161         return;
1162 
1163     HTMLInputElement* input = static_cast<HTMLInputElement*>(node);
1164     // if there's a form, then this is easy
1165     if (input->form()) {
1166         Vector<RefPtr<Node> > formElements;
1167         input->form()->getNamedElements(input->name(), formElements);
1168 
1169         unsigned len = formElements.size();
1170         for (unsigned i = 0; i < len; ++i) {
1171             Node* associateElement = formElements[i].get();
1172             if (AccessibilityObject* object = m_renderer->document()->axObjectCache()->getOrCreate(associateElement->renderer()))
1173                 linkedUIElements.append(object);
1174         }
1175     } else {
1176         RefPtr<NodeList> list = node->document()->getElementsByTagName("input");
1177         unsigned len = list->length();
1178         for (unsigned i = 0; i < len; ++i) {
1179             if (list->item(i)->hasTagName(inputTag)) {
1180                 HTMLInputElement* associateElement = static_cast<HTMLInputElement*>(list->item(i));
1181                 if (associateElement->isRadioButton() && associateElement->name() == input->name()) {
1182                     if (AccessibilityObject* object = m_renderer->document()->axObjectCache()->getOrCreate(associateElement->renderer()))
1183                         linkedUIElements.append(object);
1184                 }
1185             }
1186         }
1187     }
1188 }
1189 
1190 // linked ui elements could be all the related radio buttons in a group
1191 // or an internal anchor connection
linkedUIElements(AccessibilityChildrenVector & linkedUIElements) const1192 void AccessibilityRenderObject::linkedUIElements(AccessibilityChildrenVector& linkedUIElements) const
1193 {
1194     if (isAnchor()) {
1195         AccessibilityObject* linkedAXElement = internalLinkElement();
1196         if (linkedAXElement)
1197             linkedUIElements.append(linkedAXElement);
1198     }
1199 
1200     if (roleValue() == RadioButtonRole)
1201         addRadioButtonGroupMembers(linkedUIElements);
1202 }
1203 
exposesTitleUIElement() const1204 bool AccessibilityRenderObject::exposesTitleUIElement() const
1205 {
1206     if (!isControl())
1207         return false;
1208 
1209     // checkbox or radio buttons don't expose the title ui element unless it has a title already
1210     if (isCheckboxOrRadio() && getAttribute(titleAttr).isEmpty())
1211         return false;
1212 
1213     return true;
1214 }
1215 
titleUIElement() const1216 AccessibilityObject* AccessibilityRenderObject::titleUIElement() const
1217 {
1218     if (!m_renderer)
1219         return 0;
1220 
1221     // if isFieldset is true, the renderer is guaranteed to be a RenderFieldset
1222     if (isFieldset())
1223         return axObjectCache()->getOrCreate(toRenderFieldset(m_renderer)->findLegend());
1224 
1225     if (!exposesTitleUIElement())
1226         return 0;
1227 
1228     Node* element = m_renderer->node();
1229     HTMLLabelElement* label = labelForElement(static_cast<Element*>(element));
1230     if (label && label->renderer())
1231         return axObjectCache()->getOrCreate(label->renderer());
1232 
1233     return 0;
1234 }
1235 
ariaIsHidden() const1236 bool AccessibilityRenderObject::ariaIsHidden() const
1237 {
1238     if (equalIgnoringCase(getAttribute(aria_hiddenAttr).string(), "true"))
1239         return true;
1240 
1241     // aria-hidden hides this object and any children
1242     AccessibilityObject* object = parentObject();
1243     while (object) {
1244         if (object->isAccessibilityRenderObject() && equalIgnoringCase(static_cast<AccessibilityRenderObject*>(object)->getAttribute(aria_hiddenAttr).string(), "true"))
1245             return true;
1246         object = object->parentObject();
1247     }
1248 
1249     return false;
1250 }
1251 
accessibilityIsIgnored() const1252 bool AccessibilityRenderObject::accessibilityIsIgnored() const
1253 {
1254     // ignore invisible element
1255     if (!m_renderer || m_renderer->style()->visibility() != VISIBLE)
1256         return true;
1257 
1258     if (ariaIsHidden())
1259         return true;
1260 
1261     if (isPresentationalChildOfAriaRole())
1262         return true;
1263 
1264     // ignore popup menu items because AppKit does
1265     for (RenderObject* parent = m_renderer->parent(); parent; parent = parent->parent()) {
1266         if (parent->isMenuList())
1267             return true;
1268     }
1269 
1270     // find out if this element is inside of a label element.
1271     // if so, it may be ignored because it's the label for a checkbox or radio button
1272     HTMLLabelElement* labelElement = labelElementContainer();
1273     if (labelElement) {
1274         HTMLElement* correspondingControl = labelElement->correspondingControl();
1275         if (correspondingControl && correspondingControl->renderer()) {
1276             AccessibilityObject* controlObject = axObjectCache()->getOrCreate(correspondingControl->renderer());
1277             if (!controlObject->exposesTitleUIElement())
1278                 return true;
1279         }
1280     }
1281 
1282     AccessibilityRole ariaRole = ariaRoleAttribute();
1283     if (ariaRole == TextAreaRole || ariaRole == StaticTextRole) {
1284         String ariaText = text();
1285         return ariaText.isNull() || ariaText.isEmpty();
1286     }
1287 
1288     // NOTE: BRs always have text boxes now, so the text box check here can be removed
1289     if (m_renderer->isText()) {
1290         // static text beneath MenuItems and MenuButtons are just reported along with the menu item, so it's ignored on an individual level
1291         if (parentObjectUnignored()->ariaRoleAttribute() == MenuItemRole ||
1292             parentObjectUnignored()->ariaRoleAttribute() == MenuButtonRole)
1293             return true;
1294         RenderText* renderText = toRenderText(m_renderer);
1295         if (m_renderer->isBR() || !renderText->firstTextBox())
1296             return true;
1297 
1298         // text elements that are just empty whitespace should not be returned
1299         return renderText->text()->containsOnlyWhitespace();
1300     }
1301 
1302     if (isHeading())
1303         return false;
1304 
1305     if (isLink())
1306         return false;
1307 
1308     // all controls are accessible
1309     if (isControl())
1310         return false;
1311 
1312     // don't ignore labels, because they serve as TitleUIElements
1313     Node* node = m_renderer->node();
1314     if (node && node->hasTagName(labelTag))
1315         return false;
1316 
1317     if (m_renderer->isBlockFlow() && m_renderer->childrenInline())
1318         return !toRenderBlock(m_renderer)->firstLineBox() && !mouseButtonListener();
1319 
1320     // ignore images seemingly used as spacers
1321     if (isImage()) {
1322         if (node && node->isElementNode()) {
1323             Element* elt = static_cast<Element*>(node);
1324             const AtomicString& alt = elt->getAttribute(altAttr);
1325             // don't ignore an image that has an alt tag
1326             if (!alt.isEmpty())
1327                 return false;
1328             // informal standard is to ignore images with zero-length alt strings
1329             if (!alt.isNull())
1330                 return true;
1331         }
1332 
1333         if (node && node->hasTagName(canvasTag)) {
1334             RenderHTMLCanvas* canvas = toRenderHTMLCanvas(m_renderer);
1335             if (canvas->height() <= 1 || canvas->width() <= 1)
1336                 return true;
1337             return false;
1338         }
1339 
1340         if (isNativeImage()) {
1341             // check for one-dimensional image
1342             RenderImage* image = toRenderImage(m_renderer);
1343             if (image->height() <= 1 || image->width() <= 1)
1344                 return true;
1345 
1346             // check whether rendered image was stretched from one-dimensional file image
1347             if (image->cachedImage()) {
1348                 IntSize imageSize = image->cachedImage()->imageSize(image->view()->zoomFactor());
1349                 return imageSize.height() <= 1 || imageSize.width() <= 1;
1350             }
1351         }
1352         return false;
1353     }
1354 
1355     if (ariaRole != UnknownRole)
1356         return false;
1357 
1358     // make a platform-specific decision
1359     if (isAttachment())
1360         return accessibilityIgnoreAttachment();
1361 
1362     return !m_renderer->isListMarker() && !isWebArea();
1363 }
1364 
isLoaded() const1365 bool AccessibilityRenderObject::isLoaded() const
1366 {
1367     return !m_renderer->document()->tokenizer();
1368 }
1369 
layoutCount() const1370 int AccessibilityRenderObject::layoutCount() const
1371 {
1372     if (!m_renderer->isRenderView())
1373         return 0;
1374     return toRenderView(m_renderer)->frameView()->layoutCount();
1375 }
1376 
text() const1377 String AccessibilityRenderObject::text() const
1378 {
1379     if (!isTextControl() || isPasswordField())
1380         return String();
1381 
1382     if (isNativeTextControl())
1383         return toRenderTextControl(m_renderer)->text();
1384 
1385     Node* node = m_renderer->node();
1386     if (!node)
1387         return String();
1388     if (!node->isElementNode())
1389         return String();
1390 
1391     return static_cast<Element*>(node)->innerText();
1392 }
1393 
textLength() const1394 int AccessibilityRenderObject::textLength() const
1395 {
1396     ASSERT(isTextControl());
1397 
1398     if (isPasswordField())
1399         return -1; // need to return something distinct from 0
1400 
1401     return text().length();
1402 }
1403 
ariaSelectedTextDOMRange() const1404 PassRefPtr<Range> AccessibilityRenderObject::ariaSelectedTextDOMRange() const
1405 {
1406     Node* node = m_renderer->node();
1407     if (!node)
1408         return 0;
1409 
1410     RefPtr<Range> currentSelectionRange = selection().toNormalizedRange();
1411     if (!currentSelectionRange)
1412         return 0;
1413 
1414     ExceptionCode ec = 0;
1415     if (!currentSelectionRange->intersectsNode(node, ec))
1416         return Range::create(currentSelectionRange->ownerDocument());
1417 
1418     RefPtr<Range> ariaRange = rangeOfContents(node);
1419     Position startPosition, endPosition;
1420 
1421     // Find intersection of currentSelectionRange and ariaRange
1422     if (ariaRange->startOffset() > currentSelectionRange->startOffset())
1423         startPosition = ariaRange->startPosition();
1424     else
1425         startPosition = currentSelectionRange->startPosition();
1426 
1427     if (ariaRange->endOffset() < currentSelectionRange->endOffset())
1428         endPosition = ariaRange->endPosition();
1429     else
1430         endPosition = currentSelectionRange->endPosition();
1431 
1432     return Range::create(ariaRange->ownerDocument(), startPosition, endPosition);
1433 }
1434 
selectedText() const1435 String AccessibilityRenderObject::selectedText() const
1436 {
1437     ASSERT(isTextControl());
1438 
1439     if (isPasswordField())
1440         return String(); // need to return something distinct from empty string
1441 
1442     if (isNativeTextControl()) {
1443         RenderTextControl* textControl = toRenderTextControl(m_renderer);
1444         return textControl->text().substring(textControl->selectionStart(), textControl->selectionEnd() - textControl->selectionStart());
1445     }
1446 
1447     if (ariaRoleAttribute() == UnknownRole)
1448         return String();
1449 
1450     RefPtr<Range> ariaRange = ariaSelectedTextDOMRange();
1451     if (!ariaRange)
1452         return String();
1453     return ariaRange->text();
1454 }
1455 
accessKey() const1456 const AtomicString& AccessibilityRenderObject::accessKey() const
1457 {
1458     Node* node = m_renderer->node();
1459     if (!node)
1460         return nullAtom;
1461     if (!node->isElementNode())
1462         return nullAtom;
1463     return static_cast<Element*>(node)->getAttribute(accesskeyAttr);
1464 }
1465 
selection() const1466 VisibleSelection AccessibilityRenderObject::selection() const
1467 {
1468     return m_renderer->document()->frame()->selection()->selection();
1469 }
1470 
selectedTextRange() const1471 PlainTextRange AccessibilityRenderObject::selectedTextRange() const
1472 {
1473     ASSERT(isTextControl());
1474 
1475     if (isPasswordField())
1476         return PlainTextRange();
1477 
1478     AccessibilityRole ariaRole = ariaRoleAttribute();
1479     if (isNativeTextControl() && ariaRole == UnknownRole) {
1480         RenderTextControl* textControl = toRenderTextControl(m_renderer);
1481         return PlainTextRange(textControl->selectionStart(), textControl->selectionEnd() - textControl->selectionStart());
1482     }
1483 
1484     if (ariaRole == UnknownRole)
1485         return PlainTextRange();
1486 
1487     RefPtr<Range> ariaRange = ariaSelectedTextDOMRange();
1488     if (!ariaRange)
1489         return PlainTextRange();
1490     return PlainTextRange(ariaRange->startOffset(), ariaRange->endOffset());
1491 }
1492 
setSelectedTextRange(const PlainTextRange & range)1493 void AccessibilityRenderObject::setSelectedTextRange(const PlainTextRange& range)
1494 {
1495     if (isNativeTextControl()) {
1496         RenderTextControl* textControl = toRenderTextControl(m_renderer);
1497         textControl->setSelectionRange(range.start, range.start + range.length);
1498         return;
1499     }
1500 
1501     Document* document = m_renderer->document();
1502     if (!document)
1503         return;
1504     Frame* frame = document->frame();
1505     if (!frame)
1506         return;
1507     Node* node = m_renderer->node();
1508     frame->selection()->setSelection(VisibleSelection(Position(node, range.start),
1509         Position(node, range.start + range.length), DOWNSTREAM));
1510 }
1511 
url() const1512 KURL AccessibilityRenderObject::url() const
1513 {
1514     if (isAnchor() && m_renderer->node()->hasTagName(aTag)) {
1515         if (HTMLAnchorElement* anchor = static_cast<HTMLAnchorElement*>(anchorElement()))
1516             return anchor->href();
1517     }
1518 
1519     if (isWebArea())
1520         return m_renderer->document()->url();
1521 
1522     if (isImage() && m_renderer->node() && m_renderer->node()->hasTagName(imgTag))
1523         return static_cast<HTMLImageElement*>(m_renderer->node())->src();
1524 
1525     if (isInputImage())
1526         return static_cast<HTMLInputElement*>(m_renderer->node())->src();
1527 
1528     return KURL();
1529 }
1530 
isVisited() const1531 bool AccessibilityRenderObject::isVisited() const
1532 {
1533     return m_renderer->style()->pseudoState() == PseudoVisited;
1534 }
1535 
isRequired() const1536 bool AccessibilityRenderObject::isRequired() const
1537 {
1538     if (equalIgnoringCase(getAttribute(aria_requiredAttr).string(), "true"))
1539         return true;
1540 
1541     return false;
1542 }
1543 
isSelected() const1544 bool AccessibilityRenderObject::isSelected() const
1545 {
1546     if (!m_renderer)
1547         return false;
1548 
1549     Node* node = m_renderer->node();
1550     if (!node)
1551         return false;
1552 
1553     return false;
1554 }
1555 
isFocused() const1556 bool AccessibilityRenderObject::isFocused() const
1557 {
1558     if (!m_renderer)
1559         return false;
1560 
1561     Document* document = m_renderer->document();
1562     if (!document)
1563         return false;
1564 
1565     Node* focusedNode = document->focusedNode();
1566     if (!focusedNode)
1567         return false;
1568 
1569     // A web area is represented by the Document node in the DOM tree, which isn't focusable.
1570     // Check instead if the frame's selection controller is focused
1571     if (focusedNode == m_renderer->node() ||
1572         (roleValue() == WebAreaRole && document->frame()->selection()->isFocusedAndActive()))
1573         return true;
1574 
1575     return false;
1576 }
1577 
setFocused(bool on)1578 void AccessibilityRenderObject::setFocused(bool on)
1579 {
1580     if (!canSetFocusAttribute())
1581         return;
1582 
1583     if (!on)
1584         m_renderer->document()->setFocusedNode(0);
1585     else {
1586         if (m_renderer->node()->isElementNode())
1587             static_cast<Element*>(m_renderer->node())->focus();
1588         else
1589             m_renderer->document()->setFocusedNode(m_renderer->node());
1590     }
1591 }
1592 
setValue(const String & string)1593 void AccessibilityRenderObject::setValue(const String& string)
1594 {
1595     // FIXME: Do we want to do anything here for ARIA textboxes?
1596     if (m_renderer->isTextField()) {
1597         HTMLInputElement* input = static_cast<HTMLInputElement*>(m_renderer->node());
1598         input->setValue(string);
1599     } else if (m_renderer->isTextArea()) {
1600         HTMLTextAreaElement* textArea = static_cast<HTMLTextAreaElement*>(m_renderer->node());
1601         textArea->setValue(string);
1602     }
1603 }
1604 
isEnabled() const1605 bool AccessibilityRenderObject::isEnabled() const
1606 {
1607     ASSERT(m_renderer);
1608 
1609     if (equalIgnoringCase(getAttribute(aria_disabledAttr).string(), "true"))
1610         return false;
1611 
1612     Node* node = m_renderer->node();
1613     if (!node || !node->isElementNode())
1614         return true;
1615 
1616     return static_cast<Element*>(node)->isEnabledFormControl();
1617 }
1618 
topRenderer() const1619 RenderView* AccessibilityRenderObject::topRenderer() const
1620 {
1621     return m_renderer->document()->topDocument()->renderView();
1622 }
1623 
document() const1624 Document* AccessibilityRenderObject::document() const
1625 {
1626     return m_renderer->document();
1627 }
1628 
topDocumentFrameView() const1629 FrameView* AccessibilityRenderObject::topDocumentFrameView() const
1630 {
1631     return topRenderer()->view()->frameView();
1632 }
1633 
widget() const1634 Widget* AccessibilityRenderObject::widget() const
1635 {
1636     if (!m_renderer->isWidget())
1637         return 0;
1638     return toRenderWidget(m_renderer)->widget();
1639 }
1640 
axObjectCache() const1641 AXObjectCache* AccessibilityRenderObject::axObjectCache() const
1642 {
1643     return m_renderer->document()->axObjectCache();
1644 }
1645 
accessibilityParentForImageMap(HTMLMapElement * map) const1646 AccessibilityObject* AccessibilityRenderObject::accessibilityParentForImageMap(HTMLMapElement* map) const
1647 {
1648     // find an image that is using this map
1649     if (!m_renderer || !map)
1650         return 0;
1651 
1652     String mapName = map->getName().string().lower();
1653     RefPtr<HTMLCollection> coll = m_renderer->document()->images();
1654     for (Node* curr = coll->firstItem(); curr; curr = coll->nextItem()) {
1655         RenderObject* obj = curr->renderer();
1656         if (!obj || !curr->hasTagName(imgTag))
1657             continue;
1658 
1659         // The HTMLImageElement's useMap() value includes the '#' symbol at the beginning,
1660         // which has to be stripped off
1661         String useMapName = static_cast<HTMLImageElement*>(curr)->useMap().substring(1).lower();
1662         if (useMapName == mapName)
1663             return axObjectCache()->getOrCreate(obj);
1664     }
1665 
1666     return 0;
1667 }
1668 
getDocumentLinks(AccessibilityChildrenVector & result)1669 void AccessibilityRenderObject::getDocumentLinks(AccessibilityChildrenVector& result)
1670 {
1671     Document* document = m_renderer->document();
1672     RefPtr<HTMLCollection> coll = document->links();
1673     Node* curr = coll->firstItem();
1674     while (curr) {
1675         RenderObject* obj = curr->renderer();
1676         if (obj) {
1677             RefPtr<AccessibilityObject> axobj = document->axObjectCache()->getOrCreate(obj);
1678             ASSERT(axobj);
1679             if (!axobj->accessibilityIsIgnored() && axobj->isLink())
1680                 result.append(axobj);
1681         } else {
1682             Node* parent = curr->parent();
1683             if (parent && curr->hasTagName(areaTag) && parent->hasTagName(mapTag)) {
1684                 AccessibilityImageMapLink* areaObject = static_cast<AccessibilityImageMapLink*>(axObjectCache()->getOrCreate(ImageMapLinkRole));
1685                 areaObject->setHTMLAreaElement(static_cast<HTMLAreaElement*>(curr));
1686                 areaObject->setHTMLMapElement(static_cast<HTMLMapElement*>(parent));
1687                 areaObject->setParent(accessibilityParentForImageMap(static_cast<HTMLMapElement*>(parent)));
1688 
1689                 result.append(areaObject);
1690             }
1691         }
1692         curr = coll->nextItem();
1693     }
1694 }
1695 
documentFrameView() const1696 FrameView* AccessibilityRenderObject::documentFrameView() const
1697 {
1698     if (!m_renderer || !m_renderer->document())
1699         return 0;
1700 
1701     // this is the RenderObject's Document's Frame's FrameView
1702     return m_renderer->document()->view();
1703 }
1704 
widgetForAttachmentView() const1705 Widget* AccessibilityRenderObject::widgetForAttachmentView() const
1706 {
1707     if (!isAttachment())
1708         return 0;
1709     return toRenderWidget(m_renderer)->widget();
1710 }
1711 
frameViewIfRenderView() const1712 FrameView* AccessibilityRenderObject::frameViewIfRenderView() const
1713 {
1714     if (!m_renderer->isRenderView())
1715         return 0;
1716     // this is the RenderObject's Document's renderer's FrameView
1717     return m_renderer->view()->frameView();
1718 }
1719 
1720 // This function is like a cross-platform version of - (WebCoreTextMarkerRange*)textMarkerRange. It returns
1721 // a Range that we can convert to a WebCoreTextMarkerRange in the Obj-C file
visiblePositionRange() const1722 VisiblePositionRange AccessibilityRenderObject::visiblePositionRange() const
1723 {
1724     if (!m_renderer)
1725         return VisiblePositionRange();
1726 
1727     // construct VisiblePositions for start and end
1728     Node* node = m_renderer->node();
1729     if (!node)
1730         return VisiblePositionRange();
1731 
1732     VisiblePosition startPos = firstDeepEditingPositionForNode(node);
1733     VisiblePosition endPos = lastDeepEditingPositionForNode(node);
1734 
1735     // the VisiblePositions are equal for nodes like buttons, so adjust for that
1736     // FIXME: Really?  [button, 0] and [button, 1] are distinct (before and after the button)
1737     // I expect this code is only hit for things like empty divs?  In which case I don't think
1738     // the behavior is correct here -- eseidel
1739     if (startPos == endPos) {
1740         endPos = endPos.next();
1741         if (endPos.isNull())
1742             endPos = startPos;
1743     }
1744 
1745     return VisiblePositionRange(startPos, endPos);
1746 }
1747 
visiblePositionRangeForLine(unsigned lineCount) const1748 VisiblePositionRange AccessibilityRenderObject::visiblePositionRangeForLine(unsigned lineCount) const
1749 {
1750     if (lineCount == 0 || !m_renderer)
1751         return VisiblePositionRange();
1752 
1753     // iterate over the lines
1754     // FIXME: this is wrong when lineNumber is lineCount+1,  because nextLinePosition takes you to the
1755     // last offset of the last line
1756     VisiblePosition visiblePos = m_renderer->document()->renderer()->positionForCoordinates(0, 0);
1757     VisiblePosition savedVisiblePos;
1758     while (--lineCount != 0) {
1759         savedVisiblePos = visiblePos;
1760         visiblePos = nextLinePosition(visiblePos, 0);
1761         if (visiblePos.isNull() || visiblePos == savedVisiblePos)
1762             return VisiblePositionRange();
1763     }
1764 
1765     // make a caret selection for the marker position, then extend it to the line
1766     // NOTE: ignores results of sel.modify because it returns false when
1767     // starting at an empty line.  The resulting selection in that case
1768     // will be a caret at visiblePos.
1769     SelectionController selection;
1770     selection.setSelection(VisibleSelection(visiblePos));
1771     selection.modify(SelectionController::EXTEND, SelectionController::RIGHT, LineBoundary);
1772 
1773     return VisiblePositionRange(selection.selection().visibleStart(), selection.selection().visibleEnd());
1774 }
1775 
visiblePositionForIndex(int index) const1776 VisiblePosition AccessibilityRenderObject::visiblePositionForIndex(int index) const
1777 {
1778     if (!m_renderer)
1779         return VisiblePosition();
1780 
1781     if (isNativeTextControl())
1782         return toRenderTextControl(m_renderer)->visiblePositionForIndex(index);
1783 
1784     if (!isTextControl() && !m_renderer->isText())
1785         return VisiblePosition();
1786 
1787     Node* node = m_renderer->node();
1788     if (!node)
1789         return VisiblePosition();
1790 
1791     if (index <= 0)
1792         return VisiblePosition(node, 0, DOWNSTREAM);
1793 
1794     ExceptionCode ec = 0;
1795     RefPtr<Range> range = Range::create(m_renderer->document());
1796     range->selectNodeContents(node, ec);
1797     CharacterIterator it(range.get());
1798     it.advance(index - 1);
1799     return VisiblePosition(it.range()->endContainer(ec), it.range()->endOffset(ec), UPSTREAM);
1800 }
1801 
indexForVisiblePosition(const VisiblePosition & pos) const1802 int AccessibilityRenderObject::indexForVisiblePosition(const VisiblePosition& pos) const
1803 {
1804     if (isNativeTextControl())
1805         return toRenderTextControl(m_renderer)->indexForVisiblePosition(pos);
1806 
1807     if (!isTextControl())
1808         return 0;
1809 
1810     Node* node = m_renderer->node();
1811     if (!node)
1812         return 0;
1813 
1814     Position indexPosition = pos.deepEquivalent();
1815     if (!indexPosition.node() || indexPosition.node()->rootEditableElement() != node)
1816         return 0;
1817 
1818     ExceptionCode ec = 0;
1819     RefPtr<Range> range = Range::create(m_renderer->document());
1820     range->setStart(node, 0, ec);
1821     range->setEnd(indexPosition.node(), indexPosition.deprecatedEditingOffset(), ec);
1822     return TextIterator::rangeLength(range.get());
1823 }
1824 
boundsForVisiblePositionRange(const VisiblePositionRange & visiblePositionRange) const1825 IntRect AccessibilityRenderObject::boundsForVisiblePositionRange(const VisiblePositionRange& visiblePositionRange) const
1826 {
1827     if (visiblePositionRange.isNull())
1828         return IntRect();
1829 
1830     // Create a mutable VisiblePositionRange.
1831     VisiblePositionRange range(visiblePositionRange);
1832     IntRect rect1 = range.start.absoluteCaretBounds();
1833     IntRect rect2 = range.end.absoluteCaretBounds();
1834 
1835     // 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
1836     if (rect2.y() != rect1.y()) {
1837         VisiblePosition endOfFirstLine = endOfLine(range.start);
1838         if (range.start == endOfFirstLine) {
1839             range.start.setAffinity(DOWNSTREAM);
1840             rect1 = range.start.absoluteCaretBounds();
1841         }
1842         if (range.end == endOfFirstLine) {
1843             range.end.setAffinity(UPSTREAM);
1844             rect2 = range.end.absoluteCaretBounds();
1845         }
1846     }
1847 
1848     IntRect ourrect = rect1;
1849     ourrect.unite(rect2);
1850 
1851     // if the rectangle spans lines and contains multiple text chars, use the range's bounding box intead
1852     if (rect1.bottom() != rect2.bottom()) {
1853         RefPtr<Range> dataRange = makeRange(range.start, range.end);
1854         IntRect boundingBox = dataRange->boundingBox();
1855         String rangeString = plainText(dataRange.get());
1856         if (rangeString.length() > 1 && !boundingBox.isEmpty())
1857             ourrect = boundingBox;
1858     }
1859 
1860 #if PLATFORM(MAC)
1861     return m_renderer->document()->view()->contentsToScreen(ourrect);
1862 #else
1863     return ourrect;
1864 #endif
1865 }
1866 
setSelectedVisiblePositionRange(const VisiblePositionRange & range) const1867 void AccessibilityRenderObject::setSelectedVisiblePositionRange(const VisiblePositionRange& range) const
1868 {
1869     if (range.start.isNull() || range.end.isNull())
1870         return;
1871 
1872     // make selection and tell the document to use it. if it's zero length, then move to that position
1873     if (range.start == range.end) {
1874         m_renderer->document()->frame()->selection()->moveTo(range.start, true);
1875     }
1876     else {
1877         VisibleSelection newSelection = VisibleSelection(range.start, range.end);
1878         m_renderer->document()->frame()->selection()->setSelection(newSelection);
1879     }
1880 }
1881 
visiblePositionForPoint(const IntPoint & point) const1882 VisiblePosition AccessibilityRenderObject::visiblePositionForPoint(const IntPoint& point) const
1883 {
1884     // convert absolute point to view coordinates
1885     FrameView* frameView = m_renderer->document()->topDocument()->renderer()->view()->frameView();
1886     RenderView* renderView = topRenderer();
1887     Node* innerNode = 0;
1888 
1889     // locate the node containing the point
1890     IntPoint pointResult;
1891     while (1) {
1892         IntPoint ourpoint;
1893 #if PLATFORM(MAC)
1894         ourpoint = frameView->screenToContents(point);
1895 #else
1896         ourpoint = point;
1897 #endif
1898         HitTestRequest request(HitTestRequest::ReadOnly |
1899                                HitTestRequest::Active);
1900         HitTestResult result(ourpoint);
1901         renderView->layer()->hitTest(request, result);
1902         innerNode = result.innerNode();
1903         if (!innerNode || !innerNode->renderer())
1904             return VisiblePosition();
1905 
1906         pointResult = result.localPoint();
1907 
1908         // done if hit something other than a widget
1909         RenderObject* renderer = innerNode->renderer();
1910         if (!renderer->isWidget())
1911             break;
1912 
1913         // descend into widget (FRAME, IFRAME, OBJECT...)
1914         Widget* widget = toRenderWidget(renderer)->widget();
1915         if (!widget || !widget->isFrameView())
1916             break;
1917         Frame* frame = static_cast<FrameView*>(widget)->frame();
1918         if (!frame)
1919             break;
1920         renderView = frame->document()->renderView();
1921         frameView = static_cast<FrameView*>(widget);
1922     }
1923 
1924     return innerNode->renderer()->positionForPoint(pointResult);
1925 }
1926 
1927 // NOTE: Consider providing this utility method as AX API
visiblePositionForIndex(unsigned indexValue,bool lastIndexOK) const1928 VisiblePosition AccessibilityRenderObject::visiblePositionForIndex(unsigned indexValue, bool lastIndexOK) const
1929 {
1930     if (!isTextControl())
1931         return VisiblePosition();
1932 
1933     // lastIndexOK specifies whether the position after the last character is acceptable
1934     if (indexValue >= text().length()) {
1935         if (!lastIndexOK || indexValue > text().length())
1936             return VisiblePosition();
1937     }
1938     VisiblePosition position = visiblePositionForIndex(indexValue);
1939     position.setAffinity(DOWNSTREAM);
1940     return position;
1941 }
1942 
1943 // NOTE: Consider providing this utility method as AX API
index(const VisiblePosition & position) const1944 int AccessibilityRenderObject::index(const VisiblePosition& position) const
1945 {
1946     if (!isTextControl())
1947         return -1;
1948 
1949     Node* node = position.deepEquivalent().node();
1950     if (!node)
1951         return -1;
1952 
1953     for (RenderObject* renderer = node->renderer(); renderer && renderer->node(); renderer = renderer->parent()) {
1954         if (renderer == m_renderer)
1955             return indexForVisiblePosition(position);
1956     }
1957 
1958     return -1;
1959 }
1960 
1961 // Given a line number, the range of characters of the text associated with this accessibility
1962 // object that contains the line number.
doAXRangeForLine(unsigned lineNumber) const1963 PlainTextRange AccessibilityRenderObject::doAXRangeForLine(unsigned lineNumber) const
1964 {
1965     if (!isTextControl())
1966         return PlainTextRange();
1967 
1968     // iterate to the specified line
1969     VisiblePosition visiblePos = visiblePositionForIndex(0);
1970     VisiblePosition savedVisiblePos;
1971     for (unsigned lineCount = lineNumber; lineCount != 0; lineCount -= 1) {
1972         savedVisiblePos = visiblePos;
1973         visiblePos = nextLinePosition(visiblePos, 0);
1974         if (visiblePos.isNull() || visiblePos == savedVisiblePos)
1975             return PlainTextRange();
1976     }
1977 
1978     // make a caret selection for the marker position, then extend it to the line
1979     // NOTE: ignores results of selection.modify because it returns false when
1980     // starting at an empty line.  The resulting selection in that case
1981     // will be a caret at visiblePos.
1982     SelectionController selection;
1983     selection.setSelection(VisibleSelection(visiblePos));
1984     selection.modify(SelectionController::EXTEND, SelectionController::LEFT, LineBoundary);
1985     selection.modify(SelectionController::EXTEND, SelectionController::RIGHT, LineBoundary);
1986 
1987     // calculate the indices for the selection start and end
1988     VisiblePosition startPosition = selection.selection().visibleStart();
1989     VisiblePosition endPosition = selection.selection().visibleEnd();
1990     int index1 = indexForVisiblePosition(startPosition);
1991     int index2 = indexForVisiblePosition(endPosition);
1992 
1993     // add one to the end index for a line break not caused by soft line wrap (to match AppKit)
1994     if (endPosition.affinity() == DOWNSTREAM && endPosition.next().isNotNull())
1995         index2 += 1;
1996 
1997     // return nil rather than an zero-length range (to match AppKit)
1998     if (index1 == index2)
1999         return PlainTextRange();
2000 
2001     return PlainTextRange(index1, index2 - index1);
2002 }
2003 
2004 // The composed character range in the text associated with this accessibility object that
2005 // is specified by the given index value. This parameterized attribute returns the complete
2006 // range of characters (including surrogate pairs of multi-byte glyphs) at the given index.
doAXRangeForIndex(unsigned index) const2007 PlainTextRange AccessibilityRenderObject::doAXRangeForIndex(unsigned index) const
2008 {
2009     if (!isTextControl())
2010         return PlainTextRange();
2011 
2012     String elementText = text();
2013     if (!elementText.length() || index > elementText.length() - 1)
2014         return PlainTextRange();
2015 
2016     return PlainTextRange(index, 1);
2017 }
2018 
2019 // A substring of the text associated with this accessibility object that is
2020 // specified by the given character range.
doAXStringForRange(const PlainTextRange & range) const2021 String AccessibilityRenderObject::doAXStringForRange(const PlainTextRange& range) const
2022 {
2023     if (isPasswordField())
2024         return String();
2025 
2026     if (range.length == 0)
2027         return "";
2028 
2029     if (!isTextControl())
2030         return String();
2031 
2032     String elementText = text();
2033     if (range.start + range.length > elementText.length())
2034         return String();
2035 
2036     return elementText.substring(range.start, range.length);
2037 }
2038 
2039 // The bounding rectangle of the text associated with this accessibility object that is
2040 // specified by the given range. This is the bounding rectangle a sighted user would see
2041 // on the display screen, in pixels.
doAXBoundsForRange(const PlainTextRange & range) const2042 IntRect AccessibilityRenderObject::doAXBoundsForRange(const PlainTextRange& range) const
2043 {
2044     if (isTextControl())
2045         return boundsForVisiblePositionRange(visiblePositionRangeForRange(range));
2046     return IntRect();
2047 }
2048 
accessibilityImageMapHitTest(HTMLAreaElement * area,const IntPoint & point) const2049 AccessibilityObject* AccessibilityRenderObject::accessibilityImageMapHitTest(HTMLAreaElement* area, const IntPoint& point) const
2050 {
2051     if (!area)
2052         return 0;
2053 
2054     HTMLMapElement *map = static_cast<HTMLMapElement*>(area->parent());
2055     AccessibilityObject* parent = accessibilityParentForImageMap(map);
2056     if (!parent)
2057         return 0;
2058 
2059     AccessibilityObject::AccessibilityChildrenVector children = parent->children();
2060 
2061     unsigned count = children.size();
2062     for (unsigned k = 0; k < count; ++k) {
2063         if (children[k]->elementRect().contains(point))
2064             return children[k].get();
2065     }
2066 
2067     return 0;
2068 }
2069 
doAccessibilityHitTest(const IntPoint & point) const2070 AccessibilityObject* AccessibilityRenderObject::doAccessibilityHitTest(const IntPoint& point) const
2071 {
2072     if (!m_renderer || !m_renderer->hasLayer())
2073         return 0;
2074 
2075     RenderLayer* layer = toRenderBox(m_renderer)->layer();
2076 
2077     HitTestRequest request(HitTestRequest::ReadOnly |
2078                            HitTestRequest::Active);
2079     HitTestResult hitTestResult = HitTestResult(point);
2080     layer->hitTest(request, hitTestResult);
2081     if (!hitTestResult.innerNode())
2082         return 0;
2083     Node* node = hitTestResult.innerNode()->shadowAncestorNode();
2084 
2085     if (node->hasTagName(areaTag))
2086         return accessibilityImageMapHitTest(static_cast<HTMLAreaElement*>(node), point);
2087 
2088     RenderObject* obj = node->renderer();
2089     if (!obj)
2090         return 0;
2091 
2092     AccessibilityObject* result = obj->document()->axObjectCache()->getOrCreate(obj);
2093 
2094     if (obj->isListBox())
2095         return static_cast<AccessibilityListBox*>(result)->doAccessibilityHitTest(point);
2096 
2097     if (result->accessibilityIsIgnored())
2098         result = result->parentObjectUnignored();
2099 
2100     return result;
2101 }
2102 
focusedUIElement() const2103 AccessibilityObject* AccessibilityRenderObject::focusedUIElement() const
2104 {
2105     // get the focused node in the page
2106     Page* page = m_renderer->document()->page();
2107     if (!page)
2108         return 0;
2109 
2110     Document* focusedDocument = page->focusController()->focusedOrMainFrame()->document();
2111     Node* focusedNode = focusedDocument->focusedNode();
2112     if (!focusedNode)
2113         focusedNode = focusedDocument;
2114 
2115     RenderObject* focusedNodeRenderer = focusedNode->renderer();
2116     if (!focusedNodeRenderer)
2117         return 0;
2118 
2119     AccessibilityObject* obj = focusedNodeRenderer->document()->axObjectCache()->getOrCreate(focusedNodeRenderer);
2120 
2121     if (obj->shouldFocusActiveDescendant()) {
2122         if (AccessibilityObject* descendant = obj->activeDescendant())
2123             obj = descendant;
2124     }
2125 
2126     // the HTML element, for example, is focusable but has an AX object that is ignored
2127     if (obj->accessibilityIsIgnored())
2128         obj = obj->parentObjectUnignored();
2129 
2130     return obj;
2131 }
2132 
shouldFocusActiveDescendant() const2133 bool AccessibilityRenderObject::shouldFocusActiveDescendant() const
2134 {
2135     switch (ariaRoleAttribute()) {
2136     case GroupRole:
2137     case ComboBoxRole:
2138     case ListBoxRole:
2139     case MenuRole:
2140     case MenuBarRole:
2141     case RadioGroupRole:
2142     case RowRole:
2143     case PopUpButtonRole:
2144     case ProgressIndicatorRole:
2145     case ToolbarRole:
2146     case OutlineRole:
2147     /* FIXME: replace these with actual roles when they are added to AccessibilityRole
2148     composite
2149     alert
2150     alertdialog
2151     grid
2152     status
2153     timer
2154     tree
2155     */
2156         return true;
2157     default:
2158         return false;
2159     }
2160 }
2161 
activeDescendant() const2162 AccessibilityObject* AccessibilityRenderObject::activeDescendant() const
2163 {
2164     if (renderer()->node() && !renderer()->node()->isElementNode())
2165         return 0;
2166     Element* element = static_cast<Element*>(renderer()->node());
2167 
2168     String activeDescendantAttrStr = element->getAttribute(aria_activedescendantAttr).string();
2169     if (activeDescendantAttrStr.isNull() || activeDescendantAttrStr.isEmpty())
2170         return 0;
2171 
2172     Element* target = renderer()->document()->getElementById(activeDescendantAttrStr);
2173     if (!target)
2174         return 0;
2175 
2176     AccessibilityObject* obj = renderer()->document()->axObjectCache()->getOrCreate(target->renderer());
2177     if (obj->isAccessibilityRenderObject())
2178     // an activedescendant is only useful if it has a renderer, because that's what's needed to post the notification
2179         return obj;
2180     return 0;
2181 }
2182 
2183 
handleActiveDescendantChanged()2184 void AccessibilityRenderObject::handleActiveDescendantChanged()
2185 {
2186     Element* element = static_cast<Element*>(renderer()->node());
2187     if (!element)
2188         return;
2189     Document* doc = renderer()->document();
2190     if (!doc->frame()->selection()->isFocusedAndActive() || doc->focusedNode() != element)
2191         return;
2192     AccessibilityRenderObject* activedescendant = static_cast<AccessibilityRenderObject*>(activeDescendant());
2193 
2194     if (activedescendant && shouldFocusActiveDescendant())
2195         doc->axObjectCache()->postNotification(activedescendant->renderer(), "AXFocusedUIElementChanged", true);
2196 }
2197 
2198 
observableObject() const2199 AccessibilityObject* AccessibilityRenderObject::observableObject() const
2200 {
2201     for (RenderObject* renderer = m_renderer; renderer && renderer->node(); renderer = renderer->parent()) {
2202         if (renderer->isTextControl())
2203             return renderer->document()->axObjectCache()->getOrCreate(renderer);
2204     }
2205 
2206     return 0;
2207 }
2208 
2209 typedef HashMap<String, AccessibilityRole, CaseFoldingHash> ARIARoleMap;
2210 
createARIARoleMap()2211 static const ARIARoleMap& createARIARoleMap()
2212 {
2213     struct RoleEntry {
2214         String ariaRole;
2215         AccessibilityRole webcoreRole;
2216     };
2217 
2218     const RoleEntry roles[] = {
2219         { "button", ButtonRole },
2220         { "checkbox", CheckBoxRole },
2221         { "grid", TableRole },
2222         { "gridcell", CellRole },
2223         { "columnheader", ColumnHeaderRole },
2224         { "rowheader", RowHeaderRole },
2225         { "group", GroupRole },
2226         { "heading", HeadingRole },
2227         { "img", ImageRole },
2228         { "link", WebCoreLinkRole },
2229         { "listbox", ListBoxRole },
2230         // "option" isn't here because it may map to different roles depending on the parent element's role
2231         { "menu", MenuRole },
2232         { "menubar", GroupRole },
2233         // "menuitem" isn't here because it may map to different roles depending on the parent element's role
2234         { "menuitemcheckbox", MenuItemRole },
2235         { "menuitemradio", MenuItemRole },
2236         { "progressbar", ProgressIndicatorRole },
2237         { "radio", RadioButtonRole },
2238         { "radiogroup", RadioGroupRole },
2239         { "row", RowRole },
2240         { "range", SliderRole },
2241         { "slider", SliderRole },
2242         { "spinbutton", ProgressIndicatorRole },
2243         { "textbox", TextAreaRole },
2244         { "toolbar", ToolbarRole }
2245     };
2246     ARIARoleMap& roleMap = *new ARIARoleMap;
2247 
2248     const unsigned numRoles = sizeof(roles) / sizeof(roles[0]);
2249     for (unsigned i = 0; i < numRoles; ++i)
2250         roleMap.set(roles[i].ariaRole, roles[i].webcoreRole);
2251     return roleMap;
2252 }
2253 
ariaRoleToWebCoreRole(String value)2254 static AccessibilityRole ariaRoleToWebCoreRole(String value)
2255 {
2256     ASSERT(!value.isEmpty() && !value.isNull());
2257     static const ARIARoleMap& roleMap = createARIARoleMap();
2258     return roleMap.get(value);
2259 }
2260 
determineAriaRoleAttribute() const2261 AccessibilityRole AccessibilityRenderObject::determineAriaRoleAttribute() const
2262 {
2263     String ariaRole = getAttribute(roleAttr).string();
2264     if (ariaRole.isNull() || ariaRole.isEmpty())
2265         return UnknownRole;
2266 
2267     AccessibilityRole role = ariaRoleToWebCoreRole(ariaRole);
2268     if (role)
2269         return role;
2270     // selects and listboxes both have options as child roles, but they map to different roles within WebCore
2271     if (equalIgnoringCase(ariaRole,"option")) {
2272         if (parentObjectUnignored()->ariaRoleAttribute() == MenuRole)
2273             return MenuItemRole;
2274         if (parentObjectUnignored()->ariaRoleAttribute() == ListBoxRole)
2275             return ListBoxOptionRole;
2276     }
2277     // an aria "menuitem" may map to MenuButton or MenuItem depending on its parent
2278     if (equalIgnoringCase(ariaRole,"menuitem")) {
2279         if (parentObjectUnignored()->ariaRoleAttribute() == GroupRole)
2280             return MenuButtonRole;
2281         if (parentObjectUnignored()->ariaRoleAttribute() == MenuRole)
2282             return MenuItemRole;
2283     }
2284 
2285     return UnknownRole;
2286 }
2287 
ariaRoleAttribute() const2288 AccessibilityRole AccessibilityRenderObject::ariaRoleAttribute() const
2289 {
2290     return m_ariaRole;
2291 }
2292 
updateAccessibilityRole()2293 void AccessibilityRenderObject::updateAccessibilityRole()
2294 {
2295     m_role = determineAccessibilityRole();
2296 }
2297 
determineAccessibilityRole()2298 AccessibilityRole AccessibilityRenderObject::determineAccessibilityRole()
2299 {
2300     if (!m_renderer)
2301         return UnknownRole;
2302 
2303     m_ariaRole = determineAriaRoleAttribute();
2304 
2305     Node* node = m_renderer->node();
2306     AccessibilityRole ariaRole = ariaRoleAttribute();
2307     if (ariaRole != UnknownRole)
2308         return ariaRole;
2309 
2310     if (node && node->isLink()) {
2311         if (m_renderer->isImage())
2312             return ImageMapRole;
2313         return WebCoreLinkRole;
2314     }
2315     if (m_renderer->isListMarker())
2316         return ListMarkerRole;
2317     if (node && node->hasTagName(buttonTag))
2318         return ButtonRole;
2319     if (m_renderer->isText())
2320         return StaticTextRole;
2321     if (m_renderer->isImage()) {
2322         if (node && node->hasTagName(inputTag))
2323             return ButtonRole;
2324         return ImageRole;
2325     }
2326     if (node && node->hasTagName(canvasTag))
2327         return ImageRole;
2328 
2329     if (m_renderer->isRenderView())
2330         return WebAreaRole;
2331 
2332     if (m_renderer->isTextField())
2333         return TextFieldRole;
2334 
2335     if (m_renderer->isTextArea())
2336         return TextAreaRole;
2337 
2338     if (node && node->hasTagName(inputTag)) {
2339         HTMLInputElement* input = static_cast<HTMLInputElement*>(node);
2340         if (input->inputType() == HTMLInputElement::CHECKBOX)
2341             return CheckBoxRole;
2342         if (input->inputType() == HTMLInputElement::RADIO)
2343             return RadioButtonRole;
2344         if (input->isTextButton())
2345             return ButtonRole;
2346     }
2347 
2348     if (node && node->hasTagName(buttonTag))
2349         return ButtonRole;
2350 
2351     if (isFileUploadButton())
2352         return ButtonRole;
2353 
2354     if (m_renderer->isMenuList())
2355         return PopUpButtonRole;
2356 
2357     if (headingLevel() != 0)
2358         return HeadingRole;
2359 
2360     if (node && node->hasTagName(ddTag))
2361         return DefinitionListDefinitionRole;
2362 
2363     if (node && node->hasTagName(dtTag))
2364         return DefinitionListTermRole;
2365 
2366     if (node && (node->hasTagName(rpTag) || node->hasTagName(rtTag)))
2367         return AnnotationRole;
2368 
2369     if (m_renderer->isBlockFlow() || (node && node->hasTagName(labelTag)))
2370         return GroupRole;
2371 
2372     return UnknownRole;
2373 }
2374 
isPresentationalChildOfAriaRole() const2375 bool AccessibilityRenderObject::isPresentationalChildOfAriaRole() const
2376 {
2377     // Walk the parent chain looking for a parent that has presentational children
2378     AccessibilityObject* parent;
2379     for (parent = parentObject(); parent && !parent->ariaRoleHasPresentationalChildren(); parent = parent->parentObject())
2380         ;
2381     return parent;
2382 }
2383 
ariaRoleHasPresentationalChildren() const2384 bool AccessibilityRenderObject::ariaRoleHasPresentationalChildren() const
2385 {
2386     switch (m_ariaRole) {
2387     case ButtonRole:
2388     case SliderRole:
2389     case ImageRole:
2390     case ProgressIndicatorRole:
2391     //case SeparatorRole:
2392         return true;
2393     default:
2394         return false;
2395     }
2396 }
2397 
canSetFocusAttribute() const2398 bool AccessibilityRenderObject::canSetFocusAttribute() const
2399 {
2400     ASSERT(m_renderer);
2401     Node* node = m_renderer->node();
2402 
2403     // NOTE: It would be more accurate to ask the document whether setFocusedNode() would
2404     // do anything.  For example, setFocusedNode() will do nothing if the current focused
2405     // node will not relinquish the focus.
2406     if (!node || !node->isElementNode())
2407         return false;
2408 
2409     if (!static_cast<Element*>(node)->isEnabledFormControl())
2410         return false;
2411 
2412     switch (roleValue()) {
2413         case WebCoreLinkRole:
2414         case ImageMapLinkRole:
2415         case TextFieldRole:
2416         case TextAreaRole:
2417         case ButtonRole:
2418         case PopUpButtonRole:
2419         case CheckBoxRole:
2420         case RadioButtonRole:
2421             return true;
2422         default:
2423             return false;
2424     }
2425 }
2426 
canSetValueAttribute() const2427 bool AccessibilityRenderObject::canSetValueAttribute() const
2428 {
2429     if (equalIgnoringCase(getAttribute(aria_readonlyAttr).string(), "true"))
2430         return false;
2431 
2432     if (isWebArea())
2433         return !isReadOnly();
2434 
2435     return isTextControl() || isProgressIndicator() || isSlider();
2436 }
2437 
canSetTextRangeAttributes() const2438 bool AccessibilityRenderObject::canSetTextRangeAttributes() const
2439 {
2440     return isTextControl();
2441 }
2442 
childrenChanged()2443 void AccessibilityRenderObject::childrenChanged()
2444 {
2445     // this method is meant as a quick way of marking dirty
2446     // a portion of the accessibility tree
2447 
2448     markChildrenDirty();
2449 
2450     if (!m_renderer)
2451         return;
2452 
2453     // Go up the render parent chain, marking children as dirty.
2454     // We can't rely on the accessibilityParent() because it may not exist and we must not create an AX object here either
2455     for (RenderObject* renderParent = m_renderer->parent(); renderParent; renderParent = renderParent->parent()) {
2456         AccessibilityObject* parent = m_renderer->document()->axObjectCache()->get(renderParent);
2457         if (parent && parent->isAccessibilityRenderObject())
2458             static_cast<AccessibilityRenderObject *>(parent)->markChildrenDirty();
2459     }
2460 }
2461 
canHaveChildren() const2462 bool AccessibilityRenderObject::canHaveChildren() const
2463 {
2464     if (!m_renderer)
2465         return false;
2466 
2467     // Elements that should not have children
2468     switch (roleValue()) {
2469         case ImageRole:
2470         case ButtonRole:
2471         case PopUpButtonRole:
2472         case CheckBoxRole:
2473         case RadioButtonRole:
2474             return false;
2475         default:
2476             return true;
2477     }
2478 }
2479 
children()2480 const AccessibilityObject::AccessibilityChildrenVector& AccessibilityRenderObject::children()
2481 {
2482     if (m_childrenDirty) {
2483         clearChildren();
2484         m_childrenDirty = false;
2485     }
2486 
2487     if (!m_haveChildren)
2488         addChildren();
2489     return m_children;
2490 }
2491 
addChildren()2492 void AccessibilityRenderObject::addChildren()
2493 {
2494     // If the need to add more children in addition to existing children arises,
2495     // childrenChanged should have been called, leaving the object with no children.
2496     ASSERT(!m_haveChildren);
2497 
2498     // nothing to add if there is no RenderObject
2499     if (!m_renderer)
2500         return;
2501 
2502     m_haveChildren = true;
2503 
2504     if (!canHaveChildren())
2505         return;
2506 
2507     // add all unignored acc children
2508     for (RefPtr<AccessibilityObject> obj = firstChild(); obj; obj = obj->nextSibling()) {
2509         if (obj->accessibilityIsIgnored()) {
2510             if (!obj->hasChildren())
2511                 obj->addChildren();
2512             AccessibilityChildrenVector children = obj->children();
2513             unsigned length = children.size();
2514             for (unsigned i = 0; i < length; ++i)
2515                 m_children.append(children[i]);
2516         } else
2517             m_children.append(obj);
2518     }
2519 
2520     // for a RenderImage, add the <area> elements as individual accessibility objects
2521     if (m_renderer->isRenderImage()) {
2522         HTMLMapElement* map = toRenderImage(m_renderer)->imageMap();
2523         if (map) {
2524             for (Node* current = map->firstChild(); current; current = current->traverseNextNode(map)) {
2525 
2526                 // add an <area> element for this child if it has a link
2527                 if (current->isLink()) {
2528                     AccessibilityImageMapLink* areaObject = static_cast<AccessibilityImageMapLink*>(m_renderer->document()->axObjectCache()->getOrCreate(ImageMapLinkRole));
2529                     areaObject->setHTMLAreaElement(static_cast<HTMLAreaElement*>(current));
2530                     areaObject->setHTMLMapElement(map);
2531                     areaObject->setParent(this);
2532 
2533                     m_children.append(areaObject);
2534                 }
2535             }
2536         }
2537     }
2538 }
2539 
ariaListboxSelectedChildren(AccessibilityChildrenVector & result)2540 void AccessibilityRenderObject::ariaListboxSelectedChildren(AccessibilityChildrenVector& result)
2541 {
2542     AccessibilityObject* child = firstChild();
2543     bool isMultiselectable = false;
2544 
2545     Element* element = static_cast<Element*>(renderer()->node());
2546     if (!element || !element->isElementNode()) // do this check to ensure safety of static_cast above
2547         return;
2548 
2549     String multiselectablePropertyStr = element->getAttribute("aria-multiselectable").string();
2550     isMultiselectable = equalIgnoringCase(multiselectablePropertyStr, "true");
2551 
2552     while (child) {
2553         // every child should have aria-role option, and if so, check for selected attribute/state
2554         AccessibilityRole ariaRole = child->ariaRoleAttribute();
2555         RenderObject* childRenderer = 0;
2556         if (child->isAccessibilityRenderObject())
2557             childRenderer = static_cast<AccessibilityRenderObject*>(child)->renderer();
2558         if (childRenderer && ariaRole == ListBoxOptionRole) {
2559             Element* childElement = static_cast<Element*>(childRenderer->node());
2560             if (childElement && childElement->isElementNode()) { // do this check to ensure safety of static_cast above
2561                 String selectedAttrString = childElement->getAttribute("aria-selected").string();
2562                 if (equalIgnoringCase(selectedAttrString, "true")) {
2563                     result.append(child);
2564                     if (isMultiselectable)
2565                         return;
2566                 }
2567             }
2568         }
2569         child = child->nextSibling();
2570     }
2571 }
2572 
selectedChildren(AccessibilityChildrenVector & result)2573 void AccessibilityRenderObject::selectedChildren(AccessibilityChildrenVector& result)
2574 {
2575     ASSERT(result.isEmpty());
2576 
2577     // only listboxes should be asked for their selected children.
2578     if (ariaRoleAttribute() != ListBoxRole) { // native list boxes would be AccessibilityListBoxes, so only check for aria list boxes
2579         ASSERT_NOT_REACHED();
2580         return;
2581     }
2582     return ariaListboxSelectedChildren(result);
2583 }
2584 
ariaListboxVisibleChildren(AccessibilityChildrenVector & result)2585 void AccessibilityRenderObject::ariaListboxVisibleChildren(AccessibilityChildrenVector& result)
2586 {
2587     if (!hasChildren())
2588         addChildren();
2589 
2590     unsigned length = m_children.size();
2591     for (unsigned i = 0; i < length; i++) {
2592         if (!m_children[i]->isOffScreen())
2593             result.append(m_children[i]);
2594     }
2595 }
2596 
visibleChildren(AccessibilityChildrenVector & result)2597 void AccessibilityRenderObject::visibleChildren(AccessibilityChildrenVector& result)
2598 {
2599     ASSERT(result.isEmpty());
2600 
2601     // only listboxes are asked for their visible children.
2602     if (ariaRoleAttribute() != ListBoxRole) { // native list boxes would be AccessibilityListBoxes, so only check for aria list boxes
2603         ASSERT_NOT_REACHED();
2604         return;
2605     }
2606     return ariaListboxVisibleChildren(result);
2607 }
2608 
actionVerb() const2609 const String& AccessibilityRenderObject::actionVerb() const
2610 {
2611     // FIXME: Need to add verbs for select elements.
2612     DEFINE_STATIC_LOCAL(const String, buttonAction, (AXButtonActionVerb()));
2613     DEFINE_STATIC_LOCAL(const String, textFieldAction, (AXTextFieldActionVerb()));
2614     DEFINE_STATIC_LOCAL(const String, radioButtonAction, (AXRadioButtonActionVerb()));
2615     DEFINE_STATIC_LOCAL(const String, checkedCheckBoxAction, (AXCheckedCheckBoxActionVerb()));
2616     DEFINE_STATIC_LOCAL(const String, uncheckedCheckBoxAction, (AXUncheckedCheckBoxActionVerb()));
2617     DEFINE_STATIC_LOCAL(const String, linkAction, (AXLinkActionVerb()));
2618     DEFINE_STATIC_LOCAL(const String, noAction, ());
2619 
2620     switch (roleValue()) {
2621         case ButtonRole:
2622             return buttonAction;
2623         case TextFieldRole:
2624         case TextAreaRole:
2625             return textFieldAction;
2626         case RadioButtonRole:
2627             return radioButtonAction;
2628         case CheckBoxRole:
2629             return isChecked() ? checkedCheckBoxAction : uncheckedCheckBoxAction;
2630         case LinkRole:
2631         case WebCoreLinkRole:
2632             return linkAction;
2633         default:
2634             return noAction;
2635     }
2636 }
2637 
updateBackingStore()2638 void AccessibilityRenderObject::updateBackingStore()
2639 {
2640     if (!m_renderer)
2641         return;
2642     m_renderer->view()->layoutIfNeeded();
2643 }
2644 
2645 } // namespace WebCore
2646