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