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

Side by Side Diff: third_party/WebKit/Source/modules/accessibility/AXLayoutObject.cpp

Issue 2745713002: WIP: Modified AXPosition to work with objects with both embedded object characters and text. (Closed)
Patch Set: Simplified and cleaned up selection code in Blink > Accessibility. Created 3 years, 6 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
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 19 matching lines...) Expand all
30 30
31 #include "bindings/core/v8/ExceptionState.h" 31 #include "bindings/core/v8/ExceptionState.h"
32 #include "core/CSSPropertyNames.h" 32 #include "core/CSSPropertyNames.h"
33 #include "core/InputTypeNames.h" 33 #include "core/InputTypeNames.h"
34 #include "core/dom/AccessibleNode.h" 34 #include "core/dom/AccessibleNode.h"
35 #include "core/dom/ElementTraversal.h" 35 #include "core/dom/ElementTraversal.h"
36 #include "core/dom/Range.h" 36 #include "core/dom/Range.h"
37 #include "core/dom/shadow/ShadowRoot.h" 37 #include "core/dom/shadow/ShadowRoot.h"
38 #include "core/editing/EditingUtilities.h" 38 #include "core/editing/EditingUtilities.h"
39 #include "core/editing/FrameSelection.h" 39 #include "core/editing/FrameSelection.h"
40 #include "core/editing/RenderedPosition.h"
41 #include "core/editing/TextAffinity.h" 40 #include "core/editing/TextAffinity.h"
42 #include "core/editing/VisibleUnits.h" 41 #include "core/editing/VisibleUnits.h"
43 #include "core/editing/iterators/CharacterIterator.h" 42 #include "core/editing/iterators/CharacterIterator.h"
44 #include "core/editing/iterators/TextIterator.h" 43 #include "core/editing/iterators/TextIterator.h"
45 #include "core/frame/FrameOwner.h" 44 #include "core/frame/FrameOwner.h"
46 #include "core/frame/ImageBitmap.h" 45 #include "core/frame/ImageBitmap.h"
47 #include "core/frame/LocalFrame.h" 46 #include "core/frame/LocalFrame.h"
48 #include "core/frame/LocalFrameView.h" 47 #include "core/frame/LocalFrameView.h"
49 #include "core/frame/Settings.h" 48 #include "core/frame/Settings.h"
50 #include "core/html/HTMLCanvasElement.h" 49 #include "core/html/HTMLCanvasElement.h"
(...skipping 1665 matching lines...) Expand 10 before | Expand all | Expand 10 after
1716 for (Node& runner : NodeTraversal::InclusiveAncestorsOf(*node)) { 1715 for (Node& runner : NodeTraversal::InclusiveAncestorsOf(*node)) {
1717 if (isHTMLAnchorElement(runner) || 1716 if (isHTMLAnchorElement(runner) ||
1718 (runner.GetLayoutObject() && 1717 (runner.GetLayoutObject() &&
1719 cache.GetOrCreate(runner.GetLayoutObject())->IsAnchor())) 1718 cache.GetOrCreate(runner.GetLayoutObject())->IsAnchor()))
1720 return ToElement(&runner); 1719 return ToElement(&runner);
1721 } 1720 }
1722 1721
1723 return 0; 1722 return 0;
1724 } 1723 }
1725 1724
1726 //
1727 // Functions that retrieve the current selection.
1728 //
1729
1730 AXObjectImpl::AXRange AXLayoutObject::Selection() const {
1731 AXRange text_selection = TextControlSelection();
1732 if (text_selection.IsValid())
1733 return text_selection;
1734
1735 if (!GetLayoutObject() || !GetLayoutObject()->GetFrame())
1736 return AXRange();
1737
1738 VisibleSelection selection =
1739 GetLayoutObject()
1740 ->GetFrame()
1741 ->Selection()
1742 .ComputeVisibleSelectionInDOMTreeDeprecated();
1743 if (selection.IsNone())
1744 return AXRange();
1745
1746 VisiblePosition visible_start = selection.VisibleStart();
1747 Position start = visible_start.ToParentAnchoredPosition();
1748 TextAffinity start_affinity = visible_start.Affinity();
1749 VisiblePosition visible_end = selection.VisibleEnd();
1750 Position end = visible_end.ToParentAnchoredPosition();
1751 TextAffinity end_affinity = visible_end.Affinity();
1752
1753 Node* anchor_node = start.AnchorNode();
1754 DCHECK(anchor_node);
1755
1756 AXLayoutObject* anchor_object = nullptr;
1757 // Find the closest node that has a corresponding AXObjectImpl.
1758 // This is because some nodes may be aria hidden or might not even have
1759 // a layout object if they are part of the shadow DOM.
1760 while (anchor_node) {
1761 anchor_object = GetUnignoredObjectFromNode(*anchor_node);
1762 if (anchor_object)
1763 break;
1764
1765 if (anchor_node->nextSibling())
1766 anchor_node = anchor_node->nextSibling();
1767 else
1768 anchor_node = anchor_node->parentNode();
1769 }
1770
1771 Node* focus_node = end.AnchorNode();
1772 DCHECK(focus_node);
1773
1774 AXLayoutObject* focus_object = nullptr;
1775 while (focus_node) {
1776 focus_object = GetUnignoredObjectFromNode(*focus_node);
1777 if (focus_object)
1778 break;
1779
1780 if (focus_node->previousSibling())
1781 focus_node = focus_node->previousSibling();
1782 else
1783 focus_node = focus_node->parentNode();
1784 }
1785
1786 if (!anchor_object || !focus_object)
1787 return AXRange();
1788
1789 int anchor_offset = anchor_object->IndexForVisiblePosition(visible_start);
1790 DCHECK_GE(anchor_offset, 0);
1791 int focus_offset = focus_object->IndexForVisiblePosition(visible_end);
1792 DCHECK_GE(focus_offset, 0);
1793 return AXRange(anchor_object, anchor_offset, start_affinity, focus_object,
1794 focus_offset, end_affinity);
1795 }
1796
1797 // Gets only the start and end offsets of the selection computed using the
1798 // current object as the starting point. Returns a null selection if there is
1799 // no selection in the subtree rooted at this object.
1800 AXObjectImpl::AXRange AXLayoutObject::SelectionUnderObject() const {
1801 AXRange text_selection = TextControlSelection();
1802 if (text_selection.IsValid())
1803 return text_selection;
1804
1805 if (!GetNode() || !GetLayoutObject()->GetFrame())
1806 return AXRange();
1807
1808 VisibleSelection selection =
1809 GetLayoutObject()
1810 ->GetFrame()
1811 ->Selection()
1812 .ComputeVisibleSelectionInDOMTreeDeprecated();
1813 Range* selection_range = CreateRange(FirstEphemeralRangeOf(selection));
1814 ContainerNode* parent_node = GetNode()->parentNode();
1815 int node_index = GetNode()->NodeIndex();
1816 if (!selection_range
1817 // Selection is contained in node.
1818 || !(parent_node &&
1819 selection_range->comparePoint(parent_node, node_index,
1820 IGNORE_EXCEPTION_FOR_TESTING) < 0 &&
1821 selection_range->comparePoint(parent_node, node_index + 1,
1822 IGNORE_EXCEPTION_FOR_TESTING) > 0)) {
1823 return AXRange();
1824 }
1825
1826 int start = IndexForVisiblePosition(selection.VisibleStart());
1827 DCHECK_GE(start, 0);
1828 int end = IndexForVisiblePosition(selection.VisibleEnd());
1829 DCHECK_GE(end, 0);
1830
1831 return AXRange(start, end);
1832 }
1833
1834 AXObjectImpl::AXRange AXLayoutObject::TextControlSelection() const {
1835 if (!GetLayoutObject())
1836 return AXRange();
1837
1838 LayoutObject* layout = nullptr;
1839 if (GetLayoutObject()->IsTextControl()) {
1840 layout = GetLayoutObject();
1841 } else {
1842 Element* focused_element = GetDocument()->FocusedElement();
1843 if (focused_element && focused_element->GetLayoutObject() &&
1844 focused_element->GetLayoutObject()->IsTextControl())
1845 layout = focused_element->GetLayoutObject();
1846 }
1847
1848 if (!layout)
1849 return AXRange();
1850
1851 AXObjectImpl* ax_object = AxObjectCache().GetOrCreate(layout);
1852 if (!ax_object || !ax_object->IsAXLayoutObject())
1853 return AXRange();
1854
1855 VisibleSelection selection =
1856 layout->GetFrame()
1857 ->Selection()
1858 .ComputeVisibleSelectionInDOMTreeDeprecated();
1859 TextControlElement* text_control =
1860 ToLayoutTextControl(layout)->GetTextControlElement();
1861 DCHECK(text_control);
1862 int start = text_control->selectionStart();
1863 int end = text_control->selectionEnd();
1864
1865 return AXRange(ax_object, start, selection.VisibleStart().Affinity(),
1866 ax_object, end, selection.VisibleEnd().Affinity());
1867 }
1868
1869 int AXLayoutObject::IndexForVisiblePosition(
1870 const VisiblePosition& position) const {
1871 if (GetLayoutObject() && GetLayoutObject()->IsTextControl()) {
1872 TextControlElement* text_control =
1873 ToLayoutTextControl(GetLayoutObject())->GetTextControlElement();
1874 return text_control->IndexForVisiblePosition(position);
1875 }
1876
1877 if (!GetNode())
1878 return 0;
1879
1880 Position index_position = position.DeepEquivalent();
1881 if (index_position.IsNull())
1882 return 0;
1883
1884 Range* range = Range::Create(*GetDocument());
1885 range->setStart(GetNode(), 0, IGNORE_EXCEPTION_FOR_TESTING);
1886 range->setEnd(index_position, IGNORE_EXCEPTION_FOR_TESTING);
1887
1888 return TextIterator::RangeLength(range->StartPosition(),
1889 range->EndPosition());
1890 }
1891
1892 AXLayoutObject* AXLayoutObject::GetUnignoredObjectFromNode(Node& node) const {
1893 if (IsDetached())
1894 return nullptr;
1895
1896 AXObjectImpl* ax_object = AxObjectCache().GetOrCreate(&node);
1897 if (!ax_object)
1898 return nullptr;
1899
1900 if (ax_object->IsAXLayoutObject() && !ax_object->AccessibilityIsIgnored())
1901 return ToAXLayoutObject(ax_object);
1902
1903 return nullptr;
1904 }
1905
1906 //
1907 // Modify or take an action on an object.
1908 //
1909
1910 // Convert from an accessible object and offset to a VisiblePosition.
1911 static VisiblePosition ToVisiblePosition(AXObjectImpl* obj, int offset) {
1912 if (!obj->GetNode())
1913 return VisiblePosition();
1914
1915 Node* node = obj->GetNode();
1916 if (!node->IsTextNode()) {
1917 int child_count = obj->Children().size();
1918
1919 // Place position immediately before the container node, if there was no
1920 // children.
1921 if (child_count == 0) {
1922 if (!obj->ParentObject())
1923 return VisiblePosition();
1924 return ToVisiblePosition(obj->ParentObject(), obj->IndexInParent());
1925 }
1926
1927 // The offsets are child offsets over the AX tree. Note that we allow
1928 // for the offset to equal the number of children as |Range| does.
1929 if (offset < 0 || offset > child_count)
1930 return VisiblePosition();
1931
1932 // Clamp to between 0 and child count - 1.
1933 int clamped_offset =
1934 static_cast<unsigned>(offset) > (obj->Children().size() - 1)
1935 ? offset - 1
1936 : offset;
1937 AXObjectImpl* child_obj = obj->Children()[clamped_offset];
1938 Node* child_node = child_obj->GetNode();
1939 if (!child_node || !child_node->parentNode())
1940 return VisiblePosition();
1941
1942 // The index in parent.
1943 int adjusted_offset = child_node->NodeIndex();
1944
1945 // If we had to clamp the offset above, the client wants to select the
1946 // end of the node.
1947 if (clamped_offset != offset)
1948 adjusted_offset++;
1949
1950 return CreateVisiblePosition(
1951 Position::EditingPositionOf(child_node->parentNode(), adjusted_offset));
1952 }
1953
1954 // If it is a text node, we need to call some utility functions that use a
1955 // TextIterator to walk the characters of the node and figure out the position
1956 // corresponding to the visible character at position |offset|.
1957 ContainerNode* parent = node->parentNode();
1958 if (!parent)
1959 return VisiblePosition();
1960
1961 VisiblePosition node_position = blink::VisiblePositionBeforeNode(*node);
1962 int node_index = blink::IndexForVisiblePosition(node_position, parent);
1963 return blink::VisiblePositionForIndex(node_index + offset, parent);
1964 }
1965
1966 void AXLayoutObject::SetSelection(const AXRange& selection) {
1967 if (!GetLayoutObject() || !selection.IsValid())
1968 return;
1969
1970 AXObjectImpl* anchor_object =
1971 selection.anchor_object ? selection.anchor_object.Get() : this;
1972 AXObjectImpl* focus_object =
1973 selection.focus_object ? selection.focus_object.Get() : this;
1974
1975 if (!IsValidSelectionBound(anchor_object) ||
1976 !IsValidSelectionBound(focus_object)) {
1977 return;
1978 }
1979
1980 // The selection offsets are offsets into the accessible value.
1981 if (anchor_object == focus_object &&
1982 anchor_object->GetLayoutObject()->IsTextControl()) {
1983 TextControlElement* text_control =
1984 ToLayoutTextControl(anchor_object->GetLayoutObject())
1985 ->GetTextControlElement();
1986 if (selection.anchor_offset <= selection.focus_offset) {
1987 text_control->SetSelectionRange(selection.anchor_offset,
1988 selection.focus_offset,
1989 kSelectionHasForwardDirection);
1990 } else {
1991 text_control->SetSelectionRange(selection.focus_offset,
1992 selection.anchor_offset,
1993 kSelectionHasBackwardDirection);
1994 }
1995 return;
1996 }
1997
1998 LocalFrame* frame = GetLayoutObject()->GetFrame();
1999 if (!frame)
2000 return;
2001
2002 // TODO(editing-dev): Use of updateStyleAndLayoutIgnorePendingStylesheets
2003 // needs to be audited. see http://crbug.com/590369 for more details.
2004 // This callsite should probably move up the stack.
2005 frame->GetDocument()->UpdateStyleAndLayoutIgnorePendingStylesheets();
2006
2007 // Set the selection based on visible positions, because the offsets in
2008 // accessibility nodes are based on visible indexes, which often skips
2009 // redundant whitespace, for example.
2010 VisiblePosition anchor_visible_position =
2011 ToVisiblePosition(anchor_object, selection.anchor_offset);
2012 VisiblePosition focus_visible_position =
2013 ToVisiblePosition(focus_object, selection.focus_offset);
2014 if (anchor_visible_position.IsNull() || focus_visible_position.IsNull())
2015 return;
2016
2017 frame->Selection().SetSelection(
2018 SelectionInDOMTree::Builder()
2019 .Collapse(anchor_visible_position.ToPositionWithAffinity())
2020 .Extend(focus_visible_position.DeepEquivalent())
2021 .Build());
2022 }
2023
2024 bool AXLayoutObject::IsValidSelectionBound(
2025 const AXObjectImpl* bound_object) const {
2026 return GetLayoutObject() && bound_object && !bound_object->IsDetached() &&
2027 bound_object->IsAXLayoutObject() && bound_object->GetLayoutObject() &&
2028 bound_object->GetLayoutObject()->GetFrame() ==
2029 GetLayoutObject()->GetFrame() &&
2030 &bound_object->AxObjectCache() == &AxObjectCache();
2031 }
2032
2033 void AXLayoutObject::SetValue(const String& string) { 1725 void AXLayoutObject::SetValue(const String& string) {
2034 if (!GetNode() || !GetNode()->IsElementNode()) 1726 if (!GetNode() || !GetNode()->IsElementNode())
2035 return; 1727 return;
2036 if (!layout_object_ || !layout_object_->IsBoxModelObject()) 1728 if (!layout_object_ || !layout_object_->IsBoxModelObject())
2037 return; 1729 return;
2038 1730
2039 LayoutBoxModelObject* layout_object = ToLayoutBoxModelObject(layout_object_); 1731 LayoutBoxModelObject* layout_object = ToLayoutBoxModelObject(layout_object_);
2040 if (layout_object->IsTextField() && isHTMLInputElement(*GetNode())) 1732 if (layout_object->IsTextField() && isHTMLInputElement(*GetNode()))
2041 toHTMLInputElement(*GetNode()) 1733 toHTMLInputElement(*GetNode())
2042 .setValue(string, kDispatchInputAndChangeEvent); 1734 .setValue(string, kDispatchInputAndChangeEvent);
(...skipping 477 matching lines...) Expand 10 before | Expand all | Expand 10 after
2520 2212
2521 bool AXLayoutObject::ElementAttributeValue( 2213 bool AXLayoutObject::ElementAttributeValue(
2522 const QualifiedName& attribute_name) const { 2214 const QualifiedName& attribute_name) const {
2523 if (!layout_object_) 2215 if (!layout_object_)
2524 return false; 2216 return false;
2525 2217
2526 return EqualIgnoringASCIICase(GetAttribute(attribute_name), "true"); 2218 return EqualIgnoringASCIICase(GetAttribute(attribute_name), "true");
2527 } 2219 }
2528 2220
2529 } // namespace blink 2221 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698