#include "config.h"
#include "core/accessibility/AXObject.h"
-#include "core/accessibility/AXObjectCache.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"
{ "definition", DefinitionRole },
{ "document", DocumentRole },
{ "rowheader", RowHeaderRole },
+ { "form", FormRole },
{ "group", GroupRole },
{ "heading", HeadingRole },
{ "img", ImageRole },
{ "menu", MenuRole },
{ "menubar", MenuBarRole },
{ "menuitem", MenuItemRole },
- { "menuitemcheckbox", MenuItemRole },
- { "menuitemradio", MenuItemRole },
+ { "menuitemcheckbox", MenuItemCheckBoxRole },
+ { "menuitemradio", MenuItemRadioRole },
{ "note", NoteRole },
{ "navigation", NavigationRole },
{ "none", NoneRole },
, m_role(UnknownRole)
, m_lastKnownIsIgnoredValue(DefaultBehavior)
, m_detached(false)
+ , m_parent(0)
+ , m_lastModificationCount(-1)
+ , m_cachedIsIgnored(false)
+ , m_cachedLiveRegionRoot(0)
{
}
return m_detached;
}
-AXObjectCache* AXObject::axObjectCache() const
+AXObjectCacheImpl* AXObject::axObjectCache() const
{
Document* doc = document();
if (doc)
- return doc->axObjectCache();
+ return toAXObjectCacheImpl(doc->axObjectCache());
return 0;
}
case ComplementaryRole:
case ContentInfoRole:
case FooterRole:
+ case FormRole:
case MainRole:
case NavigationRole:
case RegionRole:
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()) {
}
}
-bool AXObject::isExpanded() const
+bool AXObject::accessibilityIsIgnored() const
{
- if (equalIgnoringCase(getAttribute(aria_expandedAttr), "true"))
- return true;
-
- return false;
+ updateCachedAttributeValuesIfNeeded();
+ return m_cachedIsIgnored;
}
-bool AXObject::accessibilityIsIgnored() const
+void AXObject::updateCachedAttributeValuesIfNeeded() const
{
- AXObjectCache* cache = axObjectCache();
+ AXObjectCacheImpl* cache = axObjectCache();
if (!cache)
- return true;
-
- AXComputedObjectAttributeCache* attributeCache = cache->computedObjectAttributeCache();
- if (attributeCache) {
- AXObjectInclusion ignored = attributeCache->getIgnored(axObjectID());
- switch (ignored) {
- case IgnoreObject:
- return true;
- case IncludeObject:
- return false;
- case DefaultBehavior:
- break;
- }
- }
-
- bool result = computeAccessibilityIsIgnored();
+ return;
- if (attributeCache)
- attributeCache->setIgnored(axObjectID(), result ? IgnoreObject : IncludeObject);
+ if (cache->modificationCount() == m_lastModificationCount)
+ return;
- return result;
+ m_lastModificationCount = cache->modificationCount();
+ m_cachedIsIgnored = computeAccessibilityIsIgnored();
+ m_cachedLiveRegionRoot = isLiveRegion() ?
+ this :
+ (parentObjectIfExists() ? parentObjectIfExists()->liveRegionRoot() : 0);
}
bool AXObject::accessibilityIsIgnoredByDefault() const
bool AXObject::supportsARIAAttributes() const
{
- return supportsARIALiveRegion()
+ return isLiveRegion()
|| supportsARIADragging()
|| supportsARIADropping()
|| supportsARIAFlowTo()
}
}
-bool AXObject::supportsARIALiveRegion() const
+bool AXObject::isLiveRegion() const
{
- const AtomicString& liveRegion = ariaLiveRegionStatus();
+ 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)
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;
if (!node)
return 0;
- AXObjectCache* cache = node->document().axObjectCache();
+ AXObjectCacheImpl* cache = toAXObjectCacheImpl(node->document().axObjectCache());
AXObject* accessibleObject = cache->getOrCreate(node->renderer());
while (accessibleObject && accessibleObject->accessibilityIsIgnored()) {
node = NodeTraversal::next(*node);
void AXObject::clearChildren()
{
- // Some objects have weak pointers to their parents and those associations need to be detached.
+ // 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();
if (!page)
return 0;
- return AXObjectCache::focusedUIElementForPage(page);
+ return AXObjectCacheImpl::focusedUIElementForPage(page);
}
Document* AXObject::document() const
// 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.
//
-// Example: the viewport is scrolled to the right just enough
-// that the object is in view.
+// 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---------+
+// +----------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).
//
{
int viewportSize = viewportMax - viewportMin;
- // If the focus size is larger than the viewport size, shrink it in the
- // direction of subfocus.
+ // 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) {
- // Subfocus must be within focus:
+ // 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);
if (subfocusMax - subfocusMin > viewportSize)
subfocusMax = subfocusMin + viewportSize;
- if (subfocusMin + viewportSize > objectMax) {
- objectMin = objectMax - viewportSize;
- } else {
- objectMin = subfocusMin;
- objectMax = 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.
&& objectMax - currentScrollOffset <= viewportMax)
return currentScrollOffset;
- // Scroll left if we're too far to the right.
- if (objectMax - currentScrollOffset > viewportMax)
- return objectMax - viewportMax;
-
- // Scroll right if we're too far to the left.
- if (objectMin - currentScrollOffset < viewportMin)
- return objectMin - viewportMin;
-
- ASSERT_NOT_REACHED();
-
- // This shouldn't happen.
- return currentScrollOffset;
+ // Center the object in the viewport.
+ return (objectMin + objectMax - viewportMin - viewportMax) / 2;
}
void AXObject::scrollToMakeVisibleWithSubFocus(const IntRect& subfocus) const
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->scrollToMakeVisible();
+ scrollParent->scrollToMakeVisibleWithSubFocus(newSubfocus);
}
void AXObject::scrollToGlobalPoint(const IntPoint& globalPoint) const