Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / accessibility / AXObject.cpp
1 /*
2  * Copyright (C) 2008, 2009, 2011 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 "core/accessibility/AXObject.h"
31
32 #include "core/accessibility/AXObjectCache.h"
33 #include "core/dom/NodeTraversal.h"
34 #include "core/editing/VisibleUnits.h"
35 #include "core/editing/htmlediting.h"
36 #include "core/frame/LocalFrame.h"
37 #include "core/rendering/RenderListItem.h"
38 #include "core/rendering/RenderTheme.h"
39 #include "core/rendering/RenderView.h"
40 #include "platform/UserGestureIndicator.h"
41 #include "platform/text/PlatformLocale.h"
42 #include "wtf/StdLibExtras.h"
43 #include "wtf/text/WTFString.h"
44
45 using blink::WebLocalizedString;
46
47 namespace blink {
48
49 using namespace HTMLNames;
50
51 typedef HashMap<String, AccessibilityRole, CaseFoldingHash> ARIARoleMap;
52
53 struct RoleEntry {
54     String ariaRole;
55     AccessibilityRole webcoreRole;
56 };
57
58 static ARIARoleMap* createARIARoleMap()
59 {
60     const RoleEntry roles[] = {
61         { "alert", AlertRole },
62         { "alertdialog", AlertDialogRole },
63         { "application", ApplicationRole },
64         { "article", ArticleRole },
65         { "banner", BannerRole },
66         { "button", ButtonRole },
67         { "checkbox", CheckBoxRole },
68         { "complementary", ComplementaryRole },
69         { "contentinfo", ContentInfoRole },
70         { "dialog", DialogRole },
71         { "directory", DirectoryRole },
72         { "grid", GridRole },
73         { "gridcell", CellRole },
74         { "columnheader", ColumnHeaderRole },
75         { "combobox", ComboBoxRole },
76         { "definition", DefinitionRole },
77         { "document", DocumentRole },
78         { "rowheader", RowHeaderRole },
79         { "group", GroupRole },
80         { "heading", HeadingRole },
81         { "img", ImageRole },
82         { "link", LinkRole },
83         { "list", ListRole },
84         { "listitem", ListItemRole },
85         { "listbox", ListBoxRole },
86         { "log", LogRole },
87         // "option" isn't here because it may map to different roles depending on the parent element's role
88         { "main", MainRole },
89         { "marquee", MarqueeRole },
90         { "math", MathRole },
91         { "menu", MenuRole },
92         { "menubar", MenuBarRole },
93         { "menuitem", MenuItemRole },
94         { "menuitemcheckbox", MenuItemRole },
95         { "menuitemradio", MenuItemRole },
96         { "note", NoteRole },
97         { "navigation", NavigationRole },
98         { "option", ListBoxOptionRole },
99         { "presentation", PresentationalRole },
100         { "progressbar", ProgressIndicatorRole },
101         { "radio", RadioButtonRole },
102         { "radiogroup", RadioGroupRole },
103         { "region", RegionRole },
104         { "row", RowRole },
105         { "scrollbar", ScrollBarRole },
106         { "search", SearchRole },
107         { "separator", SplitterRole },
108         { "slider", SliderRole },
109         { "spinbutton", SpinButtonRole },
110         { "status", StatusRole },
111         { "tab", TabRole },
112         { "tablist", TabListRole },
113         { "tabpanel", TabPanelRole },
114         { "text", StaticTextRole },
115         { "textbox", TextAreaRole },
116         { "timer", TimerRole },
117         { "toolbar", ToolbarRole },
118         { "tooltip", UserInterfaceTooltipRole },
119         { "tree", TreeRole },
120         { "treegrid", TreeGridRole },
121         { "treeitem", TreeItemRole }
122     };
123     ARIARoleMap* roleMap = new ARIARoleMap;
124
125     for (size_t i = 0; i < WTF_ARRAY_LENGTH(roles); ++i)
126         roleMap->set(roles[i].ariaRole, roles[i].webcoreRole);
127     return roleMap;
128 }
129
130 AXObject::AXObject()
131     : m_id(0)
132     , m_haveChildren(false)
133     , m_role(UnknownRole)
134     , m_lastKnownIsIgnoredValue(DefaultBehavior)
135     , m_detached(false)
136 {
137 }
138
139 AXObject::~AXObject()
140 {
141     ASSERT(isDetached());
142 }
143
144 void AXObject::detach()
145 {
146     // Clear any children and call detachFromParent on them so that
147     // no children are left with dangling pointers to their parent.
148     clearChildren();
149
150     m_detached = true;
151 }
152
153 bool AXObject::isDetached() const
154 {
155     return m_detached;
156 }
157
158 AXObjectCache* AXObject::axObjectCache() const
159 {
160     Document* doc = document();
161     if (doc)
162         return doc->axObjectCache();
163     return 0;
164 }
165
166 bool AXObject::isARIATextControl() const
167 {
168     return ariaRoleAttribute() == TextAreaRole || ariaRoleAttribute() == TextFieldRole;
169 }
170
171 bool AXObject::isButton() const
172 {
173     AccessibilityRole role = roleValue();
174
175     return role == ButtonRole || role == PopUpButtonRole || role == ToggleButtonRole;
176 }
177
178 bool AXObject::isLandmarkRelated() const
179 {
180     switch (roleValue()) {
181     case ApplicationRole:
182     case ArticleRole:
183     case BannerRole:
184     case ComplementaryRole:
185     case ContentInfoRole:
186     case FooterRole:
187     case MainRole:
188     case NavigationRole:
189     case RegionRole:
190     case SearchRole:
191         return true;
192     default:
193         return false;
194     }
195 }
196
197 bool AXObject::isMenuRelated() const
198 {
199     switch (roleValue()) {
200     case MenuRole:
201     case MenuBarRole:
202     case MenuButtonRole:
203     case MenuItemRole:
204         return true;
205     default:
206         return false;
207     }
208 }
209
210 bool AXObject::isTextControl() const
211 {
212     switch (roleValue()) {
213     case TextAreaRole:
214     case TextFieldRole:
215     case ComboBoxRole:
216         return true;
217     default:
218         return false;
219     }
220 }
221
222 bool AXObject::isClickable() const
223 {
224     switch (roleValue()) {
225     case ButtonRole:
226     case CheckBoxRole:
227     case ColorWellRole:
228     case ComboBoxRole:
229     case EditableTextRole:
230     case ImageMapLinkRole:
231     case LinkRole:
232     case ListBoxOptionRole:
233     case MenuButtonRole:
234     case PopUpButtonRole:
235     case RadioButtonRole:
236     case TabRole:
237     case TextAreaRole:
238     case TextFieldRole:
239     case ToggleButtonRole:
240         return true;
241     default:
242         return false;
243     }
244 }
245
246 bool AXObject::isExpanded() const
247 {
248     if (equalIgnoringCase(getAttribute(aria_expandedAttr), "true"))
249         return true;
250
251     return false;
252 }
253
254 bool AXObject::accessibilityIsIgnored() const
255 {
256     AXObjectCache* cache = axObjectCache();
257     if (!cache)
258         return true;
259
260     AXComputedObjectAttributeCache* attributeCache = cache->computedObjectAttributeCache();
261     if (attributeCache) {
262         AXObjectInclusion ignored = attributeCache->getIgnored(axObjectID());
263         switch (ignored) {
264         case IgnoreObject:
265             return true;
266         case IncludeObject:
267             return false;
268         case DefaultBehavior:
269             break;
270         }
271     }
272
273     bool result = computeAccessibilityIsIgnored();
274
275     if (attributeCache)
276         attributeCache->setIgnored(axObjectID(), result ? IgnoreObject : IncludeObject);
277
278     return result;
279 }
280
281 bool AXObject::accessibilityIsIgnoredByDefault() const
282 {
283     return defaultObjectInclusion() == IgnoreObject;
284 }
285
286 AXObjectInclusion AXObject::accessibilityPlatformIncludesObject() const
287 {
288     if (isMenuListPopup() || isMenuListOption())
289         return IncludeObject;
290
291     return DefaultBehavior;
292 }
293
294 AXObjectInclusion AXObject::defaultObjectInclusion() const
295 {
296     if (isInertOrAriaHidden())
297         return IgnoreObject;
298
299     if (isPresentationalChildOfAriaRole())
300         return IgnoreObject;
301
302     return accessibilityPlatformIncludesObject();
303 }
304
305 bool AXObject::isInertOrAriaHidden() const
306 {
307     bool mightBeInInertSubtree = true;
308     for (const AXObject* object = this; object; object = object->parentObject()) {
309         if (equalIgnoringCase(object->getAttribute(aria_hiddenAttr), "true"))
310             return true;
311         if (mightBeInInertSubtree && object->node()) {
312             if (object->node()->isInert())
313                 return true;
314             mightBeInInertSubtree = false;
315         }
316     }
317
318     return false;
319 }
320
321 bool AXObject::lastKnownIsIgnoredValue()
322 {
323     if (m_lastKnownIsIgnoredValue == DefaultBehavior)
324         m_lastKnownIsIgnoredValue = accessibilityIsIgnored() ? IgnoreObject : IncludeObject;
325
326     return m_lastKnownIsIgnoredValue == IgnoreObject;
327 }
328
329 void AXObject::setLastKnownIsIgnoredValue(bool isIgnored)
330 {
331     m_lastKnownIsIgnoredValue = isIgnored ? IgnoreObject : IncludeObject;
332 }
333
334 // Lacking concrete evidence of orientation, horizontal means width > height. vertical is height > width;
335 AccessibilityOrientation AXObject::orientation() const
336 {
337     LayoutRect bounds = elementRect();
338     if (bounds.size().width() > bounds.size().height())
339         return AccessibilityOrientationHorizontal;
340     if (bounds.size().height() > bounds.size().width())
341         return AccessibilityOrientationVertical;
342
343     // A tie goes to horizontal.
344     return AccessibilityOrientationHorizontal;
345 }
346
347 static String queryString(WebLocalizedString::Name name)
348 {
349     return Locale::defaultLocale().queryString(name);
350 }
351
352 String AXObject::actionVerb() const
353 {
354     // FIXME: Need to add verbs for select elements.
355
356     switch (roleValue()) {
357     case ButtonRole:
358     case ToggleButtonRole:
359         return queryString(WebLocalizedString::AXButtonActionVerb);
360     case TextFieldRole:
361     case TextAreaRole:
362         return queryString(WebLocalizedString::AXTextFieldActionVerb);
363     case RadioButtonRole:
364         return queryString(WebLocalizedString::AXRadioButtonActionVerb);
365     case CheckBoxRole:
366         return queryString(isChecked() ? WebLocalizedString::AXCheckedCheckBoxActionVerb : WebLocalizedString::AXUncheckedCheckBoxActionVerb);
367     case LinkRole:
368         return queryString(WebLocalizedString::AXLinkActionVerb);
369     case PopUpButtonRole:
370         // FIXME: Implement.
371         return String();
372     case MenuListPopupRole:
373         // FIXME: Implement.
374         return String();
375     default:
376         return emptyString();
377     }
378 }
379
380 AccessibilityButtonState AXObject::checkboxOrRadioValue() const
381 {
382     // If this is a real checkbox or radio button, AXRenderObject will handle.
383     // If it's an ARIA checkbox or radio, the aria-checked attribute should be used.
384
385     const AtomicString& result = getAttribute(aria_checkedAttr);
386     if (equalIgnoringCase(result, "true"))
387         return ButtonStateOn;
388     if (equalIgnoringCase(result, "mixed"))
389         return ButtonStateMixed;
390
391     return ButtonStateOff;
392 }
393
394 bool AXObject::ariaIsMultiline() const
395 {
396     return equalIgnoringCase(getAttribute(aria_multilineAttr), "true");
397 }
398
399 bool AXObject::ariaPressedIsPresent() const
400 {
401     return !getAttribute(aria_pressedAttr).isEmpty();
402 }
403
404 bool AXObject::supportsARIAAttributes() const
405 {
406     return supportsARIALiveRegion()
407         || supportsARIADragging()
408         || supportsARIADropping()
409         || supportsARIAFlowTo()
410         || supportsARIAOwns()
411         || hasAttribute(aria_labelAttr);
412 }
413
414 bool AXObject::supportsRangeValue() const
415 {
416     return isProgressIndicator()
417         || isSlider()
418         || isScrollbar()
419         || isSpinButton();
420 }
421
422 void AXObject::ariaTreeRows(AccessibilityChildrenVector& result)
423 {
424     AccessibilityChildrenVector axChildren = children();
425     unsigned count = axChildren.size();
426     for (unsigned k = 0; k < count; ++k) {
427         AXObject* obj = axChildren[k].get();
428
429         // Add tree items as the rows.
430         if (obj->roleValue() == TreeItemRole)
431             result.append(obj);
432
433         // Now see if this item also has rows hiding inside of it.
434         obj->ariaTreeRows(result);
435     }
436 }
437
438 bool AXObject::supportsARIALiveRegion() const
439 {
440     const AtomicString& liveRegion = ariaLiveRegionStatus();
441     return equalIgnoringCase(liveRegion, "polite") || equalIgnoringCase(liveRegion, "assertive");
442 }
443
444 void AXObject::markCachedElementRectDirty() const
445 {
446     for (unsigned i = 0; i < m_children.size(); ++i)
447         m_children[i].get()->markCachedElementRectDirty();
448 }
449
450 IntPoint AXObject::clickPoint()
451 {
452     LayoutRect rect = elementRect();
453     return roundedIntPoint(LayoutPoint(rect.x() + rect.width() / 2, rect.y() + rect.height() / 2));
454 }
455
456 IntRect AXObject::boundingBoxForQuads(RenderObject* obj, const Vector<FloatQuad>& quads)
457 {
458     ASSERT(obj);
459     if (!obj)
460         return IntRect();
461
462     size_t count = quads.size();
463     if (!count)
464         return IntRect();
465
466     IntRect result;
467     for (size_t i = 0; i < count; ++i) {
468         IntRect r = quads[i].enclosingBoundingBox();
469         if (!r.isEmpty()) {
470             if (obj->style()->hasAppearance())
471                 RenderTheme::theme().adjustRepaintRect(obj, r);
472             result.unite(r);
473         }
474     }
475     return result;
476 }
477
478 AXObject* AXObject::elementAccessibilityHitTest(const IntPoint& point) const
479 {
480     // Send the hit test back into the sub-frame if necessary.
481     if (isAttachment()) {
482         Widget* widget = widgetForAttachmentView();
483         // Normalize the point for the widget's bounds.
484         if (widget && widget->isFrameView())
485             return axObjectCache()->getOrCreate(widget)->accessibilityHitTest(IntPoint(point - widget->frameRect().location()));
486     }
487
488     // Check if there are any mock elements that need to be handled.
489     size_t count = m_children.size();
490     for (size_t k = 0; k < count; k++) {
491         if (m_children[k]->isMockObject() && m_children[k]->elementRect().contains(point))
492             return m_children[k]->elementAccessibilityHitTest(point);
493     }
494
495     return const_cast<AXObject*>(this);
496 }
497
498 const AXObject::AccessibilityChildrenVector& AXObject::children()
499 {
500     updateChildrenIfNecessary();
501
502     return m_children;
503 }
504
505 AXObject* AXObject::parentObjectUnignored() const
506 {
507     AXObject* parent;
508     for (parent = parentObject(); parent && parent->accessibilityIsIgnored(); parent = parent->parentObject()) {
509     }
510
511     return parent;
512 }
513
514 AXObject* AXObject::firstAccessibleObjectFromNode(const Node* node)
515 {
516     if (!node)
517         return 0;
518
519     AXObjectCache* cache = node->document().axObjectCache();
520     AXObject* accessibleObject = cache->getOrCreate(node->renderer());
521     while (accessibleObject && accessibleObject->accessibilityIsIgnored()) {
522         node = NodeTraversal::next(*node);
523
524         while (node && !node->renderer())
525             node = NodeTraversal::nextSkippingChildren(*node);
526
527         if (!node)
528             return 0;
529
530         accessibleObject = cache->getOrCreate(node->renderer());
531     }
532
533     return accessibleObject;
534 }
535
536 void AXObject::updateChildrenIfNecessary()
537 {
538     if (!hasChildren())
539         addChildren();
540 }
541
542 void AXObject::clearChildren()
543 {
544     // Some objects have weak pointers to their parents and those associations need to be detached.
545     size_t length = m_children.size();
546     for (size_t i = 0; i < length; i++)
547         m_children[i]->detachFromParent();
548
549     m_children.clear();
550     m_haveChildren = false;
551 }
552
553 AXObject* AXObject::focusedUIElement() const
554 {
555     Document* doc = document();
556     if (!doc)
557         return 0;
558
559     Page* page = doc->page();
560     if (!page)
561         return 0;
562
563     return AXObjectCache::focusedUIElementForPage(page);
564 }
565
566 Document* AXObject::document() const
567 {
568     FrameView* frameView = documentFrameView();
569     if (!frameView)
570         return 0;
571
572     return frameView->frame().document();
573 }
574
575 FrameView* AXObject::documentFrameView() const
576 {
577     const AXObject* object = this;
578     while (object && !object->isAXRenderObject())
579         object = object->parentObject();
580
581     if (!object)
582         return 0;
583
584     return object->documentFrameView();
585 }
586
587 String AXObject::language() const
588 {
589     const AtomicString& lang = getAttribute(langAttr);
590     if (!lang.isEmpty())
591         return lang;
592
593     AXObject* parent = parentObject();
594
595     // as a last resort, fall back to the content language specified in the meta tag
596     if (!parent) {
597         Document* doc = document();
598         if (doc)
599             return doc->contentLanguage();
600         return nullAtom;
601     }
602
603     return parent->language();
604 }
605
606 bool AXObject::hasAttribute(const QualifiedName& attribute) const
607 {
608     Node* elementNode = node();
609     if (!elementNode)
610         return false;
611
612     if (!elementNode->isElementNode())
613         return false;
614
615     Element* element = toElement(elementNode);
616     return element->fastHasAttribute(attribute);
617 }
618
619 const AtomicString& AXObject::getAttribute(const QualifiedName& attribute) const
620 {
621     Node* elementNode = node();
622     if (!elementNode)
623         return nullAtom;
624
625     if (!elementNode->isElementNode())
626         return nullAtom;
627
628     Element* element = toElement(elementNode);
629     return element->fastGetAttribute(attribute);
630 }
631
632 bool AXObject::press() const
633 {
634     Element* actionElem = actionElement();
635     if (!actionElem)
636         return false;
637     UserGestureIndicator gestureIndicator(DefinitelyProcessingNewUserGesture);
638     actionElem->accessKeyAction(true);
639     return true;
640 }
641
642 void AXObject::scrollToMakeVisible() const
643 {
644     IntRect objectRect = pixelSnappedIntRect(elementRect());
645     objectRect.setLocation(IntPoint());
646     scrollToMakeVisibleWithSubFocus(objectRect);
647 }
648
649 // This is a 1-dimensional scroll offset helper function that's applied
650 // separately in the horizontal and vertical directions, because the
651 // logic is the same. The goal is to compute the best scroll offset
652 // in order to make an object visible within a viewport.
653 //
654 // In case the whole object cannot fit, you can specify a
655 // subfocus - a smaller region within the object that should
656 // be prioritized. If the whole object can fit, the subfocus is
657 // ignored.
658 //
659 // Example: the viewport is scrolled to the right just enough
660 // that the object is in view.
661 //   Before:
662 //   +----------Viewport---------+
663 //                         +---Object---+
664 //                         +--SubFocus--+
665 //
666 //   After:
667 //          +----------Viewport---------+
668 //                         +---Object---+
669 //                         +--SubFocus--+
670 //
671 // When constraints cannot be fully satisfied, the min
672 // (left/top) position takes precedence over the max (right/bottom).
673 //
674 // Note that the return value represents the ideal new scroll offset.
675 // This may be out of range - the calling function should clip this
676 // to the available range.
677 static int computeBestScrollOffset(int currentScrollOffset, int subfocusMin, int subfocusMax, int objectMin, int objectMax, int viewportMin, int viewportMax)
678 {
679     int viewportSize = viewportMax - viewportMin;
680
681     // If the focus size is larger than the viewport size, shrink it in the
682     // direction of subfocus.
683     if (objectMax - objectMin > viewportSize) {
684         // Subfocus must be within focus:
685         subfocusMin = std::max(subfocusMin, objectMin);
686         subfocusMax = std::min(subfocusMax, objectMax);
687
688         // Subfocus must be no larger than the viewport size; favor top/left.
689         if (subfocusMax - subfocusMin > viewportSize)
690             subfocusMax = subfocusMin + viewportSize;
691
692         if (subfocusMin + viewportSize > objectMax) {
693             objectMin = objectMax - viewportSize;
694         } else {
695             objectMin = subfocusMin;
696             objectMax = subfocusMin + viewportSize;
697         }
698     }
699
700     // Exit now if the focus is already within the viewport.
701     if (objectMin - currentScrollOffset >= viewportMin
702         && objectMax - currentScrollOffset <= viewportMax)
703         return currentScrollOffset;
704
705     // Scroll left if we're too far to the right.
706     if (objectMax - currentScrollOffset > viewportMax)
707         return objectMax - viewportMax;
708
709     // Scroll right if we're too far to the left.
710     if (objectMin - currentScrollOffset < viewportMin)
711         return objectMin - viewportMin;
712
713     ASSERT_NOT_REACHED();
714
715     // This shouldn't happen.
716     return currentScrollOffset;
717 }
718
719 void AXObject::scrollToMakeVisibleWithSubFocus(const IntRect& subfocus) const
720 {
721     // Search up the parent chain until we find the first one that's scrollable.
722     AXObject* scrollParent = parentObject();
723     ScrollableArea* scrollableArea = 0;
724     while (scrollParent) {
725         scrollableArea = scrollParent->getScrollableAreaIfScrollable();
726         if (scrollableArea)
727             break;
728         scrollParent = scrollParent->parentObject();
729     }
730     if (!scrollableArea)
731         return;
732
733     IntRect objectRect = pixelSnappedIntRect(elementRect());
734     IntPoint scrollPosition = scrollableArea->scrollPosition();
735     IntRect scrollVisibleRect = scrollableArea->visibleContentRect();
736
737     int desiredX = computeBestScrollOffset(
738         scrollPosition.x(),
739         objectRect.x() + subfocus.x(), objectRect.x() + subfocus.maxX(),
740         objectRect.x(), objectRect.maxX(),
741         0, scrollVisibleRect.width());
742     int desiredY = computeBestScrollOffset(
743         scrollPosition.y(),
744         objectRect.y() + subfocus.y(), objectRect.y() + subfocus.maxY(),
745         objectRect.y(), objectRect.maxY(),
746         0, scrollVisibleRect.height());
747
748     scrollParent->scrollTo(IntPoint(desiredX, desiredY));
749
750     // Recursively make sure the scroll parent itself is visible.
751     if (scrollParent->parentObject())
752         scrollParent->scrollToMakeVisible();
753 }
754
755 void AXObject::scrollToGlobalPoint(const IntPoint& globalPoint) const
756 {
757     // Search up the parent chain and create a vector of all scrollable parent objects
758     // and ending with this object itself.
759     Vector<const AXObject*> objects;
760     AXObject* parentObject;
761     for (parentObject = this->parentObject(); parentObject; parentObject = parentObject->parentObject()) {
762         if (parentObject->getScrollableAreaIfScrollable())
763             objects.prepend(parentObject);
764     }
765     objects.append(this);
766
767     // Start with the outermost scrollable (the main window) and try to scroll the
768     // next innermost object to the given point.
769     int offsetX = 0, offsetY = 0;
770     IntPoint point = globalPoint;
771     size_t levels = objects.size() - 1;
772     for (size_t i = 0; i < levels; i++) {
773         const AXObject* outer = objects[i];
774         const AXObject* inner = objects[i + 1];
775
776         ScrollableArea* scrollableArea = outer->getScrollableAreaIfScrollable();
777
778         IntRect innerRect = inner->isAXScrollView() ? pixelSnappedIntRect(inner->parentObject()->elementRect()) : pixelSnappedIntRect(inner->elementRect());
779         IntRect objectRect = innerRect;
780         IntPoint scrollPosition = scrollableArea->scrollPosition();
781
782         // Convert the object rect into local coordinates.
783         objectRect.move(offsetX, offsetY);
784         if (!outer->isAXScrollView())
785             objectRect.move(scrollPosition.x(), scrollPosition.y());
786
787         int desiredX = computeBestScrollOffset(
788             0,
789             objectRect.x(), objectRect.maxX(),
790             objectRect.x(), objectRect.maxX(),
791             point.x(), point.x());
792         int desiredY = computeBestScrollOffset(
793             0,
794             objectRect.y(), objectRect.maxY(),
795             objectRect.y(), objectRect.maxY(),
796             point.y(), point.y());
797         outer->scrollTo(IntPoint(desiredX, desiredY));
798
799         if (outer->isAXScrollView() && !inner->isAXScrollView()) {
800             // If outer object we just scrolled is a scroll view (main window or iframe) but the
801             // inner object is not, keep track of the coordinate transformation to apply to
802             // future nested calculations.
803             scrollPosition = scrollableArea->scrollPosition();
804             offsetX -= (scrollPosition.x() + point.x());
805             offsetY -= (scrollPosition.y() + point.y());
806             point.move(scrollPosition.x() - innerRect.x(), scrollPosition.y() - innerRect.y());
807         } else if (inner->isAXScrollView()) {
808             // Otherwise, if the inner object is a scroll view, reset the coordinate transformation.
809             offsetX = 0;
810             offsetY = 0;
811         }
812     }
813 }
814
815 void AXObject::notifyIfIgnoredValueChanged()
816 {
817     bool isIgnored = accessibilityIsIgnored();
818     if (lastKnownIsIgnoredValue() != isIgnored) {
819         axObjectCache()->childrenChanged(parentObject());
820         setLastKnownIsIgnoredValue(isIgnored);
821     }
822 }
823
824 void AXObject::selectionChanged()
825 {
826     if (AXObject* parent = parentObjectIfExists())
827         parent->selectionChanged();
828 }
829
830 int AXObject::lineForPosition(const VisiblePosition& visiblePos) const
831 {
832     if (visiblePos.isNull() || !node())
833         return -1;
834
835     // If the position is not in the same editable region as this AX object, return -1.
836     Node* containerNode = visiblePos.deepEquivalent().containerNode();
837     if (!containerNode->containsIncludingShadowDOM(node()) && !node()->containsIncludingShadowDOM(containerNode))
838         return -1;
839
840     int lineCount = -1;
841     VisiblePosition currentVisiblePos = visiblePos;
842     VisiblePosition savedVisiblePos;
843
844     // move up until we get to the top
845     // FIXME: This only takes us to the top of the rootEditableElement, not the top of the
846     // top document.
847     do {
848         savedVisiblePos = currentVisiblePos;
849         VisiblePosition prevVisiblePos = previousLinePosition(currentVisiblePos, 0, HasEditableAXRole);
850         currentVisiblePos = prevVisiblePos;
851         ++lineCount;
852     }  while (currentVisiblePos.isNotNull() && !(inSameLine(currentVisiblePos, savedVisiblePos)));
853
854     return lineCount;
855 }
856
857 bool AXObject::isARIAControl(AccessibilityRole ariaRole)
858 {
859     return isARIAInput(ariaRole) || ariaRole == TextAreaRole || ariaRole == ButtonRole
860     || ariaRole == ComboBoxRole || ariaRole == SliderRole;
861 }
862
863 bool AXObject::isARIAInput(AccessibilityRole ariaRole)
864 {
865     return ariaRole == RadioButtonRole || ariaRole == CheckBoxRole || ariaRole == TextFieldRole;
866 }
867
868 AccessibilityRole AXObject::ariaRoleToWebCoreRole(const String& value)
869 {
870     ASSERT(!value.isEmpty());
871
872     static const ARIARoleMap* roleMap = createARIARoleMap();
873
874     Vector<String> roleVector;
875     value.split(' ', roleVector);
876     AccessibilityRole role = UnknownRole;
877     unsigned size = roleVector.size();
878     for (unsigned i = 0; i < size; ++i) {
879         String roleName = roleVector[i];
880         role = roleMap->get(roleName);
881         if (role)
882             return role;
883     }
884
885     return role;
886 }
887
888 AccessibilityRole AXObject::buttonRoleType() const
889 {
890     // If aria-pressed is present, then it should be exposed as a toggle button.
891     // http://www.w3.org/TR/wai-aria/states_and_properties#aria-pressed
892     if (ariaPressedIsPresent())
893         return ToggleButtonRole;
894     if (ariaHasPopup())
895         return PopUpButtonRole;
896     // We don't contemplate RadioButtonRole, as it depends on the input
897     // type.
898
899     return ButtonRole;
900 }
901
902 } // namespace blink