Index: Source/core/accessibility/AXObject.cpp |
diff --git a/Source/core/accessibility/AXObject.cpp b/Source/core/accessibility/AXObject.cpp |
deleted file mode 100644 |
index 7594b3baa1e10134854cb8c17eb2ea84a2225654..0000000000000000000000000000000000000000 |
--- a/Source/core/accessibility/AXObject.cpp |
+++ /dev/null |
@@ -1,988 +0,0 @@ |
-/* |
- * Copyright (C) 2008, 2009, 2011 Apple Inc. All rights reserved. |
- * |
- * Redistribution and use in source and binary forms, with or without |
- * modification, are permitted provided that the following conditions |
- * are met: |
- * |
- * 1. Redistributions of source code must retain the above copyright |
- * notice, this list of conditions and the following disclaimer. |
- * 2. Redistributions in binary form must reproduce the above copyright |
- * notice, this list of conditions and the following disclaimer in the |
- * documentation and/or other materials provided with the distribution. |
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of |
- * its contributors may be used to endorse or promote products derived |
- * from this software without specific prior written permission. |
- * |
- * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY |
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
- * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY |
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
- */ |
- |
-#include "config.h" |
-#include "core/accessibility/AXObject.h" |
- |
-#include "core/accessibility/AXObjectCacheImpl.h" |
-#include "core/dom/NodeTraversal.h" |
-#include "core/editing/VisibleUnits.h" |
-#include "core/editing/htmlediting.h" |
-#include "core/frame/LocalFrame.h" |
-#include "core/frame/Settings.h" |
-#include "core/rendering/RenderListItem.h" |
-#include "core/rendering/RenderTheme.h" |
-#include "core/rendering/RenderView.h" |
-#include "platform/UserGestureIndicator.h" |
-#include "platform/text/PlatformLocale.h" |
-#include "wtf/StdLibExtras.h" |
-#include "wtf/text/WTFString.h" |
- |
-using blink::WebLocalizedString; |
- |
-namespace blink { |
- |
-using namespace HTMLNames; |
- |
-typedef HashMap<String, AccessibilityRole, CaseFoldingHash> ARIARoleMap; |
- |
-struct RoleEntry { |
- String ariaRole; |
- AccessibilityRole webcoreRole; |
-}; |
- |
-static ARIARoleMap* createARIARoleMap() |
-{ |
- const RoleEntry roles[] = { |
- { "alert", AlertRole }, |
- { "alertdialog", AlertDialogRole }, |
- { "application", ApplicationRole }, |
- { "article", ArticleRole }, |
- { "banner", BannerRole }, |
- { "button", ButtonRole }, |
- { "checkbox", CheckBoxRole }, |
- { "complementary", ComplementaryRole }, |
- { "contentinfo", ContentInfoRole }, |
- { "dialog", DialogRole }, |
- { "directory", DirectoryRole }, |
- { "grid", GridRole }, |
- { "gridcell", CellRole }, |
- { "columnheader", ColumnHeaderRole }, |
- { "combobox", ComboBoxRole }, |
- { "definition", DefinitionRole }, |
- { "document", DocumentRole }, |
- { "rowheader", RowHeaderRole }, |
- { "form", FormRole }, |
- { "group", GroupRole }, |
- { "heading", HeadingRole }, |
- { "img", ImageRole }, |
- { "link", LinkRole }, |
- { "list", ListRole }, |
- { "listitem", ListItemRole }, |
- { "listbox", ListBoxRole }, |
- { "log", LogRole }, |
- // "option" isn't here because it may map to different roles depending on the parent element's role |
- { "main", MainRole }, |
- { "marquee", MarqueeRole }, |
- { "math", MathRole }, |
- { "menu", MenuRole }, |
- { "menubar", MenuBarRole }, |
- { "menuitem", MenuItemRole }, |
- { "menuitemcheckbox", MenuItemCheckBoxRole }, |
- { "menuitemradio", MenuItemRadioRole }, |
- { "note", NoteRole }, |
- { "navigation", NavigationRole }, |
- { "none", NoneRole }, |
- { "option", ListBoxOptionRole }, |
- { "presentation", PresentationalRole }, |
- { "progressbar", ProgressIndicatorRole }, |
- { "radio", RadioButtonRole }, |
- { "radiogroup", RadioGroupRole }, |
- { "region", RegionRole }, |
- { "row", RowRole }, |
- { "scrollbar", ScrollBarRole }, |
- { "search", SearchRole }, |
- { "separator", SplitterRole }, |
- { "slider", SliderRole }, |
- { "spinbutton", SpinButtonRole }, |
- { "status", StatusRole }, |
- { "tab", TabRole }, |
- { "tablist", TabListRole }, |
- { "tabpanel", TabPanelRole }, |
- { "text", StaticTextRole }, |
- { "textbox", TextAreaRole }, |
- { "timer", TimerRole }, |
- { "toolbar", ToolbarRole }, |
- { "tooltip", UserInterfaceTooltipRole }, |
- { "tree", TreeRole }, |
- { "treegrid", TreeGridRole }, |
- { "treeitem", TreeItemRole } |
- }; |
- ARIARoleMap* roleMap = new ARIARoleMap; |
- |
- for (size_t i = 0; i < WTF_ARRAY_LENGTH(roles); ++i) |
- roleMap->set(roles[i].ariaRole, roles[i].webcoreRole); |
- return roleMap; |
-} |
- |
-AXObject::AXObject() |
- : m_id(0) |
- , m_haveChildren(false) |
- , m_role(UnknownRole) |
- , m_lastKnownIsIgnoredValue(DefaultBehavior) |
- , m_detached(false) |
- , m_parent(0) |
- , m_lastModificationCount(-1) |
- , m_cachedIsIgnored(false) |
- , m_cachedLiveRegionRoot(0) |
-{ |
-} |
- |
-AXObject::~AXObject() |
-{ |
- ASSERT(isDetached()); |
-} |
- |
-void AXObject::detach() |
-{ |
- // Clear any children and call detachFromParent on them so that |
- // no children are left with dangling pointers to their parent. |
- clearChildren(); |
- |
- m_detached = true; |
-} |
- |
-bool AXObject::isDetached() const |
-{ |
- return m_detached; |
-} |
- |
-AXObjectCacheImpl* AXObject::axObjectCache() const |
-{ |
- Document* doc = document(); |
- if (doc) |
- return toAXObjectCacheImpl(doc->axObjectCache()); |
- return 0; |
-} |
- |
-bool AXObject::isARIATextControl() const |
-{ |
- return ariaRoleAttribute() == TextAreaRole || ariaRoleAttribute() == TextFieldRole; |
-} |
- |
-bool AXObject::isButton() const |
-{ |
- AccessibilityRole role = roleValue(); |
- |
- return role == ButtonRole || role == PopUpButtonRole || role == ToggleButtonRole; |
-} |
- |
-bool AXObject::isLandmarkRelated() const |
-{ |
- switch (roleValue()) { |
- case ApplicationRole: |
- case ArticleRole: |
- case BannerRole: |
- case ComplementaryRole: |
- case ContentInfoRole: |
- case FooterRole: |
- case FormRole: |
- case MainRole: |
- case NavigationRole: |
- case RegionRole: |
- case SearchRole: |
- return true; |
- default: |
- return false; |
- } |
-} |
- |
-bool AXObject::isMenuRelated() const |
-{ |
- switch (roleValue()) { |
- case MenuRole: |
- case MenuBarRole: |
- case MenuButtonRole: |
- case MenuItemRole: |
- case MenuItemCheckBoxRole: |
- case MenuItemRadioRole: |
- return true; |
- default: |
- return false; |
- } |
-} |
- |
-bool AXObject::isPasswordFieldAndShouldHideValue() const |
-{ |
- Settings* settings = document()->settings(); |
- if (!settings || settings->accessibilityPasswordValuesEnabled()) |
- return false; |
- |
- return isPasswordField(); |
-} |
- |
-bool AXObject::isTextControl() const |
-{ |
- switch (roleValue()) { |
- case TextAreaRole: |
- case TextFieldRole: |
- case ComboBoxRole: |
- return true; |
- default: |
- return false; |
- } |
-} |
- |
-bool AXObject::isClickable() const |
-{ |
- switch (roleValue()) { |
- case ButtonRole: |
- case CheckBoxRole: |
- case ColorWellRole: |
- case ComboBoxRole: |
- case EditableTextRole: |
- case ImageMapLinkRole: |
- case LinkRole: |
- case ListBoxOptionRole: |
- case MenuButtonRole: |
- case PopUpButtonRole: |
- case RadioButtonRole: |
- case TabRole: |
- case TextAreaRole: |
- case TextFieldRole: |
- case ToggleButtonRole: |
- return true; |
- default: |
- return false; |
- } |
-} |
- |
-bool AXObject::accessibilityIsIgnored() const |
-{ |
- updateCachedAttributeValuesIfNeeded(); |
- return m_cachedIsIgnored; |
-} |
- |
-void AXObject::updateCachedAttributeValuesIfNeeded() const |
-{ |
- AXObjectCacheImpl* cache = axObjectCache(); |
- if (!cache) |
- return; |
- |
- if (cache->modificationCount() == m_lastModificationCount) |
- return; |
- |
- m_lastModificationCount = cache->modificationCount(); |
- m_cachedIsIgnored = computeAccessibilityIsIgnored(); |
- m_cachedLiveRegionRoot = isLiveRegion() ? |
- this : |
- (parentObjectIfExists() ? parentObjectIfExists()->liveRegionRoot() : 0); |
-} |
- |
-bool AXObject::accessibilityIsIgnoredByDefault() const |
-{ |
- return defaultObjectInclusion() == IgnoreObject; |
-} |
- |
-AXObjectInclusion AXObject::accessibilityPlatformIncludesObject() const |
-{ |
- if (isMenuListPopup() || isMenuListOption()) |
- return IncludeObject; |
- |
- return DefaultBehavior; |
-} |
- |
-AXObjectInclusion AXObject::defaultObjectInclusion() const |
-{ |
- if (isInertOrAriaHidden()) |
- return IgnoreObject; |
- |
- if (isPresentationalChildOfAriaRole()) |
- return IgnoreObject; |
- |
- return accessibilityPlatformIncludesObject(); |
-} |
- |
-bool AXObject::isInertOrAriaHidden() const |
-{ |
- bool mightBeInInertSubtree = true; |
- for (const AXObject* object = this; object; object = object->parentObject()) { |
- if (equalIgnoringCase(object->getAttribute(aria_hiddenAttr), "true")) |
- return true; |
- if (mightBeInInertSubtree && object->node()) { |
- if (object->node()->isInert()) |
- return true; |
- mightBeInInertSubtree = false; |
- } |
- } |
- |
- return false; |
-} |
- |
-bool AXObject::lastKnownIsIgnoredValue() |
-{ |
- if (m_lastKnownIsIgnoredValue == DefaultBehavior) |
- m_lastKnownIsIgnoredValue = accessibilityIsIgnored() ? IgnoreObject : IncludeObject; |
- |
- return m_lastKnownIsIgnoredValue == IgnoreObject; |
-} |
- |
-void AXObject::setLastKnownIsIgnoredValue(bool isIgnored) |
-{ |
- m_lastKnownIsIgnoredValue = isIgnored ? IgnoreObject : IncludeObject; |
-} |
- |
-// Lacking concrete evidence of orientation, horizontal means width > height. vertical is height > width; |
-AccessibilityOrientation AXObject::orientation() const |
-{ |
- LayoutRect bounds = elementRect(); |
- if (bounds.size().width() > bounds.size().height()) |
- return AccessibilityOrientationHorizontal; |
- if (bounds.size().height() > bounds.size().width()) |
- return AccessibilityOrientationVertical; |
- |
- // A tie goes to horizontal. |
- return AccessibilityOrientationHorizontal; |
-} |
- |
-static String queryString(WebLocalizedString::Name name) |
-{ |
- return Locale::defaultLocale().queryString(name); |
-} |
- |
-String AXObject::actionVerb() const |
-{ |
- // FIXME: Need to add verbs for select elements. |
- |
- switch (roleValue()) { |
- case ButtonRole: |
- case ToggleButtonRole: |
- return queryString(WebLocalizedString::AXButtonActionVerb); |
- case TextFieldRole: |
- case TextAreaRole: |
- return queryString(WebLocalizedString::AXTextFieldActionVerb); |
- case RadioButtonRole: |
- return queryString(WebLocalizedString::AXRadioButtonActionVerb); |
- case CheckBoxRole: |
- return queryString(isChecked() ? WebLocalizedString::AXCheckedCheckBoxActionVerb : WebLocalizedString::AXUncheckedCheckBoxActionVerb); |
- case LinkRole: |
- return queryString(WebLocalizedString::AXLinkActionVerb); |
- case PopUpButtonRole: |
- // FIXME: Implement. |
- return String(); |
- case MenuListPopupRole: |
- // FIXME: Implement. |
- return String(); |
- default: |
- return emptyString(); |
- } |
-} |
- |
-AccessibilityButtonState AXObject::checkboxOrRadioValue() const |
-{ |
- // If this is a real checkbox or radio button, AXRenderObject will handle. |
- // If it's an ARIA checkbox or radio, the aria-checked attribute should be used. |
- |
- const AtomicString& result = getAttribute(aria_checkedAttr); |
- if (equalIgnoringCase(result, "true")) |
- return ButtonStateOn; |
- if (equalIgnoringCase(result, "mixed")) |
- return ButtonStateMixed; |
- |
- return ButtonStateOff; |
-} |
- |
-bool AXObject::ariaIsMultiline() const |
-{ |
- return equalIgnoringCase(getAttribute(aria_multilineAttr), "true"); |
-} |
- |
-bool AXObject::ariaPressedIsPresent() const |
-{ |
- return !getAttribute(aria_pressedAttr).isEmpty(); |
-} |
- |
-bool AXObject::supportsARIAAttributes() const |
-{ |
- return isLiveRegion() |
- || supportsARIADragging() |
- || supportsARIADropping() |
- || supportsARIAFlowTo() |
- || supportsARIAOwns() |
- || hasAttribute(aria_labelAttr); |
-} |
- |
-bool AXObject::supportsRangeValue() const |
-{ |
- return isProgressIndicator() |
- || isSlider() |
- || isScrollbar() |
- || isSpinButton(); |
-} |
- |
-void AXObject::ariaTreeRows(AccessibilityChildrenVector& result) |
-{ |
- AccessibilityChildrenVector axChildren = children(); |
- unsigned count = axChildren.size(); |
- for (unsigned k = 0; k < count; ++k) { |
- AXObject* obj = axChildren[k].get(); |
- |
- // Add tree items as the rows. |
- if (obj->roleValue() == TreeItemRole) |
- result.append(obj); |
- |
- // Now see if this item also has rows hiding inside of it. |
- obj->ariaTreeRows(result); |
- } |
-} |
- |
-bool AXObject::isLiveRegion() const |
-{ |
- const AtomicString& liveRegion = liveRegionStatus(); |
- return equalIgnoringCase(liveRegion, "polite") || equalIgnoringCase(liveRegion, "assertive"); |
-} |
- |
-const AXObject* AXObject::liveRegionRoot() const |
-{ |
- updateCachedAttributeValuesIfNeeded(); |
- return m_cachedLiveRegionRoot; |
-} |
- |
-const AtomicString& AXObject::containerLiveRegionStatus() const |
-{ |
- updateCachedAttributeValuesIfNeeded(); |
- return m_cachedLiveRegionRoot ? m_cachedLiveRegionRoot->liveRegionStatus() : nullAtom; |
-} |
- |
-const AtomicString& AXObject::containerLiveRegionRelevant() const |
-{ |
- updateCachedAttributeValuesIfNeeded(); |
- return m_cachedLiveRegionRoot ? m_cachedLiveRegionRoot->liveRegionRelevant() : nullAtom; |
-} |
- |
-bool AXObject::containerLiveRegionAtomic() const |
-{ |
- updateCachedAttributeValuesIfNeeded(); |
- return m_cachedLiveRegionRoot ? m_cachedLiveRegionRoot->liveRegionAtomic() : false; |
-} |
- |
-bool AXObject::containerLiveRegionBusy() const |
-{ |
- updateCachedAttributeValuesIfNeeded(); |
- return m_cachedLiveRegionRoot ? m_cachedLiveRegionRoot->liveRegionBusy() : false; |
-} |
- |
-void AXObject::markCachedElementRectDirty() const |
-{ |
- for (unsigned i = 0; i < m_children.size(); ++i) |
- m_children[i].get()->markCachedElementRectDirty(); |
-} |
- |
-IntPoint AXObject::clickPoint() |
-{ |
- LayoutRect rect = elementRect(); |
- return roundedIntPoint(LayoutPoint(rect.x() + rect.width() / 2, rect.y() + rect.height() / 2)); |
-} |
- |
-IntRect AXObject::boundingBoxForQuads(RenderObject* obj, const Vector<FloatQuad>& quads) |
-{ |
- ASSERT(obj); |
- if (!obj) |
- return IntRect(); |
- |
- size_t count = quads.size(); |
- if (!count) |
- return IntRect(); |
- |
- IntRect result; |
- for (size_t i = 0; i < count; ++i) { |
- IntRect r = quads[i].enclosingBoundingBox(); |
- if (!r.isEmpty()) { |
- if (obj->style()->hasAppearance()) |
- RenderTheme::theme().adjustPaintInvalidationRect(obj, r); |
- result.unite(r); |
- } |
- } |
- return result; |
-} |
- |
-AXObject* AXObject::elementAccessibilityHitTest(const IntPoint& point) const |
-{ |
- // Send the hit test back into the sub-frame if necessary. |
- if (isAttachment()) { |
- Widget* widget = widgetForAttachmentView(); |
- // Normalize the point for the widget's bounds. |
- if (widget && widget->isFrameView()) |
- return axObjectCache()->getOrCreate(widget)->accessibilityHitTest(IntPoint(point - widget->frameRect().location())); |
- } |
- |
- // Check if there are any mock elements that need to be handled. |
- size_t count = m_children.size(); |
- for (size_t k = 0; k < count; k++) { |
- if (m_children[k]->isMockObject() && m_children[k]->elementRect().contains(point)) |
- return m_children[k]->elementAccessibilityHitTest(point); |
- } |
- |
- return const_cast<AXObject*>(this); |
-} |
- |
-const AXObject::AccessibilityChildrenVector& AXObject::children() |
-{ |
- updateChildrenIfNecessary(); |
- |
- return m_children; |
-} |
- |
-AXObject* AXObject::parentObject() const |
-{ |
- if (m_detached) |
- return 0; |
- |
- if (m_parent) |
- return m_parent; |
- |
- return computeParent(); |
-} |
- |
-AXObject* AXObject::parentObjectIfExists() const |
-{ |
- if (m_detached) |
- return 0; |
- |
- if (m_parent) |
- return m_parent; |
- |
- return computeParentIfExists(); |
-} |
- |
-AXObject* AXObject::parentObjectUnignored() const |
-{ |
- AXObject* parent; |
- for (parent = parentObject(); parent && parent->accessibilityIsIgnored(); parent = parent->parentObject()) { |
- } |
- |
- return parent; |
-} |
- |
-AXObject* AXObject::firstAccessibleObjectFromNode(const Node* node) |
-{ |
- if (!node) |
- return 0; |
- |
- AXObjectCacheImpl* cache = toAXObjectCacheImpl(node->document().axObjectCache()); |
- AXObject* accessibleObject = cache->getOrCreate(node->renderer()); |
- while (accessibleObject && accessibleObject->accessibilityIsIgnored()) { |
- node = NodeTraversal::next(*node); |
- |
- while (node && !node->renderer()) |
- node = NodeTraversal::nextSkippingChildren(*node); |
- |
- if (!node) |
- return 0; |
- |
- accessibleObject = cache->getOrCreate(node->renderer()); |
- } |
- |
- return accessibleObject; |
-} |
- |
-void AXObject::updateChildrenIfNecessary() |
-{ |
- if (!hasChildren()) |
- addChildren(); |
-} |
- |
-void AXObject::clearChildren() |
-{ |
- // Detach all weak pointers from objects to their parents. |
- size_t length = m_children.size(); |
- for (size_t i = 0; i < length; i++) |
- m_children[i]->detachFromParent(); |
- |
- m_children.clear(); |
- m_haveChildren = false; |
-} |
- |
-AXObject* AXObject::focusedUIElement() const |
-{ |
- Document* doc = document(); |
- if (!doc) |
- return 0; |
- |
- Page* page = doc->page(); |
- if (!page) |
- return 0; |
- |
- return AXObjectCacheImpl::focusedUIElementForPage(page); |
-} |
- |
-Document* AXObject::document() const |
-{ |
- FrameView* frameView = documentFrameView(); |
- if (!frameView) |
- return 0; |
- |
- return frameView->frame().document(); |
-} |
- |
-FrameView* AXObject::documentFrameView() const |
-{ |
- const AXObject* object = this; |
- while (object && !object->isAXRenderObject()) |
- object = object->parentObject(); |
- |
- if (!object) |
- return 0; |
- |
- return object->documentFrameView(); |
-} |
- |
-String AXObject::language() const |
-{ |
- const AtomicString& lang = getAttribute(langAttr); |
- if (!lang.isEmpty()) |
- return lang; |
- |
- AXObject* parent = parentObject(); |
- |
- // as a last resort, fall back to the content language specified in the meta tag |
- if (!parent) { |
- Document* doc = document(); |
- if (doc) |
- return doc->contentLanguage(); |
- return nullAtom; |
- } |
- |
- return parent->language(); |
-} |
- |
-bool AXObject::hasAttribute(const QualifiedName& attribute) const |
-{ |
- Node* elementNode = node(); |
- if (!elementNode) |
- return false; |
- |
- if (!elementNode->isElementNode()) |
- return false; |
- |
- Element* element = toElement(elementNode); |
- return element->fastHasAttribute(attribute); |
-} |
- |
-const AtomicString& AXObject::getAttribute(const QualifiedName& attribute) const |
-{ |
- Node* elementNode = node(); |
- if (!elementNode) |
- return nullAtom; |
- |
- if (!elementNode->isElementNode()) |
- return nullAtom; |
- |
- Element* element = toElement(elementNode); |
- return element->fastGetAttribute(attribute); |
-} |
- |
-bool AXObject::press() const |
-{ |
- Element* actionElem = actionElement(); |
- if (!actionElem) |
- return false; |
- UserGestureIndicator gestureIndicator(DefinitelyProcessingNewUserGesture); |
- actionElem->accessKeyAction(true); |
- return true; |
-} |
- |
-void AXObject::scrollToMakeVisible() const |
-{ |
- IntRect objectRect = pixelSnappedIntRect(elementRect()); |
- objectRect.setLocation(IntPoint()); |
- scrollToMakeVisibleWithSubFocus(objectRect); |
-} |
- |
-// This is a 1-dimensional scroll offset helper function that's applied |
-// separately in the horizontal and vertical directions, because the |
-// logic is the same. The goal is to compute the best scroll offset |
-// in order to make an object visible within a viewport. |
-// |
-// If the object is already fully visible, returns the same scroll |
-// offset. |
-// |
-// In case the whole object cannot fit, you can specify a |
-// subfocus - a smaller region within the object that should |
-// be prioritized. If the whole object can fit, the subfocus is |
-// ignored. |
-// |
-// If possible, the object and subfocus are centered within the |
-// viewport. |
-// |
-// Example 1: the object is already visible, so nothing happens. |
-// +----------Viewport---------+ |
-// +---Object---+ |
-// +--SubFocus--+ |
-// |
-// Example 2: the object is not fully visible, so it's centered |
-// within the viewport. |
-// Before: |
-// +----------Viewport---------+ |
-// +---Object---+ |
-// +--SubFocus--+ |
-// |
-// After: |
-// +----------Viewport---------+ |
-// +---Object---+ |
-// +--SubFocus--+ |
-// |
-// Example 3: the object is larger than the viewport, so the |
-// viewport moves to show as much of the object as possible, |
-// while also trying to center the subfocus. |
-// Before: |
-// +----------Viewport---------+ |
-// +---------------Object--------------+ |
-// +-SubFocus-+ |
-// |
-// After: |
-// +----------Viewport---------+ |
-// +---------------Object--------------+ |
-// +-SubFocus-+ |
-// |
-// When constraints cannot be fully satisfied, the min |
-// (left/top) position takes precedence over the max (right/bottom). |
-// |
-// Note that the return value represents the ideal new scroll offset. |
-// This may be out of range - the calling function should clip this |
-// to the available range. |
-static int computeBestScrollOffset(int currentScrollOffset, int subfocusMin, int subfocusMax, int objectMin, int objectMax, int viewportMin, int viewportMax) |
-{ |
- int viewportSize = viewportMax - viewportMin; |
- |
- // If the object size is larger than the viewport size, consider |
- // only a portion that's as large as the viewport, centering on |
- // the subfocus as much as possible. |
- if (objectMax - objectMin > viewportSize) { |
- // Since it's impossible to fit the whole object in the |
- // viewport, exit now if the subfocus is already within the viewport. |
- if (subfocusMin - currentScrollOffset >= viewportMin |
- && subfocusMax - currentScrollOffset <= viewportMax) |
- return currentScrollOffset; |
- |
- // Subfocus must be within focus. |
- subfocusMin = std::max(subfocusMin, objectMin); |
- subfocusMax = std::min(subfocusMax, objectMax); |
- |
- // Subfocus must be no larger than the viewport size; favor top/left. |
- if (subfocusMax - subfocusMin > viewportSize) |
- subfocusMax = subfocusMin + viewportSize; |
- |
- // Compute the size of an object centered on the subfocus, the size of the viewport. |
- int centeredObjectMin = (subfocusMin + subfocusMax - viewportSize) / 2; |
- int centeredObjectMax = centeredObjectMin + viewportSize; |
- |
- objectMin = std::max(objectMin, centeredObjectMin); |
- objectMax = std::min(objectMax, centeredObjectMax); |
- } |
- |
- // Exit now if the focus is already within the viewport. |
- if (objectMin - currentScrollOffset >= viewportMin |
- && objectMax - currentScrollOffset <= viewportMax) |
- return currentScrollOffset; |
- |
- // Center the object in the viewport. |
- return (objectMin + objectMax - viewportMin - viewportMax) / 2; |
-} |
- |
-void AXObject::scrollToMakeVisibleWithSubFocus(const IntRect& subfocus) const |
-{ |
- // Search up the parent chain until we find the first one that's scrollable. |
- AXObject* scrollParent = parentObject(); |
- ScrollableArea* scrollableArea = 0; |
- while (scrollParent) { |
- scrollableArea = scrollParent->getScrollableAreaIfScrollable(); |
- if (scrollableArea) |
- break; |
- scrollParent = scrollParent->parentObject(); |
- } |
- if (!scrollableArea) |
- return; |
- |
- IntRect objectRect = pixelSnappedIntRect(elementRect()); |
- IntPoint scrollPosition = scrollableArea->scrollPosition(); |
- IntRect scrollVisibleRect = scrollableArea->visibleContentRect(); |
- |
- int desiredX = computeBestScrollOffset( |
- scrollPosition.x(), |
- objectRect.x() + subfocus.x(), objectRect.x() + subfocus.maxX(), |
- objectRect.x(), objectRect.maxX(), |
- 0, scrollVisibleRect.width()); |
- int desiredY = computeBestScrollOffset( |
- scrollPosition.y(), |
- objectRect.y() + subfocus.y(), objectRect.y() + subfocus.maxY(), |
- objectRect.y(), objectRect.maxY(), |
- 0, scrollVisibleRect.height()); |
- |
- scrollParent->scrollTo(IntPoint(desiredX, desiredY)); |
- |
- // Convert the subfocus into the coordinates of the scroll parent. |
- IntRect newSubfocus = subfocus; |
- IntRect newElementRect = pixelSnappedIntRect(elementRect()); |
- IntRect scrollParentRect = pixelSnappedIntRect(scrollParent->elementRect()); |
- newSubfocus.move(newElementRect.x(), newElementRect.y()); |
- newSubfocus.move(-scrollParentRect.x(), -scrollParentRect.y()); |
- |
- // Recursively make sure the scroll parent itself is visible. |
- if (scrollParent->parentObject()) |
- scrollParent->scrollToMakeVisibleWithSubFocus(newSubfocus); |
-} |
- |
-void AXObject::scrollToGlobalPoint(const IntPoint& globalPoint) const |
-{ |
- // Search up the parent chain and create a vector of all scrollable parent objects |
- // and ending with this object itself. |
- Vector<const AXObject*> objects; |
- AXObject* parentObject; |
- for (parentObject = this->parentObject(); parentObject; parentObject = parentObject->parentObject()) { |
- if (parentObject->getScrollableAreaIfScrollable()) |
- objects.prepend(parentObject); |
- } |
- objects.append(this); |
- |
- // Start with the outermost scrollable (the main window) and try to scroll the |
- // next innermost object to the given point. |
- int offsetX = 0, offsetY = 0; |
- IntPoint point = globalPoint; |
- size_t levels = objects.size() - 1; |
- for (size_t i = 0; i < levels; i++) { |
- const AXObject* outer = objects[i]; |
- const AXObject* inner = objects[i + 1]; |
- |
- ScrollableArea* scrollableArea = outer->getScrollableAreaIfScrollable(); |
- |
- IntRect innerRect = inner->isAXScrollView() ? pixelSnappedIntRect(inner->parentObject()->elementRect()) : pixelSnappedIntRect(inner->elementRect()); |
- IntRect objectRect = innerRect; |
- IntPoint scrollPosition = scrollableArea->scrollPosition(); |
- |
- // Convert the object rect into local coordinates. |
- objectRect.move(offsetX, offsetY); |
- if (!outer->isAXScrollView()) |
- objectRect.move(scrollPosition.x(), scrollPosition.y()); |
- |
- int desiredX = computeBestScrollOffset( |
- 0, |
- objectRect.x(), objectRect.maxX(), |
- objectRect.x(), objectRect.maxX(), |
- point.x(), point.x()); |
- int desiredY = computeBestScrollOffset( |
- 0, |
- objectRect.y(), objectRect.maxY(), |
- objectRect.y(), objectRect.maxY(), |
- point.y(), point.y()); |
- outer->scrollTo(IntPoint(desiredX, desiredY)); |
- |
- if (outer->isAXScrollView() && !inner->isAXScrollView()) { |
- // If outer object we just scrolled is a scroll view (main window or iframe) but the |
- // inner object is not, keep track of the coordinate transformation to apply to |
- // future nested calculations. |
- scrollPosition = scrollableArea->scrollPosition(); |
- offsetX -= (scrollPosition.x() + point.x()); |
- offsetY -= (scrollPosition.y() + point.y()); |
- point.move(scrollPosition.x() - innerRect.x(), scrollPosition.y() - innerRect.y()); |
- } else if (inner->isAXScrollView()) { |
- // Otherwise, if the inner object is a scroll view, reset the coordinate transformation. |
- offsetX = 0; |
- offsetY = 0; |
- } |
- } |
-} |
- |
-void AXObject::notifyIfIgnoredValueChanged() |
-{ |
- bool isIgnored = accessibilityIsIgnored(); |
- if (lastKnownIsIgnoredValue() != isIgnored) { |
- axObjectCache()->childrenChanged(parentObject()); |
- setLastKnownIsIgnoredValue(isIgnored); |
- } |
-} |
- |
-void AXObject::selectionChanged() |
-{ |
- if (AXObject* parent = parentObjectIfExists()) |
- parent->selectionChanged(); |
-} |
- |
-int AXObject::lineForPosition(const VisiblePosition& visiblePos) const |
-{ |
- if (visiblePos.isNull() || !node()) |
- return -1; |
- |
- // If the position is not in the same editable region as this AX object, return -1. |
- Node* containerNode = visiblePos.deepEquivalent().containerNode(); |
- if (!containerNode->containsIncludingShadowDOM(node()) && !node()->containsIncludingShadowDOM(containerNode)) |
- return -1; |
- |
- int lineCount = -1; |
- VisiblePosition currentVisiblePos = visiblePos; |
- VisiblePosition savedVisiblePos; |
- |
- // move up until we get to the top |
- // FIXME: This only takes us to the top of the rootEditableElement, not the top of the |
- // top document. |
- do { |
- savedVisiblePos = currentVisiblePos; |
- VisiblePosition prevVisiblePos = previousLinePosition(currentVisiblePos, 0, HasEditableAXRole); |
- currentVisiblePos = prevVisiblePos; |
- ++lineCount; |
- } while (currentVisiblePos.isNotNull() && !(inSameLine(currentVisiblePos, savedVisiblePos))); |
- |
- return lineCount; |
-} |
- |
-bool AXObject::isARIAControl(AccessibilityRole ariaRole) |
-{ |
- return isARIAInput(ariaRole) || ariaRole == TextAreaRole || ariaRole == ButtonRole |
- || ariaRole == ComboBoxRole || ariaRole == SliderRole; |
-} |
- |
-bool AXObject::isARIAInput(AccessibilityRole ariaRole) |
-{ |
- return ariaRole == RadioButtonRole || ariaRole == CheckBoxRole || ariaRole == TextFieldRole; |
-} |
- |
-AccessibilityRole AXObject::ariaRoleToWebCoreRole(const String& value) |
-{ |
- ASSERT(!value.isEmpty()); |
- |
- static const ARIARoleMap* roleMap = createARIARoleMap(); |
- |
- Vector<String> roleVector; |
- value.split(' ', roleVector); |
- AccessibilityRole role = UnknownRole; |
- unsigned size = roleVector.size(); |
- for (unsigned i = 0; i < size; ++i) { |
- String roleName = roleVector[i]; |
- role = roleMap->get(roleName); |
- if (role) |
- return role; |
- } |
- |
- return role; |
-} |
- |
-AccessibilityRole AXObject::buttonRoleType() const |
-{ |
- // If aria-pressed is present, then it should be exposed as a toggle button. |
- // http://www.w3.org/TR/wai-aria/states_and_properties#aria-pressed |
- if (ariaPressedIsPresent()) |
- return ToggleButtonRole; |
- if (ariaHasPopup()) |
- return PopUpButtonRole; |
- // We don't contemplate RadioButtonRole, as it depends on the input |
- // type. |
- |
- return ButtonRole; |
-} |
- |
-} // namespace blink |