Changeset 195240 in webkit
- Timestamp:
- Jan 18, 2016 4:56:13 PM (8 years ago)
- Location:
- trunk
- Files:
-
- 4 added
- 9 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r195185 r195240 1 2016-01-18 Nan Wang <n_wang@apple.com> 2 3 AX: [Mac] Implement next/previous text marker functions using TextIterator 4 https://bugs.webkit.org/show_bug.cgi?id=152728 5 6 Reviewed by Chris Fleizach. 7 8 * accessibility/mac/previous-next-text-marker-expected.txt: Added. 9 * accessibility/mac/previous-next-text-marker.html: Added. 10 * accessibility/mac/text-marker-with-user-select-none-expected.txt: Added. 11 * accessibility/mac/text-marker-with-user-select-none.html: Added. 12 1 13 2016-01-17 Simon Fraser <simon.fraser@apple.com> 2 14 -
trunk/Source/WebCore/ChangeLog
r195239 r195240 1 2016-01-18 Nan Wang <n_wang@apple.com> 2 3 AX: [Mac] Implement next/previous text marker functions using TextIterator 4 https://bugs.webkit.org/show_bug.cgi?id=152728 5 6 Reviewed by Chris Fleizach. 7 8 The existing AXTextMarker based calls are implemented using visible position, and that introduced 9 some bugs which make VoiceOver working incorrectly on Mac sometimes. Since TextIterator uses rendering 10 position, we tried to use it to refactor those AXTextMarker based calls. 11 In this patch, I implemented functions to navigate to previous/next text marker using Range and TextIterator. 12 Also added a conversion between visible position and character offset to make sure unconverted text marker 13 related functions are still working correctly. 14 15 Tests: accessibility/mac/previous-next-text-marker.html 16 accessibility/mac/text-marker-with-user-select-none.html 17 18 * accessibility/AXObjectCache.cpp: 19 (WebCore::AXObjectCache::visiblePositionForTextMarkerData): 20 (WebCore::AXObjectCache::traverseToOffsetInRange): 21 (WebCore::AXObjectCache::lengthForRange): 22 (WebCore::AXObjectCache::rangeForNodeContents): 23 (WebCore::characterOffsetsInOrder): 24 (WebCore::AXObjectCache::rangeForUnorderedCharacterOffsets): 25 (WebCore::AXObjectCache::setTextMarkerDataWithCharacterOffset): 26 (WebCore::AXObjectCache::startOrEndTextMarkerDataForRange): 27 (WebCore::AXObjectCache::textMarkerDataForCharacterOffset): 28 (WebCore::AXObjectCache::nextNode): 29 (WebCore::AXObjectCache::previousNode): 30 (WebCore::AXObjectCache::visiblePositionFromCharacterOffset): 31 (WebCore::AXObjectCache::characterOffsetFromVisiblePosition): 32 (WebCore::AXObjectCache::accessibilityObjectForTextMarkerData): 33 (WebCore::AXObjectCache::textMarkerDataForVisiblePosition): 34 * accessibility/AXObjectCache.h: 35 (WebCore::CharacterOffset::CharacterOffset): 36 (WebCore::CharacterOffset::remaining): 37 (WebCore::CharacterOffset::isNull): 38 (WebCore::AXObjectCache::setNodeInUse): 39 (WebCore::AXObjectCache::removeNodeForUse): 40 (WebCore::AXObjectCache::isNodeInUse): 41 * accessibility/AccessibilityObject.cpp: 42 (WebCore::AccessibilityObject::selectionRange): 43 (WebCore::AccessibilityObject::elementRange): 44 (WebCore::AccessibilityObject::selectText): 45 (WebCore::AccessibilityObject::lineRangeForPosition): 46 (WebCore::AccessibilityObject::replacedNodeNeedsCharacter): 47 (WebCore::renderListItemContainerForNode): 48 (WebCore::listMarkerTextForNode): 49 (WebCore::AccessibilityObject::listMarkerTextForNodeAndPosition): 50 (WebCore::AccessibilityObject::stringForRange): 51 (WebCore::AccessibilityObject::stringForVisiblePositionRange): 52 (WebCore::replacedNodeNeedsCharacter): Deleted. 53 * accessibility/AccessibilityObject.h: 54 (WebCore::AccessibilityObject::visiblePositionRange): 55 (WebCore::AccessibilityObject::visiblePositionRangeForLine): 56 (WebCore::AccessibilityObject::boundsForVisiblePositionRange): 57 (WebCore::AccessibilityObject::setSelectedVisiblePositionRange): 58 * accessibility/mac/WebAccessibilityObjectWrapperMac.mm: 59 (isTextMarkerIgnored): 60 (-[WebAccessibilityObjectWrapper accessibilityObjectForTextMarker:]): 61 (accessibilityObjectForTextMarker): 62 (-[WebAccessibilityObjectWrapper textMarkerRangeFromRange:]): 63 (textMarkerRangeFromRange): 64 (-[WebAccessibilityObjectWrapper startOrEndTextMarkerForRange:isStart:]): 65 (startOrEndTextmarkerForRange): 66 (-[WebAccessibilityObjectWrapper nextTextMarkerForNode:offset:]): 67 (-[WebAccessibilityObjectWrapper previousTextMarkerForNode:offset:]): 68 (-[WebAccessibilityObjectWrapper textMarkerForNode:offset:]): 69 (textMarkerForCharacterOffset): 70 (-[WebAccessibilityObjectWrapper rangeForTextMarkerRange:]): 71 (-[WebAccessibilityObjectWrapper characterOffsetForTextMarker:]): 72 (textMarkerForVisiblePosition): 73 (-[WebAccessibilityObjectWrapper accessibilityAttributeValue:forParameter:]): 74 1 75 2016-01-18 Olivier Blin <olivier.blin@softathome.com> 2 76 -
trunk/Source/WebCore/accessibility/AXObjectCache.cpp
r194496 r195240 82 82 #include "RenderView.h" 83 83 #include "ScrollView.h" 84 #include "TextIterator.h" 84 85 #include <wtf/DataLog.h> 85 86 … … 1419 1420 } 1420 1421 1422 CharacterOffset AXObjectCache::traverseToOffsetInRange(RefPtr<Range>range, int offset, bool toNodeEnd, bool stayWithinRange) 1423 { 1424 if (!range) 1425 return CharacterOffset(); 1426 1427 int offsetInCharacter = 0; 1428 int offsetSoFar = 0; 1429 int remaining = 0; 1430 int lastLength = 0; 1431 Node* currentNode = nullptr; 1432 bool finished = false; 1433 int lastStartOffset = 0; 1434 1435 TextIterator iterator(range.get()); 1436 1437 // When the range has zero length, there might be replaced node or brTag that we need to increment the characterOffset. 1438 if (iterator.atEnd()) { 1439 currentNode = &range->startContainer(); 1440 lastStartOffset = range->startOffset(); 1441 if (offset > 0 || toNodeEnd) { 1442 if (AccessibilityObject::replacedNodeNeedsCharacter(currentNode) || (currentNode->renderer() && currentNode->renderer()->isBR())) 1443 offsetSoFar++; 1444 lastLength = offsetSoFar; 1445 1446 // When going backwards, stayWithinRange is false. 1447 // Here when we don't have any character to move and we are going backwards, we traverse to the previous node. 1448 if (!lastLength && toNodeEnd && !stayWithinRange) { 1449 if (Node* preNode = previousNode(currentNode)) 1450 return traverseToOffsetInRange(rangeForNodeContents(preNode), offset, toNodeEnd); 1451 return CharacterOffset(); 1452 } 1453 } 1454 } 1455 1456 for (; !iterator.atEnd(); iterator.advance()) { 1457 int currentLength = iterator.text().length(); 1458 1459 Node& node = iterator.range()->startContainer(); 1460 currentNode = &node; 1461 // When currentLength == 0, we check if there's any replaced node. 1462 // If not, we skip the node with no length. 1463 if (!currentLength) { 1464 int subOffset = iterator.range()->startOffset(); 1465 Node* childNode = node.traverseToChildAt(subOffset); 1466 if (AccessibilityObject::replacedNodeNeedsCharacter(childNode)) { 1467 offsetSoFar++; 1468 currentLength++; 1469 currentNode = childNode; 1470 } else 1471 continue; 1472 } else { 1473 // Ignore space, new line, tag node. 1474 if (currentLength == 1 && isSpaceOrNewline(iterator.text()[0])) 1475 continue; 1476 offsetSoFar += currentLength; 1477 } 1478 1479 lastLength = currentLength; 1480 lastStartOffset = iterator.range()->startOffset(); 1481 1482 // Break early if we have advanced enough characters. 1483 if (!toNodeEnd && offsetSoFar >= offset) { 1484 offsetInCharacter = offset - (offsetSoFar - currentLength); 1485 finished = true; 1486 break; 1487 } 1488 } 1489 1490 if (!finished) { 1491 offsetInCharacter = lastLength; 1492 if (!toNodeEnd) 1493 remaining = offset - offsetSoFar; 1494 } 1495 1496 return CharacterOffset(currentNode, lastStartOffset, offsetInCharacter, remaining); 1497 } 1498 1499 int AXObjectCache::lengthForRange(Range* range) 1500 { 1501 if (!range) 1502 return -1; 1503 1504 int length = 0; 1505 for (TextIterator it(range); !it.atEnd(); it.advance()) { 1506 // non-zero length means textual node, zero length means replaced node (AKA "attachments" in AX) 1507 if (it.text().length()) 1508 length += it.text().length(); 1509 else { 1510 // locate the node and starting offset for this replaced range 1511 Node& node = it.range()->startContainer(); 1512 int offset = it.range()->startOffset(); 1513 if (AccessibilityObject::replacedNodeNeedsCharacter(node.traverseToChildAt(offset))) 1514 ++length; 1515 } 1516 } 1517 1518 return length; 1519 } 1520 1521 RefPtr<Range> AXObjectCache::rangeForNodeContents(Node* node) 1522 { 1523 if (!node) 1524 return nullptr; 1525 1526 Document* document = &node->document(); 1527 if (!document) 1528 return nullptr; 1529 RefPtr<Range> range = Range::create(*document); 1530 ExceptionCode ec = 0; 1531 range->selectNodeContents(node, ec); 1532 return ec ? nullptr : range; 1533 } 1534 1535 static bool characterOffsetsInOrder(const CharacterOffset& characterOffset1, const CharacterOffset& characterOffset2) 1536 { 1537 if (characterOffset1.isNull() || characterOffset2.isNull()) 1538 return false; 1539 1540 if (characterOffset1.node == characterOffset2.node) 1541 return characterOffset1.offset <= characterOffset2.offset; 1542 1543 RefPtr<Range> range1 = AXObjectCache::rangeForNodeContents(characterOffset1.node); 1544 RefPtr<Range> range2 = AXObjectCache::rangeForNodeContents(characterOffset2.node); 1545 return range1->compareBoundaryPoints(Range::START_TO_START, range2.get(), IGNORE_EXCEPTION) <= 0; 1546 } 1547 1548 RefPtr<Range> AXObjectCache::rangeForUnorderedCharacterOffsets(const CharacterOffset& characterOffset1, const CharacterOffset& characterOffset2) 1549 { 1550 if (characterOffset1.isNull() || characterOffset2.isNull()) 1551 return nullptr; 1552 1553 bool alreadyInOrder = characterOffsetsInOrder(characterOffset1, characterOffset2); 1554 CharacterOffset startCharacterOffset = alreadyInOrder ? characterOffset1 : characterOffset2; 1555 CharacterOffset endCharacterOffset = alreadyInOrder ? characterOffset2 : characterOffset1; 1556 1557 int endOffset = endCharacterOffset.offset; 1558 1559 // endOffset can be out of bounds sometimes if the node is a replaced node or has brTag. 1560 if (startCharacterOffset.node == endCharacterOffset.node) { 1561 RefPtr<Range> nodeRange = AXObjectCache::rangeForNodeContents(startCharacterOffset.node); 1562 int nodeLength = TextIterator::rangeLength(nodeRange.get()); 1563 if (endOffset > nodeLength) 1564 endOffset = nodeLength; 1565 } 1566 1567 int startOffset = startCharacterOffset.startIndex + startCharacterOffset.offset; 1568 endOffset = endCharacterOffset.startIndex + endOffset; 1569 1570 // If start node is a replaced node and it has children, we want to include the replaced node itself in the range. 1571 Node* startNode = startCharacterOffset.node; 1572 if (AccessibilityObject::replacedNodeNeedsCharacter(startNode) && (startNode->hasChildNodes() || startNode != endCharacterOffset.node)) { 1573 startOffset = startNode->computeNodeIndex(); 1574 startNode = startNode->parentNode(); 1575 } 1576 1577 RefPtr<Range> result = Range::create(m_document); 1578 ExceptionCode ecStart = 0, ecEnd = 0; 1579 result->setStart(startNode, startOffset, ecStart); 1580 result->setEnd(endCharacterOffset.node, endOffset, ecEnd); 1581 if (ecStart || ecEnd) 1582 return nullptr; 1583 1584 return result; 1585 } 1586 1587 void AXObjectCache::setTextMarkerDataWithCharacterOffset(TextMarkerData& textMarkerData, const CharacterOffset& characterOffset) 1588 { 1589 if (characterOffset.isNull()) 1590 return; 1591 1592 Node* domNode = characterOffset.node; 1593 if (is<HTMLInputElement>(*domNode) && downcast<HTMLInputElement>(*domNode).isPasswordField()) { 1594 textMarkerData.ignored = true; 1595 return; 1596 } 1597 1598 RefPtr<AccessibilityObject> obj = this->getOrCreate(domNode); 1599 1600 // Convert to visible position. 1601 VisiblePosition visiblePosition = visiblePositionFromCharacterOffset(obj.get(), characterOffset); 1602 int vpOffset = 0; 1603 if (!visiblePosition.isNull()) { 1604 Position deepPos = visiblePosition.deepEquivalent(); 1605 vpOffset = deepPos.deprecatedEditingOffset(); 1606 } 1607 1608 textMarkerData.axID = obj.get()->axObjectID(); 1609 textMarkerData.node = domNode; 1610 textMarkerData.characterOffset = characterOffset.offset; 1611 textMarkerData.characterStartIndex = characterOffset.startIndex; 1612 textMarkerData.offset = vpOffset; 1613 textMarkerData.affinity = visiblePosition.affinity(); 1614 1615 this->setNodeInUse(domNode); 1616 } 1617 1618 void AXObjectCache::startOrEndTextMarkerDataForRange(TextMarkerData& textMarkerData, RefPtr<Range> range, bool isStart) 1619 { 1620 memset(&textMarkerData, 0, sizeof(TextMarkerData)); 1621 1622 if (!range) 1623 return; 1624 1625 // If it's end text marker, we want to go to the end of the range, and stay within the range. 1626 bool stayWithinRange = !isStart; 1627 1628 // Change the start of the range, so the character offset starts from node beginning. 1629 int offset = 0; 1630 Node* node = &range->startContainer(); 1631 if (node->offsetInCharacters()) { 1632 CharacterOffset nodeStartOffset = traverseToOffsetInRange(rangeForNodeContents(node), 0, false); 1633 offset = std::max(range->startOffset() - nodeStartOffset.startIndex, 0); 1634 range->setStart(node, nodeStartOffset.startIndex); 1635 } 1636 1637 CharacterOffset characterOffset = traverseToOffsetInRange(range, offset, !isStart, stayWithinRange); 1638 setTextMarkerDataWithCharacterOffset(textMarkerData, characterOffset); 1639 } 1640 1641 void AXObjectCache::textMarkerDataForCharacterOffset(TextMarkerData& textMarkerData, Node& node, int offset, bool toNodeEnd) 1642 { 1643 memset(&textMarkerData, 0, sizeof(TextMarkerData)); 1644 1645 Node* domNode = &node; 1646 if (!domNode) 1647 return; 1648 1649 // If offset <= 0, means we want to go to the previous node. 1650 if (offset <= 0 && !toNodeEnd) { 1651 // Set the offset to the amount of characters we need to go backwards. 1652 offset = - offset + 1; 1653 while (offset > 0 && textMarkerData.characterOffset <= offset) { 1654 offset -= textMarkerData.characterOffset; 1655 domNode = previousNode(domNode); 1656 if (domNode) { 1657 textMarkerDataForCharacterOffset(textMarkerData, *domNode, 0, true); 1658 offset--; 1659 } else 1660 return; 1661 } 1662 if (offset > 0) 1663 textMarkerDataForCharacterOffset(textMarkerData, *domNode, offset, false); 1664 return; 1665 } 1666 1667 RefPtr<Range> range = rangeForNodeContents(domNode); 1668 1669 // Traverse the offset amount of characters forward and see if there's remaining offsets. 1670 // Keep traversing to the next node when there's remaining offsets. 1671 CharacterOffset characterOffset = traverseToOffsetInRange(range, offset, toNodeEnd); 1672 while (!characterOffset.isNull() && characterOffset.remaining() && !toNodeEnd) { 1673 domNode = nextNode(domNode); 1674 if (!domNode) 1675 return; 1676 range = rangeForNodeContents(domNode); 1677 characterOffset = traverseToOffsetInRange(range, characterOffset.remaining(), toNodeEnd); 1678 } 1679 1680 setTextMarkerDataWithCharacterOffset(textMarkerData, characterOffset); 1681 } 1682 1683 Node* AXObjectCache::nextNode(Node* node) const 1684 { 1685 if (!node) 1686 return nullptr; 1687 1688 return NodeTraversal::nextSkippingChildren(*node); 1689 } 1690 1691 Node* AXObjectCache::previousNode(Node* node) const 1692 { 1693 if (!node) 1694 return nullptr; 1695 1696 // First child of body shouldn't have previous node. 1697 if (node->parentNode() && node->parentNode()->renderer() && node->parentNode()->renderer()->isBody() && !node->previousSibling()) 1698 return nullptr; 1699 1700 return NodeTraversal::previousSkippingChildren(*node); 1701 } 1702 1703 VisiblePosition AXObjectCache::visiblePositionFromCharacterOffset(AccessibilityObject* obj, const CharacterOffset& characterOffset) 1704 { 1705 if (!obj) 1706 return VisiblePosition(); 1707 1708 // nextVisiblePosition means advancing one character. Use this to calculate the character offset. 1709 VisiblePositionRange vpRange = obj->visiblePositionRange(); 1710 VisiblePosition start = vpRange.start; 1711 VisiblePosition result = start; 1712 for (int i = 0; i < characterOffset.offset; i++) 1713 result = obj->nextVisiblePosition(result); 1714 1715 return result; 1716 } 1717 1718 CharacterOffset AXObjectCache::characterOffsetFromVisiblePosition(AccessibilityObject* obj, const VisiblePosition& visiblePos) 1719 { 1720 if (!obj) 1721 return 0; 1722 1723 // Use nextVisiblePosition to calculate how many characters we need to traverse to the current position. 1724 Position deepPos = visiblePos.deepEquivalent(); 1725 VisiblePositionRange vpRange = obj->visiblePositionRange(); 1726 VisiblePosition vp = vpRange.start; 1727 int characterOffset = 0; 1728 Position vpDeepPos = vp.deepEquivalent(); 1729 1730 while (!vpDeepPos.isNull() && !deepPos.equals(vpDeepPos)) { 1731 vp = obj->nextVisiblePosition(vp); 1732 vpDeepPos = vp.deepEquivalent(); 1733 characterOffset++; 1734 } 1735 1736 return traverseToOffsetInRange(rangeForNodeContents(obj->node()), characterOffset, false); 1737 } 1738 1739 AccessibilityObject* AXObjectCache::accessibilityObjectForTextMarkerData(TextMarkerData& textMarkerData) 1740 { 1741 if (!isNodeInUse(textMarkerData.node)) 1742 return nullptr; 1743 1744 Node* domNode = textMarkerData.node; 1745 return this->getOrCreate(domNode); 1746 } 1747 1421 1748 void AXObjectCache::textMarkerDataForVisiblePosition(TextMarkerData& textMarkerData, const VisiblePosition& visiblePos) 1422 1749 { … … 1444 1771 textMarkerData.node = domNode; 1445 1772 textMarkerData.offset = deepPos.deprecatedEditingOffset(); 1446 textMarkerData.affinity = visiblePos.affinity(); 1773 textMarkerData.affinity = visiblePos.affinity(); 1774 1775 // convert to character offset 1776 CharacterOffset characterOffset = characterOffsetFromVisiblePosition(obj.get(), visiblePos); 1777 textMarkerData.characterOffset = characterOffset.offset; 1778 textMarkerData.characterStartIndex = characterOffset.startIndex; 1447 1779 1448 1780 cache->setNodeInUse(domNode); -
trunk/Source/WebCore/accessibility/AXObjectCache.h
r191931 r195240 52 52 Node* node; 53 53 int offset; 54 int characterStartIndex; 55 int characterOffset; 56 bool ignored; 54 57 EAffinity affinity; 58 }; 59 60 struct CharacterOffset { 61 Node* node; 62 int startIndex; 63 int offset; 64 int remainingOffset; 65 66 CharacterOffset(Node* n = nullptr, int startIndex = 0, int offset = 0, int remaining = 0) 67 : node(n) 68 , startIndex(startIndex) 69 , offset(offset) 70 , remainingOffset(remaining) 71 { } 72 73 int remaining() const { return remainingOffset; } 74 bool isNull() const { return !node; } 55 75 }; 56 76 … … 165 185 void textMarkerDataForVisiblePosition(TextMarkerData&, const VisiblePosition&); 166 186 VisiblePosition visiblePositionForTextMarkerData(TextMarkerData&); 187 void textMarkerDataForCharacterOffset(TextMarkerData&, Node&, int, bool toNodeEnd = false); 188 void startOrEndTextMarkerDataForRange(TextMarkerData&, RefPtr<Range>, bool); 189 AccessibilityObject* accessibilityObjectForTextMarkerData(TextMarkerData&); 190 RefPtr<Range> rangeForUnorderedCharacterOffsets(const CharacterOffset&, const CharacterOffset&); 191 static RefPtr<Range> rangeForNodeContents(Node*); 192 static int lengthForRange(Range*); 167 193 168 194 enum AXNotification { … … 255 281 void removeNodeForUse(Node* n) { m_textMarkerNodes.remove(n); } 256 282 bool isNodeInUse(Node* n) { return m_textMarkerNodes.contains(n); } 283 284 Node* nextNode(Node*) const; 285 Node* previousNode(Node*) const; 286 CharacterOffset traverseToOffsetInRange(RefPtr<Range>, int, bool, bool stayWithinRange = false); 287 VisiblePosition visiblePositionFromCharacterOffset(AccessibilityObject*, const CharacterOffset&); 288 CharacterOffset characterOffsetFromVisiblePosition(AccessibilityObject*, const VisiblePosition&); 289 void setTextMarkerDataWithCharacterOffset(TextMarkerData&, const CharacterOffset&); 257 290 258 291 private: -
trunk/Source/WebCore/accessibility/AccessibilityObject.cpp
r194496 r195240 716 716 } 717 717 718 RefPtr<Range> AccessibilityObject::elementRange() const 719 { 720 return AXObjectCache::rangeForNodeContents(node()); 721 } 722 718 723 String AccessibilityObject::selectText(AccessibilitySelectTextCriteria* criteria) 719 724 { … … 1204 1209 } 1205 1210 1206 static boolreplacedNodeNeedsCharacter(Node* replacedNode)1211 bool AccessibilityObject::replacedNodeNeedsCharacter(Node* replacedNode) 1207 1212 { 1208 1213 // we should always be given a rendered node and a replaced node, but be safe … … 1229 1234 return nullptr; 1230 1235 } 1231 1236 1237 static String listMarkerTextForNode(Node* node) 1238 { 1239 RenderListItem* listItem = renderListItemContainerForNode(node); 1240 if (!listItem) 1241 return String(); 1242 1243 // If this is in a list item, we need to manually add the text for the list marker 1244 // because a RenderListMarker does not have a Node equivalent and thus does not appear 1245 // when iterating text. 1246 return listItem->markerTextWithSuffix(); 1247 } 1248 1232 1249 // Returns the text associated with a list marker if this node is contained within a list item. 1233 1250 String AccessibilityObject::listMarkerTextForNodeAndPosition(Node* node, const VisiblePosition& visiblePositionStart) const … … 1237 1254 return String(); 1238 1255 1239 RenderListItem* listItem = renderListItemContainerForNode(node); 1240 if (!listItem) 1256 return listMarkerTextForNode(node); 1257 } 1258 1259 String AccessibilityObject::stringForRange(RefPtr<Range> range) const 1260 { 1261 if (!range) 1241 1262 return String(); 1242 1243 // If this is in a list item, we need to manually add the text for the list marker 1244 // because a RenderListMarker does not have a Node equivalent and thus does not appear 1245 // when iterating text. 1246 return listItem->markerTextWithSuffix(); 1263 1264 TextIterator it(range.get()); 1265 if (it.atEnd()) 1266 return String(); 1267 1268 StringBuilder builder; 1269 for (; !it.atEnd(); it.advance()) { 1270 // non-zero length means textual node, zero length means replaced node (AKA "attachments" in AX) 1271 if (it.text().length()) { 1272 // Add a textual representation for list marker text. 1273 builder.append(listMarkerTextForNode(it.node())); 1274 it.appendTextToStringBuilder(builder); 1275 } else { 1276 // locate the node and starting offset for this replaced range 1277 Node& node = it.range()->startContainer(); 1278 ASSERT(&node == &it.range()->endContainer()); 1279 int offset = it.range()->startOffset(); 1280 if (replacedNodeNeedsCharacter(node.traverseToChildAt(offset))) 1281 builder.append(objectReplacementCharacter); 1282 } 1283 } 1284 1285 return builder.toString(); 1247 1286 } 1248 1287 -
trunk/Source/WebCore/accessibility/AccessibilityObject.h
r194496 r195240 818 818 virtual VisiblePositionRange visiblePositionRangeForLine(unsigned) const { return VisiblePositionRange(); } 819 819 820 RefPtr<Range> elementRange() const; 821 static bool replacedNodeNeedsCharacter(Node* replacedNode); 822 820 823 VisiblePositionRange visiblePositionRangeForUnorderedPositions(const VisiblePosition&, const VisiblePosition&) const; 821 824 VisiblePositionRange positionOfLeftWord(const VisiblePosition&) const; … … 830 833 831 834 String stringForVisiblePositionRange(const VisiblePositionRange&) const; 835 String stringForRange(RefPtr<Range>) const; 832 836 virtual IntRect boundsForVisiblePositionRange(const VisiblePositionRange&) const { return IntRect(); } 833 837 int lengthForVisiblePositionRange(const VisiblePositionRange&) const; -
trunk/Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperMac.mm
r194318 r195240 782 782 783 783 #pragma mark Text Marker helpers 784 785 static bool isTextMarkerIgnored(id textMarker) 786 { 787 if (!textMarker) 788 return false; 789 790 TextMarkerData textMarkerData; 791 if (!wkGetBytesFromAXTextMarker(textMarker, &textMarkerData, sizeof(textMarkerData))) 792 return false; 793 794 return textMarkerData.ignored; 795 } 796 797 - (AccessibilityObject*)accessibilityObjectForTextMarker:(id)textMarker 798 { 799 return accessibilityObjectForTextMarker(m_object->axObjectCache(), textMarker); 800 } 801 802 static AccessibilityObject* accessibilityObjectForTextMarker(AXObjectCache* cache, id textMarker) 803 { 804 if (!textMarker || !cache || isTextMarkerIgnored(textMarker)) 805 return nullptr; 806 807 TextMarkerData textMarkerData; 808 if (!wkGetBytesFromAXTextMarker(textMarker, &textMarkerData, sizeof(textMarkerData))) 809 return nullptr; 810 return cache->accessibilityObjectForTextMarkerData(textMarkerData); 811 } 812 813 - (id)textMarkerRangeFromRange:(const RefPtr<Range>)range 814 { 815 return textMarkerRangeFromRange(m_object->axObjectCache(), range); 816 } 817 818 static id textMarkerRangeFromRange(AXObjectCache *cache, const RefPtr<Range> range) 819 { 820 id startTextMarker = startOrEndTextmarkerForRange(cache, range, true); 821 id endTextMarker = startOrEndTextmarkerForRange(cache, range, false); 822 return textMarkerRangeFromMarkers(startTextMarker, endTextMarker); 823 } 824 825 - (id)startOrEndTextMarkerForRange:(const RefPtr<Range>)range isStart:(BOOL)isStart 826 { 827 return startOrEndTextmarkerForRange(m_object->axObjectCache(), range, isStart); 828 } 829 830 static id startOrEndTextmarkerForRange(AXObjectCache* cache, RefPtr<Range> range, bool isStart) 831 { 832 if (!cache) 833 return nil; 834 835 TextMarkerData textMarkerData; 836 cache->startOrEndTextMarkerDataForRange(textMarkerData, range, isStart); 837 if (!textMarkerData.axID) 838 return nil; 839 840 return CFBridgingRelease(wkCreateAXTextMarker(&textMarkerData, sizeof(textMarkerData))); 841 } 842 843 - (id)nextTextMarkerForNode:(Node&)node offset:(int)offset 844 { 845 int nextOffset = offset + 1; 846 id textMarker = [self textMarkerForNode:node offset:nextOffset]; 847 if (isTextMarkerIgnored(textMarker)) 848 textMarker = [self nextTextMarkerForNode:node offset:nextOffset]; 849 return textMarker; 850 } 851 852 - (id)previousTextMarkerForNode:(Node&)node offset:(int)offset 853 { 854 int previousOffset = offset - 1; 855 id textMarker = [self textMarkerForNode:node offset:previousOffset]; 856 if (isTextMarkerIgnored(textMarker)) 857 textMarker = [self previousTextMarkerForNode:node offset:previousOffset]; 858 return textMarker; 859 } 860 861 - (id)textMarkerForNode:(Node&)node offset:(int)offset 862 { 863 return textMarkerForCharacterOffset(m_object->axObjectCache(), node, offset); 864 } 865 866 static id textMarkerForCharacterOffset(AXObjectCache* cache, Node& node, int offset, bool toNodeEnd = false) 867 { 868 if (!cache) 869 return nil; 870 871 Node* domNode = &node; 872 if (!domNode) 873 return nil; 874 875 TextMarkerData textMarkerData; 876 cache->textMarkerDataForCharacterOffset(textMarkerData, node, offset, toNodeEnd); 877 if (!textMarkerData.axID && !textMarkerData.ignored) 878 return nil; 879 880 return CFBridgingRelease(wkCreateAXTextMarker(&textMarkerData, sizeof(textMarkerData))); 881 } 882 883 - (RefPtr<Range>)rangeForTextMarkerRange:(id)textMarkerRange 884 { 885 if (!textMarkerRange) 886 return nullptr; 887 888 id startTextMarker = AXTextMarkerRangeStart(textMarkerRange); 889 id endTextMarker = AXTextMarkerRangeEnd(textMarkerRange); 890 891 if (!startTextMarker || !endTextMarker) 892 return nullptr; 893 894 AXObjectCache* cache = m_object->axObjectCache(); 895 if (!cache) 896 return nullptr; 897 898 CharacterOffset startCharacterOffset = [self characterOffsetForTextMarker:startTextMarker]; 899 CharacterOffset endCharacterOffset = [self characterOffsetForTextMarker:endTextMarker]; 900 return cache->rangeForUnorderedCharacterOffsets(startCharacterOffset, endCharacterOffset); 901 } 902 903 - (CharacterOffset)characterOffsetForTextMarker:(id)textMarker 904 { 905 if (!textMarker || isTextMarkerIgnored(textMarker)) 906 return CharacterOffset(); 907 908 TextMarkerData textMarkerData; 909 if (!wkGetBytesFromAXTextMarker(textMarker, &textMarkerData, sizeof(textMarkerData))) 910 return CharacterOffset(); 911 912 return CharacterOffset(textMarkerData.node, textMarkerData.characterStartIndex, textMarkerData.characterOffset); 913 } 784 914 785 915 static id textMarkerForVisiblePosition(AXObjectCache* cache, const VisiblePosition& visiblePos) … … 3829 3959 3830 3960 if ([attribute isEqualToString:@"AXUIElementForTextMarker"]) { 3831 VisiblePosition visiblePos = [self visiblePositionForTextMarker:(textMarker)]; 3832 AccessibilityObject* axObject = m_object->accessibilityObjectForPosition(visiblePos); 3961 AccessibilityObject* axObject = [self accessibilityObjectForTextMarker:textMarker]; 3833 3962 if (!axObject) 3834 3963 return nil; … … 3837 3966 3838 3967 if ([attribute isEqualToString:@"AXTextMarkerRangeForUIElement"]) { 3839 VisiblePositionRange vpRange = uiElement.get()->visiblePositionRange();3840 return [self textMarkerRangeFrom VisiblePositions:vpRange.start endPosition:vpRange.end];3968 RefPtr<Range> range = uiElement.get()->elementRange(); 3969 return [self textMarkerRangeFromRange:range]; 3841 3970 } 3842 3971 … … 3852 3981 3853 3982 if ([attribute isEqualToString:@"AXStringForTextMarkerRange"]) { 3854 VisiblePositionRange visiblePosRange = [self visiblePositionRangeForTextMarkerRange:textMarkerRange];3855 return m_object->stringFor VisiblePositionRange(visiblePosRange);3983 RefPtr<Range> range = [self rangeForTextMarkerRange:textMarkerRange]; 3984 return m_object->stringForRange(range); 3856 3985 } 3857 3986 … … 3896 4025 return nil; 3897 4026 3898 VisiblePosition visiblePos1 = [self visiblePositionForTextMarker:(textMarker1)]; 3899 VisiblePosition visiblePos2 = [self visiblePositionForTextMarker:(textMarker2)]; 3900 VisiblePositionRange vpRange = m_object->visiblePositionRangeForUnorderedPositions(visiblePos1, visiblePos2); 3901 return [self textMarkerRangeFromVisiblePositions:vpRange.start endPosition:vpRange.end]; 4027 AXObjectCache* cache = m_object->axObjectCache(); 4028 if (!cache) 4029 return nil; 4030 CharacterOffset characterOffset1 = [self characterOffsetForTextMarker:textMarker1]; 4031 CharacterOffset characterOffset2 = [self characterOffsetForTextMarker:textMarker2]; 4032 RefPtr<Range> range = cache->rangeForUnorderedCharacterOffsets(characterOffset1, characterOffset2); 4033 return [self textMarkerRangeFromRange:range]; 3902 4034 } 3903 4035 3904 4036 if ([attribute isEqualToString:@"AXNextTextMarkerForTextMarker"]) { 3905 VisiblePosition visiblePos = [self visiblePositionForTextMarker:(textMarker)];3906 return [self textMarkerForVisiblePosition:m_object->nextVisiblePosition(visiblePos)];4037 CharacterOffset characterOffset = [self characterOffsetForTextMarker:textMarker]; 4038 return [self nextTextMarkerForNode:*characterOffset.node offset:characterOffset.offset]; 3907 4039 } 3908 4040 3909 4041 if ([attribute isEqualToString:@"AXPreviousTextMarkerForTextMarker"]) { 3910 VisiblePosition visiblePos = [self visiblePositionForTextMarker:(textMarker)];3911 return [self textMarkerForVisiblePosition:m_object->previousVisiblePosition(visiblePos)];4042 CharacterOffset characterOffset = [self characterOffsetForTextMarker:textMarker]; 4043 return [self previousTextMarkerForNode:*characterOffset.node offset:characterOffset.offset]; 3912 4044 } 3913 4045 … … 3995 4127 3996 4128 if ([attribute isEqualToString:@"AXLengthForTextMarkerRange"]) { 3997 VisiblePositionRange visiblePosRange = [self visiblePositionRangeForTextMarkerRange:textMarkerRange];3998 int length = m_object->lengthForVisiblePositionRange(visiblePosRange);4129 RefPtr<Range> range = [self rangeForTextMarkerRange:textMarkerRange]; 4130 int length = AXObjectCache::lengthForRange(range.get()); 3999 4131 if (length < 0) 4000 4132 return nil; … … 4004 4136 // Used only by DumpRenderTree (so far). 4005 4137 if ([attribute isEqualToString:@"AXStartTextMarkerForTextMarkerRange"]) { 4006 VisiblePositionRange visiblePosRange = [self visiblePositionRangeForTextMarkerRange:textMarkerRange];4007 return [self textMarkerForVisiblePosition:visiblePosRange.start];4138 RefPtr<Range> range = [self rangeForTextMarkerRange:textMarkerRange]; 4139 return [self startOrEndTextMarkerForRange:range isStart:YES]; 4008 4140 } 4009 4141 4010 4142 if ([attribute isEqualToString:@"AXEndTextMarkerForTextMarkerRange"]) { 4011 VisiblePositionRange visiblePosRange = [self visiblePositionRangeForTextMarkerRange:textMarkerRange];4012 return [self textMarkerForVisiblePosition:visiblePosRange.end];4143 RefPtr<Range> range = [self rangeForTextMarkerRange:textMarkerRange]; 4144 return [self startOrEndTextMarkerForRange:range isStart:NO]; 4013 4145 } 4014 4146 -
trunk/Tools/ChangeLog
r195232 r195240 1 2016-01-18 Nan Wang <n_wang@apple.com> 2 3 AX: [Mac] Implement next/previous text marker functions using TextIterator 4 https://bugs.webkit.org/show_bug.cgi?id=152728 5 6 Reviewed by Chris Fleizach. 7 8 * WebKitTestRunner/InjectedBundle/mac/AccessibilityUIElementMac.mm: 9 (WTR::AccessibilityUIElement::accessibilityElementForTextMarker): 10 1 11 2016-01-18 Csaba Osztrogonác <ossy@webkit.org> 2 12 -
trunk/Tools/WebKitTestRunner/InjectedBundle/mac/AccessibilityUIElementMac.mm
r194364 r195240 1802 1802 BEGIN_AX_OBJC_EXCEPTIONS 1803 1803 id uiElement = [m_element accessibilityAttributeValue:@"AXUIElementForTextMarker" forParameter:(id)marker->platformTextMarker()]; 1804 return AccessibilityUIElement::create(uiElement); 1804 if (uiElement) 1805 return AccessibilityUIElement::create(uiElement); 1805 1806 END_AX_OBJC_EXCEPTIONS 1806 1807
Note: See TracChangeset
for help on using the changeset viewer.