Index: third_party/WebKit/Source/modules/accessibility/AXObject.cpp |
diff --git a/third_party/WebKit/Source/modules/accessibility/AXObject.cpp b/third_party/WebKit/Source/modules/accessibility/AXObject.cpp |
index c2d02b361161065a82b7d46cb864e56795efd32a..7eb163da5d77be9db58fde61995147aeeea8191c 100644 |
--- a/third_party/WebKit/Source/modules/accessibility/AXObject.cpp |
+++ b/third_party/WebKit/Source/modules/accessibility/AXObject.cpp |
@@ -28,6 +28,7 @@ |
#include "modules/accessibility/AXObject.h" |
+#include "SkMatrix44.h" |
#include "core/editing/EditingUtilities.h" |
#include "core/editing/VisibleUnits.h" |
#include "core/frame/FrameView.h" |
@@ -36,6 +37,7 @@ |
#include "core/html/HTMLDialogElement.h" |
#include "core/html/HTMLFrameOwnerElement.h" |
#include "core/html/parser/HTMLParserIdioms.h" |
+#include "core/layout/LayoutInline.h" |
#include "core/layout/LayoutTheme.h" |
#include "modules/accessibility/AXObjectCacheImpl.h" |
#include "platform/UserGestureIndicator.h" |
@@ -1132,51 +1134,11 @@ bool AXObject::containerLiveRegionBusy() const |
return m_cachedLiveRegionRoot && m_cachedLiveRegionRoot->liveRegionBusy(); |
} |
-void AXObject::markCachedElementRectDirty() const |
-{ |
- for (const auto& child : m_children) |
- child->markCachedElementRectDirty(); |
-} |
- |
-IntPoint AXObject::clickPoint() |
-{ |
- LayoutRect rect = elementRect(); |
- return roundedIntPoint(LayoutPoint(rect.x() + rect.width() / 2, rect.y() + rect.height() / 2)); |
-} |
- |
-SkMatrix44 AXObject::transformFromLocalParentFrame() const |
-{ |
- return SkMatrix44(); |
-} |
- |
-IntRect AXObject::boundingBoxForQuads(LayoutObject* 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()) { |
- // TODO(pdr): Should this be using visualOverflowRect? |
- if (obj->style()->hasAppearance()) |
- LayoutTheme::theme().addVisualOverflow(*obj, r); |
- result.unite(r); |
- } |
- } |
- return result; |
-} |
- |
AXObject* AXObject::elementAccessibilityHitTest(const IntPoint& point) const |
{ |
// Check if there are any mock elements that need to be handled. |
for (const auto& child : m_children) { |
- if (child->isMockObject() && child->elementRect().contains(point)) |
+ if (child->isMockObject() && child->getBoundsInFrameCoordinates().contains(point)) |
return child->elementAccessibilityHitTest(point); |
} |
@@ -1352,11 +1314,111 @@ void AXObject::setScrollOffset(const IntPoint& offset) const |
area->setScrollPosition(DoublePoint(offset.x(), offset.y()), ProgrammaticScroll); |
} |
-void AXObject::getRelativeBounds(AXObject** container, FloatRect& boundsInContainer, SkMatrix44& containerTransform) const |
+void AXObject::getRelativeBounds(AXObject** outContainer, FloatRect& outBoundsInContainer, SkMatrix44& outContainerTransform) const |
{ |
- *container = nullptr; |
- boundsInContainer = FloatRect(); |
- containerTransform.setIdentity(); |
+ *outContainer = nullptr; |
+ outBoundsInContainer = FloatRect(); |
+ outContainerTransform.setIdentity(); |
+ |
+ // First check if it has explicit bounds, for example if this element is tied to a |
+ // canvas path. When explicit coordinates are provided, the ID of the explicit container |
+ // element that the coordinates are relative to must be provided too. |
+ if (!m_explicitElementRect.isEmpty()) { |
+ *outContainer = axObjectCache().objectFromAXID(m_explicitContainerID); |
+ if (*outContainer) { |
+ outBoundsInContainer = FloatRect(m_explicitElementRect); |
+ return; |
+ } |
+ } |
+ |
+ LayoutObject* layoutObject = layoutObjectForRelativeBounds(); |
+ if (!layoutObject) |
+ return; |
+ |
+ if (isWebArea()) { |
+ if (layoutObject->frame()->view()) |
+ outBoundsInContainer.setSize(FloatSize(layoutObject->frame()->view()->contentsSize())); |
+ return; |
+ } |
+ |
+ // First compute the container. The container must be an ancestor in the accessibility tree, and |
+ // its LayoutObject must be an ancestor in the layout tree. Get the first such ancestor that's |
+ // either scrollable or has a paint layer. |
+ AXObject* container = parentObjectUnignored(); |
+ LayoutObject* containerLayoutObject = nullptr; |
+ while (container) { |
+ containerLayoutObject = container->getLayoutObject(); |
+ if (containerLayoutObject && containerLayoutObject->isBoxModelObject() && layoutObject->isDescendantOf(containerLayoutObject)) { |
+ if (container->isScrollableContainer() || containerLayoutObject->hasLayer()) |
+ break; |
+ } |
+ |
+ container = container->parentObjectUnignored(); |
+ } |
+ |
+ if (!container) |
+ return; |
+ *outContainer = container; |
+ |
+ // Next get the local bounds of this LayoutObject, which is typically |
+ // a rect at point (0, 0) with the width and height of the LayoutObject. |
+ LayoutRect localBounds; |
+ if (layoutObject->isText()) { |
+ Vector<FloatQuad> quads; |
+ toLayoutText(layoutObject)->quads(quads, LayoutText::ClipToEllipsis, LayoutText::LocalQuads); |
+ for (const FloatQuad& quad : quads) |
+ localBounds.unite(LayoutRect(quad.boundingBox())); |
+ } else if (layoutObject->isLayoutInline()) { |
+ Vector<LayoutRect> rects; |
+ toLayoutInline(layoutObject)->addOutlineRects(rects, LayoutPoint(), LayoutObject::IncludeBlockVisualOverflow); |
+ localBounds = unionRect(rects); |
+ } else if (layoutObject->isBox()) { |
+ localBounds = LayoutRect(LayoutPoint(), toLayoutBox(layoutObject)->size()); |
+ } else if (layoutObject->isSVG()) { |
+ localBounds = LayoutRect(layoutObject->strokeBoundingBox()); |
+ } else { |
+ DCHECK(false); |
+ } |
+ outBoundsInContainer = FloatRect(localBounds); |
+ |
+ // If the container has a scroll offset, subtract that out because we want our |
+ // bounds to be relative to the *unscrolled* position of the container object. |
+ ScrollableArea* scrollableArea = container->getScrollableAreaIfScrollable(); |
+ if (scrollableArea && !container->isWebArea()) { |
+ IntPoint scrollPosition = scrollableArea->scrollPosition(); |
+ outBoundsInContainer.move(FloatSize(scrollPosition.x(), scrollPosition.y())); |
+ } |
+ |
+ // Compute the transform between the container's coordinate space and this object. |
+ // If the transform is just a simple translation, apply that to the bounding box, but |
+ // if it's a non-trivial transformation like a rotation, scaling, etc. then return |
+ // the full matrix instead. |
+ TransformationMatrix transform = layoutObject->localToAncestorTransform(toLayoutBoxModelObject(containerLayoutObject)); |
+ if (transform.isIdentityOr2DTranslation()) { |
+ outBoundsInContainer.move(transform.to2DTranslation()); |
+ } else { |
+ outContainerTransform = TransformationMatrix::toSkMatrix44(transform); |
+ } |
+} |
+ |
+LayoutRect AXObject::getBoundsInFrameCoordinates() const |
+{ |
+ AXObject* container = nullptr; |
+ FloatRect bounds; |
+ SkMatrix44 transform; |
+ getRelativeBounds(&container, bounds, transform); |
+ FloatRect computedBounds(0, 0, bounds.width(), bounds.height()); |
+ while (container && container != this) { |
+ computedBounds.move(bounds.x(), bounds.y()); |
+ if (!container->isWebArea()) |
+ computedBounds.move(-container->scrollOffset().x(), -container->scrollOffset().y()); |
+ if (!transform.isIdentity()) { |
+ TransformationMatrix transformationMatrix(transform); |
+ transformationMatrix.mapRect(computedBounds); |
+ } |
+ container->getRelativeBounds(&container, bounds, transform); |
+ } |
+ return LayoutRect(computedBounds); |
} |
// |
@@ -1375,7 +1437,7 @@ bool AXObject::press() const |
void AXObject::scrollToMakeVisible() const |
{ |
- IntRect objectRect = pixelSnappedIntRect(elementRect()); |
+ IntRect objectRect = pixelSnappedIntRect(getBoundsInFrameCoordinates()); |
objectRect.setLocation(IntPoint()); |
scrollToMakeVisibleWithSubFocus(objectRect); |
} |
@@ -1485,14 +1547,14 @@ void AXObject::scrollToMakeVisibleWithSubFocus(const IntRect& subfocus) const |
if (!scrollParent || !scrollableArea) |
return; |
- IntRect objectRect = pixelSnappedIntRect(elementRect()); |
+ IntRect objectRect = pixelSnappedIntRect(getBoundsInFrameCoordinates()); |
IntPoint scrollPosition = scrollableArea->scrollPosition(); |
IntRect scrollVisibleRect = scrollableArea->visibleContentRect(); |
// Convert the object rect into local coordinates. |
if (!scrollParent->isWebArea()) { |
objectRect.moveBy(scrollPosition); |
- objectRect.moveBy(-pixelSnappedIntRect(scrollParent->elementRect()).location()); |
+ objectRect.moveBy(-pixelSnappedIntRect(scrollParent->getBoundsInFrameCoordinates()).location()); |
} |
int desiredX = computeBestScrollOffset( |
@@ -1510,8 +1572,8 @@ void AXObject::scrollToMakeVisibleWithSubFocus(const IntRect& subfocus) const |
// Convert the subfocus into the coordinates of the scroll parent. |
IntRect newSubfocus = subfocus; |
- IntRect newElementRect = pixelSnappedIntRect(elementRect()); |
- IntRect scrollParentRect = pixelSnappedIntRect(scrollParent->elementRect()); |
+ IntRect newElementRect = pixelSnappedIntRect(getBoundsInFrameCoordinates()); |
+ IntRect scrollParentRect = pixelSnappedIntRect(scrollParent->getBoundsInFrameCoordinates()); |
newSubfocus.move(newElementRect.x(), newElementRect.y()); |
newSubfocus.move(-scrollParentRect.x(), -scrollParentRect.y()); |
@@ -1542,7 +1604,7 @@ void AXObject::scrollToGlobalPoint(const IntPoint& globalPoint) const |
const AXObject* inner = objects[i + 1]; |
ScrollableArea* scrollableArea = outer->getScrollableAreaIfScrollable(); |
- IntRect innerRect = inner->isWebArea() ? pixelSnappedIntRect(inner->parentObject()->elementRect()) : pixelSnappedIntRect(inner->elementRect()); |
+ IntRect innerRect = inner->isWebArea() ? pixelSnappedIntRect(inner->parentObject()->getBoundsInFrameCoordinates()) : pixelSnappedIntRect(inner->getBoundsInFrameCoordinates()); |
IntRect objectRect = innerRect; |
IntPoint scrollPosition = scrollableArea->scrollPosition(); |