• 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 "AccessibilityImageMapLink.h"
34 #include "AccessibilityListBox.h"
35 #include "CharacterNames.h"
36 #include "EventNames.h"
37 #include "FloatRect.h"
38 #include "Frame.h"
39 #include "FrameLoader.h"
40 #include "HTMLAreaElement.h"
41 #include "HTMLFormElement.h"
42 #include "HTMLFrameElementBase.h"
43 #include "HTMLImageElement.h"
44 #include "HTMLInputElement.h"
45 #include "HTMLLabelElement.h"
46 #include "HTMLMapElement.h"
47 #include "HTMLOptGroupElement.h"
48 #include "HTMLOptionElement.h"
49 #include "HTMLOptionsCollection.h"
50 #include "HTMLSelectElement.h"
51 #include "HTMLTextAreaElement.h"
52 #include "HitTestRequest.h"
53 #include "HitTestResult.h"
54 #include "LocalizedStrings.h"
55 #include "NodeList.h"
56 #include "ProgressTracker.h"
57 #include "RenderButton.h"
58 #include "RenderFieldset.h"
59 #include "RenderFileUploadControl.h"
60 #include "RenderHTMLCanvas.h"
61 #include "RenderImage.h"
62 #include "RenderInline.h"
63 #include "RenderListBox.h"
64 #include "RenderListMarker.h"
65 #include "RenderMenuList.h"
66 #include "RenderText.h"
67 #include "RenderTextControl.h"
68 #include "RenderTextFragment.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     , m_childrenDirty(false)
90     , m_roleForMSAA(UnknownRole)
91 {
92     updateAccessibilityRole();
93 #ifndef NDEBUG
94     m_renderer->setHasAXObject(true);
95 #endif
96 }
97 
~AccessibilityRenderObject()98 AccessibilityRenderObject::~AccessibilityRenderObject()
99 {
100     ASSERT(isDetached());
101 }
102 
create(RenderObject * renderer)103 PassRefPtr<AccessibilityRenderObject> AccessibilityRenderObject::create(RenderObject* renderer)
104 {
105     return adoptRef(new AccessibilityRenderObject(renderer));
106 }
107 
detach()108 void AccessibilityRenderObject::detach()
109 {
110     clearChildren();
111     AccessibilityObject::detach();
112 
113 #ifndef NDEBUG
114     if (m_renderer)
115         m_renderer->setHasAXObject(false);
116 #endif
117     m_renderer = 0;
118 }
119 
firstChild() const120 AccessibilityObject* AccessibilityRenderObject::firstChild() const
121 {
122     if (!m_renderer)
123         return 0;
124 
125     RenderObject* firstChild = m_renderer->firstChild();
126     if (!firstChild)
127         return 0;
128 
129     return m_renderer->document()->axObjectCache()->getOrCreate(firstChild);
130 }
131 
lastChild() const132 AccessibilityObject* AccessibilityRenderObject::lastChild() const
133 {
134     if (!m_renderer)
135         return 0;
136 
137     RenderObject* lastChild = m_renderer->lastChild();
138     if (!lastChild)
139         return 0;
140 
141     return m_renderer->document()->axObjectCache()->getOrCreate(lastChild);
142 }
143 
previousSibling() const144 AccessibilityObject* AccessibilityRenderObject::previousSibling() const
145 {
146     if (!m_renderer)
147         return 0;
148 
149     RenderObject* previousSibling = m_renderer->previousSibling();
150     if (!previousSibling)
151         return 0;
152 
153     return m_renderer->document()->axObjectCache()->getOrCreate(previousSibling);
154 }
155 
nextSibling() const156 AccessibilityObject* AccessibilityRenderObject::nextSibling() const
157 {
158     if (!m_renderer)
159         return 0;
160 
161     RenderObject* nextSibling = m_renderer->nextSibling();
162     if (!nextSibling)
163         return 0;
164 
165     return m_renderer->document()->axObjectCache()->getOrCreate(nextSibling);
166 }
167 
parentObjectIfExists() const168 AccessibilityObject* AccessibilityRenderObject::parentObjectIfExists() const
169 {
170     if (!m_renderer)
171         return 0;
172 
173     RenderObject* parent = m_renderer->parent();
174     if (!parent)
175         return 0;
176 
177     return m_renderer->document()->axObjectCache()->get(parent);
178 }
179 
parentObject() const180 AccessibilityObject* AccessibilityRenderObject::parentObject() const
181 {
182     if (!m_renderer)
183         return 0;
184 
185     RenderObject* parent = m_renderer->parent();
186     if (!parent)
187         return 0;
188 
189     if (ariaRoleAttribute() == MenuBarRole)
190         return m_renderer->document()->axObjectCache()->getOrCreate(parent);
191 
192     // menuButton and its corresponding menu are DOM siblings, but Accessibility needs them to be parent/child
193     if (ariaRoleAttribute() == MenuRole) {
194         AccessibilityObject* parent = menuButtonForMenu();
195         if (parent)
196             return parent;
197     }
198 
199     return m_renderer->document()->axObjectCache()->getOrCreate(parent);
200 }
201 
isWebArea() const202 bool AccessibilityRenderObject::isWebArea() const
203 {
204     return roleValue() == WebAreaRole;
205 }
206 
isImageButton() const207 bool AccessibilityRenderObject::isImageButton() const
208 {
209     return isNativeImage() && roleValue() == ButtonRole;
210 }
211 
isAnchor() const212 bool AccessibilityRenderObject::isAnchor() const
213 {
214     return !isNativeImage() && isLink();
215 }
216 
isNativeTextControl() const217 bool AccessibilityRenderObject::isNativeTextControl() const
218 {
219     return m_renderer->isTextControl();
220 }
221 
isTextControl() const222 bool AccessibilityRenderObject::isTextControl() const
223 {
224     AccessibilityRole role = roleValue();
225     return role == TextAreaRole || role == TextFieldRole;
226 }
227 
isNativeImage() const228 bool AccessibilityRenderObject::isNativeImage() const
229 {
230     return m_renderer->isImage();
231 }
232 
isImage() const233 bool AccessibilityRenderObject::isImage() const
234 {
235     return roleValue() == ImageRole;
236 }
237 
isAttachment() const238 bool AccessibilityRenderObject::isAttachment() const
239 {
240     if (!m_renderer)
241         return false;
242 
243     // Widgets are the replaced elements that we represent to AX as attachments
244     bool isWidget = m_renderer && m_renderer->isWidget();
245     ASSERT(!isWidget || (m_renderer->isReplaced() && !isImage()));
246     return isWidget && ariaRoleAttribute() == UnknownRole;
247 }
248 
isPasswordField() const249 bool AccessibilityRenderObject::isPasswordField() const
250 {
251     ASSERT(m_renderer);
252     if (!m_renderer->node() || !m_renderer->node()->isHTMLElement())
253         return false;
254     if (ariaRoleAttribute() != UnknownRole)
255         return false;
256 
257     InputElement* inputElement = toInputElement(static_cast<Element*>(m_renderer->node()));
258     if (!inputElement)
259         return false;
260 
261     return inputElement->isPasswordField();
262 }
263 
isCheckboxOrRadio() const264 bool AccessibilityRenderObject::isCheckboxOrRadio() const
265 {
266     AccessibilityRole role = roleValue();
267     return role == RadioButtonRole || role == CheckBoxRole;
268 }
269 
isFileUploadButton() const270 bool AccessibilityRenderObject::isFileUploadButton() const
271 {
272     if (m_renderer && m_renderer->node() && m_renderer->node()->hasTagName(inputTag)) {
273         HTMLInputElement* input = static_cast<HTMLInputElement*>(m_renderer->node());
274         return input->inputType() == HTMLInputElement::FILE;
275     }
276 
277     return false;
278 }
279 
isInputImage() const280 bool AccessibilityRenderObject::isInputImage() const
281 {
282     if (m_renderer && m_renderer->node() && m_renderer->node()->hasTagName(inputTag)) {
283         HTMLInputElement* input = static_cast<HTMLInputElement*>(m_renderer->node());
284         return input->inputType() == HTMLInputElement::IMAGE;
285     }
286 
287     return false;
288 }
289 
isProgressIndicator() const290 bool AccessibilityRenderObject::isProgressIndicator() const
291 {
292     return roleValue() == ProgressIndicatorRole;
293 }
294 
isSlider() const295 bool AccessibilityRenderObject::isSlider() const
296 {
297     return roleValue() == SliderRole;
298 }
299 
isMenuRelated() const300 bool AccessibilityRenderObject::isMenuRelated() const
301 {
302     AccessibilityRole role = roleValue();
303     return role == MenuRole
304         || role == MenuBarRole
305         || role == MenuButtonRole
306         || role == MenuItemRole;
307 }
308 
isMenu() const309 bool AccessibilityRenderObject::isMenu() const
310 {
311     return roleValue() == MenuRole;
312 }
313 
isMenuBar() const314 bool AccessibilityRenderObject::isMenuBar() const
315 {
316     return roleValue() == MenuBarRole;
317 }
318 
isMenuButton() const319 bool AccessibilityRenderObject::isMenuButton() const
320 {
321     return roleValue() == MenuButtonRole;
322 }
323 
isMenuItem() const324 bool AccessibilityRenderObject::isMenuItem() const
325 {
326     return roleValue() == MenuItemRole;
327 }
328 
isPressed() const329 bool AccessibilityRenderObject::isPressed() const
330 {
331     ASSERT(m_renderer);
332     if (roleValue() != ButtonRole)
333         return false;
334 
335     Node* node = m_renderer->node();
336     if (!node)
337         return false;
338 
339     // If this is an ARIA button, check the aria-pressed attribute rather than node()->active()
340     if (ariaRoleAttribute() == ButtonRole) {
341         if (equalIgnoringCase(getAttribute(aria_pressedAttr).string(), "true"))
342             return true;
343         return false;
344     }
345 
346     return node->active();
347 }
348 
isIndeterminate() const349 bool AccessibilityRenderObject::isIndeterminate() const
350 {
351     ASSERT(m_renderer);
352     if (!m_renderer->node() || !m_renderer->node()->isElementNode())
353         return false;
354 
355     InputElement* inputElement = toInputElement(static_cast<Element*>(m_renderer->node()));
356     if (!inputElement)
357         return false;
358 
359     return inputElement->isIndeterminate();
360 }
361 
isChecked() const362 bool AccessibilityRenderObject::isChecked() const
363 {
364     ASSERT(m_renderer);
365     if (!m_renderer->node() || !m_renderer->node()->isElementNode())
366         return false;
367 
368     // First test for native checkedness semantics
369     InputElement* inputElement = toInputElement(static_cast<Element*>(m_renderer->node()));
370     if (inputElement)
371         return inputElement->isChecked();
372 
373     // Else, if this is an ARIA checkbox or radio, respect the aria-checked attribute
374     AccessibilityRole ariaRole = ariaRoleAttribute();
375     if (ariaRole == RadioButtonRole || ariaRole == CheckBoxRole) {
376         if (equalIgnoringCase(getAttribute(aria_checkedAttr), "true"))
377             return true;
378         return false;
379     }
380 
381     // Otherwise it's not checked
382     return false;
383 }
384 
isHovered() const385 bool AccessibilityRenderObject::isHovered() const
386 {
387     ASSERT(m_renderer);
388     return m_renderer->node() && m_renderer->node()->hovered();
389 }
390 
isMultiSelectable() const391 bool AccessibilityRenderObject::isMultiSelectable() const
392 {
393     ASSERT(m_renderer);
394 
395     const AtomicString& ariaMultiSelectable = getAttribute(aria_multiselectableAttr);
396     if (equalIgnoringCase(ariaMultiSelectable, "true"))
397         return true;
398     if (equalIgnoringCase(ariaMultiSelectable, "false"))
399         return false;
400 
401     if (!m_renderer->isListBox())
402         return false;
403     return m_renderer->node() && static_cast<HTMLSelectElement*>(m_renderer->node())->multiple();
404 }
405 
isReadOnly() const406 bool AccessibilityRenderObject::isReadOnly() const
407 {
408     ASSERT(m_renderer);
409 
410     if (isWebArea()) {
411         Document* document = m_renderer->document();
412         if (!document)
413             return true;
414 
415         HTMLElement* body = document->body();
416         if (body && body->isContentEditable())
417             return false;
418 
419         Frame* frame = document->frame();
420         if (!frame)
421             return true;
422 
423         return !frame->isContentEditable();
424     }
425 
426     if (m_renderer->isTextField())
427         return static_cast<HTMLInputElement*>(m_renderer->node())->readOnly();
428     if (m_renderer->isTextArea())
429         return static_cast<HTMLTextAreaElement*>(m_renderer->node())->readOnly();
430 
431     return !m_renderer->node() || !m_renderer->node()->isContentEditable();
432 }
433 
isOffScreen() const434 bool AccessibilityRenderObject::isOffScreen() const
435 {
436     ASSERT(m_renderer);
437     IntRect contentRect = m_renderer->absoluteClippedOverflowRect();
438     FrameView* view = m_renderer->document()->frame()->view();
439     FloatRect viewRect = view->visibleContentRect();
440     viewRect.intersect(contentRect);
441     return viewRect.isEmpty();
442 }
443 
headingLevel() const444 int AccessibilityRenderObject::headingLevel() const
445 {
446     // headings can be in block flow and non-block flow
447     if (!m_renderer)
448         return 0;
449 
450     Node* node = m_renderer->node();
451     if (!node)
452         return 0;
453 
454     if (ariaRoleAttribute() == HeadingRole)  {
455         if (!node->isElementNode())
456             return 0;
457         Element* element = static_cast<Element*>(node);
458         return element->getAttribute(aria_levelAttr).toInt();
459     }
460 
461     if (node->hasTagName(h1Tag))
462         return 1;
463 
464     if (node->hasTagName(h2Tag))
465         return 2;
466 
467     if (node->hasTagName(h3Tag))
468         return 3;
469 
470     if (node->hasTagName(h4Tag))
471         return 4;
472 
473     if (node->hasTagName(h5Tag))
474         return 5;
475 
476     if (node->hasTagName(h6Tag))
477         return 6;
478 
479     return 0;
480 }
481 
isHeading() const482 bool AccessibilityRenderObject::isHeading() const
483 {
484     return roleValue() == HeadingRole;
485 }
486 
isLink() const487 bool AccessibilityRenderObject::isLink() const
488 {
489     return roleValue() == WebCoreLinkRole;
490 }
491 
isControl() const492 bool AccessibilityRenderObject::isControl() const
493 {
494     if (!m_renderer)
495         return false;
496 
497     Node* node = m_renderer->node();
498     return node && ((node->isElementNode() && static_cast<Element*>(node)->isFormControlElement())
499                     || AccessibilityObject::isARIAControl(ariaRoleAttribute()));
500 }
501 
isFieldset() const502 bool AccessibilityRenderObject::isFieldset() const
503 {
504     if (!m_renderer)
505         return false;
506 
507     return m_renderer->isFieldset();
508 }
509 
isGroup() const510 bool AccessibilityRenderObject::isGroup() const
511 {
512     return roleValue() == GroupRole;
513 }
514 
selectedRadioButton()515 AccessibilityObject* AccessibilityRenderObject::selectedRadioButton()
516 {
517     if (!isRadioGroup())
518         return 0;
519 
520     // Find the child radio button that is selected (ie. the intValue == 1).
521     int count = m_children.size();
522     for (int i = 0; i < count; ++i) {
523         AccessibilityObject* object = m_children[i].get();
524         if (object->roleValue() == RadioButtonRole && object->intValue() == 1)
525             return object;
526     }
527     return 0;
528 }
529 
selectedTabItem()530 AccessibilityObject* AccessibilityRenderObject::selectedTabItem()
531 {
532     if (!isTabList())
533         return 0;
534 
535     // Find the child tab item that is selected (ie. the intValue == 1).
536     AccessibilityObject::AccessibilityChildrenVector tabs;
537     tabChildren(tabs);
538 
539     int count = tabs.size();
540     for (int i = 0; i < count; ++i) {
541         AccessibilityObject* object = m_children[i].get();
542         if (object->isTabItem() && object->intValue() == 1)
543             return object;
544     }
545     return 0;
546 }
547 
getAttribute(const QualifiedName & attribute) const548 const AtomicString& AccessibilityRenderObject::getAttribute(const QualifiedName& attribute) const
549 {
550     Node* node = m_renderer->node();
551     if (!node)
552         return nullAtom;
553 
554     if (!node->isElementNode())
555         return nullAtom;
556 
557     Element* element = static_cast<Element*>(node);
558     return element->getAttribute(attribute);
559 }
560 
anchorElement() const561 Element* AccessibilityRenderObject::anchorElement() const
562 {
563     if (!m_renderer)
564         return 0;
565 
566     AXObjectCache* cache = axObjectCache();
567     RenderObject* currRenderer;
568 
569     // Search up the render tree for a RenderObject with a DOM node.  Defer to an earlier continuation, though.
570     for (currRenderer = m_renderer; currRenderer && !currRenderer->node(); currRenderer = currRenderer->parent()) {
571         if (currRenderer->isRenderBlock()) {
572             RenderInline* continuation = toRenderBlock(currRenderer)->inlineContinuation();
573             if (continuation)
574                 return cache->getOrCreate(continuation)->anchorElement();
575         }
576     }
577 
578     // bail if none found
579     if (!currRenderer)
580         return 0;
581 
582     // search up the DOM tree for an anchor element
583     // NOTE: this assumes that any non-image with an anchor is an HTMLAnchorElement
584     Node* node = currRenderer->node();
585     for ( ; node; node = node->parentNode()) {
586         if (node->hasTagName(aTag) || (node->renderer() && cache->getOrCreate(node->renderer())->isAnchor()))
587             return static_cast<Element*>(node);
588     }
589 
590     return 0;
591 }
592 
actionElement() const593 Element* AccessibilityRenderObject::actionElement() const
594 {
595     if (!m_renderer)
596         return 0;
597 
598     Node* node = m_renderer->node();
599     if (node) {
600         if (node->hasTagName(inputTag)) {
601             HTMLInputElement* input = static_cast<HTMLInputElement*>(node);
602             if (!input->disabled() && (isCheckboxOrRadio() || input->isTextButton()))
603                 return input;
604         } else if (node->hasTagName(buttonTag))
605             return static_cast<Element*>(node);
606     }
607 
608     if (isFileUploadButton())
609         return static_cast<Element*>(m_renderer->node());
610 
611     if (AccessibilityObject::isARIAInput(ariaRoleAttribute()))
612         return static_cast<Element*>(m_renderer->node());
613 
614     if (isImageButton())
615         return static_cast<Element*>(m_renderer->node());
616 
617     if (m_renderer->isMenuList())
618         return static_cast<Element*>(m_renderer->node());
619 
620     AccessibilityRole role = roleValue();
621     if (role == ButtonRole || role == PopUpButtonRole)
622         return static_cast<Element*>(m_renderer->node());
623 
624     Element* elt = anchorElement();
625     if (!elt)
626         elt = mouseButtonListener();
627     return elt;
628 }
629 
mouseButtonListener() const630 Element* AccessibilityRenderObject::mouseButtonListener() const
631 {
632     Node* node = m_renderer->node();
633     if (!node)
634         return 0;
635 
636     // check if our parent is a mouse button listener
637     while (node && !node->isElementNode())
638         node = node->parent();
639 
640     if (!node)
641         return 0;
642 
643     // FIXME: Do the continuation search like anchorElement does
644     for (Element* element = static_cast<Element*>(node); element; element = element->parentElement()) {
645         if (element->getAttributeEventListener(eventNames().clickEvent) || element->getAttributeEventListener(eventNames().mousedownEvent) || element->getAttributeEventListener(eventNames().mouseupEvent))
646             return element;
647     }
648 
649     return 0;
650 }
651 
increment()652 void AccessibilityRenderObject::increment()
653 {
654     if (roleValue() != SliderRole)
655         return;
656 
657     changeValueByPercent(5);
658 }
659 
decrement()660 void AccessibilityRenderObject::decrement()
661 {
662     if (roleValue() != SliderRole)
663         return;
664 
665     changeValueByPercent(-5);
666 }
667 
siblingWithAriaRole(String role,Node * node)668 static Element* siblingWithAriaRole(String role, Node* node)
669 {
670     Node* sibling = node->parent()->firstChild();
671     while (sibling) {
672         if (sibling->isElementNode()) {
673             String siblingAriaRole = static_cast<Element*>(sibling)->getAttribute(roleAttr).string();
674             if (equalIgnoringCase(siblingAriaRole, role))
675                 return static_cast<Element*>(sibling);
676         }
677         sibling = sibling->nextSibling();
678     }
679 
680     return 0;
681 }
682 
menuElementForMenuButton() const683 Element* AccessibilityRenderObject::menuElementForMenuButton() const
684 {
685     if (ariaRoleAttribute() != MenuButtonRole)
686         return 0;
687 
688     return siblingWithAriaRole("menu", renderer()->node());
689 }
690 
menuForMenuButton() const691 AccessibilityObject* AccessibilityRenderObject::menuForMenuButton() const
692 {
693     Element* menu = menuElementForMenuButton();
694     if (menu && menu->renderer())
695         return m_renderer->document()->axObjectCache()->getOrCreate(menu->renderer());
696     return 0;
697 }
698 
menuItemElementForMenu() const699 Element* AccessibilityRenderObject::menuItemElementForMenu() const
700 {
701     if (ariaRoleAttribute() != MenuRole)
702         return 0;
703 
704     return siblingWithAriaRole("menuitem", renderer()->node());
705 }
706 
menuButtonForMenu() const707 AccessibilityObject* AccessibilityRenderObject::menuButtonForMenu() const
708 {
709     Element* menuItem = menuItemElementForMenu();
710 
711     if (menuItem && menuItem->renderer()) {
712         // ARIA just has generic menu items.  AppKit needs to know if this is a top level items like MenuBarButton or MenuBarItem
713         AccessibilityObject* menuItemAX = m_renderer->document()->axObjectCache()->getOrCreate(menuItem->renderer());
714         if (menuItemAX->isMenuButton())
715             return menuItemAX;
716     }
717     return 0;
718 }
719 
helpText() const720 String AccessibilityRenderObject::helpText() const
721 {
722     if (!m_renderer)
723         return String();
724 
725     for (RenderObject* curr = m_renderer; curr; curr = curr->parent()) {
726         if (curr->node() && curr->node()->isHTMLElement()) {
727             const AtomicString& summary = static_cast<Element*>(curr->node())->getAttribute(summaryAttr);
728             if (!summary.isEmpty())
729                 return summary;
730             const AtomicString& title = static_cast<Element*>(curr->node())->getAttribute(titleAttr);
731             if (!title.isEmpty())
732                 return title;
733         }
734     }
735 
736     return String();
737 }
738 
hierarchicalLevel() const739 unsigned AccessibilityRenderObject::hierarchicalLevel() const
740 {
741     if (!m_renderer)
742         return 0;
743 
744     Node* node = m_renderer->node();
745     if (!node || !node->isElementNode())
746         return 0;
747     Element* element = static_cast<Element*>(node);
748     String ariaLevel = element->getAttribute(aria_levelAttr);
749     if (!ariaLevel.isEmpty())
750         return ariaLevel.toInt();
751 
752     // Only tree item will calculate its level through the DOM currently.
753     if (roleValue() != TreeItemRole)
754         return 0;
755 
756     // Hierarchy leveling starts at 0.
757     // We measure tree hierarchy by the number of groups that the item is within.
758     unsigned level = 0;
759     AccessibilityObject* parent = parentObject();
760     while (parent) {
761         AccessibilityRole parentRole = parent->roleValue();
762         if (parentRole == GroupRole)
763             level++;
764         else if (parentRole == TreeRole)
765             break;
766 
767         parent = parent->parentObject();
768     }
769 
770     return level;
771 }
772 
language() const773 String AccessibilityRenderObject::language() const
774 {
775     if (!m_renderer)
776         return String();
777 
778     // Defer to parent if this element doesn't have a language set
779     Node* node = m_renderer->node();
780     if (!node)
781         return AccessibilityObject::language();
782 
783     if (!node->isElementNode())
784         return AccessibilityObject::language();
785 
786     String language = static_cast<Element*>(node)->getAttribute(langAttr);
787     if (language.isEmpty())
788         return AccessibilityObject::language();
789     return language;
790 }
791 
textUnderElement() const792 String AccessibilityRenderObject::textUnderElement() const
793 {
794     if (!m_renderer)
795         return String();
796 
797     if (isFileUploadButton())
798         return toRenderFileUploadControl(m_renderer)->buttonValue();
799 
800     Node* node = m_renderer->node();
801     if (node) {
802         if (Frame* frame = node->document()->frame()) {
803             // catch stale WebCoreAXObject (see <rdar://problem/3960196>)
804             if (frame->document() != node->document())
805                 return String();
806             return plainText(rangeOfContents(node).get());
807         }
808     }
809 
810     // Sometimes text fragments don't have Node's associated with them (like when
811     // CSS content is used to insert text).
812     if (m_renderer->isText()) {
813         RenderText* renderTextObject = toRenderText(m_renderer);
814         if (renderTextObject->isTextFragment())
815             return String(static_cast<RenderTextFragment*>(m_renderer)->contentString());
816     }
817 
818     // return the null string for anonymous text because it is non-trivial to get
819     // the actual text and, so far, that is not needed
820     return String();
821 }
822 
hasIntValue() const823 bool AccessibilityRenderObject::hasIntValue() const
824 {
825     if (isHeading())
826         return true;
827 
828     if (m_renderer->node() && isCheckboxOrRadio())
829         return true;
830 
831     return false;
832 }
833 
intValue() const834 int AccessibilityRenderObject::intValue() const
835 {
836     if (!m_renderer || isPasswordField())
837         return 0;
838 
839     if (isHeading())
840         return headingLevel();
841 
842     Node* node = m_renderer->node();
843     if (!node || !isCheckboxOrRadio())
844         return 0;
845 
846     // If this is an ARIA checkbox or radio, check the aria-checked attribute rather than node()->checked()
847     AccessibilityRole ariaRole = ariaRoleAttribute();
848     if (ariaRole == RadioButtonRole || ariaRole == CheckBoxRole) {
849         if (equalIgnoringCase(getAttribute(aria_checkedAttr).string(), "true"))
850             return true;
851         return false;
852     }
853 
854     return static_cast<HTMLInputElement*>(node)->checked();
855 }
856 
valueDescription() const857 String AccessibilityRenderObject::valueDescription() const
858 {
859     // Only sliders and progress bars support value descriptions currently.
860     if (!isProgressIndicator() && !isSlider())
861         return String();
862 
863     return getAttribute(aria_valuetextAttr).string();
864 }
865 
valueForRange() const866 float AccessibilityRenderObject::valueForRange() const
867 {
868     if (!isProgressIndicator() && !isSlider() && !isScrollbar())
869         return 0.0f;
870 
871     return getAttribute(aria_valuenowAttr).toFloat();
872 }
873 
maxValueForRange() const874 float AccessibilityRenderObject::maxValueForRange() const
875 {
876     if (!isProgressIndicator() && !isSlider())
877         return 0.0f;
878 
879     return getAttribute(aria_valuemaxAttr).toFloat();
880 }
881 
minValueForRange() const882 float AccessibilityRenderObject::minValueForRange() const
883 {
884     if (!isProgressIndicator() && !isSlider())
885         return 0.0f;
886 
887     return getAttribute(aria_valueminAttr).toFloat();
888 }
889 
stringValue() const890 String AccessibilityRenderObject::stringValue() const
891 {
892     if (!m_renderer || isPasswordField())
893         return String();
894 
895     if (ariaRoleAttribute() == StaticTextRole)
896         return text();
897 
898     if (m_renderer->isText())
899         return textUnderElement();
900 
901     if (m_renderer->isMenuList())
902         return toRenderMenuList(m_renderer)->text();
903 
904     if (m_renderer->isListMarker())
905         return toRenderListMarker(m_renderer)->text();
906 
907     if (m_renderer->isRenderButton())
908         return toRenderButton(m_renderer)->text();
909 
910     if (isWebArea()) {
911         if (m_renderer->document()->frame())
912             return String();
913 
914         // FIXME: should use startOfDocument and endOfDocument (or rangeForDocument?) here
915         VisiblePosition startVisiblePosition = m_renderer->positionForCoordinates(0, 0);
916         VisiblePosition endVisiblePosition = m_renderer->positionForCoordinates(INT_MAX, INT_MAX);
917         if (startVisiblePosition.isNull() || endVisiblePosition.isNull())
918             return String();
919 
920         return plainText(makeRange(startVisiblePosition, endVisiblePosition).get());
921     }
922 
923     if (isTextControl())
924         return text();
925 
926     if (isFileUploadButton())
927         return toRenderFileUploadControl(m_renderer)->fileTextValue();
928 
929     // FIXME: We might need to implement a value here for more types
930     // FIXME: It would be better not to advertise a value at all for the types for which we don't implement one;
931     // this would require subclassing or making accessibilityAttributeNames do something other than return a
932     // single static array.
933     return String();
934 }
935 
936 // This function implements the ARIA accessible name as described by the Mozilla
937 // ARIA Implementer's Guide.
accessibleNameForNode(Node * node)938 static String accessibleNameForNode(Node* node)
939 {
940     if (node->isTextNode())
941         return static_cast<Text*>(node)->data();
942 
943     if (node->hasTagName(inputTag))
944         return static_cast<HTMLInputElement*>(node)->value();
945 
946     if (node->isHTMLElement()) {
947         const AtomicString& alt = static_cast<HTMLElement*>(node)->getAttribute(altAttr);
948         if (!alt.isEmpty())
949             return alt;
950     }
951 
952     return String();
953 }
954 
accessibilityDescriptionForElements(Vector<Element * > & elements) const955 String AccessibilityRenderObject::accessibilityDescriptionForElements(Vector<Element*> &elements) const
956 {
957     Vector<UChar> ariaLabel;
958     unsigned size = elements.size();
959     for (unsigned i = 0; i < size; ++i) {
960         Element* idElement = elements[i];
961 
962         String nameFragment = accessibleNameForNode(idElement);
963         ariaLabel.append(nameFragment.characters(), nameFragment.length());
964         for (Node* n = idElement->firstChild(); n; n = n->traverseNextNode(idElement)) {
965             nameFragment = accessibleNameForNode(n);
966             ariaLabel.append(nameFragment.characters(), nameFragment.length());
967         }
968 
969         if (i != size - 1)
970             ariaLabel.append(' ');
971     }
972     return String::adopt(ariaLabel);
973 }
974 
975 
elementsFromAttribute(Vector<Element * > & elements,const QualifiedName & attribute) const976 void AccessibilityRenderObject::elementsFromAttribute(Vector<Element*>& elements, const QualifiedName& attribute) const
977 {
978     Node* node = m_renderer->node();
979     if (!node || !node->isElementNode())
980         return;
981 
982     Document* document = m_renderer->document();
983     if (!document)
984         return;
985 
986     String idList = getAttribute(attribute).string();
987     if (idList.isEmpty())
988         return;
989 
990     idList.replace('\n', ' ');
991     Vector<String> idVector;
992     idList.split(' ', idVector);
993 
994     unsigned size = idVector.size();
995     for (unsigned i = 0; i < size; ++i) {
996         String idName = idVector[i];
997         Element* idElement = document->getElementById(idName);
998         if (idElement)
999             elements.append(idElement);
1000     }
1001 }
1002 
ariaLabeledByElements(Vector<Element * > & elements) const1003 void AccessibilityRenderObject::ariaLabeledByElements(Vector<Element*>& elements) const
1004 {
1005     elementsFromAttribute(elements, aria_labeledbyAttr);
1006     if (!elements.size())
1007         elementsFromAttribute(elements, aria_labelledbyAttr);
1008 }
1009 
ariaLabeledByAttribute() const1010 String AccessibilityRenderObject::ariaLabeledByAttribute() const
1011 {
1012     Vector<Element*> elements;
1013     ariaLabeledByElements(elements);
1014 
1015     return accessibilityDescriptionForElements(elements);
1016 }
1017 
labelForElement(Element * element)1018 static HTMLLabelElement* labelForElement(Element* element)
1019 {
1020     RefPtr<NodeList> list = element->document()->getElementsByTagName("label");
1021     unsigned len = list->length();
1022     for (unsigned i = 0; i < len; i++) {
1023         if (list->item(i)->hasTagName(labelTag)) {
1024             HTMLLabelElement* label = static_cast<HTMLLabelElement*>(list->item(i));
1025             if (label->correspondingControl() == element)
1026                 return label;
1027         }
1028     }
1029 
1030     return 0;
1031 }
1032 
labelElementContainer() const1033 HTMLLabelElement* AccessibilityRenderObject::labelElementContainer() const
1034 {
1035     if (!m_renderer)
1036         return false;
1037 
1038     // the control element should not be considered part of the label
1039     if (isControl())
1040         return false;
1041 
1042     // find if this has a parent that is a label
1043     for (Node* parentNode = m_renderer->node(); parentNode; parentNode = parentNode->parentNode()) {
1044         if (parentNode->hasTagName(labelTag))
1045             return static_cast<HTMLLabelElement*>(parentNode);
1046     }
1047 
1048     return 0;
1049 }
1050 
title() const1051 String AccessibilityRenderObject::title() const
1052 {
1053     AccessibilityRole ariaRole = ariaRoleAttribute();
1054 
1055     if (!m_renderer)
1056         return String();
1057 
1058     Node* node = m_renderer->node();
1059     if (!node)
1060         return String();
1061 
1062     String ariaLabel = ariaLabeledByAttribute();
1063     if (!ariaLabel.isEmpty())
1064         return ariaLabel;
1065 
1066     const AtomicString& title = getAttribute(titleAttr);
1067     if (!title.isEmpty())
1068         return title;
1069 
1070     bool isInputTag = node->hasTagName(inputTag);
1071     if (isInputTag) {
1072         HTMLInputElement* input = static_cast<HTMLInputElement*>(node);
1073         if (input->isTextButton())
1074             return input->value();
1075     }
1076 
1077     if (isInputTag || AccessibilityObject::isARIAInput(ariaRole) || isControl()) {
1078         HTMLLabelElement* label = labelForElement(static_cast<Element*>(node));
1079         if (label && !titleUIElement())
1080             return label->innerText();
1081 
1082         const AtomicString& placeholder = getAttribute(placeholderAttr);
1083         if (!placeholder.isEmpty())
1084             return placeholder;
1085     }
1086 
1087     if (roleValue() == ButtonRole
1088         || ariaRole == ListBoxOptionRole
1089         || ariaRole == MenuItemRole
1090         || ariaRole == MenuButtonRole
1091         || ariaRole == RadioButtonRole
1092         || ariaRole == CheckBoxRole
1093         || ariaRole == TabRole
1094         || isHeading())
1095         return textUnderElement();
1096 
1097     if (isLink())
1098         return textUnderElement();
1099 
1100     return String();
1101 }
1102 
ariaDescribedByAttribute() const1103 String AccessibilityRenderObject::ariaDescribedByAttribute() const
1104 {
1105     Vector<Element*> elements;
1106     elementsFromAttribute(elements, aria_describedbyAttr);
1107 
1108     return accessibilityDescriptionForElements(elements);
1109 }
1110 
accessibilityDescription() const1111 String AccessibilityRenderObject::accessibilityDescription() const
1112 {
1113     if (!m_renderer)
1114         return String();
1115 
1116     String ariaLabel = getAttribute(aria_labelAttr).string();
1117     if (!ariaLabel.isEmpty())
1118         return ariaLabel;
1119 
1120     String ariaDescription = ariaDescribedByAttribute();
1121     if (!ariaDescription.isEmpty())
1122         return ariaDescription;
1123 
1124     if (isImage() || isInputImage() || isNativeImage()) {
1125         Node* node = m_renderer->node();
1126         if (node && node->isHTMLElement()) {
1127             const AtomicString& alt = static_cast<HTMLElement*>(node)->getAttribute(altAttr);
1128             if (alt.isEmpty())
1129                 return String();
1130             return alt;
1131         }
1132     }
1133 
1134     if (isWebArea()) {
1135         Document* document = m_renderer->document();
1136         Node* owner = document->ownerElement();
1137         if (owner) {
1138             if (owner->hasTagName(frameTag) || owner->hasTagName(iframeTag)) {
1139                 const AtomicString& title = static_cast<HTMLFrameElementBase*>(owner)->getAttribute(titleAttr);
1140                 if (!title.isEmpty())
1141                     return title;
1142                 return static_cast<HTMLFrameElementBase*>(owner)->getAttribute(nameAttr);
1143             }
1144             if (owner->isHTMLElement())
1145                 return static_cast<HTMLElement*>(owner)->getAttribute(nameAttr);
1146         }
1147         owner = document->body();
1148         if (owner && owner->isHTMLElement())
1149             return static_cast<HTMLElement*>(owner)->getAttribute(nameAttr);
1150     }
1151 
1152     return String();
1153 }
1154 
boundingBoxRect() const1155 IntRect AccessibilityRenderObject::boundingBoxRect() const
1156 {
1157     RenderObject* obj = m_renderer;
1158 
1159     if (!obj)
1160         return IntRect();
1161 
1162     if (obj->node()) // If we are a continuation, we want to make sure to use the primary renderer.
1163         obj = obj->node()->renderer();
1164 
1165     Vector<FloatQuad> quads;
1166     if (obj->isText())
1167         obj->absoluteQuads(quads);
1168     else
1169         obj->absoluteFocusRingQuads(quads);
1170     const size_t n = quads.size();
1171     if (!n)
1172         return IntRect();
1173 
1174     IntRect result;
1175     for (size_t i = 0; i < n; ++i) {
1176         IntRect r = quads[i].enclosingBoundingBox();
1177         if (!r.isEmpty()) {
1178             if (obj->style()->hasAppearance())
1179                 obj->theme()->adjustRepaintRect(obj, r);
1180             result.unite(r);
1181         }
1182     }
1183     return result;
1184 }
1185 
checkboxOrRadioRect() const1186 IntRect AccessibilityRenderObject::checkboxOrRadioRect() const
1187 {
1188     if (!m_renderer)
1189         return IntRect();
1190 
1191     HTMLLabelElement* label = labelForElement(static_cast<Element*>(m_renderer->node()));
1192     if (!label || !label->renderer())
1193         return boundingBoxRect();
1194 
1195     IntRect labelRect = axObjectCache()->getOrCreate(label->renderer())->elementRect();
1196     labelRect.unite(boundingBoxRect());
1197     return labelRect;
1198 }
1199 
elementRect() const1200 IntRect AccessibilityRenderObject::elementRect() const
1201 {
1202     // a checkbox or radio button should encompass its label
1203     if (isCheckboxOrRadio())
1204         return checkboxOrRadioRect();
1205 
1206     return boundingBoxRect();
1207 }
1208 
size() const1209 IntSize AccessibilityRenderObject::size() const
1210 {
1211     IntRect rect = elementRect();
1212     return rect.size();
1213 }
1214 
clickPoint() const1215 IntPoint AccessibilityRenderObject::clickPoint() const
1216 {
1217     // use the default position unless this is an editable web area, in which case we use the selection bounds.
1218     if (!isWebArea() || isReadOnly())
1219         return AccessibilityObject::clickPoint();
1220 
1221     VisibleSelection visSelection = selection();
1222     VisiblePositionRange range = VisiblePositionRange(visSelection.visibleStart(), visSelection.visibleEnd());
1223     IntRect bounds = boundsForVisiblePositionRange(range);
1224 #if PLATFORM(MAC)
1225     bounds.setLocation(m_renderer->document()->view()->screenToContents(bounds.location()));
1226 #endif
1227     return IntPoint(bounds.x() + (bounds.width() / 2), bounds.y() - (bounds.height() / 2));
1228 }
1229 
internalLinkElement() const1230 AccessibilityObject* AccessibilityRenderObject::internalLinkElement() const
1231 {
1232     Element* element = anchorElement();
1233     if (!element)
1234         return 0;
1235 
1236     // Right now, we do not support ARIA links as internal link elements
1237     if (!element->hasTagName(aTag))
1238         return 0;
1239     HTMLAnchorElement* anchor = static_cast<HTMLAnchorElement*>(element);
1240 
1241     KURL linkURL = anchor->href();
1242     String fragmentIdentifier = linkURL.fragmentIdentifier();
1243     if (fragmentIdentifier.isEmpty())
1244         return 0;
1245 
1246     // check if URL is the same as current URL
1247     linkURL.removeFragmentIdentifier();
1248     if (m_renderer->document()->url() != linkURL)
1249         return 0;
1250 
1251     Node* linkedNode = m_renderer->document()->findAnchor(fragmentIdentifier);
1252     if (!linkedNode)
1253         return 0;
1254 
1255     // The element we find may not be accessible, so find the first accessible object.
1256     return firstAccessibleObjectFromNode(linkedNode);
1257 }
1258 
addRadioButtonGroupMembers(AccessibilityChildrenVector & linkedUIElements) const1259 void AccessibilityRenderObject::addRadioButtonGroupMembers(AccessibilityChildrenVector& linkedUIElements) const
1260 {
1261     if (!m_renderer || roleValue() != RadioButtonRole)
1262         return;
1263 
1264     Node* node = m_renderer->node();
1265     if (!node || !node->hasTagName(inputTag))
1266         return;
1267 
1268     HTMLInputElement* input = static_cast<HTMLInputElement*>(node);
1269     // if there's a form, then this is easy
1270     if (input->form()) {
1271         Vector<RefPtr<Node> > formElements;
1272         input->form()->getNamedElements(input->name(), formElements);
1273 
1274         unsigned len = formElements.size();
1275         for (unsigned i = 0; i < len; ++i) {
1276             Node* associateElement = formElements[i].get();
1277             if (AccessibilityObject* object = m_renderer->document()->axObjectCache()->getOrCreate(associateElement->renderer()))
1278                 linkedUIElements.append(object);
1279         }
1280     } else {
1281         RefPtr<NodeList> list = node->document()->getElementsByTagName("input");
1282         unsigned len = list->length();
1283         for (unsigned i = 0; i < len; ++i) {
1284             if (list->item(i)->hasTagName(inputTag)) {
1285                 HTMLInputElement* associateElement = static_cast<HTMLInputElement*>(list->item(i));
1286                 if (associateElement->isRadioButton() && associateElement->name() == input->name()) {
1287                     if (AccessibilityObject* object = m_renderer->document()->axObjectCache()->getOrCreate(associateElement->renderer()))
1288                         linkedUIElements.append(object);
1289                 }
1290             }
1291         }
1292     }
1293 }
1294 
1295 // linked ui elements could be all the related radio buttons in a group
1296 // or an internal anchor connection
linkedUIElements(AccessibilityChildrenVector & linkedUIElements) const1297 void AccessibilityRenderObject::linkedUIElements(AccessibilityChildrenVector& linkedUIElements) const
1298 {
1299     ariaFlowToElements(linkedUIElements);
1300 
1301     if (isAnchor()) {
1302         AccessibilityObject* linkedAXElement = internalLinkElement();
1303         if (linkedAXElement)
1304             linkedUIElements.append(linkedAXElement);
1305     }
1306 
1307     if (roleValue() == RadioButtonRole)
1308         addRadioButtonGroupMembers(linkedUIElements);
1309 }
1310 
hasTextAlternative() const1311 bool AccessibilityRenderObject::hasTextAlternative() const
1312 {
1313     // ARIA: section 2A, bullet #3 says if aria-labeledby or aria-label appears, it should
1314     // override the "label" element association.
1315     if (!ariaLabeledByAttribute().isEmpty() || !getAttribute(aria_labelAttr).string().isEmpty())
1316         return true;
1317 
1318     return false;
1319 }
1320 
supportsARIAFlowTo() const1321 bool AccessibilityRenderObject::supportsARIAFlowTo() const
1322 {
1323     return !getAttribute(aria_flowtoAttr).string().isEmpty();
1324 }
1325 
ariaFlowToElements(AccessibilityChildrenVector & flowTo) const1326 void AccessibilityRenderObject::ariaFlowToElements(AccessibilityChildrenVector& flowTo) const
1327 {
1328     Vector<Element*> elements;
1329     elementsFromAttribute(elements, aria_flowtoAttr);
1330 
1331     AXObjectCache* cache = axObjectCache();
1332     unsigned count = elements.size();
1333     for (unsigned k = 0; k < count; ++k) {
1334         Element* element = elements[k];
1335         AccessibilityObject* flowToElement = cache->getOrCreate(element->renderer());
1336         if (flowToElement)
1337             flowTo.append(flowToElement);
1338     }
1339 
1340 }
1341 
supportsARIADropping()1342 bool AccessibilityRenderObject::supportsARIADropping()
1343 {
1344     const AtomicString& dropEffect = getAttribute(aria_dropeffectAttr).string();
1345     return !dropEffect.isEmpty();
1346 }
1347 
supportsARIADragging()1348 bool AccessibilityRenderObject::supportsARIADragging()
1349 {
1350     const AtomicString& grabbed = getAttribute(aria_grabbedAttr).string();
1351     return equalIgnoringCase(grabbed, "true") || equalIgnoringCase(grabbed, "false");
1352 }
1353 
isARIAGrabbed()1354 bool AccessibilityRenderObject::isARIAGrabbed()
1355 {
1356     return elementAttributeValue(aria_grabbedAttr);
1357 }
1358 
setARIAGrabbed(bool grabbed)1359 void AccessibilityRenderObject::setARIAGrabbed(bool grabbed)
1360 {
1361     setElementAttributeValue(aria_grabbedAttr, grabbed);
1362 }
1363 
determineARIADropEffects(Vector<String> & effects)1364 void AccessibilityRenderObject::determineARIADropEffects(Vector<String>& effects)
1365 {
1366     String dropEffects = getAttribute(aria_dropeffectAttr).string();
1367     if (dropEffects.isEmpty()) {
1368         effects.clear();
1369         return;
1370     }
1371 
1372     dropEffects.replace('\n', ' ');
1373     dropEffects.split(' ', effects);
1374 }
1375 
exposesTitleUIElement() const1376 bool AccessibilityRenderObject::exposesTitleUIElement() const
1377 {
1378     if (!isControl())
1379         return false;
1380 
1381     // checkbox or radio buttons don't expose the title ui element unless it has a title already
1382     if (isCheckboxOrRadio() && getAttribute(titleAttr).isEmpty())
1383         return false;
1384 
1385     if (hasTextAlternative())
1386         return false;
1387 
1388     return true;
1389 }
1390 
titleUIElement() const1391 AccessibilityObject* AccessibilityRenderObject::titleUIElement() const
1392 {
1393     if (!m_renderer)
1394         return 0;
1395 
1396     // if isFieldset is true, the renderer is guaranteed to be a RenderFieldset
1397     if (isFieldset())
1398         return axObjectCache()->getOrCreate(toRenderFieldset(m_renderer)->findLegend());
1399 
1400     if (!exposesTitleUIElement())
1401         return 0;
1402 
1403     Node* element = m_renderer->node();
1404     HTMLLabelElement* label = labelForElement(static_cast<Element*>(element));
1405     if (label && label->renderer())
1406         return axObjectCache()->getOrCreate(label->renderer());
1407 
1408     return 0;
1409 }
1410 
ariaIsHidden() const1411 bool AccessibilityRenderObject::ariaIsHidden() const
1412 {
1413     if (equalIgnoringCase(getAttribute(aria_hiddenAttr).string(), "true"))
1414         return true;
1415 
1416     // aria-hidden hides this object and any children
1417     AccessibilityObject* object = parentObject();
1418     while (object) {
1419         if (object->isAccessibilityRenderObject() && equalIgnoringCase(static_cast<AccessibilityRenderObject*>(object)->getAttribute(aria_hiddenAttr).string(), "true"))
1420             return true;
1421         object = object->parentObject();
1422     }
1423 
1424     return false;
1425 }
1426 
isDescendantOfBarrenParent() const1427 bool AccessibilityRenderObject::isDescendantOfBarrenParent() const
1428 {
1429     for (AccessibilityObject* object = parentObject(); object; object = object->parentObject()) {
1430         if (!object->canHaveChildren())
1431             return true;
1432     }
1433 
1434     return false;
1435 }
1436 
isAllowedChildOfTree() const1437 bool AccessibilityRenderObject::isAllowedChildOfTree() const
1438 {
1439     // Determine if this is in a tree. If so, we apply special behavior to make it work like an AXOutline.
1440     AccessibilityObject* axObj = parentObject();
1441     bool isInTree = false;
1442     while (axObj) {
1443         if (axObj->isTree()) {
1444             isInTree = true;
1445             break;
1446         }
1447         axObj = axObj->parentObject();
1448     }
1449 
1450     // If the object is in a tree, only tree items should be exposed (and the children of tree items).
1451     if (isInTree) {
1452         AccessibilityRole role = roleValue();
1453         if (role != TreeItemRole && role != StaticTextRole)
1454             return false;
1455     }
1456     return true;
1457 }
1458 
accessibilityIsIgnored() const1459 bool AccessibilityRenderObject::accessibilityIsIgnored() const
1460 {
1461     // Is the platform interested in this object?
1462     AccessibilityObjectPlatformInclusion decision = accessibilityPlatformIncludesObject();
1463     if (decision == IncludeObject)
1464         return false;
1465     if (decision == IgnoreObject)
1466         return true;
1467     // the decision must, therefore, be DefaultBehavior.
1468 
1469     // ignore invisible element
1470     if (!m_renderer || m_renderer->style()->visibility() != VISIBLE)
1471         return true;
1472 
1473     if (ariaIsHidden())
1474         return true;
1475 
1476     if (isPresentationalChildOfAriaRole())
1477         return true;
1478 
1479     // If this element is within a parent that cannot have children, it should not be exposed.
1480     if (isDescendantOfBarrenParent())
1481         return true;
1482 
1483     if (roleValue() == IgnoredRole)
1484         return true;
1485 
1486     // An ARIA tree can only have tree items and static text as children.
1487     if (!isAllowedChildOfTree())
1488         return true;
1489 
1490     // ignore popup menu items because AppKit does
1491     for (RenderObject* parent = m_renderer->parent(); parent; parent = parent->parent()) {
1492         if (parent->isMenuList())
1493             return true;
1494     }
1495 
1496     // find out if this element is inside of a label element.
1497     // if so, it may be ignored because it's the label for a checkbox or radio button
1498     AccessibilityObject* controlObject = correspondingControlForLabelElement();
1499     if (controlObject && !controlObject->exposesTitleUIElement() && controlObject->isCheckboxOrRadio())
1500         return true;
1501 
1502     AccessibilityRole ariaRole = ariaRoleAttribute();
1503     if (ariaRole == TextAreaRole || ariaRole == StaticTextRole) {
1504         String ariaText = text();
1505         return ariaText.isNull() || ariaText.isEmpty();
1506     }
1507 
1508     // NOTE: BRs always have text boxes now, so the text box check here can be removed
1509     if (m_renderer->isText()) {
1510         // static text beneath MenuItems and MenuButtons are just reported along with the menu item, so it's ignored on an individual level
1511         if (parentObjectUnignored()->ariaRoleAttribute() == MenuItemRole
1512             || parentObjectUnignored()->ariaRoleAttribute() == MenuButtonRole)
1513             return true;
1514         RenderText* renderText = toRenderText(m_renderer);
1515         if (m_renderer->isBR() || !renderText->firstTextBox())
1516             return true;
1517 
1518         // text elements that are just empty whitespace should not be returned
1519         return renderText->text()->containsOnlyWhitespace();
1520     }
1521 
1522     if (isHeading())
1523         return false;
1524 
1525     if (isLink())
1526         return false;
1527 
1528     // all controls are accessible
1529     if (isControl())
1530         return false;
1531 
1532     if (ariaRole != UnknownRole)
1533         return false;
1534 
1535     // don't ignore labels, because they serve as TitleUIElements
1536     Node* node = m_renderer->node();
1537     if (node && node->hasTagName(labelTag))
1538         return false;
1539 
1540     // Anything that is content editable should not be ignored.
1541     // However, one cannot just call node->isContentEditable() since that will ask if its parents
1542     // are also editable. Only the top level content editable region should be exposed.
1543     if (node && node->isElementNode()) {
1544         Element* element = static_cast<Element*>(node);
1545         const AtomicString& contentEditable = element->getAttribute(contenteditableAttr);
1546         if (equalIgnoringCase(contentEditable, "true"))
1547             return false;
1548     }
1549 
1550     if (m_renderer->isBlockFlow() && m_renderer->childrenInline())
1551         return !toRenderBlock(m_renderer)->firstLineBox() && !mouseButtonListener();
1552 
1553     // ignore images seemingly used as spacers
1554     if (isImage()) {
1555         if (node && node->isElementNode()) {
1556             Element* elt = static_cast<Element*>(node);
1557             const AtomicString& alt = elt->getAttribute(altAttr);
1558             // don't ignore an image that has an alt tag
1559             if (!alt.isEmpty())
1560                 return false;
1561             // informal standard is to ignore images with zero-length alt strings
1562             if (!alt.isNull())
1563                 return true;
1564         }
1565 
1566         if (node && node->hasTagName(canvasTag)) {
1567             RenderHTMLCanvas* canvas = toRenderHTMLCanvas(m_renderer);
1568             if (canvas->height() <= 1 || canvas->width() <= 1)
1569                 return true;
1570             return false;
1571         }
1572 
1573         if (isNativeImage()) {
1574             // check for one-dimensional image
1575             RenderImage* image = toRenderImage(m_renderer);
1576             if (image->height() <= 1 || image->width() <= 1)
1577                 return true;
1578 
1579             // check whether rendered image was stretched from one-dimensional file image
1580             if (image->cachedImage()) {
1581                 IntSize imageSize = image->cachedImage()->imageSize(image->view()->zoomFactor());
1582                 return imageSize.height() <= 1 || imageSize.width() <= 1;
1583             }
1584         }
1585         return false;
1586     }
1587 
1588     // make a platform-specific decision
1589     if (isAttachment())
1590         return accessibilityIgnoreAttachment();
1591 
1592     return !m_renderer->isListMarker() && !isWebArea();
1593 }
1594 
isLoaded() const1595 bool AccessibilityRenderObject::isLoaded() const
1596 {
1597     return !m_renderer->document()->tokenizer();
1598 }
1599 
estimatedLoadingProgress() const1600 double AccessibilityRenderObject::estimatedLoadingProgress() const
1601 {
1602     if (!m_renderer)
1603         return 0;
1604 
1605     if (isLoaded())
1606         return 1.0;
1607 
1608     Page* page = m_renderer->document()->page();
1609     if (!page)
1610         return 0;
1611 
1612     return page->progress()->estimatedProgress();
1613 }
1614 
layoutCount() const1615 int AccessibilityRenderObject::layoutCount() const
1616 {
1617     if (!m_renderer->isRenderView())
1618         return 0;
1619     return toRenderView(m_renderer)->frameView()->layoutCount();
1620 }
1621 
text() const1622 String AccessibilityRenderObject::text() const
1623 {
1624     // If this is a user defined static text, use the accessible name computation.
1625     if (ariaRoleAttribute() == StaticTextRole)
1626         return accessibilityDescription();
1627 
1628     if (!isTextControl() || isPasswordField())
1629         return String();
1630 
1631     if (isNativeTextControl())
1632         return toRenderTextControl(m_renderer)->text();
1633 
1634     Node* node = m_renderer->node();
1635     if (!node)
1636         return String();
1637     if (!node->isElementNode())
1638         return String();
1639 
1640     return static_cast<Element*>(node)->innerText();
1641 }
1642 
textLength() const1643 int AccessibilityRenderObject::textLength() const
1644 {
1645     ASSERT(isTextControl());
1646 
1647     if (isPasswordField())
1648         return -1; // need to return something distinct from 0
1649 
1650     return text().length();
1651 }
1652 
ariaSelectedTextDOMRange() const1653 PassRefPtr<Range> AccessibilityRenderObject::ariaSelectedTextDOMRange() const
1654 {
1655     Node* node = m_renderer->node();
1656     if (!node)
1657         return 0;
1658 
1659     RefPtr<Range> currentSelectionRange = selection().toNormalizedRange();
1660     if (!currentSelectionRange)
1661         return 0;
1662 
1663     ExceptionCode ec = 0;
1664     if (!currentSelectionRange->intersectsNode(node, ec))
1665         return Range::create(currentSelectionRange->ownerDocument());
1666 
1667     RefPtr<Range> ariaRange = rangeOfContents(node);
1668     Position startPosition, endPosition;
1669 
1670     // Find intersection of currentSelectionRange and ariaRange
1671     if (ariaRange->startOffset() > currentSelectionRange->startOffset())
1672         startPosition = ariaRange->startPosition();
1673     else
1674         startPosition = currentSelectionRange->startPosition();
1675 
1676     if (ariaRange->endOffset() < currentSelectionRange->endOffset())
1677         endPosition = ariaRange->endPosition();
1678     else
1679         endPosition = currentSelectionRange->endPosition();
1680 
1681     return Range::create(ariaRange->ownerDocument(), startPosition, endPosition);
1682 }
1683 
selectedText() const1684 String AccessibilityRenderObject::selectedText() const
1685 {
1686     ASSERT(isTextControl());
1687 
1688     if (isPasswordField())
1689         return String(); // need to return something distinct from empty string
1690 
1691     if (isNativeTextControl()) {
1692         RenderTextControl* textControl = toRenderTextControl(m_renderer);
1693         return textControl->text().substring(textControl->selectionStart(), textControl->selectionEnd() - textControl->selectionStart());
1694     }
1695 
1696     if (ariaRoleAttribute() == UnknownRole)
1697         return String();
1698 
1699     RefPtr<Range> ariaRange = ariaSelectedTextDOMRange();
1700     if (!ariaRange)
1701         return String();
1702     return ariaRange->text();
1703 }
1704 
accessKey() const1705 const AtomicString& AccessibilityRenderObject::accessKey() const
1706 {
1707     Node* node = m_renderer->node();
1708     if (!node)
1709         return nullAtom;
1710     if (!node->isElementNode())
1711         return nullAtom;
1712     return static_cast<Element*>(node)->getAttribute(accesskeyAttr);
1713 }
1714 
selection() const1715 VisibleSelection AccessibilityRenderObject::selection() const
1716 {
1717     return m_renderer->document()->frame()->selection()->selection();
1718 }
1719 
selectedTextRange() const1720 PlainTextRange AccessibilityRenderObject::selectedTextRange() const
1721 {
1722     ASSERT(isTextControl());
1723 
1724     if (isPasswordField())
1725         return PlainTextRange();
1726 
1727     AccessibilityRole ariaRole = ariaRoleAttribute();
1728     if (isNativeTextControl() && ariaRole == UnknownRole) {
1729         RenderTextControl* textControl = toRenderTextControl(m_renderer);
1730         return PlainTextRange(textControl->selectionStart(), textControl->selectionEnd() - textControl->selectionStart());
1731     }
1732 
1733     if (ariaRole == UnknownRole)
1734         return PlainTextRange();
1735 
1736     RefPtr<Range> ariaRange = ariaSelectedTextDOMRange();
1737     if (!ariaRange)
1738         return PlainTextRange();
1739     return PlainTextRange(ariaRange->startOffset(), ariaRange->endOffset());
1740 }
1741 
setSelectedTextRange(const PlainTextRange & range)1742 void AccessibilityRenderObject::setSelectedTextRange(const PlainTextRange& range)
1743 {
1744     if (isNativeTextControl()) {
1745         RenderTextControl* textControl = toRenderTextControl(m_renderer);
1746         textControl->setSelectionRange(range.start, range.start + range.length);
1747         return;
1748     }
1749 
1750     Document* document = m_renderer->document();
1751     if (!document)
1752         return;
1753     Frame* frame = document->frame();
1754     if (!frame)
1755         return;
1756     Node* node = m_renderer->node();
1757     frame->selection()->setSelection(VisibleSelection(Position(node, range.start),
1758         Position(node, range.start + range.length), DOWNSTREAM));
1759 }
1760 
url() const1761 KURL AccessibilityRenderObject::url() const
1762 {
1763     if (isAnchor() && m_renderer->node()->hasTagName(aTag)) {
1764         if (HTMLAnchorElement* anchor = static_cast<HTMLAnchorElement*>(anchorElement()))
1765             return anchor->href();
1766     }
1767 
1768     if (isWebArea())
1769         return m_renderer->document()->url();
1770 
1771     if (isImage() && m_renderer->node() && m_renderer->node()->hasTagName(imgTag))
1772         return static_cast<HTMLImageElement*>(m_renderer->node())->src();
1773 
1774     if (isInputImage())
1775         return static_cast<HTMLInputElement*>(m_renderer->node())->src();
1776 
1777     return KURL();
1778 }
1779 
isVisited() const1780 bool AccessibilityRenderObject::isVisited() const
1781 {
1782     return m_renderer->style()->pseudoState() == PseudoVisited;
1783 }
1784 
isExpanded() const1785 bool AccessibilityRenderObject::isExpanded() const
1786 {
1787     if (equalIgnoringCase(getAttribute(aria_expandedAttr).string(), "true"))
1788         return true;
1789 
1790     return false;
1791 }
1792 
setElementAttributeValue(const QualifiedName & attributeName,bool value)1793 void AccessibilityRenderObject::setElementAttributeValue(const QualifiedName& attributeName, bool value)
1794 {
1795     if (!m_renderer)
1796         return;
1797 
1798     Node* node = m_renderer->node();
1799     if (!node || !node->isElementNode())
1800         return;
1801 
1802     Element* element = static_cast<Element*>(node);
1803     element->setAttribute(attributeName, (value) ? "true" : "false");
1804 }
1805 
elementAttributeValue(const QualifiedName & attributeName) const1806 bool AccessibilityRenderObject::elementAttributeValue(const QualifiedName& attributeName) const
1807 {
1808     if (!m_renderer)
1809         return false;
1810 
1811     return equalIgnoringCase(getAttribute(attributeName), "true");
1812 }
1813 
setIsExpanded(bool isExpanded)1814 void AccessibilityRenderObject::setIsExpanded(bool isExpanded)
1815 {
1816     // Combo boxes, tree items and rows can be expanded (in different ways on different platforms).
1817     // That action translates into setting the aria-expanded attribute to true.
1818     AccessibilityRole role = roleValue();
1819     switch (role) {
1820     case ComboBoxRole:
1821     case TreeItemRole:
1822     case RowRole:
1823         setElementAttributeValue(aria_expandedAttr, isExpanded);
1824         break;
1825     default:
1826         break;
1827     }
1828 }
1829 
isRequired() const1830 bool AccessibilityRenderObject::isRequired() const
1831 {
1832     if (equalIgnoringCase(getAttribute(aria_requiredAttr).string(), "true"))
1833         return true;
1834 
1835     return false;
1836 }
1837 
isSelected() const1838 bool AccessibilityRenderObject::isSelected() const
1839 {
1840     if (!m_renderer)
1841         return false;
1842 
1843     Node* node = m_renderer->node();
1844     if (!node)
1845         return false;
1846 
1847     String ariaSelected = getAttribute(aria_selectedAttr).string();
1848     if (equalIgnoringCase(ariaSelected, "true"))
1849         return true;
1850 
1851     if (isTabItem() && isTabItemSelected())
1852         return true;
1853 
1854     return false;
1855 }
1856 
isTabItemSelected() const1857 bool AccessibilityRenderObject::isTabItemSelected() const
1858 {
1859     if (!isTabItem() || !m_renderer)
1860         return false;
1861 
1862     Node* node = m_renderer->node();
1863     if (!node || !node->isElementNode())
1864         return false;
1865 
1866     // The ARIA spec says a tab item can also be selected if it is aria-labeled by a tabpanel
1867     // that has keyboard focus inside of it, or if a tabpanel in its aria-controls list has KB
1868     // focus inside of it.
1869     AccessibilityObject* focusedElement = focusedUIElement();
1870     if (!focusedElement)
1871         return false;
1872 
1873     Vector<Element*> elements;
1874     elementsFromAttribute(elements, aria_controlsAttr);
1875 
1876     unsigned count = elements.size();
1877     for (unsigned k = 0; k < count; ++k) {
1878         Element* element = elements[k];
1879         AccessibilityObject* tabPanel = axObjectCache()->getOrCreate(element->renderer());
1880 
1881         // A tab item should only control tab panels.
1882         if (!tabPanel || tabPanel->roleValue() != TabPanelRole)
1883             continue;
1884 
1885         AccessibilityObject* checkFocusElement = focusedElement;
1886         // Check if the focused element is a descendant of the element controlled by the tab item.
1887         while (checkFocusElement) {
1888             if (tabPanel == checkFocusElement)
1889                 return true;
1890             checkFocusElement = checkFocusElement->parentObject();
1891         }
1892     }
1893 
1894     return false;
1895 }
1896 
isFocused() const1897 bool AccessibilityRenderObject::isFocused() const
1898 {
1899     if (!m_renderer)
1900         return false;
1901 
1902     Document* document = m_renderer->document();
1903     if (!document)
1904         return false;
1905 
1906     Node* focusedNode = document->focusedNode();
1907     if (!focusedNode)
1908         return false;
1909 
1910     // A web area is represented by the Document node in the DOM tree, which isn't focusable.
1911     // Check instead if the frame's selection controller is focused
1912     if (focusedNode == m_renderer->node()
1913         || (roleValue() == WebAreaRole && document->frame()->selection()->isFocusedAndActive()))
1914         return true;
1915 
1916     return false;
1917 }
1918 
setFocused(bool on)1919 void AccessibilityRenderObject::setFocused(bool on)
1920 {
1921     if (!canSetFocusAttribute())
1922         return;
1923 
1924     if (!on)
1925         m_renderer->document()->setFocusedNode(0);
1926     else {
1927         if (m_renderer->node()->isElementNode())
1928             static_cast<Element*>(m_renderer->node())->focus();
1929         else
1930             m_renderer->document()->setFocusedNode(m_renderer->node());
1931     }
1932 }
1933 
changeValueByPercent(float percentChange)1934 void AccessibilityRenderObject::changeValueByPercent(float percentChange)
1935 {
1936     float range = maxValueForRange() - minValueForRange();
1937     float value = valueForRange();
1938 
1939     value += range * (percentChange / 100);
1940     setValue(String::number(value));
1941 
1942     axObjectCache()->postNotification(m_renderer, AXObjectCache::AXValueChanged, true);
1943 }
1944 
setSelected(bool enabled)1945 void AccessibilityRenderObject::setSelected(bool enabled)
1946 {
1947     setElementAttributeValue(aria_selectedAttr, enabled);
1948 }
1949 
setSelectedRows(AccessibilityChildrenVector & selectedRows)1950 void AccessibilityRenderObject::setSelectedRows(AccessibilityChildrenVector& selectedRows)
1951 {
1952     // Setting selected only makes sense in trees and tables (and tree-tables).
1953     AccessibilityRole role = roleValue();
1954     if (role != TreeRole && role != TreeGridRole && role != TableRole)
1955         return;
1956 
1957     bool isMulti = isMultiSelectable();
1958     unsigned count = selectedRows.size();
1959     if (count > 1 && !isMulti)
1960         count = 1;
1961 
1962     for (unsigned k = 0; k < count; ++k)
1963         selectedRows[k]->setSelected(true);
1964 }
1965 
setValue(const String & string)1966 void AccessibilityRenderObject::setValue(const String& string)
1967 {
1968     if (!m_renderer)
1969         return;
1970 
1971     // FIXME: Do we want to do anything here for ARIA textboxes?
1972     if (m_renderer->isTextField()) {
1973         HTMLInputElement* input = static_cast<HTMLInputElement*>(m_renderer->node());
1974         input->setValue(string);
1975     } else if (m_renderer->isTextArea()) {
1976         HTMLTextAreaElement* textArea = static_cast<HTMLTextAreaElement*>(m_renderer->node());
1977         textArea->setValue(string);
1978     } else if (roleValue() == SliderRole) {
1979         Node* element = m_renderer->node();
1980         if (element && element->isElementNode())
1981             static_cast<Element*>(element)->setAttribute(aria_valuenowAttr, string);
1982     }
1983 }
1984 
ariaOwnsElements(AccessibilityChildrenVector & axObjects) const1985 void AccessibilityRenderObject::ariaOwnsElements(AccessibilityChildrenVector& axObjects) const
1986 {
1987     Vector<Element*> elements;
1988     elementsFromAttribute(elements, aria_ownsAttr);
1989 
1990     unsigned count = elements.size();
1991     for (unsigned k = 0; k < count; ++k) {
1992         RenderObject* render = elements[k]->renderer();
1993         AccessibilityObject* obj = axObjectCache()->getOrCreate(render);
1994         if (obj)
1995             axObjects.append(obj);
1996     }
1997 }
1998 
supportsARIAOwns() const1999 bool AccessibilityRenderObject::supportsARIAOwns() const
2000 {
2001     if (!m_renderer)
2002         return false;
2003     const AtomicString& ariaOwns = getAttribute(aria_ownsAttr).string();
2004 
2005     return !ariaOwns.isEmpty();
2006 }
2007 
isEnabled() const2008 bool AccessibilityRenderObject::isEnabled() const
2009 {
2010     ASSERT(m_renderer);
2011 
2012     if (equalIgnoringCase(getAttribute(aria_disabledAttr).string(), "true"))
2013         return false;
2014 
2015     Node* node = m_renderer->node();
2016     if (!node || !node->isElementNode())
2017         return true;
2018 
2019     return static_cast<Element*>(node)->isEnabledFormControl();
2020 }
2021 
topRenderer() const2022 RenderView* AccessibilityRenderObject::topRenderer() const
2023 {
2024     return m_renderer->document()->topDocument()->renderView();
2025 }
2026 
document() const2027 Document* AccessibilityRenderObject::document() const
2028 {
2029     if (!m_renderer)
2030         return 0;
2031     return m_renderer->document();
2032 }
2033 
topDocumentFrameView() const2034 FrameView* AccessibilityRenderObject::topDocumentFrameView() const
2035 {
2036     return topRenderer()->view()->frameView();
2037 }
2038 
widget() const2039 Widget* AccessibilityRenderObject::widget() const
2040 {
2041     if (!m_renderer->isWidget())
2042         return 0;
2043     return toRenderWidget(m_renderer)->widget();
2044 }
2045 
axObjectCache() const2046 AXObjectCache* AccessibilityRenderObject::axObjectCache() const
2047 {
2048     return m_renderer->document()->axObjectCache();
2049 }
2050 
accessibilityParentForImageMap(HTMLMapElement * map) const2051 AccessibilityObject* AccessibilityRenderObject::accessibilityParentForImageMap(HTMLMapElement* map) const
2052 {
2053     // find an image that is using this map
2054     if (!map)
2055         return 0;
2056 
2057     HTMLImageElement* imageElement = map->imageElement();
2058     if (!imageElement)
2059         return 0;
2060 
2061     return axObjectCache()->getOrCreate(imageElement->renderer());
2062 }
2063 
getDocumentLinks(AccessibilityChildrenVector & result)2064 void AccessibilityRenderObject::getDocumentLinks(AccessibilityChildrenVector& result)
2065 {
2066     Document* document = m_renderer->document();
2067     RefPtr<HTMLCollection> coll = document->links();
2068     Node* curr = coll->firstItem();
2069     while (curr) {
2070         RenderObject* obj = curr->renderer();
2071         if (obj) {
2072             RefPtr<AccessibilityObject> axobj = document->axObjectCache()->getOrCreate(obj);
2073             ASSERT(axobj);
2074             if (!axobj->accessibilityIsIgnored() && axobj->isLink())
2075                 result.append(axobj);
2076         } else {
2077             Node* parent = curr->parent();
2078             if (parent && curr->hasTagName(areaTag) && parent->hasTagName(mapTag)) {
2079                 AccessibilityImageMapLink* areaObject = static_cast<AccessibilityImageMapLink*>(axObjectCache()->getOrCreate(ImageMapLinkRole));
2080                 areaObject->setHTMLAreaElement(static_cast<HTMLAreaElement*>(curr));
2081                 areaObject->setHTMLMapElement(static_cast<HTMLMapElement*>(parent));
2082                 areaObject->setParent(accessibilityParentForImageMap(static_cast<HTMLMapElement*>(parent)));
2083 
2084                 result.append(areaObject);
2085             }
2086         }
2087         curr = coll->nextItem();
2088     }
2089 }
2090 
documentFrameView() const2091 FrameView* AccessibilityRenderObject::documentFrameView() const
2092 {
2093     if (!m_renderer || !m_renderer->document())
2094         return 0;
2095 
2096     // this is the RenderObject's Document's Frame's FrameView
2097     return m_renderer->document()->view();
2098 }
2099 
widgetForAttachmentView() const2100 Widget* AccessibilityRenderObject::widgetForAttachmentView() const
2101 {
2102     if (!isAttachment())
2103         return 0;
2104     return toRenderWidget(m_renderer)->widget();
2105 }
2106 
frameViewIfRenderView() const2107 FrameView* AccessibilityRenderObject::frameViewIfRenderView() const
2108 {
2109     if (!m_renderer->isRenderView())
2110         return 0;
2111     // this is the RenderObject's Document's renderer's FrameView
2112     return m_renderer->view()->frameView();
2113 }
2114 
2115 // This function is like a cross-platform version of - (WebCoreTextMarkerRange*)textMarkerRange. It returns
2116 // a Range that we can convert to a WebCoreTextMarkerRange in the Obj-C file
visiblePositionRange() const2117 VisiblePositionRange AccessibilityRenderObject::visiblePositionRange() const
2118 {
2119     if (!m_renderer)
2120         return VisiblePositionRange();
2121 
2122     // construct VisiblePositions for start and end
2123     Node* node = m_renderer->node();
2124     if (!node)
2125         return VisiblePositionRange();
2126 
2127     VisiblePosition startPos = firstDeepEditingPositionForNode(node);
2128     VisiblePosition endPos = lastDeepEditingPositionForNode(node);
2129 
2130     // the VisiblePositions are equal for nodes like buttons, so adjust for that
2131     // FIXME: Really?  [button, 0] and [button, 1] are distinct (before and after the button)
2132     // I expect this code is only hit for things like empty divs?  In which case I don't think
2133     // the behavior is correct here -- eseidel
2134     if (startPos == endPos) {
2135         endPos = endPos.next();
2136         if (endPos.isNull())
2137             endPos = startPos;
2138     }
2139 
2140     return VisiblePositionRange(startPos, endPos);
2141 }
2142 
visiblePositionRangeForLine(unsigned lineCount) const2143 VisiblePositionRange AccessibilityRenderObject::visiblePositionRangeForLine(unsigned lineCount) const
2144 {
2145     if (!lineCount || !m_renderer)
2146         return VisiblePositionRange();
2147 
2148     // iterate over the lines
2149     // FIXME: this is wrong when lineNumber is lineCount+1,  because nextLinePosition takes you to the
2150     // last offset of the last line
2151     VisiblePosition visiblePos = m_renderer->document()->renderer()->positionForCoordinates(0, 0);
2152     VisiblePosition savedVisiblePos;
2153     while (--lineCount) {
2154         savedVisiblePos = visiblePos;
2155         visiblePos = nextLinePosition(visiblePos, 0);
2156         if (visiblePos.isNull() || visiblePos == savedVisiblePos)
2157             return VisiblePositionRange();
2158     }
2159 
2160     // make a caret selection for the marker position, then extend it to the line
2161     // NOTE: ignores results of sel.modify because it returns false when
2162     // starting at an empty line.  The resulting selection in that case
2163     // will be a caret at visiblePos.
2164     SelectionController selection;
2165     selection.setSelection(VisibleSelection(visiblePos));
2166     selection.modify(SelectionController::EXTEND, SelectionController::RIGHT, LineBoundary);
2167 
2168     return VisiblePositionRange(selection.selection().visibleStart(), selection.selection().visibleEnd());
2169 }
2170 
visiblePositionForIndex(int index) const2171 VisiblePosition AccessibilityRenderObject::visiblePositionForIndex(int index) const
2172 {
2173     if (!m_renderer)
2174         return VisiblePosition();
2175 
2176     if (isNativeTextControl())
2177         return toRenderTextControl(m_renderer)->visiblePositionForIndex(index);
2178 
2179     if (!isTextControl() && !m_renderer->isText())
2180         return VisiblePosition();
2181 
2182     Node* node = m_renderer->node();
2183     if (!node)
2184         return VisiblePosition();
2185 
2186     if (index <= 0)
2187         return VisiblePosition(node, 0, DOWNSTREAM);
2188 
2189     ExceptionCode ec = 0;
2190     RefPtr<Range> range = Range::create(m_renderer->document());
2191     range->selectNodeContents(node, ec);
2192     CharacterIterator it(range.get());
2193     it.advance(index - 1);
2194     return VisiblePosition(it.range()->endContainer(ec), it.range()->endOffset(ec), UPSTREAM);
2195 }
2196 
indexForVisiblePosition(const VisiblePosition & pos) const2197 int AccessibilityRenderObject::indexForVisiblePosition(const VisiblePosition& pos) const
2198 {
2199     if (isNativeTextControl())
2200         return toRenderTextControl(m_renderer)->indexForVisiblePosition(pos);
2201 
2202     if (!isTextControl())
2203         return 0;
2204 
2205     Node* node = m_renderer->node();
2206     if (!node)
2207         return 0;
2208 
2209     Position indexPosition = pos.deepEquivalent();
2210     if (!indexPosition.node() || indexPosition.node()->rootEditableElement() != node)
2211         return 0;
2212 
2213     ExceptionCode ec = 0;
2214     RefPtr<Range> range = Range::create(m_renderer->document());
2215     range->setStart(node, 0, ec);
2216     range->setEnd(indexPosition.node(), indexPosition.deprecatedEditingOffset(), ec);
2217     return TextIterator::rangeLength(range.get());
2218 }
2219 
boundsForVisiblePositionRange(const VisiblePositionRange & visiblePositionRange) const2220 IntRect AccessibilityRenderObject::boundsForVisiblePositionRange(const VisiblePositionRange& visiblePositionRange) const
2221 {
2222     if (visiblePositionRange.isNull())
2223         return IntRect();
2224 
2225     // Create a mutable VisiblePositionRange.
2226     VisiblePositionRange range(visiblePositionRange);
2227     IntRect rect1 = range.start.absoluteCaretBounds();
2228     IntRect rect2 = range.end.absoluteCaretBounds();
2229 
2230     // 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
2231     if (rect2.y() != rect1.y()) {
2232         VisiblePosition endOfFirstLine = endOfLine(range.start);
2233         if (range.start == endOfFirstLine) {
2234             range.start.setAffinity(DOWNSTREAM);
2235             rect1 = range.start.absoluteCaretBounds();
2236         }
2237         if (range.end == endOfFirstLine) {
2238             range.end.setAffinity(UPSTREAM);
2239             rect2 = range.end.absoluteCaretBounds();
2240         }
2241     }
2242 
2243     IntRect ourrect = rect1;
2244     ourrect.unite(rect2);
2245 
2246     // if the rectangle spans lines and contains multiple text chars, use the range's bounding box intead
2247     if (rect1.bottom() != rect2.bottom()) {
2248         RefPtr<Range> dataRange = makeRange(range.start, range.end);
2249         IntRect boundingBox = dataRange->boundingBox();
2250         String rangeString = plainText(dataRange.get());
2251         if (rangeString.length() > 1 && !boundingBox.isEmpty())
2252             ourrect = boundingBox;
2253     }
2254 
2255 #if PLATFORM(MAC)
2256     return m_renderer->document()->view()->contentsToScreen(ourrect);
2257 #else
2258     return ourrect;
2259 #endif
2260 }
2261 
setSelectedVisiblePositionRange(const VisiblePositionRange & range) const2262 void AccessibilityRenderObject::setSelectedVisiblePositionRange(const VisiblePositionRange& range) const
2263 {
2264     if (range.start.isNull() || range.end.isNull())
2265         return;
2266 
2267     // make selection and tell the document to use it. if it's zero length, then move to that position
2268     if (range.start == range.end)
2269         m_renderer->document()->frame()->selection()->moveTo(range.start, true);
2270     else {
2271         VisibleSelection newSelection = VisibleSelection(range.start, range.end);
2272         m_renderer->document()->frame()->selection()->setSelection(newSelection);
2273     }
2274 }
2275 
visiblePositionForPoint(const IntPoint & point) const2276 VisiblePosition AccessibilityRenderObject::visiblePositionForPoint(const IntPoint& point) const
2277 {
2278     // convert absolute point to view coordinates
2279     FrameView* frameView = m_renderer->document()->topDocument()->renderer()->view()->frameView();
2280     RenderView* renderView = topRenderer();
2281     Node* innerNode = 0;
2282 
2283     // locate the node containing the point
2284     IntPoint pointResult;
2285     while (1) {
2286         IntPoint ourpoint;
2287 #if PLATFORM(MAC)
2288         ourpoint = frameView->screenToContents(point);
2289 #else
2290         ourpoint = point;
2291 #endif
2292         HitTestRequest request(HitTestRequest::ReadOnly |
2293                                HitTestRequest::Active);
2294         HitTestResult result(ourpoint);
2295         renderView->layer()->hitTest(request, result);
2296         innerNode = result.innerNode();
2297         if (!innerNode || !innerNode->renderer())
2298             return VisiblePosition();
2299 
2300         pointResult = result.localPoint();
2301 
2302         // done if hit something other than a widget
2303         RenderObject* renderer = innerNode->renderer();
2304         if (!renderer->isWidget())
2305             break;
2306 
2307         // descend into widget (FRAME, IFRAME, OBJECT...)
2308         Widget* widget = toRenderWidget(renderer)->widget();
2309         if (!widget || !widget->isFrameView())
2310             break;
2311         Frame* frame = static_cast<FrameView*>(widget)->frame();
2312         if (!frame)
2313             break;
2314         renderView = frame->document()->renderView();
2315         frameView = static_cast<FrameView*>(widget);
2316     }
2317 
2318     return innerNode->renderer()->positionForPoint(pointResult);
2319 }
2320 
2321 // NOTE: Consider providing this utility method as AX API
visiblePositionForIndex(unsigned indexValue,bool lastIndexOK) const2322 VisiblePosition AccessibilityRenderObject::visiblePositionForIndex(unsigned indexValue, bool lastIndexOK) const
2323 {
2324     if (!isTextControl())
2325         return VisiblePosition();
2326 
2327     // lastIndexOK specifies whether the position after the last character is acceptable
2328     if (indexValue >= text().length()) {
2329         if (!lastIndexOK || indexValue > text().length())
2330             return VisiblePosition();
2331     }
2332     VisiblePosition position = visiblePositionForIndex(indexValue);
2333     position.setAffinity(DOWNSTREAM);
2334     return position;
2335 }
2336 
2337 // NOTE: Consider providing this utility method as AX API
index(const VisiblePosition & position) const2338 int AccessibilityRenderObject::index(const VisiblePosition& position) const
2339 {
2340     if (!isTextControl())
2341         return -1;
2342 
2343     Node* node = position.deepEquivalent().node();
2344     if (!node)
2345         return -1;
2346 
2347     for (RenderObject* renderer = node->renderer(); renderer && renderer->node(); renderer = renderer->parent()) {
2348         if (renderer == m_renderer)
2349             return indexForVisiblePosition(position);
2350     }
2351 
2352     return -1;
2353 }
2354 
2355 // Given a line number, the range of characters of the text associated with this accessibility
2356 // object that contains the line number.
doAXRangeForLine(unsigned lineNumber) const2357 PlainTextRange AccessibilityRenderObject::doAXRangeForLine(unsigned lineNumber) const
2358 {
2359     if (!isTextControl())
2360         return PlainTextRange();
2361 
2362     // iterate to the specified line
2363     VisiblePosition visiblePos = visiblePositionForIndex(0);
2364     VisiblePosition savedVisiblePos;
2365     for (unsigned lineCount = lineNumber; lineCount; lineCount -= 1) {
2366         savedVisiblePos = visiblePos;
2367         visiblePos = nextLinePosition(visiblePos, 0);
2368         if (visiblePos.isNull() || visiblePos == savedVisiblePos)
2369             return PlainTextRange();
2370     }
2371 
2372     // make a caret selection for the marker position, then extend it to the line
2373     // NOTE: ignores results of selection.modify because it returns false when
2374     // starting at an empty line.  The resulting selection in that case
2375     // will be a caret at visiblePos.
2376     SelectionController selection;
2377     selection.setSelection(VisibleSelection(visiblePos));
2378     selection.modify(SelectionController::EXTEND, SelectionController::LEFT, LineBoundary);
2379     selection.modify(SelectionController::EXTEND, SelectionController::RIGHT, LineBoundary);
2380 
2381     // calculate the indices for the selection start and end
2382     VisiblePosition startPosition = selection.selection().visibleStart();
2383     VisiblePosition endPosition = selection.selection().visibleEnd();
2384     int index1 = indexForVisiblePosition(startPosition);
2385     int index2 = indexForVisiblePosition(endPosition);
2386 
2387     // add one to the end index for a line break not caused by soft line wrap (to match AppKit)
2388     if (endPosition.affinity() == DOWNSTREAM && endPosition.next().isNotNull())
2389         index2 += 1;
2390 
2391     // return nil rather than an zero-length range (to match AppKit)
2392     if (index1 == index2)
2393         return PlainTextRange();
2394 
2395     return PlainTextRange(index1, index2 - index1);
2396 }
2397 
2398 // The composed character range in the text associated with this accessibility object that
2399 // is specified by the given index value. This parameterized attribute returns the complete
2400 // range of characters (including surrogate pairs of multi-byte glyphs) at the given index.
doAXRangeForIndex(unsigned index) const2401 PlainTextRange AccessibilityRenderObject::doAXRangeForIndex(unsigned index) const
2402 {
2403     if (!isTextControl())
2404         return PlainTextRange();
2405 
2406     String elementText = text();
2407     if (!elementText.length() || index > elementText.length() - 1)
2408         return PlainTextRange();
2409 
2410     return PlainTextRange(index, 1);
2411 }
2412 
2413 // A substring of the text associated with this accessibility object that is
2414 // specified by the given character range.
doAXStringForRange(const PlainTextRange & range) const2415 String AccessibilityRenderObject::doAXStringForRange(const PlainTextRange& range) const
2416 {
2417     if (isPasswordField())
2418         return String();
2419 
2420     if (!range.length)
2421         return String();
2422 
2423     if (!isTextControl())
2424         return String();
2425 
2426     String elementText = text();
2427     if (range.start + range.length > elementText.length())
2428         return String();
2429 
2430     return elementText.substring(range.start, range.length);
2431 }
2432 
2433 // The bounding rectangle of the text associated with this accessibility object that is
2434 // specified by the given range. This is the bounding rectangle a sighted user would see
2435 // on the display screen, in pixels.
doAXBoundsForRange(const PlainTextRange & range) const2436 IntRect AccessibilityRenderObject::doAXBoundsForRange(const PlainTextRange& range) const
2437 {
2438     if (isTextControl())
2439         return boundsForVisiblePositionRange(visiblePositionRangeForRange(range));
2440     return IntRect();
2441 }
2442 
accessibilityImageMapHitTest(HTMLAreaElement * area,const IntPoint & point) const2443 AccessibilityObject* AccessibilityRenderObject::accessibilityImageMapHitTest(HTMLAreaElement* area, const IntPoint& point) const
2444 {
2445     if (!area)
2446         return 0;
2447 
2448     HTMLMapElement* map = static_cast<HTMLMapElement*>(area->parent());
2449     AccessibilityObject* parent = accessibilityParentForImageMap(map);
2450     if (!parent)
2451         return 0;
2452 
2453     AccessibilityObject::AccessibilityChildrenVector children = parent->children();
2454 
2455     unsigned count = children.size();
2456     for (unsigned k = 0; k < count; ++k) {
2457         if (children[k]->elementRect().contains(point))
2458             return children[k].get();
2459     }
2460 
2461     return 0;
2462 }
2463 
doAccessibilityHitTest(const IntPoint & point) const2464 AccessibilityObject* AccessibilityRenderObject::doAccessibilityHitTest(const IntPoint& point) const
2465 {
2466     if (!m_renderer || !m_renderer->hasLayer())
2467         return 0;
2468 
2469     RenderLayer* layer = toRenderBox(m_renderer)->layer();
2470 
2471     HitTestRequest request(HitTestRequest::ReadOnly |
2472                            HitTestRequest::Active);
2473     HitTestResult hitTestResult = HitTestResult(point);
2474     layer->hitTest(request, hitTestResult);
2475     if (!hitTestResult.innerNode())
2476         return 0;
2477     Node* node = hitTestResult.innerNode()->shadowAncestorNode();
2478 
2479     if (node->hasTagName(areaTag))
2480         return accessibilityImageMapHitTest(static_cast<HTMLAreaElement*>(node), point);
2481 
2482     RenderObject* obj = node->renderer();
2483     if (!obj)
2484         return 0;
2485 
2486     AccessibilityObject* result = obj->document()->axObjectCache()->getOrCreate(obj);
2487 
2488     if (obj->isListBox())
2489         return static_cast<AccessibilityListBox*>(result)->doAccessibilityHitTest(point);
2490 
2491     if (result->accessibilityIsIgnored()) {
2492         // If this element is the label of a control, a hit test should return the control.
2493         AccessibilityObject* controlObject = result->correspondingControlForLabelElement();
2494         if (controlObject && !controlObject->exposesTitleUIElement())
2495             return controlObject;
2496 
2497         result = result->parentObjectUnignored();
2498     }
2499 
2500     return result;
2501 }
2502 
focusedUIElement() const2503 AccessibilityObject* AccessibilityRenderObject::focusedUIElement() const
2504 {
2505     Page* page = m_renderer->document()->page();
2506     if (!page)
2507         return 0;
2508 
2509     return AXObjectCache::focusedUIElementForPage(page);
2510 }
2511 
shouldFocusActiveDescendant() const2512 bool AccessibilityRenderObject::shouldFocusActiveDescendant() const
2513 {
2514     switch (ariaRoleAttribute()) {
2515     case GroupRole:
2516     case ComboBoxRole:
2517     case ListBoxRole:
2518     case MenuRole:
2519     case MenuBarRole:
2520     case RadioGroupRole:
2521     case RowRole:
2522     case PopUpButtonRole:
2523     case ProgressIndicatorRole:
2524     case ToolbarRole:
2525     case OutlineRole:
2526     case TreeRole:
2527     case GridRole:
2528     /* FIXME: replace these with actual roles when they are added to AccessibilityRole
2529     composite
2530     alert
2531     alertdialog
2532     status
2533     timer
2534     */
2535         return true;
2536     default:
2537         return false;
2538     }
2539 }
2540 
activeDescendant() const2541 AccessibilityObject* AccessibilityRenderObject::activeDescendant() const
2542 {
2543     if (!m_renderer)
2544         return 0;
2545 
2546     if (m_renderer->node() && !m_renderer->node()->isElementNode())
2547         return 0;
2548     Element* element = static_cast<Element*>(m_renderer->node());
2549 
2550     String activeDescendantAttrStr = element->getAttribute(aria_activedescendantAttr).string();
2551     if (activeDescendantAttrStr.isNull() || activeDescendantAttrStr.isEmpty())
2552         return 0;
2553 
2554     Element* target = document()->getElementById(activeDescendantAttrStr);
2555     if (!target)
2556         return 0;
2557 
2558     AccessibilityObject* obj = axObjectCache()->getOrCreate(target->renderer());
2559     if (obj && obj->isAccessibilityRenderObject())
2560     // an activedescendant is only useful if it has a renderer, because that's what's needed to post the notification
2561         return obj;
2562     return 0;
2563 }
2564 
2565 
handleActiveDescendantChanged()2566 void AccessibilityRenderObject::handleActiveDescendantChanged()
2567 {
2568     Element* element = static_cast<Element*>(renderer()->node());
2569     if (!element)
2570         return;
2571     Document* doc = renderer()->document();
2572     if (!doc->frame()->selection()->isFocusedAndActive() || doc->focusedNode() != element)
2573         return;
2574     AccessibilityRenderObject* activedescendant = static_cast<AccessibilityRenderObject*>(activeDescendant());
2575 
2576     if (activedescendant && shouldFocusActiveDescendant())
2577         doc->axObjectCache()->postNotification(m_renderer, AXObjectCache::AXActiveDescendantChanged, true);
2578 }
2579 
correspondingControlForLabelElement() const2580 AccessibilityObject* AccessibilityRenderObject::correspondingControlForLabelElement() const
2581 {
2582     HTMLLabelElement* labelElement = labelElementContainer();
2583     if (!labelElement)
2584         return 0;
2585 
2586     HTMLElement* correspondingControl = labelElement->correspondingControl();
2587     if (!correspondingControl)
2588         return 0;
2589 
2590     return axObjectCache()->getOrCreate(correspondingControl->renderer());
2591 }
2592 
correspondingLabelForControlElement() const2593 AccessibilityObject* AccessibilityRenderObject::correspondingLabelForControlElement() const
2594 {
2595     if (!m_renderer)
2596         return 0;
2597 
2598     Node* node = m_renderer->node();
2599     if (node && node->isHTMLElement()) {
2600         HTMLLabelElement* label = labelForElement(static_cast<Element*>(node));
2601         if (label)
2602             return axObjectCache()->getOrCreate(label->renderer());
2603     }
2604 
2605     return 0;
2606 }
2607 
observableObject() const2608 AccessibilityObject* AccessibilityRenderObject::observableObject() const
2609 {
2610     for (RenderObject* renderer = m_renderer; renderer && renderer->node(); renderer = renderer->parent()) {
2611         if (renderer->isTextControl())
2612             return renderer->document()->axObjectCache()->getOrCreate(renderer);
2613     }
2614 
2615     return 0;
2616 }
2617 
determineAriaRoleAttribute() const2618 AccessibilityRole AccessibilityRenderObject::determineAriaRoleAttribute() const
2619 {
2620     String ariaRole = getAttribute(roleAttr).string();
2621     if (ariaRole.isNull() || ariaRole.isEmpty())
2622         return UnknownRole;
2623 
2624     AccessibilityRole role = ariaRoleToWebCoreRole(ariaRole);
2625 
2626     if (role == ButtonRole && elementAttributeValue(aria_haspopupAttr))
2627         role = PopUpButtonRole;
2628 
2629     if (role)
2630         return role;
2631     // selects and listboxes both have options as child roles, but they map to different roles within WebCore
2632     if (equalIgnoringCase(ariaRole, "option")) {
2633         if (parentObjectUnignored()->ariaRoleAttribute() == MenuRole)
2634             return MenuItemRole;
2635         if (parentObjectUnignored()->ariaRoleAttribute() == ListBoxRole)
2636             return ListBoxOptionRole;
2637     }
2638     // an aria "menuitem" may map to MenuButton or MenuItem depending on its parent
2639     if (equalIgnoringCase(ariaRole, "menuitem")) {
2640         if (parentObjectUnignored()->ariaRoleAttribute() == GroupRole)
2641             return MenuButtonRole;
2642         if (parentObjectUnignored()->ariaRoleAttribute() == MenuRole)
2643             return MenuItemRole;
2644     }
2645 
2646     return UnknownRole;
2647 }
2648 
ariaRoleAttribute() const2649 AccessibilityRole AccessibilityRenderObject::ariaRoleAttribute() const
2650 {
2651     return m_ariaRole;
2652 }
2653 
updateAccessibilityRole()2654 void AccessibilityRenderObject::updateAccessibilityRole()
2655 {
2656     m_role = determineAccessibilityRole();
2657 }
2658 
determineAccessibilityRole()2659 AccessibilityRole AccessibilityRenderObject::determineAccessibilityRole()
2660 {
2661     if (!m_renderer)
2662         return UnknownRole;
2663 
2664     m_ariaRole = determineAriaRoleAttribute();
2665 
2666     Node* node = m_renderer->node();
2667     AccessibilityRole ariaRole = ariaRoleAttribute();
2668     if (ariaRole != UnknownRole)
2669         return ariaRole;
2670 
2671     if (node && node->isLink()) {
2672         if (m_renderer->isImage())
2673             return ImageMapRole;
2674         return WebCoreLinkRole;
2675     }
2676     if (m_renderer->isListMarker())
2677         return ListMarkerRole;
2678     if (node && node->hasTagName(buttonTag))
2679         return ButtonRole;
2680     if (m_renderer->isText())
2681         return StaticTextRole;
2682     if (m_renderer->isImage()) {
2683         if (node && node->hasTagName(inputTag))
2684             return ButtonRole;
2685         return ImageRole;
2686     }
2687     if (node && node->hasTagName(canvasTag))
2688         return ImageRole;
2689 
2690     if (m_renderer->isRenderView())
2691         return WebAreaRole;
2692 
2693     if (m_renderer->isTextField())
2694         return TextFieldRole;
2695 
2696     if (m_renderer->isTextArea())
2697         return TextAreaRole;
2698 
2699     if (node && node->hasTagName(inputTag)) {
2700         HTMLInputElement* input = static_cast<HTMLInputElement*>(node);
2701         if (input->inputType() == HTMLInputElement::CHECKBOX)
2702             return CheckBoxRole;
2703         if (input->inputType() == HTMLInputElement::RADIO)
2704             return RadioButtonRole;
2705         if (input->isTextButton())
2706             return ButtonRole;
2707     }
2708 
2709     if (node && node->hasTagName(buttonTag))
2710         return ButtonRole;
2711 
2712     if (isFileUploadButton())
2713         return ButtonRole;
2714 
2715     if (m_renderer->isMenuList())
2716         return PopUpButtonRole;
2717 
2718     if (headingLevel())
2719         return HeadingRole;
2720 
2721     if (node && node->hasTagName(ddTag))
2722         return DefinitionListDefinitionRole;
2723 
2724     if (node && node->hasTagName(dtTag))
2725         return DefinitionListTermRole;
2726 
2727     if (node && (node->hasTagName(rpTag) || node->hasTagName(rtTag)))
2728         return AnnotationRole;
2729 
2730     if (m_renderer->isBlockFlow() || (node && node->hasTagName(labelTag)))
2731         return GroupRole;
2732 
2733     return UnknownRole;
2734 }
2735 
orientation() const2736 AccessibilityOrientation AccessibilityRenderObject::orientation() const
2737 {
2738     const AtomicString& ariaOrientation = getAttribute(aria_orientationAttr).string();
2739     if (equalIgnoringCase(ariaOrientation, "horizontal"))
2740         return AccessibilityOrientationHorizontal;
2741     if (equalIgnoringCase(ariaOrientation, "vertical"))
2742         return AccessibilityOrientationVertical;
2743 
2744     return AccessibilityObject::orientation();
2745 }
2746 
isPresentationalChildOfAriaRole() const2747 bool AccessibilityRenderObject::isPresentationalChildOfAriaRole() const
2748 {
2749     // Walk the parent chain looking for a parent that has presentational children
2750     AccessibilityObject* parent;
2751     for (parent = parentObject(); parent && !parent->ariaRoleHasPresentationalChildren(); parent = parent->parentObject())
2752     { }
2753 
2754     return parent;
2755 }
2756 
ariaRoleHasPresentationalChildren() const2757 bool AccessibilityRenderObject::ariaRoleHasPresentationalChildren() const
2758 {
2759     switch (m_ariaRole) {
2760     case ButtonRole:
2761     case SliderRole:
2762     case ImageRole:
2763     case ProgressIndicatorRole:
2764     //case SeparatorRole:
2765         return true;
2766     default:
2767         return false;
2768     }
2769 }
2770 
canSetFocusAttribute() const2771 bool AccessibilityRenderObject::canSetFocusAttribute() const
2772 {
2773     ASSERT(m_renderer);
2774     Node* node = m_renderer->node();
2775 
2776     // NOTE: It would be more accurate to ask the document whether setFocusedNode() would
2777     // do anything.  For example, setFocusedNode() will do nothing if the current focused
2778     // node will not relinquish the focus.
2779     if (!node || !node->isElementNode())
2780         return false;
2781 
2782     if (!static_cast<Element*>(node)->isEnabledFormControl())
2783         return false;
2784 
2785     switch (roleValue()) {
2786     case WebCoreLinkRole:
2787     case ImageMapLinkRole:
2788     case TextFieldRole:
2789     case TextAreaRole:
2790     case ButtonRole:
2791     case PopUpButtonRole:
2792     case CheckBoxRole:
2793     case RadioButtonRole:
2794     case SliderRole:
2795         return true;
2796     default:
2797         return false;
2798     }
2799 }
2800 
canSetExpandedAttribute() const2801 bool AccessibilityRenderObject::canSetExpandedAttribute() const
2802 {
2803     // An object can be expanded if it aria-expanded is true or false.
2804     String ariaExpanded = getAttribute(aria_expandedAttr).string();
2805     return equalIgnoringCase(ariaExpanded, "true") || equalIgnoringCase(ariaExpanded, "false");
2806 }
2807 
canSetValueAttribute() const2808 bool AccessibilityRenderObject::canSetValueAttribute() const
2809 {
2810     if (equalIgnoringCase(getAttribute(aria_readonlyAttr).string(), "true"))
2811         return false;
2812 
2813     // Any node could be contenteditable, so isReadOnly should be relied upon
2814     // for this information for all elements.
2815     return isProgressIndicator() || isSlider() || !isReadOnly();
2816 }
2817 
canSetTextRangeAttributes() const2818 bool AccessibilityRenderObject::canSetTextRangeAttributes() const
2819 {
2820     return isTextControl();
2821 }
2822 
contentChanged()2823 void AccessibilityRenderObject::contentChanged()
2824 {
2825     // If this element supports ARIA live regions, then notify the AT of changes.
2826     for (RenderObject* renderParent = m_renderer->parent(); renderParent; renderParent = renderParent->parent()) {
2827         AccessibilityObject* parent = m_renderer->document()->axObjectCache()->get(renderParent);
2828         if (!parent)
2829             continue;
2830 
2831         // If we find a parent that has ARIA live region on, send the notification and stop processing.
2832         // The spec does not talk about nested live regions.
2833         if (parent->supportsARIALiveRegion()) {
2834             axObjectCache()->postNotification(renderParent, AXObjectCache::AXLiveRegionChanged, true);
2835             break;
2836         }
2837     }
2838 }
2839 
childrenChanged()2840 void AccessibilityRenderObject::childrenChanged()
2841 {
2842     // this method is meant as a quick way of marking dirty
2843     // a portion of the accessibility tree
2844 
2845     if (!m_renderer)
2846         return;
2847 
2848     // Go up the render parent chain, marking children as dirty.
2849     // We can't rely on the accessibilityParent() because it may not exist and we must not create an AX object here either
2850     // At the same time, process ARIA live region changes.
2851     for (RenderObject* renderParent = m_renderer; renderParent; renderParent = renderParent->parent()) {
2852         AccessibilityObject* parent = m_renderer->document()->axObjectCache()->get(renderParent);
2853         if (!parent || !parent->isAccessibilityRenderObject())
2854             continue;
2855 
2856         AccessibilityRenderObject* axParent = static_cast<AccessibilityRenderObject*>(parent);
2857         // Only do work if the children haven't been marked dirty. This has the effect of blocking
2858         // future live region change notifications until the AX tree has been accessed again. This
2859         // is a good performance win for all parties.
2860         if (!axParent->needsToUpdateChildren()) {
2861             axParent->setNeedsToUpdateChildren();
2862 
2863             // If this element supports ARIA live regions, then notify the AT of changes.
2864             if (axParent->supportsARIALiveRegion())
2865                 axObjectCache()->postNotification(renderParent, AXObjectCache::AXLiveRegionChanged, true);
2866         }
2867     }
2868 }
2869 
canHaveChildren() const2870 bool AccessibilityRenderObject::canHaveChildren() const
2871 {
2872     if (!m_renderer)
2873         return false;
2874 
2875     // Elements that should not have children
2876     switch (roleValue()) {
2877     case ImageRole:
2878     case ButtonRole:
2879     case PopUpButtonRole:
2880     case CheckBoxRole:
2881     case RadioButtonRole:
2882     case TabRole:
2883     case StaticTextRole:
2884     case ListBoxOptionRole:
2885     case ScrollBarRole:
2886         return false;
2887     default:
2888         return true;
2889     }
2890 }
2891 
children()2892 const AccessibilityObject::AccessibilityChildrenVector& AccessibilityRenderObject::children()
2893 {
2894     if (m_childrenDirty) {
2895         clearChildren();
2896         m_childrenDirty = false;
2897     }
2898 
2899     if (!m_haveChildren)
2900         addChildren();
2901     return m_children;
2902 }
2903 
addChildren()2904 void AccessibilityRenderObject::addChildren()
2905 {
2906     // If the need to add more children in addition to existing children arises,
2907     // childrenChanged should have been called, leaving the object with no children.
2908     ASSERT(!m_haveChildren);
2909 
2910     // nothing to add if there is no RenderObject
2911     if (!m_renderer)
2912         return;
2913 
2914     m_haveChildren = true;
2915 
2916     if (!canHaveChildren())
2917         return;
2918 
2919     // add all unignored acc children
2920     for (RefPtr<AccessibilityObject> obj = firstChild(); obj; obj = obj->nextSibling()) {
2921         if (obj->accessibilityIsIgnored()) {
2922             if (!obj->hasChildren())
2923                 obj->addChildren();
2924             AccessibilityChildrenVector children = obj->children();
2925             unsigned length = children.size();
2926             for (unsigned i = 0; i < length; ++i)
2927                 m_children.append(children[i]);
2928         } else
2929             m_children.append(obj);
2930     }
2931 
2932     // for a RenderImage, add the <area> elements as individual accessibility objects
2933     if (m_renderer->isRenderImage()) {
2934         HTMLMapElement* map = toRenderImage(m_renderer)->imageMap();
2935         if (map) {
2936             for (Node* current = map->firstChild(); current; current = current->traverseNextNode(map)) {
2937 
2938                 // add an <area> element for this child if it has a link
2939                 if (current->hasTagName(areaTag) && current->isLink()) {
2940                     AccessibilityImageMapLink* areaObject = static_cast<AccessibilityImageMapLink*>(m_renderer->document()->axObjectCache()->getOrCreate(ImageMapLinkRole));
2941                     areaObject->setHTMLAreaElement(static_cast<HTMLAreaElement*>(current));
2942                     areaObject->setHTMLMapElement(map);
2943                     areaObject->setParent(this);
2944 
2945                     m_children.append(areaObject);
2946                 }
2947             }
2948         }
2949     }
2950 }
2951 
ariaLiveRegionStatus() const2952 const AtomicString& AccessibilityRenderObject::ariaLiveRegionStatus() const
2953 {
2954     DEFINE_STATIC_LOCAL(const AtomicString, liveRegionStatusAssertive, ("assertive"));
2955     DEFINE_STATIC_LOCAL(const AtomicString, liveRegionStatusPolite, ("polite"));
2956     DEFINE_STATIC_LOCAL(const AtomicString, liveRegionStatusOff, ("off"));
2957 
2958     const AtomicString& liveRegionStatus = getAttribute(aria_liveAttr);
2959     // These roles have implicit live region status.
2960     if (liveRegionStatus.isEmpty()) {
2961         switch (roleValue()) {
2962         case ApplicationAlertDialogRole:
2963         case ApplicationAlertRole:
2964             return liveRegionStatusAssertive;
2965         case ApplicationLogRole:
2966         case ApplicationStatusRole:
2967             return liveRegionStatusPolite;
2968         case ApplicationTimerRole:
2969             return liveRegionStatusOff;
2970         default:
2971             break;
2972         }
2973     }
2974 
2975     return liveRegionStatus;
2976 }
2977 
ariaLiveRegionRelevant() const2978 const AtomicString& AccessibilityRenderObject::ariaLiveRegionRelevant() const
2979 {
2980     DEFINE_STATIC_LOCAL(const AtomicString, defaultLiveRegionRelevant, ("additions text"));
2981     const AtomicString& relevant = getAttribute(aria_relevantAttr);
2982 
2983     // Default aria-relevant = "additions text".
2984     if (relevant.isEmpty())
2985         return defaultLiveRegionRelevant;
2986 
2987     return relevant;
2988 }
2989 
ariaLiveRegionAtomic() const2990 bool AccessibilityRenderObject::ariaLiveRegionAtomic() const
2991 {
2992     return elementAttributeValue(aria_atomicAttr);
2993 }
2994 
ariaLiveRegionBusy() const2995 bool AccessibilityRenderObject::ariaLiveRegionBusy() const
2996 {
2997     return elementAttributeValue(aria_busyAttr);
2998 }
2999 
ariaSelectedRows(AccessibilityChildrenVector & result)3000 void AccessibilityRenderObject::ariaSelectedRows(AccessibilityChildrenVector& result)
3001 {
3002     // Get all the rows.
3003     AccessibilityChildrenVector allRows;
3004     ariaTreeRows(allRows);
3005 
3006     // Determine which rows are selected.
3007     bool isMulti = isMultiSelectable();
3008 
3009     // Prefer active descendant over aria-selected.
3010     AccessibilityObject* activeDesc = activeDescendant();
3011     if (activeDesc && (activeDesc->isTreeItem() || activeDesc->isTableRow())) {
3012         result.append(activeDesc);
3013         if (!isMulti)
3014             return;
3015     }
3016 
3017     unsigned count = allRows.size();
3018     for (unsigned k = 0; k < count; ++k) {
3019         if (allRows[k]->isSelected()) {
3020             result.append(allRows[k]);
3021             if (!isMulti)
3022                 break;
3023         }
3024     }
3025 }
3026 
ariaListboxSelectedChildren(AccessibilityChildrenVector & result)3027 void AccessibilityRenderObject::ariaListboxSelectedChildren(AccessibilityChildrenVector& result)
3028 {
3029     AccessibilityObject* child = firstChild();
3030 
3031     Element* element = static_cast<Element*>(renderer()->node());
3032     if (!element || !element->isElementNode()) // do this check to ensure safety of static_cast above
3033         return;
3034 
3035     bool isMulti = isMultiSelectable();
3036 
3037     while (child) {
3038         // every child should have aria-role option, and if so, check for selected attribute/state
3039         AccessibilityRole ariaRole = child->ariaRoleAttribute();
3040         RenderObject* childRenderer = 0;
3041         if (child->isAccessibilityRenderObject())
3042             childRenderer = static_cast<AccessibilityRenderObject*>(child)->renderer();
3043         if (childRenderer && ariaRole == ListBoxOptionRole) {
3044             Element* childElement = static_cast<Element*>(childRenderer->node());
3045             if (childElement && childElement->isElementNode()) { // do this check to ensure safety of static_cast above
3046                 String selectedAttrString = childElement->getAttribute(aria_selectedAttr).string();
3047                 if (equalIgnoringCase(selectedAttrString, "true")) {
3048                     result.append(child);
3049                     if (isMulti)
3050                         return;
3051                 }
3052             }
3053         }
3054         child = child->nextSibling();
3055     }
3056 }
3057 
selectedChildren(AccessibilityChildrenVector & result)3058 void AccessibilityRenderObject::selectedChildren(AccessibilityChildrenVector& result)
3059 {
3060     ASSERT(result.isEmpty());
3061 
3062     // only listboxes should be asked for their selected children.
3063     AccessibilityRole role = roleValue();
3064     if (role == ListBoxRole) // native list boxes would be AccessibilityListBoxes, so only check for aria list boxes
3065         ariaListboxSelectedChildren(result);
3066     else if (role == TreeRole || role == TreeGridRole || role == TableRole)
3067         ariaSelectedRows(result);
3068 }
3069 
ariaListboxVisibleChildren(AccessibilityChildrenVector & result)3070 void AccessibilityRenderObject::ariaListboxVisibleChildren(AccessibilityChildrenVector& result)
3071 {
3072     if (!hasChildren())
3073         addChildren();
3074 
3075     unsigned length = m_children.size();
3076     for (unsigned i = 0; i < length; i++) {
3077         if (!m_children[i]->isOffScreen())
3078             result.append(m_children[i]);
3079     }
3080 }
3081 
visibleChildren(AccessibilityChildrenVector & result)3082 void AccessibilityRenderObject::visibleChildren(AccessibilityChildrenVector& result)
3083 {
3084     ASSERT(result.isEmpty());
3085 
3086     // only listboxes are asked for their visible children.
3087     if (ariaRoleAttribute() != ListBoxRole) { // native list boxes would be AccessibilityListBoxes, so only check for aria list boxes
3088         ASSERT_NOT_REACHED();
3089         return;
3090     }
3091     return ariaListboxVisibleChildren(result);
3092 }
3093 
tabChildren(AccessibilityChildrenVector & result)3094 void AccessibilityRenderObject::tabChildren(AccessibilityChildrenVector& result)
3095 {
3096     ASSERT(roleValue() == TabListRole);
3097 
3098     unsigned length = m_children.size();
3099     for (unsigned i = 0; i < length; ++i) {
3100         if (m_children[i]->isTabItem())
3101             result.append(m_children[i]);
3102     }
3103 }
3104 
actionVerb() const3105 const String& AccessibilityRenderObject::actionVerb() const
3106 {
3107     // FIXME: Need to add verbs for select elements.
3108     DEFINE_STATIC_LOCAL(const String, buttonAction, (AXButtonActionVerb()));
3109     DEFINE_STATIC_LOCAL(const String, textFieldAction, (AXTextFieldActionVerb()));
3110     DEFINE_STATIC_LOCAL(const String, radioButtonAction, (AXRadioButtonActionVerb()));
3111     DEFINE_STATIC_LOCAL(const String, checkedCheckBoxAction, (AXCheckedCheckBoxActionVerb()));
3112     DEFINE_STATIC_LOCAL(const String, uncheckedCheckBoxAction, (AXUncheckedCheckBoxActionVerb()));
3113     DEFINE_STATIC_LOCAL(const String, linkAction, (AXLinkActionVerb()));
3114     DEFINE_STATIC_LOCAL(const String, noAction, ());
3115 
3116     switch (roleValue()) {
3117     case ButtonRole:
3118         return buttonAction;
3119     case TextFieldRole:
3120     case TextAreaRole:
3121         return textFieldAction;
3122     case RadioButtonRole:
3123         return radioButtonAction;
3124     case CheckBoxRole:
3125         return isChecked() ? checkedCheckBoxAction : uncheckedCheckBoxAction;
3126     case LinkRole:
3127     case WebCoreLinkRole:
3128         return linkAction;
3129     default:
3130         return noAction;
3131     }
3132 }
3133 
updateBackingStore()3134 void AccessibilityRenderObject::updateBackingStore()
3135 {
3136     if (!m_renderer)
3137         return;
3138 
3139     // Updating layout may delete m_renderer and this object.
3140     m_renderer->document()->updateLayoutIgnorePendingStylesheets();
3141 }
3142 
isLinkable(const AccessibilityRenderObject & object)3143 static bool isLinkable(const AccessibilityRenderObject& object)
3144 {
3145     if (!object.renderer())
3146         return false;
3147 
3148     // See https://wiki.mozilla.org/Accessibility/AT-Windows-API for the elements
3149     // Mozilla considers linkable.
3150     return object.isLink() || object.isImage() || object.renderer()->isText();
3151 }
3152 
stringValueForMSAA() const3153 String AccessibilityRenderObject::stringValueForMSAA() const
3154 {
3155     if (isLinkable(*this)) {
3156         Element* anchor = anchorElement();
3157         if (anchor && anchor->hasTagName(aTag))
3158             return static_cast<HTMLAnchorElement*>(anchor)->href();
3159     }
3160 
3161     return stringValue();
3162 }
3163 
isLinked() const3164 bool AccessibilityRenderObject::isLinked() const
3165 {
3166     if (!isLinkable(*this))
3167         return false;
3168 
3169     Element* anchor = anchorElement();
3170     if (!anchor || !anchor->hasTagName(aTag))
3171         return false;
3172 
3173     return !static_cast<HTMLAnchorElement*>(anchor)->href().isEmpty();
3174 }
3175 
nameForMSAA() const3176 String AccessibilityRenderObject::nameForMSAA() const
3177 {
3178     if (m_renderer && m_renderer->isText())
3179         return textUnderElement();
3180 
3181     return title();
3182 }
3183 
shouldReturnTagNameAsRoleForMSAA(const Element & element)3184 static bool shouldReturnTagNameAsRoleForMSAA(const Element& element)
3185 {
3186     // See "document structure",
3187     // https://wiki.mozilla.org/Accessibility/AT-Windows-API
3188     // FIXME: Add the other tag names that should be returned as the role.
3189     return element.hasTagName(h1Tag) || element.hasTagName(h2Tag)
3190         || element.hasTagName(h3Tag) || element.hasTagName(h4Tag)
3191         || element.hasTagName(h5Tag) || element.hasTagName(h6Tag);
3192 }
3193 
stringRoleForMSAA() const3194 String AccessibilityRenderObject::stringRoleForMSAA() const
3195 {
3196     if (!m_renderer)
3197         return String();
3198 
3199     Node* node = m_renderer->node();
3200     if (!node || !node->isElementNode())
3201         return String();
3202 
3203     Element* element = static_cast<Element*>(node);
3204     if (!shouldReturnTagNameAsRoleForMSAA(*element))
3205         return String();
3206 
3207     return element->tagName();
3208 }
3209 
positionalDescriptionForMSAA() const3210 String AccessibilityRenderObject::positionalDescriptionForMSAA() const
3211 {
3212     // See "positional descriptions",
3213     // https://wiki.mozilla.org/Accessibility/AT-Windows-API
3214     if (isHeading())
3215         return "L" + String::number(headingLevel());
3216 
3217     // FIXME: Add positional descriptions for other elements.
3218     return String();
3219 }
3220 
descriptionForMSAA() const3221 String AccessibilityRenderObject::descriptionForMSAA() const
3222 {
3223     String description = positionalDescriptionForMSAA();
3224     if (!description.isEmpty())
3225         return description;
3226 
3227     description = accessibilityDescription();
3228     if (!description.isEmpty()) {
3229         // From the Mozilla MSAA implementation:
3230         // "Signal to screen readers that this description is speakable and is not
3231         // a formatted positional information description. Don't localize the
3232         // 'Description: ' part of this string, it will be parsed out by assistive
3233         // technologies."
3234         return "Description: " + description;
3235     }
3236 
3237     return String();
3238 }
3239 
msaaRoleForRenderer(const RenderObject * renderer)3240 static AccessibilityRole msaaRoleForRenderer(const RenderObject* renderer)
3241 {
3242     if (!renderer)
3243         return UnknownRole;
3244 
3245     if (renderer->isText())
3246         return EditableTextRole;
3247 
3248     if (renderer->isListItem())
3249         return ListItemRole;
3250 
3251     return UnknownRole;
3252 }
3253 
roleValueForMSAA() const3254 AccessibilityRole AccessibilityRenderObject::roleValueForMSAA() const
3255 {
3256     if (m_roleForMSAA != UnknownRole)
3257         return m_roleForMSAA;
3258 
3259     m_roleForMSAA = msaaRoleForRenderer(m_renderer);
3260 
3261     if (m_roleForMSAA == UnknownRole)
3262         m_roleForMSAA = roleValue();
3263 
3264     return m_roleForMSAA;
3265 }
3266 
3267 } // namespace WebCore
3268