Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(31)

Side by Side Diff: Source/core/accessibility/AccessibilityRenderObject.cpp

Issue 14740025: Simplify and add caching for accessible bounding box calculation. (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Update comment Created 7 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2008 Apple Inc. All rights reserved. 2 * Copyright (C) 2008 Apple Inc. All rights reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions 5 * modification, are permitted provided that the following conditions
6 * are met: 6 * are met:
7 * 7 *
8 * 1. Redistributions of source code must retain the above copyright 8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer. 9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright 10 * 2. Redistributions in binary form must reproduce the above copyright
(...skipping 205 matching lines...) Expand 10 before | Expand all | Expand 10 after
216 if (renderer->isRenderInline() && !renderer->isReplaced()) 216 if (renderer->isRenderInline() && !renderer->isReplaced())
217 return toRenderInline(renderer)->continuation(); 217 return toRenderInline(renderer)->continuation();
218 if (renderer->isRenderBlock()) 218 if (renderer->isRenderBlock())
219 return toRenderBlock(renderer)->inlineElementContinuation(); 219 return toRenderBlock(renderer)->inlineElementContinuation();
220 return 0; 220 return 0;
221 } 221 }
222 222
223 AccessibilityRenderObject::AccessibilityRenderObject(RenderObject* renderer) 223 AccessibilityRenderObject::AccessibilityRenderObject(RenderObject* renderer)
224 : AccessibilityNodeObject(renderer->node()) 224 : AccessibilityNodeObject(renderer->node())
225 , m_renderer(renderer) 225 , m_renderer(renderer)
226 , m_cachedElementRectDirty(true)
226 { 227 {
227 #ifndef NDEBUG 228 #ifndef NDEBUG
228 m_renderer->setHasAXObject(true); 229 m_renderer->setHasAXObject(true);
229 #endif 230 #endif
230 } 231 }
231 232
232 PassRefPtr<AccessibilityRenderObject> AccessibilityRenderObject::create(RenderOb ject* renderer) 233 PassRefPtr<AccessibilityRenderObject> AccessibilityRenderObject::create(RenderOb ject* renderer)
233 { 234 {
234 return adoptRef(new AccessibilityRenderObject(renderer)); 235 return adoptRef(new AccessibilityRenderObject(renderer));
235 } 236 }
236 237
237 AccessibilityRenderObject::~AccessibilityRenderObject() 238 AccessibilityRenderObject::~AccessibilityRenderObject()
238 { 239 {
239 ASSERT(isDetached()); 240 ASSERT(isDetached());
240 } 241 }
241 242
242 LayoutRect AccessibilityRenderObject::elementRect() const 243 LayoutRect AccessibilityRenderObject::elementRect()
243 { 244 {
244 // a checkbox or radio button should encompass its label 245 for (AccessibilityObject* obj = this; obj; obj = obj->parentObject())
245 if (isCheckboxOrRadio()) 246 updateCachedElementRect();
246 return checkboxOrRadioRect();
247 247
248 return boundingBoxRect(); 248 if (m_cachedElementRectDirty) {
249 m_cachedElementRect = computeElementRect();
250 m_cachedElementRectDirty = false;
251 }
252
253 return m_cachedElementRect;
249 } 254 }
250 255
251 int AccessibilityRenderObject::layoutCount() const 256 int AccessibilityRenderObject::layoutCount() const
252 { 257 {
253 if (!m_renderer->isRenderView()) 258 if (!m_renderer->isRenderView())
254 return 0; 259 return 0;
255 return toRenderView(m_renderer)->frameView()->layoutCount(); 260 return toRenderView(m_renderer)->frameView()->layoutCount();
256 } 261 }
257 262
258 void AccessibilityRenderObject::setRenderer(RenderObject* renderer) 263 void AccessibilityRenderObject::setRenderer(RenderObject* renderer)
(...skipping 762 matching lines...) Expand 10 before | Expand all | Expand 10 after
1021 if (isAnchor()) { 1026 if (isAnchor()) {
1022 AccessibilityObject* linkedAXElement = internalLinkElement(); 1027 AccessibilityObject* linkedAXElement = internalLinkElement();
1023 if (linkedAXElement) 1028 if (linkedAXElement)
1024 linkedUIElements.append(linkedAXElement); 1029 linkedUIElements.append(linkedAXElement);
1025 } 1030 }
1026 1031
1027 if (roleValue() == RadioButtonRole) 1032 if (roleValue() == RadioButtonRole)
1028 addRadioButtonGroupMembers(linkedUIElements); 1033 addRadioButtonGroupMembers(linkedUIElements);
1029 } 1034 }
1030 1035
1031 AccessibilityOrientation AccessibilityRenderObject::orientation() const 1036 AccessibilityOrientation AccessibilityRenderObject::orientation()
1032 { 1037 {
1033 const AtomicString& ariaOrientation = getAttribute(aria_orientationAttr); 1038 const AtomicString& ariaOrientation = getAttribute(aria_orientationAttr);
1034 if (equalIgnoringCase(ariaOrientation, "horizontal")) 1039 if (equalIgnoringCase(ariaOrientation, "horizontal"))
1035 return AccessibilityOrientationHorizontal; 1040 return AccessibilityOrientationHorizontal;
1036 if (equalIgnoringCase(ariaOrientation, "vertical")) 1041 if (equalIgnoringCase(ariaOrientation, "vertical"))
1037 return AccessibilityOrientationVertical; 1042 return AccessibilityOrientationVertical;
1038 1043
1039 return AccessibilityObject::orientation(); 1044 return AccessibilityObject::orientation();
1040 } 1045 }
1041 1046
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after
1146 case CheckBoxRole: 1151 case CheckBoxRole:
1147 return isChecked() ? checkedCheckBoxAction : uncheckedCheckBoxAction; 1152 return isChecked() ? checkedCheckBoxAction : uncheckedCheckBoxAction;
1148 case LinkRole: 1153 case LinkRole:
1149 case WebCoreLinkRole: 1154 case WebCoreLinkRole:
1150 return linkAction; 1155 return linkAction;
1151 default: 1156 default:
1152 return noAction; 1157 return noAction;
1153 } 1158 }
1154 } 1159 }
1155 1160
1156 LayoutRect AccessibilityRenderObject::checkboxOrRadioRect() const
1157 {
1158 if (!m_renderer)
1159 return LayoutRect();
1160
1161 HTMLLabelElement* label = labelForElement(toElement(m_renderer->node()));
1162 if (!label || !label->renderer())
1163 return boundingBoxRect();
1164
1165 LayoutRect labelRect = axObjectCache()->getOrCreate(label)->elementRect();
1166 labelRect.unite(boundingBoxRect());
1167 return labelRect;
1168 }
1169
1170 void AccessibilityRenderObject::selectedChildren(AccessibilityChildrenVector& re sult) 1161 void AccessibilityRenderObject::selectedChildren(AccessibilityChildrenVector& re sult)
1171 { 1162 {
1172 ASSERT(result.isEmpty()); 1163 ASSERT(result.isEmpty());
1173 1164
1174 // only listboxes should be asked for their selected children. 1165 // only listboxes should be asked for their selected children.
1175 AccessibilityRole role = roleValue(); 1166 AccessibilityRole role = roleValue();
1176 if (role == ListBoxRole) // native list boxes would be AccessibilityListBoxe s, so only check for aria list boxes 1167 if (role == ListBoxRole) // native list boxes would be AccessibilityListBoxe s, so only check for aria list boxes
1177 ariaListboxSelectedChildren(result); 1168 ariaListboxSelectedChildren(result);
1178 else if (role == TreeRole || role == TreeGridRole || role == TableRole) 1169 else if (role == TreeRole || role == TreeGridRole || role == TableRole)
1179 ariaSelectedRows(result); 1170 ariaSelectedRows(result);
(...skipping 346 matching lines...) Expand 10 before | Expand all | Expand 10 after
1526 } 1517 }
1527 } 1518 }
1528 1519
1529 return String(); 1520 return String();
1530 } 1521 }
1531 1522
1532 // 1523 //
1533 // Position and size. 1524 // Position and size.
1534 // 1525 //
1535 1526
1536 LayoutRect AccessibilityRenderObject::boundingBoxRect() const 1527 void AccessibilityRenderObject::updateCachedElementRect()
1537 { 1528 {
1538 RenderObject* obj = m_renderer; 1529 if (!renderer())
1530 return;
1539 1531
1540 if (!obj) 1532 if (!renderer()->isBox()) {
1541 return LayoutRect(); 1533 AccessibilityNodeObject::updateCachedElementRect();
1534 return;
1535 }
1542 1536
1543 if (obj->node()) // If we are a continuation, we want to make sure to use th e primary renderer. 1537 RenderBox* box = toRenderBox(m_renderer);
1544 obj = obj->node()->renderer(); 1538 if (box->frameRect() != m_cachedFrameRect) {
1539 m_cachedFrameRect = box->frameRect();
1540 markCachedElementRectDirty();
1541 }
1542 }
1545 1543
1546 // absoluteFocusRingQuads will query the hierarchy below this element, which for large webpages can be very slow. 1544 void AccessibilityRenderObject::markCachedElementRectDirty()
1547 // For a web area, which will have the most elements of any element, absolut eQuads should be used. 1545 {
1548 // We should also use absoluteQuads for SVG elements, otherwise transforms w on't be applied. 1546 if (!m_cachedElementRectDirty) {
Julien - ping for review 2013/05/03 20:54:20 WebKit favored early returns to avoid unneeded nes
dmazzoni 2013/05/06 04:58:01 Done.
1549 Vector<FloatQuad> quads; 1547 m_cachedElementRectDirty = true;
1550 bool isSVGRoot = false; 1548 // Marks children recursively, if this element changed.
1551 #if ENABLE(SVG) 1549 AccessibilityNodeObject::markCachedElementRectDirty();
1552 if (obj->isSVGRoot()) 1550 }
1553 isSVGRoot = true;
1554 #endif
1555 if (obj->isText())
1556 toRenderText(obj)->absoluteQuads(quads, 0, RenderText::ClipToEllipsis);
1557 else if (isWebArea() || isSeamlessWebArea() || isSVGRoot)
1558 obj->absoluteQuads(quads);
1559 else
1560 obj->absoluteFocusRingQuads(quads);
1561
1562 LayoutRect result = boundingBoxForQuads(obj, quads);
1563
1564 #if ENABLE(SVG)
1565 Document* document = this->document();
1566 if (document && document->isSVGDocument())
1567 offsetBoundingBoxForRemoteSVGElement(result);
1568 #endif
1569
1570 // The size of the web area should be the content size, not the clipped size .
1571 if ((isWebArea() || isSeamlessWebArea()) && obj->frame()->view())
1572 result.setSize(obj->frame()->view()->contentsSize());
1573
1574 return result;
1575 } 1551 }
1576 1552
1577 IntPoint AccessibilityRenderObject::clickPoint() 1553 IntPoint AccessibilityRenderObject::clickPoint()
1578 { 1554 {
1579 // Headings are usually much wider than their textual content. If the mid po int is used, often it can be wrong. 1555 // Headings are usually much wider than their textual content. If the mid po int is used, often it can be wrong.
1580 if (isHeading() && children().size() == 1) 1556 if (isHeading() && children().size() == 1)
1581 return children()[0]->clickPoint(); 1557 return children()[0]->clickPoint();
1582 1558
1583 // use the default position unless this is an editable web area, in which ca se we use the selection bounds. 1559 // use the default position unless this is an editable web area, in which ca se we use the selection bounds.
1584 if (!isWebArea() || isReadOnly()) 1560 if (!isWebArea() || isReadOnly())
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
1629 AccessibilityObject* controlObject = result->correspondingControlForLabe lElement(); 1605 AccessibilityObject* controlObject = result->correspondingControlForLabe lElement();
1630 if (controlObject && !controlObject->exposesTitleUIElement()) 1606 if (controlObject && !controlObject->exposesTitleUIElement())
1631 return controlObject; 1607 return controlObject;
1632 1608
1633 result = result->parentObjectUnignored(); 1609 result = result->parentObjectUnignored();
1634 } 1610 }
1635 1611
1636 return result; 1612 return result;
1637 } 1613 }
1638 1614
1639 AccessibilityObject* AccessibilityRenderObject::elementAccessibilityHitTest(cons t IntPoint& point) const 1615 AccessibilityObject* AccessibilityRenderObject::elementAccessibilityHitTest(cons t IntPoint& point)
1640 { 1616 {
1641 if (isSVGImage()) 1617 if (isSVGImage())
1642 return remoteSVGElementHitTest(point); 1618 return remoteSVGElementHitTest(point);
1643 1619
1644 return AccessibilityObject::elementAccessibilityHitTest(point); 1620 return AccessibilityObject::elementAccessibilityHitTest(point);
1645 } 1621 }
1646 1622
1647 // 1623 //
1648 // High-level accessibility tree access. 1624 // High-level accessibility tree access.
1649 // 1625 //
(...skipping 1226 matching lines...) Expand 10 before | Expand all | Expand 10 after
2876 ASSERT(rootSVGObject && rootSVGObject->isAccessibilitySVGRoot()); 2852 ASSERT(rootSVGObject && rootSVGObject->isAccessibilitySVGRoot());
2877 if (!rootSVGObject->isAccessibilitySVGRoot()) 2853 if (!rootSVGObject->isAccessibilitySVGRoot())
2878 return 0; 2854 return 0;
2879 2855
2880 return toAccessibilitySVGRoot(rootSVGObject); 2856 return toAccessibilitySVGRoot(rootSVGObject);
2881 #else 2857 #else
2882 return 0; 2858 return 0;
2883 #endif 2859 #endif
2884 } 2860 }
2885 2861
2886 AccessibilityObject* AccessibilityRenderObject::remoteSVGElementHitTest(const In tPoint& point) const 2862 AccessibilityObject* AccessibilityRenderObject::remoteSVGElementHitTest(const In tPoint& point)
2887 { 2863 {
2888 AccessibilityObject* remote = remoteSVGRootElement(); 2864 AccessibilityObject* remote = remoteSVGRootElement();
2889 if (!remote) 2865 if (!remote)
2890 return 0; 2866 return 0;
2891 2867
2892 IntSize offset = point - roundedIntPoint(boundingBoxRect().location()); 2868 IntSize offset = point - roundedIntPoint(elementRect().location());
2893 return remote->accessibilityHitTest(IntPoint(offset)); 2869 return remote->accessibilityHitTest(IntPoint(offset));
2894 } 2870 }
2895 2871
2896 // The boundingBox for elements within the remote SVG element needs to be offset by its position 2872 // The boundingBox for elements within the remote SVG element needs to be offset by its position
2897 // within the parent page, otherwise they are in relative coordinates only. 2873 // within the parent page, otherwise they are in relative coordinates only.
2898 void AccessibilityRenderObject::offsetBoundingBoxForRemoteSVGElement(LayoutRect& rect) const 2874 void AccessibilityRenderObject::offsetBoundingBoxForRemoteSVGElement(LayoutRect& rect) const
2899 { 2875 {
2900 for (AccessibilityObject* parent = parentObject(); parent; parent = parent-> parentObject()) { 2876 for (AccessibilityObject* parent = parentObject(); parent; parent = parent-> parentObject()) {
2901 if (parent->isAccessibilitySVGRoot()) { 2877 if (parent->isAccessibilitySVGRoot()) {
2902 rect.moveBy(parent->parentObject()->boundingBoxRect().location()); 2878 rect.moveBy(parent->parentObject()->elementRect().location());
2903 break; 2879 break;
2904 } 2880 }
2905 } 2881 }
2906 } 2882 }
2907 2883
2908 // Hidden children are those that are not rendered or visible, but are specifica lly marked as aria-hidden=false, 2884 // Hidden children are those that are not rendered or visible, but are specifica lly marked as aria-hidden=false,
2909 // meaning that they should be exposed to the AX hierarchy. 2885 // meaning that they should be exposed to the AX hierarchy.
2910 void AccessibilityRenderObject::addHiddenChildren() 2886 void AccessibilityRenderObject::addHiddenChildren()
2911 { 2887 {
2912 Node* node = this->node(); 2888 Node* node = this->node();
(...skipping 209 matching lines...) Expand 10 before | Expand all | Expand 10 after
3122 3098
3123 // If native tag of the parent element matches an acceptable name, then return 3099 // If native tag of the parent element matches an acceptable name, then return
3124 // based on its presentational status. 3100 // based on its presentational status.
3125 if (possibleParentTagNames->contains(toElement(elementNode)->tagQName()) ) 3101 if (possibleParentTagNames->contains(toElement(elementNode)->tagQName()) )
3126 return parent->roleValue() == PresentationalRole; 3102 return parent->roleValue() == PresentationalRole;
3127 } 3103 }
3128 3104
3129 return false; 3105 return false;
3130 } 3106 }
3131 3107
3108 LayoutRect AccessibilityRenderObject::computeElementRect()
3109 {
3110 RenderObject* obj = m_renderer;
3111
3112 if (!obj)
3113 return LayoutRect();
3114
3115 if (obj->node()) // If we are a continuation, we want to make sure to use th e primary renderer.
3116 obj = obj->node()->renderer();
3117
3118 // absoluteFocusRingQuads will query the hierarchy below this element, which for large webpages can be very slow.
3119 // For a web area, which will have the most elements of any element, absolut eQuads should be used.
3120 // We should also use absoluteQuads for SVG elements, otherwise transforms w on't be applied.
3121 Vector<FloatQuad> quads;
3122 bool isSVGRoot = false;
3123 #if ENABLE(SVG)
3124 if (obj->isSVGRoot())
3125 isSVGRoot = true;
3126 #endif
3127 if (obj->isText())
3128 toRenderText(obj)->absoluteQuads(quads, 0, RenderText::ClipToEllipsis);
3129 else if (isWebArea() || isSeamlessWebArea() || isSVGRoot)
3130 obj->absoluteQuads(quads);
3131 else
3132 obj->absoluteFocusRingQuads(quads);
3133
3134 LayoutRect result = boundingBoxForQuads(obj, quads);
3135
3136 #if ENABLE(SVG)
3137 Document* document = this->document();
3138 if (document && document->isSVGDocument())
3139 offsetBoundingBoxForRemoteSVGElement(result);
3140 #endif
3141
3142 // The size of the web area should be the content size, not the clipped size .
3143 if ((isWebArea() || isSeamlessWebArea()) && obj->frame()->view())
3144 result.setSize(obj->frame()->view()->contentsSize());
3145
3146 // Checkboxes and radio buttons include their label as part of their rect.
3147 if (isCheckboxOrRadio()) {
3148 HTMLLabelElement* label = labelForElement(toElement(m_renderer->node())) ;
3149 if (label && !label->renderer()) {
3150 LayoutRect labelRect = axObjectCache()->getOrCreate(label)->elementR ect();
3151 result.unite(labelRect);
3152 }
3153 }
3154
3155 return result;
3156 }
3157
3132 } // namespace WebCore 3158 } // namespace WebCore
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698