OLD | NEW |
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 162 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
173 if (layoutObject->isLayoutInline() && !layoutObject->isAtomicInlineLevel()) | 173 if (layoutObject->isLayoutInline() && !layoutObject->isAtomicInlineLevel()) |
174 return toLayoutInline(layoutObject)->continuation(); | 174 return toLayoutInline(layoutObject)->continuation(); |
175 if (layoutObject->isLayoutBlockFlow()) | 175 if (layoutObject->isLayoutBlockFlow()) |
176 return toLayoutBlockFlow(layoutObject)->inlineElementContinuation(); | 176 return toLayoutBlockFlow(layoutObject)->inlineElementContinuation(); |
177 return 0; | 177 return 0; |
178 } | 178 } |
179 | 179 |
180 AXLayoutObject::AXLayoutObject(LayoutObject* layoutObject, AXObjectCacheImpl& ax
ObjectCache) | 180 AXLayoutObject::AXLayoutObject(LayoutObject* layoutObject, AXObjectCacheImpl& ax
ObjectCache) |
181 : AXNodeObject(layoutObject->node(), axObjectCache) | 181 : AXNodeObject(layoutObject->node(), axObjectCache) |
182 , m_layoutObject(layoutObject) | 182 , m_layoutObject(layoutObject) |
183 , m_cachedElementRectDirty(true) | |
184 { | 183 { |
185 #if ENABLE(ASSERT) | 184 #if ENABLE(ASSERT) |
186 m_layoutObject->setHasAXObject(true); | 185 m_layoutObject->setHasAXObject(true); |
187 #endif | 186 #endif |
188 } | 187 } |
189 | 188 |
190 AXLayoutObject* AXLayoutObject::create(LayoutObject* layoutObject, AXObjectCache
Impl& axObjectCache) | 189 AXLayoutObject* AXLayoutObject::create(LayoutObject* layoutObject, AXObjectCache
Impl& axObjectCache) |
191 { | 190 { |
192 return new AXLayoutObject(layoutObject, axObjectCache); | 191 return new AXLayoutObject(layoutObject, axObjectCache); |
193 } | 192 } |
194 | 193 |
195 AXLayoutObject::~AXLayoutObject() | 194 AXLayoutObject::~AXLayoutObject() |
196 { | 195 { |
197 ASSERT(isDetached()); | 196 ASSERT(isDetached()); |
198 } | 197 } |
199 | 198 |
200 LayoutRect AXLayoutObject::elementRect() const | |
201 { | |
202 if (!m_explicitElementRect.isEmpty()) { | |
203 LayoutRect bounds = m_explicitElementRect; | |
204 AXObject* canvas = axObjectCache().objectFromAXID(m_explicitContainerID)
; | |
205 if (canvas) | |
206 bounds.moveBy(canvas->elementRect().location()); | |
207 return bounds; | |
208 } | |
209 | |
210 // FIXME(dmazzoni): use relative bounds instead since this is a bottleneck. | |
211 // http://crbug.com/618120 | |
212 return computeElementRect(); | |
213 } | |
214 | |
215 SkMatrix44 AXLayoutObject::transformFromLocalParentFrame() const | |
216 { | |
217 if (!m_layoutObject) | |
218 return SkMatrix44(); | |
219 LayoutView* layoutView = toLayoutView(LayoutAPIShim::layoutObjectFrom(docume
ntFrameView()->layoutViewItem())); | |
220 | |
221 FrameView* parentFrameView = documentFrameView()->parentFrameView(); | |
222 if (!parentFrameView) | |
223 return SkMatrix44(); | |
224 LayoutView* parentLayoutView = toLayoutView(LayoutAPIShim::layoutObjectFrom(
parentFrameView->layoutViewItem())); | |
225 | |
226 TransformationMatrix accumulatedTransform = layoutView->localToAncestorTrans
form(parentLayoutView, TraverseDocumentBoundaries); | |
227 IntPoint scrollPosition = documentFrameView()->scrollPosition(); | |
228 accumulatedTransform.translate(scrollPosition.x(), scrollPosition.y()); | |
229 return TransformationMatrix::toSkMatrix44(accumulatedTransform); | |
230 } | |
231 | |
232 LayoutBoxModelObject* AXLayoutObject::getLayoutBoxModelObject() const | 199 LayoutBoxModelObject* AXLayoutObject::getLayoutBoxModelObject() const |
233 { | 200 { |
234 if (!m_layoutObject || !m_layoutObject->isBoxModelObject()) | 201 if (!m_layoutObject || !m_layoutObject->isBoxModelObject()) |
235 return 0; | 202 return 0; |
236 return toLayoutBoxModelObject(m_layoutObject); | 203 return toLayoutBoxModelObject(m_layoutObject); |
237 } | 204 } |
238 | 205 |
239 ScrollableArea* AXLayoutObject::getScrollableAreaIfScrollable() const | 206 ScrollableArea* AXLayoutObject::getScrollableAreaIfScrollable() const |
240 { | 207 { |
241 if (isWebArea()) | 208 if (isWebArea()) |
242 return documentFrameView(); | 209 return documentFrameView(); |
243 | 210 |
244 if (!m_layoutObject || !m_layoutObject->isBox()) | 211 if (!m_layoutObject || !m_layoutObject->isBox()) |
245 return 0; | 212 return 0; |
246 | 213 |
247 LayoutBox* box = toLayoutBox(m_layoutObject); | 214 LayoutBox* box = toLayoutBox(m_layoutObject); |
248 if (!box->canBeScrolledAndHasScrollableArea()) | 215 if (!box->canBeScrolledAndHasScrollableArea()) |
249 return 0; | 216 return 0; |
250 | 217 |
251 return box->getScrollableArea(); | 218 return box->getScrollableArea(); |
252 } | 219 } |
253 | 220 |
254 void AXLayoutObject::getRelativeBounds(AXObject** outContainer, FloatRect& outBo
undsInContainer, SkMatrix44& outContainerTransform) const | |
255 { | |
256 *outContainer = nullptr; | |
257 outBoundsInContainer = FloatRect(); | |
258 outContainerTransform.setIdentity(); | |
259 | |
260 // First check if it has explicit bounds, for example if this element is tie
d to a | |
261 // canvas path. When explicit coordinates are provided, the ID of the explic
it container | |
262 // element that the coordinates are relative to must be provided too. | |
263 if (!m_explicitElementRect.isEmpty()) { | |
264 *outContainer = axObjectCache().objectFromAXID(m_explicitContainerID); | |
265 if (*outContainer) { | |
266 outBoundsInContainer = FloatRect(m_explicitElementRect); | |
267 return; | |
268 } | |
269 } | |
270 | |
271 if (!m_layoutObject) | |
272 return; | |
273 | |
274 if (isWebArea()) { | |
275 if (m_layoutObject->frame()->view()) | |
276 outBoundsInContainer.setSize(FloatSize(m_layoutObject->frame()->view
()->contentsSize())); | |
277 return; | |
278 } | |
279 | |
280 // First compute the container. The container must be an ancestor in the acc
essibility tree, and | |
281 // its LayoutObject must be an ancestor in the layout tree. Get the first su
ch ancestor that's | |
282 // either scrollable or has a paint layer. | |
283 AXObject* container = parentObjectUnignored(); | |
284 LayoutObject* containerLayoutObject = nullptr; | |
285 while (container) { | |
286 containerLayoutObject = container->getLayoutObject(); | |
287 if (containerLayoutObject && containerLayoutObject->isBoxModelObject() &
& m_layoutObject->isDescendantOf(containerLayoutObject)) { | |
288 if (container->isScrollableContainer() || containerLayoutObject->has
Layer()) | |
289 break; | |
290 } | |
291 | |
292 container = container->parentObjectUnignored(); | |
293 } | |
294 | |
295 if (!container) | |
296 return; | |
297 *outContainer = container; | |
298 | |
299 // Next get the local bounds of this LayoutObject, which is typically | |
300 // a rect at point (0, 0) with the width and height of the LayoutObject. | |
301 LayoutRect localBounds; | |
302 if (m_layoutObject->isText()) { | |
303 Vector<FloatQuad> quads; | |
304 toLayoutText(m_layoutObject)->quads(quads, LayoutText::ClipToEllipsis, L
ayoutText::LocalQuads); | |
305 for (const FloatQuad& quad : quads) | |
306 localBounds.unite(LayoutRect(quad.boundingBox())); | |
307 } else if (m_layoutObject->isLayoutInline()) { | |
308 Vector<LayoutRect> rects; | |
309 toLayoutInline(m_layoutObject)->addOutlineRects(rects, LayoutPoint(), La
youtObject::IncludeBlockVisualOverflow); | |
310 localBounds = unionRect(rects); | |
311 } else if (m_layoutObject->isBox()) { | |
312 localBounds = LayoutRect(LayoutPoint(), toLayoutBox(m_layoutObject)->siz
e()); | |
313 } else if (m_layoutObject->isSVG()) { | |
314 localBounds = LayoutRect(m_layoutObject->strokeBoundingBox()); | |
315 } else { | |
316 DCHECK(false); | |
317 } | |
318 outBoundsInContainer = FloatRect(localBounds); | |
319 | |
320 // If the container has a scroll offset, subtract that out because we want o
ur | |
321 // bounds to be relative to the *unscrolled* position of the container objec
t. | |
322 ScrollableArea* scrollableArea = container->getScrollableAreaIfScrollable(); | |
323 if (scrollableArea && !container->isWebArea()) { | |
324 IntPoint scrollPosition = scrollableArea->scrollPosition(); | |
325 outBoundsInContainer.move(FloatSize(scrollPosition.x(), scrollPosition.y
())); | |
326 } | |
327 | |
328 // Compute the transform between the container's coordinate space and this o
bject. | |
329 // If the transform is just a simple translation, apply that to the bounding
box, but | |
330 // if it's a non-trivial transformation like a rotation, scaling, etc. then
return | |
331 // the full matrix instead. | |
332 TransformationMatrix transform = m_layoutObject->localToAncestorTransform(to
LayoutBoxModelObject(containerLayoutObject)); | |
333 if (transform.isIdentityOr2DTranslation()) { | |
334 outBoundsInContainer.move(transform.to2DTranslation()); | |
335 } else { | |
336 outContainerTransform = TransformationMatrix::toSkMatrix44(transform); | |
337 } | |
338 } | |
339 | |
340 static bool isImageOrAltText(LayoutBoxModelObject* box, Node* node) | 221 static bool isImageOrAltText(LayoutBoxModelObject* box, Node* node) |
341 { | 222 { |
342 if (box && box->isImage()) | 223 if (box && box->isImage()) |
343 return true; | 224 return true; |
344 if (isHTMLImageElement(node)) | 225 if (isHTMLImageElement(node)) |
345 return true; | 226 return true; |
346 if (isHTMLInputElement(node) && toHTMLInputElement(node)->hasFallbackContent
()) | 227 if (isHTMLInputElement(node) && toHTMLInputElement(node)->hasFallbackContent
()) |
347 return true; | 228 return true; |
348 return false; | 229 return false; |
349 } | 230 } |
(...skipping 1083 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1433 } | 1314 } |
1434 return elementAttributeValue(aria_atomicAttr); | 1315 return elementAttributeValue(aria_atomicAttr); |
1435 } | 1316 } |
1436 | 1317 |
1437 bool AXLayoutObject::liveRegionBusy() const | 1318 bool AXLayoutObject::liveRegionBusy() const |
1438 { | 1319 { |
1439 return elementAttributeValue(aria_busyAttr); | 1320 return elementAttributeValue(aria_busyAttr); |
1440 } | 1321 } |
1441 | 1322 |
1442 // | 1323 // |
1443 // Position and size. | |
1444 // | |
1445 | |
1446 void AXLayoutObject::checkCachedElementRect() const | |
1447 { | |
1448 if (m_cachedElementRectDirty) | |
1449 return; | |
1450 | |
1451 if (!m_layoutObject) | |
1452 return; | |
1453 | |
1454 if (!m_layoutObject->isBox()) | |
1455 return; | |
1456 | |
1457 bool dirty = false; | |
1458 LayoutBox* box = toLayoutBox(m_layoutObject); | |
1459 if (box->frameRect() != m_cachedFrameRect) | |
1460 dirty = true; | |
1461 | |
1462 if (box->canBeScrolledAndHasScrollableArea()) { | |
1463 ScrollableArea* scrollableArea = box->getScrollableArea(); | |
1464 if (scrollableArea && scrollableArea->scrollPosition() != m_cachedScroll
Position) | |
1465 dirty = true; | |
1466 } | |
1467 | |
1468 if (dirty) | |
1469 markCachedElementRectDirty(); | |
1470 } | |
1471 | |
1472 void AXLayoutObject::updateCachedElementRect() const | |
1473 { | |
1474 if (!m_cachedElementRectDirty) | |
1475 return; | |
1476 | |
1477 if (!m_layoutObject) | |
1478 return; | |
1479 | |
1480 if (!m_layoutObject->isBox()) | |
1481 return; | |
1482 | |
1483 LayoutBox* box = toLayoutBox(m_layoutObject); | |
1484 m_cachedFrameRect = box->frameRect(); | |
1485 | |
1486 if (box->canBeScrolledAndHasScrollableArea()) { | |
1487 ScrollableArea* scrollableArea = box->getScrollableArea(); | |
1488 if (scrollableArea) | |
1489 m_cachedScrollPosition = scrollableArea->scrollPosition(); | |
1490 } | |
1491 | |
1492 m_cachedElementRect = computeElementRect(); | |
1493 m_cachedElementRectDirty = false; | |
1494 } | |
1495 | |
1496 void AXLayoutObject::markCachedElementRectDirty() const | |
1497 { | |
1498 if (m_cachedElementRectDirty) | |
1499 return; | |
1500 | |
1501 // Marks children recursively, if this element changed. | |
1502 m_cachedElementRectDirty = true; | |
1503 for (AXObject* child = rawFirstChild(); child; child = child->rawNextSibling
()) | |
1504 child->markCachedElementRectDirty(); | |
1505 } | |
1506 | |
1507 IntPoint AXLayoutObject::clickPoint() | |
1508 { | |
1509 // Headings are usually much wider than their textual content. If the mid po
int is used, often it can be wrong. | |
1510 if (isHeading() && children().size() == 1) | |
1511 return children()[0]->clickPoint(); | |
1512 | |
1513 // use the default position unless this is an editable web area, in which ca
se we use the selection bounds. | |
1514 if (!isWebArea() || isReadOnly()) | |
1515 return AXObject::clickPoint(); | |
1516 | |
1517 IntRect bounds = pixelSnappedIntRect(elementRect()); | |
1518 return IntPoint(bounds.x() + (bounds.width() / 2), bounds.y() - (bounds.heig
ht() / 2)); | |
1519 } | |
1520 | |
1521 // | |
1522 // Hit testing. | 1324 // Hit testing. |
1523 // | 1325 // |
1524 | 1326 |
1525 AXObject* AXLayoutObject::accessibilityHitTest(const IntPoint& point) const | 1327 AXObject* AXLayoutObject::accessibilityHitTest(const IntPoint& point) const |
1526 { | 1328 { |
1527 if (!m_layoutObject || !m_layoutObject->hasLayer()) | 1329 if (!m_layoutObject || !m_layoutObject->hasLayer()) |
1528 return nullptr; | 1330 return nullptr; |
1529 | 1331 |
1530 PaintLayer* layer = toLayoutBox(m_layoutObject)->layer(); | 1332 PaintLayer* layer = toLayoutBox(m_layoutObject)->layer(); |
1531 | 1333 |
(...skipping 809 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2341 AXObject* AXLayoutObject::accessibilityImageMapHitTest(HTMLAreaElement* area, co
nst IntPoint& point) const | 2143 AXObject* AXLayoutObject::accessibilityImageMapHitTest(HTMLAreaElement* area, co
nst IntPoint& point) const |
2342 { | 2144 { |
2343 if (!area) | 2145 if (!area) |
2344 return 0; | 2146 return 0; |
2345 | 2147 |
2346 AXObject* parent = axObjectCache().getOrCreate(area->imageElement()); | 2148 AXObject* parent = axObjectCache().getOrCreate(area->imageElement()); |
2347 if (!parent) | 2149 if (!parent) |
2348 return 0; | 2150 return 0; |
2349 | 2151 |
2350 for (const auto& child : parent->children()) { | 2152 for (const auto& child : parent->children()) { |
2351 if (child->elementRect().contains(point)) | 2153 if (child->getBoundsInFrameCoordinates().contains(point)) |
2352 return child.get(); | 2154 return child.get(); |
2353 } | 2155 } |
2354 | 2156 |
2355 return 0; | 2157 return 0; |
2356 } | 2158 } |
2357 | 2159 |
2358 LayoutObject* AXLayoutObject::layoutParentObject() const | 2160 LayoutObject* AXLayoutObject::layoutParentObject() const |
2359 { | 2161 { |
2360 if (!m_layoutObject) | 2162 if (!m_layoutObject) |
2361 return 0; | 2163 return 0; |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2415 // remote SVG document. I'm disabling this support until it can be fixed pro
perly. | 2217 // remote SVG document. I'm disabling this support until it can be fixed pro
perly. |
2416 return 0; | 2218 return 0; |
2417 } | 2219 } |
2418 | 2220 |
2419 AXObject* AXLayoutObject::remoteSVGElementHitTest(const IntPoint& point) const | 2221 AXObject* AXLayoutObject::remoteSVGElementHitTest(const IntPoint& point) const |
2420 { | 2222 { |
2421 AXObject* remote = remoteSVGRootElement(); | 2223 AXObject* remote = remoteSVGRootElement(); |
2422 if (!remote) | 2224 if (!remote) |
2423 return 0; | 2225 return 0; |
2424 | 2226 |
2425 IntSize offset = point - roundedIntPoint(elementRect().location()); | 2227 IntSize offset = point - roundedIntPoint(getBoundsInFrameCoordinates().locat
ion()); |
2426 return remote->accessibilityHitTest(IntPoint(offset)); | 2228 return remote->accessibilityHitTest(IntPoint(offset)); |
2427 } | 2229 } |
2428 | 2230 |
2429 // The boundingBox for elements within the remote SVG element needs to be offset
by its position | 2231 // The boundingBox for elements within the remote SVG element needs to be offset
by its position |
2430 // within the parent page, otherwise they are in relative coordinates only. | 2232 // within the parent page, otherwise they are in relative coordinates only. |
2431 void AXLayoutObject::offsetBoundingBoxForRemoteSVGElement(LayoutRect& rect) cons
t | 2233 void AXLayoutObject::offsetBoundingBoxForRemoteSVGElement(LayoutRect& rect) cons
t |
2432 { | 2234 { |
2433 for (AXObject* parent = parentObject(); parent; parent = parent->parentObjec
t()) { | 2235 for (AXObject* parent = parentObject(); parent; parent = parent->parentObjec
t()) { |
2434 if (parent->isAXSVGRoot()) { | 2236 if (parent->isAXSVGRoot()) { |
2435 rect.moveBy(parent->parentObject()->elementRect().location()); | 2237 rect.moveBy(parent->parentObject()->getBoundsInFrameCoordinates().lo
cation()); |
2436 break; | 2238 break; |
2437 } | 2239 } |
2438 } | 2240 } |
2439 } | 2241 } |
2440 | 2242 |
2441 // Hidden children are those that are not laid out or visible, but are specifica
lly marked as aria-hidden=false, | 2243 // Hidden children are those that are not laid out or visible, but are specifica
lly marked as aria-hidden=false, |
2442 // meaning that they should be exposed to the AX hierarchy. | 2244 // meaning that they should be exposed to the AX hierarchy. |
2443 void AXLayoutObject::addHiddenChildren() | 2245 void AXLayoutObject::addHiddenChildren() |
2444 { | 2246 { |
2445 Node* node = this->getNode(); | 2247 Node* node = this->getNode(); |
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2567 } | 2369 } |
2568 | 2370 |
2569 bool AXLayoutObject::elementAttributeValue(const QualifiedName& attributeName) c
onst | 2371 bool AXLayoutObject::elementAttributeValue(const QualifiedName& attributeName) c
onst |
2570 { | 2372 { |
2571 if (!m_layoutObject) | 2373 if (!m_layoutObject) |
2572 return false; | 2374 return false; |
2573 | 2375 |
2574 return equalIgnoringCase(getAttribute(attributeName), "true"); | 2376 return equalIgnoringCase(getAttribute(attributeName), "true"); |
2575 } | 2377 } |
2576 | 2378 |
2577 LayoutRect AXLayoutObject::computeElementRect() const | |
2578 { | |
2579 LayoutObject* obj = m_layoutObject; | |
2580 | |
2581 if (!obj) | |
2582 return LayoutRect(); | |
2583 | |
2584 if (obj->node()) // If we are a continuation, we want to make sure to use th
e primary layoutObject. | |
2585 obj = obj->node()->layoutObject(); | |
2586 | |
2587 // absoluteFocusRingBoundingBox will query the hierarchy below this element,
which for large webpages can be very slow. | |
2588 // For a web area, which will have the most elements of any element, absolut
eQuads should be used. | |
2589 // We should also use absoluteQuads for SVG elements, otherwise transforms w
on't be applied. | |
2590 | |
2591 LayoutRect result; | |
2592 if (obj->isText()) { | |
2593 Vector<FloatQuad> quads; | |
2594 toLayoutText(obj)->quads(quads, LayoutText::ClipToEllipsis, LayoutText::
AbsoluteQuads); | |
2595 result = LayoutRect(boundingBoxForQuads(obj, quads)); | |
2596 } else if (isWebArea() || obj->isSVGRoot()) { | |
2597 result = LayoutRect(obj->absoluteBoundingBoxRect()); | |
2598 } else { | |
2599 result = LayoutRect(obj->absoluteElementBoundingBoxRect()); | |
2600 } | |
2601 | |
2602 Document* document = this->getDocument(); | |
2603 if (document && document->isSVGDocument()) | |
2604 offsetBoundingBoxForRemoteSVGElement(result); | |
2605 if (document && document->frame() && document->frame()->pagePopupOwner()) { | |
2606 IntPoint popupOrigin = document->view()->contentsToScreen(IntRect()).loc
ation(); | |
2607 IntPoint mainOrigin = axObjectCache().rootObject()->documentFrameView()-
>contentsToScreen(IntRect()).location(); | |
2608 result.moveBy(IntPoint(popupOrigin - mainOrigin)); | |
2609 } | |
2610 | |
2611 // The size of the web area should be the content size, not the clipped size
. | |
2612 if (isWebArea() && obj->frame()->view()) | |
2613 result.setSize(LayoutSize(obj->frame()->view()->contentsSize())); | |
2614 | |
2615 // Checkboxes and radio buttons include their labels as part of their rect. | |
2616 if (isCheckboxOrRadio() && isLabelableElement(obj->node())) { | |
2617 LabelsNodeList* labels = toLabelableElement(obj->node())->labels(); | |
2618 if (labels) { | |
2619 for (unsigned labelIndex = 0; labelIndex < labels->length(); ++label
Index) { | |
2620 AXObject* labelAXObject = axObjectCache().getOrCreate(labels->it
em(labelIndex)); | |
2621 if (labelAXObject) { | |
2622 LayoutRect labelRect = labelAXObject->elementRect(); | |
2623 result.unite(labelRect); | |
2624 } | |
2625 } | |
2626 } | |
2627 } | |
2628 | |
2629 return result; | |
2630 } | |
2631 | |
2632 } // namespace blink | 2379 } // namespace blink |
OLD | NEW |