Index: Source/core/accessibility/AccessibilityRenderObject.cpp |
diff --git a/Source/core/accessibility/AccessibilityRenderObject.cpp b/Source/core/accessibility/AccessibilityRenderObject.cpp |
index 5a6d7e97aacfd72664fdda9c2922e66f4dc97321..86594e681d888900a9609b4bd5f37c9e32074bd8 100644 |
--- a/Source/core/accessibility/AccessibilityRenderObject.cpp |
+++ b/Source/core/accessibility/AccessibilityRenderObject.cpp |
@@ -223,6 +223,7 @@ static RenderBoxModelObject* nextContinuation(RenderObject* renderer) |
AccessibilityRenderObject::AccessibilityRenderObject(RenderObject* renderer) |
: AccessibilityNodeObject(renderer->node()) |
, m_renderer(renderer) |
+ , m_cachedElementRectDirty(true) |
{ |
#ifndef NDEBUG |
m_renderer->setHasAXObject(true); |
@@ -241,11 +242,17 @@ AccessibilityRenderObject::~AccessibilityRenderObject() |
LayoutRect AccessibilityRenderObject::elementRect() const |
{ |
- // a checkbox or radio button should encompass its label |
- if (isCheckboxOrRadio()) |
- return checkboxOrRadioRect(); |
+ if (!m_renderer) |
+ return LayoutRect(); |
+ if (!m_renderer->isBox()) |
+ return computeElementRect(); |
+ |
+ for (const AccessibilityObject* obj = this; obj; obj = obj->parentObject()) |
+ obj->checkCachedElementRect(); |
+ for (const AccessibilityObject* obj = this; obj; obj = obj->parentObject()) |
+ obj->updateCachedElementRect(); |
- return boundingBoxRect(); |
+ return m_cachedElementRect; |
} |
int AccessibilityRenderObject::layoutCount() const |
@@ -1153,20 +1160,6 @@ const String& AccessibilityRenderObject::actionVerb() const |
} |
} |
-LayoutRect AccessibilityRenderObject::checkboxOrRadioRect() const |
-{ |
- if (!m_renderer) |
- return LayoutRect(); |
- |
- HTMLLabelElement* label = labelForElement(toElement(m_renderer->node())); |
- if (!label || !label->renderer()) |
- return boundingBoxRect(); |
- |
- LayoutRect labelRect = axObjectCache()->getOrCreate(label)->elementRect(); |
- labelRect.unite(boundingBoxRect()); |
- return labelRect; |
-} |
- |
void AccessibilityRenderObject::selectedChildren(AccessibilityChildrenVector& result) |
{ |
ASSERT(result.isEmpty()); |
@@ -1533,45 +1526,69 @@ String AccessibilityRenderObject::helpText() const |
// Position and size. |
// |
-LayoutRect AccessibilityRenderObject::boundingBoxRect() const |
+void AccessibilityRenderObject::checkCachedElementRect() const |
{ |
- RenderObject* obj = m_renderer; |
+ if (m_cachedElementRectDirty) |
+ return; |
- if (!obj) |
- return LayoutRect(); |
+ if (!m_renderer) |
+ return; |
- if (obj->node()) // If we are a continuation, we want to make sure to use the primary renderer. |
- obj = obj->node()->renderer(); |
+ if (!m_renderer->isBox()) { |
+ AccessibilityNodeObject::checkCachedElementRect(); |
+ return; |
+ } |
- // absoluteFocusRingQuads will query the hierarchy below this element, which for large webpages can be very slow. |
- // For a web area, which will have the most elements of any element, absoluteQuads should be used. |
- // We should also use absoluteQuads for SVG elements, otherwise transforms won't be applied. |
- Vector<FloatQuad> quads; |
- bool isSVGRoot = false; |
-#if ENABLE(SVG) |
- if (obj->isSVGRoot()) |
- isSVGRoot = true; |
-#endif |
- if (obj->isText()) |
- toRenderText(obj)->absoluteQuads(quads, 0, RenderText::ClipToEllipsis); |
- else if (isWebArea() || isSeamlessWebArea() || isSVGRoot) |
- obj->absoluteQuads(quads); |
- else |
- obj->absoluteFocusRingQuads(quads); |
+ bool dirty = false; |
+ RenderBox* box = toRenderBox(m_renderer); |
+ if (box->frameRect() != m_cachedFrameRect) |
+ dirty = true; |
- LayoutRect result = boundingBoxForQuads(obj, quads); |
+ if (box->canBeScrolledAndHasScrollableArea()) { |
+ ScrollableArea* scrollableArea = box->layer(); |
+ if (scrollableArea && scrollableArea->scrollPosition() != m_cachedScrollPosition) |
+ dirty = true; |
+ } |
-#if ENABLE(SVG) |
- Document* document = this->document(); |
- if (document && document->isSVGDocument()) |
- offsetBoundingBoxForRemoteSVGElement(result); |
-#endif |
+ if (dirty) |
+ markCachedElementRectDirty(); |
+} |
- // The size of the web area should be the content size, not the clipped size. |
- if ((isWebArea() || isSeamlessWebArea()) && obj->frame()->view()) |
- result.setSize(obj->frame()->view()->contentsSize()); |
+void AccessibilityRenderObject::updateCachedElementRect() const |
+{ |
+ if (!m_cachedElementRectDirty) |
+ return; |
- return result; |
+ if (!m_renderer) |
+ return; |
+ |
+ if (!m_renderer->isBox()) { |
+ AccessibilityNodeObject::updateCachedElementRect(); |
+ return; |
+ } |
+ |
+ RenderBox* box = toRenderBox(m_renderer); |
+ m_cachedFrameRect = box->frameRect(); |
+ |
+ if (box->canBeScrolledAndHasScrollableArea()) { |
+ ScrollableArea* scrollableArea = box->layer(); |
+ if (scrollableArea) |
+ m_cachedScrollPosition = scrollableArea->scrollPosition(); |
+ } |
+ |
+ m_cachedElementRect = computeElementRect(); |
+ m_cachedElementRectDirty = false; |
+} |
+ |
+void AccessibilityRenderObject::markCachedElementRectDirty() const |
+{ |
+ if (m_cachedElementRectDirty) |
+ return; |
+ |
+ // Marks children recursively, if this element changed. |
+ m_cachedElementRectDirty = true; |
+ for (AccessibilityObject* child = firstChild(); child; child = child->nextSibling()) |
+ child->markCachedElementRectDirty(); |
} |
IntPoint AccessibilityRenderObject::clickPoint() |
@@ -2889,7 +2906,7 @@ AccessibilityObject* AccessibilityRenderObject::remoteSVGElementHitTest(const In |
if (!remote) |
return 0; |
- IntSize offset = point - roundedIntPoint(boundingBoxRect().location()); |
+ IntSize offset = point - roundedIntPoint(elementRect().location()); |
return remote->accessibilityHitTest(IntPoint(offset)); |
} |
@@ -2899,7 +2916,7 @@ void AccessibilityRenderObject::offsetBoundingBoxForRemoteSVGElement(LayoutRect& |
{ |
for (AccessibilityObject* parent = parentObject(); parent; parent = parent->parentObject()) { |
if (parent->isAccessibilitySVGRoot()) { |
- rect.moveBy(parent->parentObject()->boundingBoxRect().location()); |
+ rect.moveBy(parent->parentObject()->elementRect().location()); |
break; |
} |
} |
@@ -3129,4 +3146,54 @@ bool AccessibilityRenderObject::inheritsPresentationalRole() const |
return false; |
} |
+LayoutRect AccessibilityRenderObject::computeElementRect() const |
+{ |
+ RenderObject* obj = m_renderer; |
+ |
+ if (!obj) |
+ return LayoutRect(); |
+ |
+ if (obj->node()) // If we are a continuation, we want to make sure to use the primary renderer. |
+ obj = obj->node()->renderer(); |
+ |
+ // absoluteFocusRingQuads will query the hierarchy below this element, which for large webpages can be very slow. |
+ // For a web area, which will have the most elements of any element, absoluteQuads should be used. |
+ // We should also use absoluteQuads for SVG elements, otherwise transforms won't be applied. |
+ Vector<FloatQuad> quads; |
+ bool isSVGRoot = false; |
+#if ENABLE(SVG) |
+ if (obj->isSVGRoot()) |
+ isSVGRoot = true; |
+#endif |
+ if (obj->isText()) |
+ toRenderText(obj)->absoluteQuads(quads, 0, RenderText::ClipToEllipsis); |
+ else if (isWebArea() || isSeamlessWebArea() || isSVGRoot) |
+ obj->absoluteQuads(quads); |
+ else |
+ obj->absoluteFocusRingQuads(quads); |
+ |
+ LayoutRect result = boundingBoxForQuads(obj, quads); |
+ |
+#if ENABLE(SVG) |
+ Document* document = this->document(); |
+ if (document && document->isSVGDocument()) |
+ offsetBoundingBoxForRemoteSVGElement(result); |
+#endif |
+ |
+ // The size of the web area should be the content size, not the clipped size. |
+ if ((isWebArea() || isSeamlessWebArea()) && obj->frame()->view()) |
+ result.setSize(obj->frame()->view()->contentsSize()); |
+ |
+ // Checkboxes and radio buttons include their label as part of their rect. |
+ if (isCheckboxOrRadio()) { |
+ HTMLLabelElement* label = labelForElement(toElement(m_renderer->node())); |
+ if (label && !label->renderer()) { |
+ LayoutRect labelRect = axObjectCache()->getOrCreate(label)->elementRect(); |
+ result.unite(labelRect); |
+ } |
+ } |
+ |
+ return result; |
+} |
+ |
} // namespace WebCore |