OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright (C) 2012, Google Inc. All rights reserved. | 2 * Copyright (C) 2012, Google 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 |
11 * notice, this list of conditions and the following disclaimer in the | 11 * notice, this list of conditions and the following disclaimer in the |
12 * documentation and/or other materials provided with the distribution. | 12 * documentation and/or other materials provided with the distribution. |
13 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of | 13 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of |
14 * its contributors may be used to endorse or promote products derived | 14 * its contributors may be used to endorse or promote products derived |
15 * from this software without specific prior written permission. | 15 * from this software without specific prior written permission. |
16 * | 16 * |
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY | 17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY |
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | 18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | 19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY | 20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY |
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | 21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | 22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | 23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
27 */ | 27 */ |
28 | 28 |
29 #include "config.h" | 29 #include "config.h" |
30 #include "modules/accessibility/AXNodeObject.h" | 30 #include "modules/accessibility/AXNodeObject.h" |
31 | 31 |
32 #include "core/InputTypeNames.h" | 32 #include "core/InputTypeNames.h" |
33 #include "core/dom/NodeTraversal.h" | 33 #include "core/dom/NodeTraversal.h" |
34 #include "core/dom/Text.h" | 34 #include "core/dom/Text.h" |
35 #include "core/dom/shadow/ComposedTreeTraversal.h" | |
35 #include "core/html/HTMLDListElement.h" | 36 #include "core/html/HTMLDListElement.h" |
36 #include "core/html/HTMLFieldSetElement.h" | 37 #include "core/html/HTMLFieldSetElement.h" |
37 #include "core/html/HTMLFrameElementBase.h" | 38 #include "core/html/HTMLFrameElementBase.h" |
38 #include "core/html/HTMLImageElement.h" | 39 #include "core/html/HTMLImageElement.h" |
39 #include "core/html/HTMLInputElement.h" | 40 #include "core/html/HTMLInputElement.h" |
40 #include "core/html/HTMLLabelElement.h" | 41 #include "core/html/HTMLLabelElement.h" |
41 #include "core/html/HTMLLegendElement.h" | 42 #include "core/html/HTMLLegendElement.h" |
42 #include "core/html/HTMLMediaElement.h" | 43 #include "core/html/HTMLMediaElement.h" |
43 #include "core/html/HTMLMeterElement.h" | 44 #include "core/html/HTMLMeterElement.h" |
44 #include "core/html/HTMLPlugInElement.h" | 45 #include "core/html/HTMLPlugInElement.h" |
45 #include "core/html/HTMLSelectElement.h" | 46 #include "core/html/HTMLSelectElement.h" |
46 #include "core/html/HTMLTableCaptionElement.h" | 47 #include "core/html/HTMLTableCaptionElement.h" |
47 #include "core/html/HTMLTableCellElement.h" | 48 #include "core/html/HTMLTableCellElement.h" |
48 #include "core/html/HTMLTableElement.h" | 49 #include "core/html/HTMLTableElement.h" |
49 #include "core/html/HTMLTableRowElement.h" | 50 #include "core/html/HTMLTableRowElement.h" |
50 #include "core/html/HTMLTableSectionElement.h" | 51 #include "core/html/HTMLTableSectionElement.h" |
51 #include "core/html/HTMLTextAreaElement.h" | 52 #include "core/html/HTMLTextAreaElement.h" |
52 #include "core/html/parser/HTMLParserIdioms.h" | 53 #include "core/html/parser/HTMLParserIdioms.h" |
53 #include "core/html/shadow/MediaControlElements.h" | 54 #include "core/html/shadow/MediaControlElements.h" |
54 #include "core/layout/LayoutObject.h" | 55 #include "core/layout/LayoutObject.h" |
55 #include "modules/accessibility/AXObjectCacheImpl.h" | 56 #include "modules/accessibility/AXObjectCacheImpl.h" |
56 #include "platform/UserGestureIndicator.h" | 57 #include "platform/UserGestureIndicator.h" |
58 #include "platform/text/PlatformLocale.h" | |
57 #include "wtf/text/StringBuilder.h" | 59 #include "wtf/text/StringBuilder.h" |
58 | 60 |
59 | 61 |
60 namespace blink { | 62 namespace blink { |
61 | 63 |
62 using namespace HTMLNames; | 64 using namespace HTMLNames; |
63 | 65 |
64 AXNodeObject::AXNodeObject(Node* node, AXObjectCacheImpl& axObjectCache) | 66 AXNodeObject::AXNodeObject(Node* node, AXObjectCacheImpl& axObjectCache) |
65 : AXObject(axObjectCache) | 67 : AXObject(axObjectCache) |
66 , m_ariaRole(UnknownRole) | 68 , m_ariaRole(UnknownRole) |
(...skipping 176 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
243 static bool isRequiredOwnedElement(AXObject* parent, AccessibilityRole currentRo le, HTMLElement* currentElement) | 245 static bool isRequiredOwnedElement(AXObject* parent, AccessibilityRole currentRo le, HTMLElement* currentElement) |
244 { | 246 { |
245 Node* parentNode = parent->node(); | 247 Node* parentNode = parent->node(); |
246 if (!parentNode || !parentNode->isHTMLElement()) | 248 if (!parentNode || !parentNode->isHTMLElement()) |
247 return false; | 249 return false; |
248 | 250 |
249 if (currentRole == ListItemRole) | 251 if (currentRole == ListItemRole) |
250 return isListElement(parentNode); | 252 return isListElement(parentNode); |
251 if (currentRole == ListMarkerRole) | 253 if (currentRole == ListMarkerRole) |
252 return isHTMLLIElement(*parentNode); | 254 return isHTMLLIElement(*parentNode); |
253 if (currentRole == MenuItemCheckBoxRole || currentRole == MenuItemRole || c urrentRole == MenuItemRadioRole) | 255 if (currentRole == MenuItemCheckBoxRole || currentRole == MenuItemRole || cu rrentRole == MenuItemRadioRole) |
254 return isHTMLMenuElement(*parentNode); | 256 return isHTMLMenuElement(*parentNode); |
255 | 257 |
256 if (!currentElement) | 258 if (!currentElement) |
257 return false; | 259 return false; |
258 if (isHTMLTableCellElement(*currentElement)) | 260 if (isHTMLTableCellElement(*currentElement)) |
259 return isHTMLTableRowElement(*parentNode); | 261 return isHTMLTableRowElement(*parentNode); |
260 if (isHTMLTableRowElement(*currentElement)) | 262 if (isHTMLTableRowElement(*currentElement)) |
261 return isHTMLTableSectionElement(toHTMLElement(*parentNode)); | 263 return isHTMLTableSectionElement(toHTMLElement(*parentNode)); |
262 | 264 |
263 // In case of ListboxRole and it's child, ListBoxOptionRole, | 265 // In case of ListboxRole and it's child, ListBoxOptionRole, |
264 // Inheritance of presentation role is handled in AXListBoxOption | 266 // Inheritance of presentation role is handled in AXListBoxOption |
265 // Because ListBoxOption Role doesn't have any child. | 267 // Because ListBoxOption Role doesn't have any child. |
266 // If it's just ignored because of presentation, we can't see any AX tree re lated to ListBoxOption. | 268 // If it's just ignored because of presentation, we can't see any AX tree re lated to ListBoxOption. |
267 return false; | 269 return false; |
268 } | 270 } |
269 | 271 |
270 | |
271 const AXObject* AXNodeObject::inheritsPresentationalRoleFrom() const | 272 const AXObject* AXNodeObject::inheritsPresentationalRoleFrom() const |
272 { | 273 { |
273 // ARIA states if an item can get focus, it should not be presentational. | 274 // ARIA states if an item can get focus, it should not be presentational. |
274 if (canSetFocusAttribute()) | 275 if (canSetFocusAttribute()) |
275 return 0; | 276 return 0; |
276 | 277 |
277 if (isPresentational()) | 278 if (isPresentational()) |
278 return this; | 279 return this; |
279 | 280 |
280 // http://www.w3.org/TR/wai-aria/complete#presentation | 281 // http://www.w3.org/TR/wai-aria/complete#presentation |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
329 if (node()->isLink() || (isHTMLAnchorElement(*node()) && isClickable())) | 330 if (node()->isLink() || (isHTMLAnchorElement(*node()) && isClickable())) |
330 return LinkRole; | 331 return LinkRole; |
331 | 332 |
332 if (isHTMLButtonElement(*node())) | 333 if (isHTMLButtonElement(*node())) |
333 return buttonRoleType(); | 334 return buttonRoleType(); |
334 | 335 |
335 if (isHTMLDetailsElement(*node())) | 336 if (isHTMLDetailsElement(*node())) |
336 return DetailsRole; | 337 return DetailsRole; |
337 | 338 |
338 if (isHTMLSummaryElement(*node())) { | 339 if (isHTMLSummaryElement(*node())) { |
339 if (node()->parentNode() && isHTMLDetailsElement(node()->parentNode())) | 340 if (ComposedTreeTraversal::parent(*node()) && isHTMLDetailsElement(Compo sedTreeTraversal::parent(*node()))) |
340 return DisclosureTriangleRole; | 341 return DisclosureTriangleRole; |
341 return UnknownRole; | 342 return UnknownRole; |
342 } | 343 } |
343 | 344 |
344 if (isHTMLInputElement(*node())) { | 345 if (isHTMLInputElement(*node())) { |
345 HTMLInputElement& input = toHTMLInputElement(*node()); | 346 HTMLInputElement& input = toHTMLInputElement(*node()); |
346 const AtomicString& type = input.type(); | 347 const AtomicString& type = input.type(); |
347 if (input.dataList()) | 348 if (input.dataList()) |
348 return ComboBoxRole; | 349 return ComboBoxRole; |
349 if (type == InputTypeNames::button) { | 350 if (type == InputTypeNames::button) { |
(...skipping 1421 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1771 return placeholder; | 1772 return placeholder; |
1772 } | 1773 } |
1773 | 1774 |
1774 return String(); | 1775 return String(); |
1775 } | 1776 } |
1776 | 1777 |
1777 // | 1778 // |
1778 // New AX name calculation. | 1779 // New AX name calculation. |
1779 // | 1780 // |
1780 | 1781 |
1781 String AXNodeObject::textAlternative(bool recursive, bool inAriaLabelledByTraver sal, AXObjectSet& visited, AXNameFrom& nameFrom, AXObjectVector& nameObjects, Na meSources* nameSources) const | 1782 String AXNodeObject::textAlternative(bool recursive, bool inAriaLabelledByTraver sal, AXObjectSet& visited, AXNameFrom& nameFrom, AXObjectVector* nameObjects, Na meSources* nameSources) const |
1782 { | 1783 { |
1784 // If nameSources is non-null, nameObjects is used in filling it in, so it m ust be non-null as well. | |
1785 if (nameSources) | |
1786 assert(nameObjects); | |
1787 | |
1783 bool alreadyVisited = visited.contains(this); | 1788 bool alreadyVisited = visited.contains(this); |
1784 bool foundTextAlternative = false; | 1789 bool foundTextAlternative = false; |
1785 visited.add(this); | 1790 visited.add(this); |
1786 String textAlternative; | 1791 String textAlternative; |
1787 | 1792 |
1788 if (!node() && !layoutObject()) | 1793 if (!node() && !layoutObject()) |
1789 return String(); | 1794 return String(); |
1790 | 1795 |
1791 // Step 2A from: http://www.w3.org/TR/accname-aam-1.1 | 1796 // Step 2A from: http://www.w3.org/TR/accname-aam-1.1 |
1792 if (!recursive && layoutObject() | 1797 if (!recursive && layoutObject() |
(...skipping 14 matching lines...) Expand all Loading... | |
1807 if (hasAttribute(attr)) { | 1812 if (hasAttribute(attr)) { |
1808 if (nameSources) | 1813 if (nameSources) |
1809 nameSources->last().attributeValue = getAttribute(attr); | 1814 nameSources->last().attributeValue = getAttribute(attr); |
1810 | 1815 |
1811 textAlternative = textFromAriaLabelledby(visited, nameObjects); | 1816 textAlternative = textFromAriaLabelledby(visited, nameObjects); |
1812 | 1817 |
1813 if (!textAlternative.isNull()) { | 1818 if (!textAlternative.isNull()) { |
1814 if (nameSources) { | 1819 if (nameSources) { |
1815 NameSource& source = nameSources->last(); | 1820 NameSource& source = nameSources->last(); |
1816 source.type = nameFrom; | 1821 source.type = nameFrom; |
1817 source.nameObjects = nameObjects; | 1822 source.nameObjects = *nameObjects; |
1818 source.text = textAlternative; | 1823 source.text = textAlternative; |
1819 foundTextAlternative = true; | 1824 foundTextAlternative = true; |
1820 } else { | 1825 } else { |
1821 return textAlternative; | 1826 return textAlternative; |
1822 } | 1827 } |
1823 } else if (nameSources) { | 1828 } else if (nameSources) { |
1824 nameSources->last().invalid = true; | 1829 nameSources->last().invalid = true; |
1825 } | 1830 } |
1826 } | 1831 } |
1827 } | 1832 } |
(...skipping 19 matching lines...) Expand all Loading... | |
1847 } | 1852 } |
1848 } | 1853 } |
1849 } | 1854 } |
1850 | 1855 |
1851 // Step 2D from: http://www.w3.org/TR/accname-aam-1.1 | 1856 // Step 2D from: http://www.w3.org/TR/accname-aam-1.1 |
1852 textAlternative = nativeTextAlternative(visited, nameFrom, nameObjects, name Sources, &foundTextAlternative); | 1857 textAlternative = nativeTextAlternative(visited, nameFrom, nameObjects, name Sources, &foundTextAlternative); |
1853 if (!textAlternative.isNull() && !nameSources) | 1858 if (!textAlternative.isNull() && !nameSources) |
1854 return textAlternative; | 1859 return textAlternative; |
1855 | 1860 |
1856 // Step 2E from: http://www.w3.org/TR/accname-aam-1.1 | 1861 // Step 2E from: http://www.w3.org/TR/accname-aam-1.1 |
1857 | 1862 if (recursive && !inAriaLabelledByTraversal && isControl()) { |
1863 // No need to set any name source info in a recursive call. | |
1864 if (roleValue() == TextFieldRole || roleValue() == ComboBoxRole) | |
1865 return text(); | |
1866 if (isRange()) { | |
1867 if (hasAttribute(aria_valuetextAttr)) | |
1868 return getAttribute(aria_valuetextAttr).string(); | |
1869 return String::number(valueForRange()); | |
1870 } | |
1871 } | |
1858 | 1872 |
1859 // Step 2F / 2G from: http://www.w3.org/TR/accname-aam-1.1 | 1873 // Step 2F / 2G from: http://www.w3.org/TR/accname-aam-1.1 |
1860 if (recursive || nameFromContents()) { | 1874 if (recursive || nameFromContents()) { |
1861 nameFrom = AXNameFromContents; | 1875 nameFrom = AXNameFromContents; |
1862 if (nameSources) { | 1876 if (nameSources) { |
1863 nameSources->append(NameSource(foundTextAlternative)); | 1877 nameSources->append(NameSource(foundTextAlternative)); |
1864 nameSources->last().type = nameFrom; | 1878 nameSources->last().type = nameFrom; |
1865 } | 1879 } |
1866 | 1880 |
1867 Node* node = this->node(); | 1881 Node* node = this->node(); |
1868 if (node && node->isTextNode()) | 1882 if (node && node->isTextNode()) |
1869 textAlternative = toText(node)->wholeText(); | 1883 textAlternative = toText(node)->wholeText(); |
1870 else | 1884 else |
1871 textAlternative = textFromDescendants(visited); | 1885 textAlternative = textFromDescendants(visited); |
1872 | 1886 |
1873 if (nameSources) { | 1887 if (!textAlternative.isEmpty()) { |
1874 foundTextAlternative = true; | 1888 if (nameSources) { |
1875 nameSources->last().text = textAlternative; | 1889 foundTextAlternative = true; |
1876 } else { | 1890 nameSources->last().text = textAlternative; |
1877 return textAlternative; | 1891 } else { |
1892 return textAlternative; | |
1893 } | |
1894 } | |
1895 } | |
1896 | |
1897 // Step 2H from: http://www.w3.org/TR/accname-aam-1.1 | |
1898 nameFrom = AXNameFromAttribute; | |
1899 if (nameSources) { | |
1900 nameSources->append(NameSource(foundTextAlternative, titleAttr)); | |
1901 nameSources->last().type = nameFrom; | |
1902 } | |
1903 if (hasAttribute(titleAttr)) { | |
1904 const AtomicString& title = getAttribute(titleAttr); | |
1905 if (!title.isEmpty()) { | |
1906 textAlternative = title; | |
1907 if (nameSources) { | |
1908 foundTextAlternative = true; | |
1909 nameSources->last().text = textAlternative; | |
1910 } else { | |
1911 return textAlternative; | |
1912 } | |
1878 } | 1913 } |
1879 } | 1914 } |
1880 | 1915 |
1881 nameFrom = AXNameFromUninitialized; | 1916 nameFrom = AXNameFromUninitialized; |
1882 | 1917 |
1883 if (nameSources && !nameSources->isEmpty()) { | 1918 if (foundTextAlternative) { |
1884 for (size_t i = 0; i < nameSources->size(); ++i) { | 1919 for (size_t i = 0; i < nameSources->size(); ++i) { |
1885 if (!(*nameSources)[i].text.isNull() && !(*nameSources)[i].supersede d) { | 1920 if (!(*nameSources)[i].text.isNull() && !(*nameSources)[i].supersede d) { |
1886 nameFrom = (*nameSources)[i].type; | 1921 NameSource& nameSource = (*nameSources)[i]; |
1887 nameObjects = (*nameSources)[i].nameObjects; | 1922 nameFrom = nameSource.type; |
1888 return (*nameSources)[i].text; | 1923 if (!nameSource.nameObjects.isEmpty()) |
1924 *nameObjects = nameSource.nameObjects; | |
1925 return nameSource.text; | |
1889 } | 1926 } |
1890 } | 1927 } |
1891 } | 1928 } |
1892 | 1929 |
1893 return String(); | 1930 return String(); |
1894 } | 1931 } |
1895 | 1932 |
1896 String AXNodeObject::textFromDescendants(AXObjectSet& visited) const | 1933 String AXNodeObject::textFromDescendants(AXObjectSet& visited) const |
1897 { | 1934 { |
1898 StringBuilder accumulatedText; | 1935 StringBuilder accumulatedText; |
1899 AXObject* previous = nullptr; | 1936 AXObject* previous = nullptr; |
1900 for (AXObject* child = firstChild(); child; child = child->nextSibling()) { | 1937 for (AXObject* child = firstChild(); child; child = child->nextSibling()) { |
1901 // If we're going between two layoutObjects that are in separate LayoutB oxes, add | 1938 // If we're going between two layoutObjects that are in separate LayoutB oxes, add |
1902 // whitespace if it wasn't there already. Intuitively if you have | 1939 // whitespace if it wasn't there already. Intuitively if you have |
1903 // <span>Hello</span><span>World</span>, those are part of the same Layo utBox | 1940 // <span>Hello</span><span>World</span>, those are part of the same Layo utBox |
1904 // so we should return "HelloWorld", but given <div>Hello</div><div>Worl d</div> the | 1941 // so we should return "HelloWorld", but given <div>Hello</div><div>Worl d</div> the |
1905 // strings are in separate boxes so we should return "Hello World". | 1942 // strings are in separate boxes so we should return "Hello World". |
1906 if (previous && accumulatedText.length() && !isHTMLSpace(accumulatedText [accumulatedText.length() - 1])) { | 1943 if (previous && accumulatedText.length() && !isHTMLSpace(accumulatedText [accumulatedText.length() - 1])) { |
1907 if (!isSameLayoutBox(child->layoutObject(), previous->layoutObject() )) | 1944 if (!isSameLayoutBox(child->layoutObject(), previous->layoutObject() )) |
1908 accumulatedText.append(' '); | 1945 accumulatedText.append(' '); |
1909 } | 1946 } |
1910 | 1947 |
1911 AXNameFrom nameFrom; | 1948 String result = recursiveTextAlternative(*child, false, visited); |
1912 AXObjectVector nameObjects; | |
1913 String result = child->textAlternative(true, false, visited, nameFrom, n ameObjects, nullptr); | |
1914 accumulatedText.append(result); | 1949 accumulatedText.append(result); |
1915 previous = child; | 1950 previous = child; |
1916 } | 1951 } |
1917 | 1952 |
1918 return accumulatedText.toString(); | 1953 return accumulatedText.toString(); |
1919 } | 1954 } |
1920 | 1955 |
1921 String AXNodeObject::textFromElements(bool inAriaLabelledbyTraversal, AXObjectSe t& visited, WillBeHeapVector<RawPtrWillBeMember<Element>>& elements, AXObjectVec tor& nameObjects) const | 1956 String AXNodeObject::textFromElements(bool inAriaLabelledbyTraversal, AXObjectSe t& visited, WillBeHeapVector<RawPtrWillBeMember<Element>>& elements, AXObjectVec tor* nameObjects) const |
1922 { | 1957 { |
1923 StringBuilder accumulatedText; | 1958 StringBuilder accumulatedText; |
1924 bool foundValidElement = false; | 1959 bool foundValidElement = false; |
1925 AXObjectVector localNameObjects; | 1960 AXObjectVector localNameObjects; |
1926 | 1961 |
1927 for (const auto& element : elements) { | 1962 for (const auto& element : elements) { |
1928 RefPtrWillBeRawPtr<AXObject> axElement = axObjectCache().getOrCreate(ele ment); | 1963 RefPtrWillBeRawPtr<AXObject> axElement = axObjectCache().getOrCreate(ele ment); |
1929 if (axElement) { | 1964 if (axElement) { |
1930 foundValidElement = true; | 1965 foundValidElement = true; |
1931 localNameObjects.append(axElement.get()); | 1966 localNameObjects.append(axElement.get()); |
1932 | 1967 |
1933 String result = recursiveTextAlternative(*axElement, inAriaLabelledb yTraversal, visited); | 1968 String result = recursiveTextAlternative(*axElement, inAriaLabelledb yTraversal, visited); |
1934 if (!result.isEmpty()) { | 1969 if (!result.isEmpty()) { |
1935 if (!accumulatedText.isEmpty()) | 1970 if (!accumulatedText.isEmpty()) |
1936 accumulatedText.append(" "); | 1971 accumulatedText.append(" "); |
1937 accumulatedText.append(result); | 1972 accumulatedText.append(result); |
1938 } | 1973 } |
1939 } | 1974 } |
1940 } | 1975 } |
1941 if (!foundValidElement) | 1976 if (!foundValidElement) |
1942 return String(); | 1977 return String(); |
1943 nameObjects = localNameObjects; | 1978 if (nameObjects) |
1979 *nameObjects = localNameObjects; | |
1944 return accumulatedText.toString(); | 1980 return accumulatedText.toString(); |
1945 } | 1981 } |
1946 | 1982 |
1947 String AXNodeObject::textFromAriaLabelledby(AXObjectSet& visited, AXObjectVector & nameObjects) const | 1983 String AXNodeObject::textFromAriaLabelledby(AXObjectSet& visited, AXObjectVector * nameObjects) const |
1948 { | 1984 { |
1949 WillBeHeapVector<RawPtrWillBeMember<Element>> elements; | 1985 WillBeHeapVector<RawPtrWillBeMember<Element>> elements; |
1950 ariaLabelledbyElements(elements); | 1986 ariaLabelledbyElements(elements); |
1951 return textFromElements(true, visited, elements, nameObjects); | 1987 return textFromElements(true, visited, elements, nameObjects); |
1952 } | 1988 } |
1953 | 1989 |
1954 LayoutRect AXNodeObject::elementRect() const | 1990 LayoutRect AXNodeObject::elementRect() const |
1955 { | 1991 { |
1956 // First check if it has a custom rect, for example if this element is tied to a canvas path. | 1992 // First check if it has a custom rect, for example if this element is tied to a canvas path. |
1957 if (!m_explicitElementRect.isEmpty()) | 1993 if (!m_explicitElementRect.isEmpty()) |
(...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2140 } | 2176 } |
2141 | 2177 |
2142 Element* AXNodeObject::actionElement() const | 2178 Element* AXNodeObject::actionElement() const |
2143 { | 2179 { |
2144 Node* node = this->node(); | 2180 Node* node = this->node(); |
2145 if (!node) | 2181 if (!node) |
2146 return 0; | 2182 return 0; |
2147 | 2183 |
2148 if (isHTMLInputElement(*node)) { | 2184 if (isHTMLInputElement(*node)) { |
2149 HTMLInputElement& input = toHTMLInputElement(*node); | 2185 HTMLInputElement& input = toHTMLInputElement(*node); |
2150 if (!input.isDisabledFormControl() && (isCheckboxOrRadio() || input.isTe xtButton() | 2186 if (!input.isDisabledFormControl() && (isCheckboxOrRadio() || input.isTe xtButton() || input.type() == InputTypeNames::file)) |
2151 || input.type() == InputTypeNames::file)) | |
2152 return &input; | 2187 return &input; |
2153 } else if (isHTMLButtonElement(*node)) { | 2188 } else if (isHTMLButtonElement(*node)) { |
2154 return toElement(node); | 2189 return toElement(node); |
2155 } | 2190 } |
2156 | 2191 |
2157 if (AXObject::isARIAInput(ariaRoleAttribute())) | 2192 if (AXObject::isARIAInput(ariaRoleAttribute())) |
2158 return toElement(node); | 2193 return toElement(node); |
2159 | 2194 |
2160 if (isImageButton()) | 2195 if (isImageButton()) |
2161 return toElement(node); | 2196 return toElement(node); |
(...skipping 285 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2447 ariaLabelledbyElements(elements); | 2482 ariaLabelledbyElements(elements); |
2448 | 2483 |
2449 for (const auto& element : elements) { | 2484 for (const auto& element : elements) { |
2450 RefPtrWillBeRawPtr<AXObject> axElement = axObjectCache().getOrCreate (element); | 2485 RefPtrWillBeRawPtr<AXObject> axElement = axObjectCache().getOrCreate (element); |
2451 textOrder.append(AccessibilityText::create(ariaLabelledby, Alternati veText, axElement)); | 2486 textOrder.append(AccessibilityText::create(ariaLabelledby, Alternati veText, axElement)); |
2452 } | 2487 } |
2453 } | 2488 } |
2454 } | 2489 } |
2455 | 2490 |
2456 // Based on http://rawgit.com/w3c/aria/master/html-aam/html-aam.html#accessible- name-and-description-calculation | 2491 // Based on http://rawgit.com/w3c/aria/master/html-aam/html-aam.html#accessible- name-and-description-calculation |
2457 String AXNodeObject::nativeTextAlternative(AXObjectSet& visited, AXNameFrom& nam eFrom, AXObjectVector& nameObjects, NameSources* nameSources, bool* foundTextAlt ernative) const | 2492 String AXNodeObject::nativeTextAlternative(AXObjectSet& visited, AXNameFrom& nam eFrom, AXObjectVector* nameObjects, NameSources* nameSources, bool* foundTextAlt ernative) const |
2458 { | 2493 { |
2459 if (!node()) | 2494 if (!node()) |
2460 return String(); | 2495 return String(); |
2461 | 2496 |
2497 // If nameSources is non-null, nameObjects is used in filling it in, so it m ust be non-null as well. | |
2498 if (nameSources) | |
2499 assert(nameObjects); | |
2500 | |
2462 String textAlternative; | 2501 String textAlternative; |
2463 AXObjectVector localNameObjects; | 2502 AXObjectVector localNameObjects; |
2464 | 2503 |
2465 const HTMLInputElement* inputElement = nullptr; | 2504 const HTMLInputElement* inputElement = nullptr; |
2466 if (isHTMLInputElement(node())) | 2505 if (isHTMLInputElement(node())) |
2467 inputElement = toHTMLInputElement(node()); | 2506 inputElement = toHTMLInputElement(node()); |
2468 | 2507 |
2508 // 5.1/5.5 Text inputs, Other labelable Elements | |
2509 HTMLElement* htmlElement = nullptr; | |
2510 if (node()->isHTMLElement()) | |
2511 htmlElement = toHTMLElement(node()); | |
2512 if (htmlElement && htmlElement->isLabelable()) { | |
2513 // label | |
2514 nameFrom = AXNameFromRelatedElement; | |
2515 if (nameSources) { | |
2516 nameSources->append(NameSource(*foundTextAlternative)); | |
2517 nameSources->last().type = nameFrom; | |
2518 nameSources->last().nativeSource = AXTextFromNativeHTMLLabel; | |
2519 } | |
2520 HTMLLabelElement* label = labelForElement(htmlElement); | |
2521 if (label) { | |
2522 RefPtrWillBeRawPtr<AXObject> labelAXObject = axObjectCache().getOrCr eate(label); | |
2523 if (labelAXObject) { | |
2524 if (nameObjects) { | |
2525 localNameObjects.append(labelAXObject.get()); | |
2526 *nameObjects = localNameObjects; | |
2527 localNameObjects.clear(); | |
2528 } | |
2529 textAlternative = recursiveTextAlternative(*labelAXObject, false , visited); | |
2530 | |
2531 if (nameSources) { | |
2532 NameSource& source = nameSources->last(); | |
2533 source.nameObjects = *nameObjects; | |
2534 source.text = textAlternative; | |
2535 if (label->getAttribute(forAttr).isNull()) | |
2536 source.nativeSource = AXTextFromNativeHTMLLabelWrapped; | |
2537 else | |
2538 source.nativeSource = AXTextFromNativeHTMLLabelFor; | |
2539 *foundTextAlternative = true; | |
2540 } else { | |
2541 return textAlternative; | |
2542 } | |
2543 } | |
2544 } | |
2545 } | |
2546 | |
2469 // 5.2 input type="button", input type="submit" and input type="reset" | 2547 // 5.2 input type="button", input type="submit" and input type="reset" |
2470 if (inputElement && inputElement->isTextButton()) { | 2548 if (inputElement && inputElement->isTextButton()) { |
2471 // value attribue | 2549 // value attribue |
2472 nameFrom = AXNameFromAttribute; | 2550 nameFrom = AXNameFromAttribute; |
2473 if (nameSources) { | 2551 if (nameSources) { |
2474 nameSources->append(NameSource(*foundTextAlternative, valueAttr)); | 2552 nameSources->append(NameSource(*foundTextAlternative, valueAttr)); |
2475 nameSources->last().type = nameFrom; | 2553 nameSources->last().type = nameFrom; |
2476 } | 2554 } |
2477 String value = inputElement->value(); | 2555 String value = inputElement->value(); |
2478 if (!value.isNull()) { | 2556 if (!value.isNull()) { |
2479 textAlternative = value; | 2557 textAlternative = value; |
2480 if (nameSources) { | 2558 if (nameSources) { |
2481 NameSource& source = nameSources->last(); | 2559 NameSource& source = nameSources->last(); |
2482 source.text = textAlternative; | 2560 source.text = textAlternative; |
2483 *foundTextAlternative = true; | 2561 *foundTextAlternative = true; |
2484 } else { | 2562 } else { |
2485 return textAlternative; | 2563 return textAlternative; |
2486 } | 2564 } |
2487 } | 2565 } |
2488 | |
2489 // localised default value ("Submit" or "Reset") | |
2490 String defaultValue = inputElement->defaultValue(); | |
2491 if (!defaultValue.isNull()) { | |
2492 // defaultValue is always set for submit and reset buttons, and neve r set for anything else. | |
2493 nameFrom = AXNameFromAttribute; | |
2494 textAlternative = defaultValue; | |
2495 if (nameSources) { | |
2496 nameSources->append(NameSource(*foundTextAlternative, typeAttr)) ; | |
2497 NameSource& source = nameSources->last(); | |
2498 source.attributeValue = inputElement->getAttribute(typeAttr); | |
2499 source.text = textAlternative; | |
2500 *foundTextAlternative = true; | |
2501 } else { | |
2502 return textAlternative; | |
2503 } | |
2504 } | |
2505 return textAlternative; | 2566 return textAlternative; |
2506 } | 2567 } |
2507 | 2568 |
2508 // 5.3 input type="image" | 2569 // 5.3 input type="image" |
2509 if (inputElement && inputElement->getAttribute(typeAttr) == InputTypeNames:: image) { | 2570 if (inputElement && inputElement->getAttribute(typeAttr) == InputTypeNames:: image) { |
2510 // alt attr | 2571 // alt attr |
2511 nameFrom = AXNameFromAttribute; | 2572 nameFrom = AXNameFromAttribute; |
2512 if (nameSources) { | 2573 if (nameSources) { |
2513 nameSources->append(NameSource(*foundTextAlternative, altAttr)); | 2574 nameSources->append(NameSource(*foundTextAlternative, altAttr)); |
2514 nameSources->last().type = nameFrom; | 2575 nameSources->last().type = nameFrom; |
(...skipping 22 matching lines...) Expand all Loading... | |
2537 textAlternative = value; | 2598 textAlternative = value; |
2538 if (nameSources) { | 2599 if (nameSources) { |
2539 NameSource& source = nameSources->last(); | 2600 NameSource& source = nameSources->last(); |
2540 source.text = textAlternative; | 2601 source.text = textAlternative; |
2541 *foundTextAlternative = true; | 2602 *foundTextAlternative = true; |
2542 } else { | 2603 } else { |
2543 return textAlternative; | 2604 return textAlternative; |
2544 } | 2605 } |
2545 } | 2606 } |
2546 | 2607 |
2608 // localised default value ("Submit") | |
dmazzoni
2015/08/24 18:02:03
Oh, got it - <input type=image> implies a Submit b
| |
2609 nameFrom = AXNameFromAttribute; | |
2610 textAlternative = inputElement->locale().queryString(WebLocalizedString: :SubmitButtonDefaultLabel); | |
2611 if (nameSources) { | |
2612 nameSources->append(NameSource(*foundTextAlternative, typeAttr)); | |
2613 NameSource& source = nameSources->last(); | |
2614 source.attributeValue = inputElement->getAttribute(typeAttr); | |
2615 source.type = nameFrom; | |
2616 source.text = textAlternative; | |
2617 *foundTextAlternative = true; | |
2618 } else { | |
2619 return textAlternative; | |
2620 } | |
2547 return textAlternative; | 2621 return textAlternative; |
2548 } | 2622 } |
2549 | 2623 |
2550 // 5.5 Other labelable Elements | 2624 // 5.1 Text inputs - step 3 (placeholder attribute) |
2551 if (node()->isHTMLElement() && toHTMLElement(node())->isLabelable()) { | 2625 if (htmlElement && htmlElement->isTextFormControl()) { |
2552 // label | 2626 nameFrom = AXNameFromPlaceholder; |
2553 nameFrom = AXNameFromRelatedElement; | |
2554 if (nameSources) { | 2627 if (nameSources) { |
2555 nameSources->append(NameSource(*foundTextAlternative)); | 2628 nameSources->append(NameSource(*foundTextAlternative, placeholderAtt r)); |
2556 nameSources->last().type = nameFrom; | 2629 NameSource& source = nameSources->last(); |
2557 nameSources->last().nativeSource = AXTextFromNativeHTMLLabel; | 2630 source.type = nameFrom; |
2558 } | 2631 } |
2559 HTMLLabelElement* label = labelForElement(toHTMLElement(node())); | 2632 HTMLElement* element = toHTMLElement(node()); |
2560 if (label) { | 2633 if (element->fastHasAttribute(placeholderAttr)) { |
2561 RefPtrWillBeRawPtr<AXObject> labelAXObject = axObjectCache().getOrCr eate(label); | 2634 const AtomicString& placeholder = element->fastGetAttribute(placehol derAttr); |
2562 if (labelAXObject) { | 2635 if (!placeholder.isEmpty()) { |
2563 localNameObjects.append(labelAXObject.get()); | 2636 textAlternative = placeholder; |
2564 nameObjects = localNameObjects; | |
2565 localNameObjects.clear(); | |
2566 | |
2567 textAlternative = recursiveTextAlternative(*labelAXObject, false , visited); | |
2568 | |
2569 if (nameSources) { | 2637 if (nameSources) { |
2570 NameSource& source = nameSources->last(); | 2638 NameSource& source = nameSources->last(); |
2571 source.nameObjects = nameObjects; | |
2572 source.text = textAlternative; | 2639 source.text = textAlternative; |
2573 if (label->getAttribute(forAttr).isNull()) | 2640 source.attributeValue = placeholder; |
2574 source.nativeSource = AXTextFromNativeHTMLLabelWrapped; | |
2575 else | |
2576 source.nativeSource = AXTextFromNativeHTMLLabelFor; | |
2577 *foundTextAlternative = true; | |
2578 } else { | 2641 } else { |
2579 return textAlternative; | 2642 return textAlternative; |
2580 } | 2643 } |
2581 } | 2644 } |
2582 } | 2645 } |
2583 return textAlternative; | 2646 return textAlternative; |
2584 } | 2647 } |
2585 | 2648 |
2586 // 5.7 figure and figcaption Elements | 2649 // 5.7 figure and figcaption Elements |
2587 if (node()->hasTagName(figureTag)) { | 2650 if (node()->hasTagName(figureTag)) { |
2588 // figcaption | 2651 // figcaption |
2589 nameFrom = AXNameFromRelatedElement; | 2652 nameFrom = AXNameFromRelatedElement; |
2590 if (nameSources) { | 2653 if (nameSources) { |
2591 nameSources->append(NameSource(*foundTextAlternative)); | 2654 nameSources->append(NameSource(*foundTextAlternative)); |
2592 nameSources->last().type = nameFrom; | 2655 nameSources->last().type = nameFrom; |
2593 nameSources->last().nativeSource = AXTextFromNativeHTMLFigcaption; | 2656 nameSources->last().nativeSource = AXTextFromNativeHTMLFigcaption; |
2594 } | 2657 } |
2595 Element* figcaption = nullptr; | 2658 Element* figcaption = nullptr; |
2596 for (Element& element : ElementTraversal::descendantsOf(*(node()))) { | 2659 for (Element& element : ElementTraversal::descendantsOf(*(node()))) { |
2597 if (element.hasTagName(figcaptionTag)) { | 2660 if (element.hasTagName(figcaptionTag)) { |
2598 figcaption = &element; | 2661 figcaption = &element; |
2599 break; | 2662 break; |
2600 } | 2663 } |
2601 } | 2664 } |
2602 if (figcaption) { | 2665 if (figcaption) { |
2603 RefPtrWillBeRawPtr<AXObject> figcaptionAXObject = axObjectCache().ge tOrCreate(figcaption); | 2666 RefPtrWillBeRawPtr<AXObject> figcaptionAXObject = axObjectCache().ge tOrCreate(figcaption); |
2604 if (figcaptionAXObject) { | 2667 if (figcaptionAXObject) { |
2605 localNameObjects.append(figcaptionAXObject.get()); | 2668 if (nameObjects) { |
2606 nameObjects = localNameObjects; | 2669 localNameObjects.append(figcaptionAXObject.get()); |
2607 localNameObjects.clear(); | 2670 *nameObjects = localNameObjects; |
2671 localNameObjects.clear(); | |
2672 } | |
2608 | 2673 |
2609 textAlternative = recursiveTextAlternative(*figcaptionAXObject, false, visited); | 2674 textAlternative = recursiveTextAlternative(*figcaptionAXObject, false, visited); |
2610 | 2675 |
2611 if (nameSources) { | 2676 if (nameSources) { |
2612 NameSource& source = nameSources->last(); | 2677 NameSource& source = nameSources->last(); |
2613 source.nameObjects = nameObjects; | 2678 source.nameObjects = *nameObjects; |
2614 source.text = textAlternative; | 2679 source.text = textAlternative; |
2615 } else { | 2680 } else { |
2616 return textAlternative; | 2681 return textAlternative; |
2617 } | 2682 } |
2618 } | 2683 } |
2619 } | 2684 } |
2620 return textAlternative; | 2685 return textAlternative; |
2621 } | 2686 } |
2622 | 2687 |
2623 // 5.8 img Element | 2688 // 5.8 img Element |
2624 if (isHTMLImageElement(node())) { | 2689 if (isHTMLImageElement(node())) { |
2625 HTMLImageElement* imgElement = toHTMLImageElement(node()); | |
2626 | |
2627 // alt | 2690 // alt |
2628 nameFrom = AXNameFromAttribute; | 2691 nameFrom = AXNameFromAttribute; |
2629 if (nameSources) { | 2692 if (nameSources) { |
2630 nameSources->append(NameSource(*foundTextAlternative, altAttr)); | 2693 nameSources->append(NameSource(*foundTextAlternative, altAttr)); |
2631 nameSources->last().type = nameFrom; | 2694 nameSources->last().type = nameFrom; |
2632 } | 2695 } |
2633 if (imgElement->hasAttribute(altAttr)) { | 2696 if (hasAttribute(altAttr)) { |
2634 AtomicString alt = imgElement->getAttribute(altAttr); | 2697 AtomicString alt = getAttribute(altAttr); |
2635 textAlternative = alt; | 2698 textAlternative = alt; |
2636 if (nameSources) { | 2699 if (nameSources) { |
2637 NameSource& source = nameSources->last(); | 2700 NameSource& source = nameSources->last(); |
2638 source.attributeValue = alt; | 2701 source.attributeValue = alt; |
2639 source.text = textAlternative; | 2702 source.text = textAlternative; |
2640 *foundTextAlternative = true; | 2703 *foundTextAlternative = true; |
2641 } else { | 2704 } else { |
2642 return textAlternative; | 2705 return textAlternative; |
2643 } | 2706 } |
2644 } | 2707 } |
2645 return textAlternative; | 2708 return textAlternative; |
2646 } | 2709 } |
2647 | 2710 |
2648 // 5.9 table Element | 2711 // 5.9 table Element |
2649 if (isHTMLTableElement(node())) { | 2712 if (isHTMLTableElement(node())) { |
2650 HTMLTableElement* tableElement = toHTMLTableElement(node()); | 2713 HTMLTableElement* tableElement = toHTMLTableElement(node()); |
2651 | 2714 |
2652 // caption | 2715 // caption |
2653 nameFrom = AXNameFromRelatedElement; | 2716 nameFrom = AXNameFromRelatedElement; |
2654 if (nameSources) { | 2717 if (nameSources) { |
2655 nameSources->append(NameSource(*foundTextAlternative)); | 2718 nameSources->append(NameSource(*foundTextAlternative)); |
2656 nameSources->last().type = nameFrom; | 2719 nameSources->last().type = nameFrom; |
2657 nameSources->last().nativeSource = AXTextFromNativeHTMLTableCaption; | 2720 nameSources->last().nativeSource = AXTextFromNativeHTMLTableCaption; |
2658 } | 2721 } |
2659 HTMLTableCaptionElement* caption = tableElement->caption(); | 2722 HTMLTableCaptionElement* caption = tableElement->caption(); |
2660 if (caption) { | 2723 if (caption) { |
2661 RefPtrWillBeRawPtr<AXObject> captionAXObject = axObjectCache().getOr Create(caption); | 2724 RefPtrWillBeRawPtr<AXObject> captionAXObject = axObjectCache().getOr Create(caption); |
2662 if (captionAXObject) { | 2725 if (captionAXObject) { |
2663 localNameObjects.append(captionAXObject.get()); | 2726 if (nameObjects) { |
2664 nameObjects = localNameObjects; | 2727 localNameObjects.append(captionAXObject.get()); |
2665 localNameObjects.clear(); | 2728 *nameObjects = localNameObjects; |
2729 localNameObjects.clear(); | |
2730 } | |
2666 | 2731 |
2667 textAlternative = recursiveTextAlternative(*captionAXObject, fal se, visited); | 2732 textAlternative = recursiveTextAlternative(*captionAXObject, fal se, visited); |
2668 if (nameSources) { | 2733 if (nameSources) { |
2669 NameSource& source = nameSources->last(); | 2734 NameSource& source = nameSources->last(); |
2670 source.nameObjects = nameObjects; | 2735 source.nameObjects = *nameObjects; |
2671 source.text = textAlternative; | 2736 source.text = textAlternative; |
2672 } else { | 2737 } else { |
2673 return textAlternative; | 2738 return textAlternative; |
2674 } | 2739 } |
2675 } | 2740 } |
2676 } | 2741 } |
2742 | |
2743 // summary | |
2744 nameFrom = AXNameFromAttribute; | |
2745 if (nameSources) { | |
2746 nameSources->append(NameSource(*foundTextAlternative)); | |
2747 nameSources->last().type = nameFrom; | |
2748 } | |
2749 if (hasAttribute(summaryAttr)) { | |
2750 AtomicString summary = getAttribute(summaryAttr); | |
2751 textAlternative = summary; | |
2752 if (nameSources) { | |
2753 NameSource& source = nameSources->last(); | |
2754 source.attributeValue = summary; | |
2755 source.text = textAlternative; | |
2756 *foundTextAlternative = true; | |
2757 } else { | |
2758 return textAlternative; | |
2759 } | |
2760 } | |
2761 | |
2677 return textAlternative; | 2762 return textAlternative; |
2678 } | 2763 } |
2679 | 2764 |
2680 return textAlternative; | 2765 return textAlternative; |
2681 } | 2766 } |
2682 | 2767 |
2683 DEFINE_TRACE(AXNodeObject) | 2768 DEFINE_TRACE(AXNodeObject) |
2684 { | 2769 { |
2685 visitor->trace(m_node); | 2770 visitor->trace(m_node); |
2686 AXObject::trace(visitor); | 2771 AXObject::trace(visitor); |
2687 } | 2772 } |
2688 | 2773 |
2689 } // namespace blink | 2774 } // namespace blink |
OLD | NEW |