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