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 |
(...skipping 12 matching lines...) Loading... |
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/Element.h" |
33 #include "core/dom/NodeTraversal.h" | 34 #include "core/dom/NodeTraversal.h" |
34 #include "core/dom/Text.h" | 35 #include "core/dom/Text.h" |
35 #include "core/dom/shadow/ComposedTreeTraversal.h" | 36 #include "core/dom/shadow/ComposedTreeTraversal.h" |
36 #include "core/html/HTMLDListElement.h" | 37 #include "core/html/HTMLDListElement.h" |
37 #include "core/html/HTMLFieldSetElement.h" | 38 #include "core/html/HTMLFieldSetElement.h" |
38 #include "core/html/HTMLFrameElementBase.h" | 39 #include "core/html/HTMLFrameElementBase.h" |
39 #include "core/html/HTMLImageElement.h" | 40 #include "core/html/HTMLImageElement.h" |
40 #include "core/html/HTMLInputElement.h" | 41 #include "core/html/HTMLInputElement.h" |
41 #include "core/html/HTMLLabelElement.h" | 42 #include "core/html/HTMLLabelElement.h" |
42 #include "core/html/HTMLLegendElement.h" | 43 #include "core/html/HTMLLegendElement.h" |
(...skipping 1449 matching lines...) Loading... |
1492 if (ariaRoleAttribute() == StaticTextRole) { | 1493 if (ariaRoleAttribute() == StaticTextRole) { |
1493 String staticText = text(); | 1494 String staticText = text(); |
1494 if (!staticText.length()) | 1495 if (!staticText.length()) |
1495 staticText = deprecatedTextUnderElement(TextUnderElementAll); | 1496 staticText = deprecatedTextUnderElement(TextUnderElementAll); |
1496 return staticText; | 1497 return staticText; |
1497 } | 1498 } |
1498 | 1499 |
1499 if (node->isTextNode()) | 1500 if (node->isTextNode()) |
1500 return deprecatedTextUnderElement(TextUnderElementAll); | 1501 return deprecatedTextUnderElement(TextUnderElementAll); |
1501 | 1502 |
| 1503 return stringValueOfControl(); |
| 1504 } |
| 1505 |
| 1506 String AXNodeObject::stringValueOfControl() const |
| 1507 { |
| 1508 Node* node = this->node(); |
| 1509 if (!node) |
| 1510 return String(); |
| 1511 |
1502 if (isHTMLSelectElement(*node)) { | 1512 if (isHTMLSelectElement(*node)) { |
1503 HTMLSelectElement& selectElement = toHTMLSelectElement(*node); | 1513 HTMLSelectElement& selectElement = toHTMLSelectElement(*node); |
1504 int selectedIndex = selectElement.selectedIndex(); | 1514 int selectedIndex = selectElement.selectedIndex(); |
1505 const WillBeHeapVector<RawPtrWillBeMember<HTMLElement>>& listItems = sel
ectElement.listItems(); | 1515 const WillBeHeapVector<RawPtrWillBeMember<HTMLElement>>& listItems = sel
ectElement.listItems(); |
1506 if (selectedIndex >= 0 && static_cast<size_t>(selectedIndex) < listItems
.size()) { | 1516 if (selectedIndex >= 0 && static_cast<size_t>(selectedIndex) < listItems
.size()) { |
1507 const AtomicString& overriddenDescription = listItems[selectedIndex]
->fastGetAttribute(aria_labelAttr); | 1517 const AtomicString& overriddenDescription = listItems[selectedIndex]
->fastGetAttribute(aria_labelAttr); |
1508 if (!overriddenDescription.isNull()) | 1518 if (!overriddenDescription.isNull()) |
1509 return overriddenDescription; | 1519 return overriddenDescription; |
1510 } | 1520 } |
1511 if (!selectElement.multiple()) | 1521 if (!selectElement.multiple()) |
1512 return selectElement.value(); | 1522 return selectElement.value(); |
1513 return String(); | 1523 return String(); |
1514 } | 1524 } |
1515 | 1525 |
1516 if (isNativeTextControl()) | 1526 if (isNativeTextControl()) |
1517 return text(); | 1527 return text(); |
1518 | 1528 |
1519 // FIXME: We might need to implement a value here for more types | 1529 // Handle other HTML input elements that aren't text controls, like date and
time |
1520 // FIXME: It would be better not to advertise a value at all for the types f
or which we don't implement one; | 1530 // controls, by returning the string value, with the exception of checkboxes |
1521 // this would require subclassing or making accessibilityAttributeNames do s
omething other than return a | 1531 // and radio buttons (which would return "on"). |
1522 // single static array. | 1532 if (isHTMLInputElement(node)) { |
| 1533 HTMLInputElement* input = toHTMLInputElement(node); |
| 1534 if (input->type() != InputTypeNames::checkbox && input->type() != InputT
ypeNames::radio) |
| 1535 return input->value(); |
| 1536 } |
| 1537 |
1523 return String(); | 1538 return String(); |
1524 } | 1539 } |
1525 | 1540 |
1526 String AXNodeObject::ariaDescribedByAttribute() const | 1541 String AXNodeObject::ariaDescribedByAttribute() const |
1527 { | 1542 { |
1528 WillBeHeapVector<RawPtrWillBeMember<Element>> elements; | 1543 WillBeHeapVector<RawPtrWillBeMember<Element>> elements; |
1529 elementsFromAttribute(elements, aria_describedbyAttr); | 1544 elementsFromAttribute(elements, aria_describedbyAttr); |
1530 | 1545 |
1531 return accessibilityDescriptionForElements(elements); | 1546 return accessibilityDescriptionForElements(elements); |
1532 } | 1547 } |
(...skipping 385 matching lines...) Loading... |
1918 } | 1933 } |
1919 | 1934 |
1920 // Step 2D from: http://www.w3.org/TR/accname-aam-1.1 | 1935 // Step 2D from: http://www.w3.org/TR/accname-aam-1.1 |
1921 textAlternative = nativeTextAlternative(visited, nameFrom, relatedObjects, n
ameSources, &foundTextAlternative); | 1936 textAlternative = nativeTextAlternative(visited, nameFrom, relatedObjects, n
ameSources, &foundTextAlternative); |
1922 if (!textAlternative.isNull() && !nameSources) | 1937 if (!textAlternative.isNull() && !nameSources) |
1923 return textAlternative; | 1938 return textAlternative; |
1924 | 1939 |
1925 // Step 2E from: http://www.w3.org/TR/accname-aam-1.1 | 1940 // Step 2E from: http://www.w3.org/TR/accname-aam-1.1 |
1926 if (recursive && !inAriaLabelledByTraversal && isControl()) { | 1941 if (recursive && !inAriaLabelledByTraversal && isControl()) { |
1927 // No need to set any name source info in a recursive call. | 1942 // No need to set any name source info in a recursive call. |
1928 if (roleValue() == TextFieldRole || roleValue() == ComboBoxRole) | |
1929 return text(); | |
1930 if (isRange()) { | 1943 if (isRange()) { |
1931 const AtomicString& ariaValuetext = getAttribute(aria_valuetextAttr)
; | 1944 const AtomicString& ariaValuetext = getAttribute(aria_valuetextAttr)
; |
1932 if (!ariaValuetext.isNull()) | 1945 if (!ariaValuetext.isNull()) |
1933 return ariaValuetext.string(); | 1946 return ariaValuetext.string(); |
1934 return String::number(valueForRange()); | 1947 return String::number(valueForRange()); |
1935 } | 1948 } |
| 1949 |
| 1950 return stringValueOfControl(); |
1936 } | 1951 } |
1937 | 1952 |
1938 // Step 2F / 2G from: http://www.w3.org/TR/accname-aam-1.1 | 1953 // Step 2F / 2G from: http://www.w3.org/TR/accname-aam-1.1 |
1939 if (recursive || nameFromContents()) { | 1954 if (recursive || nameFromContents()) { |
1940 nameFrom = AXNameFromContents; | 1955 nameFrom = AXNameFromContents; |
1941 if (nameSources) { | 1956 if (nameSources) { |
1942 nameSources->append(NameSource(foundTextAlternative)); | 1957 nameSources->append(NameSource(foundTextAlternative)); |
1943 nameSources->last().type = nameFrom; | 1958 nameSources->last().type = nameFrom; |
1944 } | 1959 } |
1945 | 1960 |
1946 Node* node = this->node(); | 1961 Node* node = this->node(); |
1947 if (node && node->isTextNode()) | 1962 if (node && node->isTextNode()) |
1948 textAlternative = toText(node)->wholeText(); | 1963 textAlternative = toText(node)->wholeText(); |
| 1964 else if (isHTMLBRElement(node)) |
| 1965 textAlternative = String("\n"); |
1949 else | 1966 else |
1950 textAlternative = textFromDescendants(visited); | 1967 textAlternative = textFromDescendants(visited); |
1951 | 1968 |
1952 if (!textAlternative.isEmpty()) { | 1969 if (!textAlternative.isEmpty()) { |
1953 if (nameSources) { | 1970 if (nameSources) { |
1954 foundTextAlternative = true; | 1971 foundTextAlternative = true; |
1955 nameSources->last().text = textAlternative; | 1972 nameSources->last().text = textAlternative; |
1956 } else { | 1973 } else { |
1957 return textAlternative; | 1974 return textAlternative; |
1958 } | 1975 } |
(...skipping 32 matching lines...) Loading... |
1991 } | 2008 } |
1992 | 2009 |
1993 return String(); | 2010 return String(); |
1994 } | 2011 } |
1995 | 2012 |
1996 String AXNodeObject::textFromDescendants(AXObjectSet& visited) const | 2013 String AXNodeObject::textFromDescendants(AXObjectSet& visited) const |
1997 { | 2014 { |
1998 StringBuilder accumulatedText; | 2015 StringBuilder accumulatedText; |
1999 AXObject* previous = nullptr; | 2016 AXObject* previous = nullptr; |
2000 for (AXObject* child = firstChild(); child; child = child->nextSibling()) { | 2017 for (AXObject* child = firstChild(); child; child = child->nextSibling()) { |
| 2018 // Skip hidden children |
| 2019 if (child->isInertOrAriaHidden()) |
| 2020 continue; |
| 2021 |
2001 // If we're going between two layoutObjects that are in separate LayoutB
oxes, add | 2022 // If we're going between two layoutObjects that are in separate LayoutB
oxes, add |
2002 // whitespace if it wasn't there already. Intuitively if you have | 2023 // whitespace if it wasn't there already. Intuitively if you have |
2003 // <span>Hello</span><span>World</span>, those are part of the same Layo
utBox | 2024 // <span>Hello</span><span>World</span>, those are part of the same Layo
utBox |
2004 // so we should return "HelloWorld", but given <div>Hello</div><div>Worl
d</div> the | 2025 // so we should return "HelloWorld", but given <div>Hello</div><div>Worl
d</div> the |
2005 // strings are in separate boxes so we should return "Hello World". | 2026 // strings are in separate boxes so we should return "Hello World". |
2006 if (previous && accumulatedText.length() && !isHTMLSpace(accumulatedText
[accumulatedText.length() - 1])) { | 2027 if (previous && accumulatedText.length() && !isHTMLSpace(accumulatedText
[accumulatedText.length() - 1])) { |
2007 if (!isInSameNonInlineBlockFlow(child->layoutObject(), previous->lay
outObject())) | 2028 if (!isInSameNonInlineBlockFlow(child->layoutObject(), previous->lay
outObject())) |
2008 accumulatedText.append(' '); | 2029 accumulatedText.append(' '); |
2009 } | 2030 } |
2010 | 2031 |
(...skipping 207 matching lines...) Loading... |
2218 } | 2239 } |
2219 | 2240 |
2220 bool AXNodeObject::canHaveChildren() const | 2241 bool AXNodeObject::canHaveChildren() const |
2221 { | 2242 { |
2222 // If this is an AXLayoutObject, then it's okay if this object | 2243 // If this is an AXLayoutObject, then it's okay if this object |
2223 // doesn't have a node - there are some layoutObjects that don't have associ
ated | 2244 // doesn't have a node - there are some layoutObjects that don't have associ
ated |
2224 // nodes, like scroll areas and css-generated text. | 2245 // nodes, like scroll areas and css-generated text. |
2225 if (!node() && !isAXLayoutObject()) | 2246 if (!node() && !isAXLayoutObject()) |
2226 return false; | 2247 return false; |
2227 | 2248 |
| 2249 if (node() && isHTMLMapElement(node())) |
| 2250 return false; |
| 2251 |
2228 // Elements that should not have children | 2252 // Elements that should not have children |
2229 switch (roleValue()) { | 2253 switch (roleValue()) { |
2230 case ImageRole: | 2254 case ImageRole: |
2231 case ButtonRole: | 2255 case ButtonRole: |
2232 case PopUpButtonRole: | 2256 case PopUpButtonRole: |
2233 case CheckBoxRole: | 2257 case CheckBoxRole: |
2234 case RadioButtonRole: | 2258 case RadioButtonRole: |
2235 case SwitchRole: | 2259 case SwitchRole: |
2236 case TabRole: | 2260 case TabRole: |
2237 case ToggleButtonRole: | 2261 case ToggleButtonRole: |
(...skipping 517 matching lines...) Loading... |
2755 source.text = textAlternative; | 2779 source.text = textAlternative; |
2756 *foundTextAlternative = true; | 2780 *foundTextAlternative = true; |
2757 } else { | 2781 } else { |
2758 return textAlternative; | 2782 return textAlternative; |
2759 } | 2783 } |
2760 } | 2784 } |
2761 } | 2785 } |
2762 return textAlternative; | 2786 return textAlternative; |
2763 } | 2787 } |
2764 | 2788 |
2765 // 5.8 img Element | 2789 // 5.8 img or area Element |
2766 if (isHTMLImageElement(node())) { | 2790 if (isHTMLImageElement(node()) || isHTMLAreaElement(node()) || (layoutObject
() && layoutObject()->isSVGImage())) { |
2767 // alt | 2791 // alt |
2768 nameFrom = AXNameFromAttribute; | 2792 nameFrom = AXNameFromAttribute; |
2769 if (nameSources) { | 2793 if (nameSources) { |
2770 nameSources->append(NameSource(*foundTextAlternative, altAttr)); | 2794 nameSources->append(NameSource(*foundTextAlternative, altAttr)); |
2771 nameSources->last().type = nameFrom; | 2795 nameSources->last().type = nameFrom; |
2772 } | 2796 } |
2773 const AtomicString& alt = getAttribute(altAttr); | 2797 const AtomicString& alt = getAttribute(altAttr); |
2774 if (!alt.isNull()) { | 2798 if (!alt.isNull()) { |
2775 textAlternative = alt; | 2799 textAlternative = alt; |
2776 if (nameSources) { | 2800 if (nameSources) { |
(...skipping 56 matching lines...) Loading... |
2833 source.text = textAlternative; | 2857 source.text = textAlternative; |
2834 *foundTextAlternative = true; | 2858 *foundTextAlternative = true; |
2835 } else { | 2859 } else { |
2836 return textAlternative; | 2860 return textAlternative; |
2837 } | 2861 } |
2838 } | 2862 } |
2839 | 2863 |
2840 return textAlternative; | 2864 return textAlternative; |
2841 } | 2865 } |
2842 | 2866 |
| 2867 // Fieldset / legend. |
| 2868 if (isHTMLFieldSetElement(node())) { |
| 2869 nameFrom = AXNameFromRelatedElement; |
| 2870 if (nameSources) { |
| 2871 nameSources->append(NameSource(*foundTextAlternative)); |
| 2872 nameSources->last().type = nameFrom; |
| 2873 nameSources->last().nativeSource = AXTextFromNativeHTMLLegend; |
| 2874 } |
| 2875 HTMLElement* legend = toHTMLFieldSetElement(node())->legend(); |
| 2876 if (legend) { |
| 2877 AXObject* legendAXObject = axObjectCache().getOrCreate(legend); |
| 2878 // Avoid an infinite loop |
| 2879 if (legendAXObject && !visited.contains(legendAXObject)) { |
| 2880 textAlternative = recursiveTextAlternative(*legendAXObject, fals
e, visited); |
| 2881 |
| 2882 if (relatedObjects) { |
| 2883 localRelatedObjects.append(new NameSourceRelatedObject(legen
dAXObject, textAlternative)); |
| 2884 *relatedObjects = localRelatedObjects; |
| 2885 localRelatedObjects.clear(); |
| 2886 } |
| 2887 |
| 2888 if (nameSources) { |
| 2889 NameSource& source = nameSources->last(); |
| 2890 source.relatedObjects = *relatedObjects; |
| 2891 source.text = textAlternative; |
| 2892 *foundTextAlternative = true; |
| 2893 } else { |
| 2894 return textAlternative; |
| 2895 } |
| 2896 } |
| 2897 } |
| 2898 } |
| 2899 |
2843 return textAlternative; | 2900 return textAlternative; |
2844 } | 2901 } |
2845 | 2902 |
2846 String AXNodeObject::description(AXNameFrom nameFrom, AXDescriptionFrom& descrip
tionFrom, AXObjectVector* descriptionObjects) const | 2903 String AXNodeObject::description(AXNameFrom nameFrom, AXDescriptionFrom& descrip
tionFrom, AXObjectVector* descriptionObjects) const |
2847 { | 2904 { |
2848 AXRelatedObjectVector relatedObjects; | 2905 AXRelatedObjectVector relatedObjects; |
2849 String result = description(nameFrom, descriptionFrom, nullptr, &relatedObje
cts); | 2906 String result = description(nameFrom, descriptionFrom, nullptr, &relatedObje
cts); |
2850 if (descriptionObjects) { | 2907 if (descriptionObjects) { |
2851 descriptionObjects->clear(); | 2908 descriptionObjects->clear(); |
2852 for (size_t i = 0; i < relatedObjects.size(); i++) | 2909 for (size_t i = 0; i < relatedObjects.size(); i++) |
(...skipping 159 matching lines...) Loading... |
3012 description = title; | 3069 description = title; |
3013 if (descriptionSources) { | 3070 if (descriptionSources) { |
3014 foundDescription = true; | 3071 foundDescription = true; |
3015 descriptionSources->last().text = description; | 3072 descriptionSources->last().text = description; |
3016 } else { | 3073 } else { |
3017 return description; | 3074 return description; |
3018 } | 3075 } |
3019 } | 3076 } |
3020 } | 3077 } |
3021 | 3078 |
| 3079 // aria-help. |
| 3080 // FIXME: this is not part of the official standard, but it's needed because
the built-in date/time controls use it. |
| 3081 descriptionFrom = AXDescriptionFromAttribute; |
| 3082 if (descriptionSources) { |
| 3083 descriptionSources->append(DescriptionSource(foundDescription, aria_help
Attr)); |
| 3084 descriptionSources->last().type = descriptionFrom; |
| 3085 } |
| 3086 const AtomicString& help = getAttribute(aria_helpAttr); |
| 3087 if (!help.isEmpty()) { |
| 3088 description = help; |
| 3089 if (descriptionSources) { |
| 3090 foundDescription = true; |
| 3091 descriptionSources->last().text = description; |
| 3092 } else { |
| 3093 return description; |
| 3094 } |
| 3095 } |
| 3096 |
3022 descriptionFrom = AXDescriptionFromUninitialized; | 3097 descriptionFrom = AXDescriptionFromUninitialized; |
3023 | 3098 |
3024 if (foundDescription) { | 3099 if (foundDescription) { |
3025 for (size_t i = 0; i < descriptionSources->size(); ++i) { | 3100 for (size_t i = 0; i < descriptionSources->size(); ++i) { |
3026 if (!(*descriptionSources)[i].text.isNull() && !(*descriptionSources
)[i].superseded) { | 3101 if (!(*descriptionSources)[i].text.isNull() && !(*descriptionSources
)[i].superseded) { |
3027 DescriptionSource& descriptionSource = (*descriptionSources)[i]; | 3102 DescriptionSource& descriptionSource = (*descriptionSources)[i]; |
3028 descriptionFrom = descriptionSource.type; | 3103 descriptionFrom = descriptionSource.type; |
3029 if (!descriptionSource.relatedObjects.isEmpty()) | 3104 if (!descriptionSource.relatedObjects.isEmpty()) |
3030 *relatedObjects = descriptionSource.relatedObjects; | 3105 *relatedObjects = descriptionSource.relatedObjects; |
3031 return descriptionSource.text; | 3106 return descriptionSource.text; |
3032 } | 3107 } |
3033 } | 3108 } |
3034 } | 3109 } |
3035 | 3110 |
3036 return String(); | 3111 return String(); |
3037 } | 3112 } |
3038 | 3113 |
3039 DEFINE_TRACE(AXNodeObject) | 3114 DEFINE_TRACE(AXNodeObject) |
3040 { | 3115 { |
3041 visitor->trace(m_node); | 3116 visitor->trace(m_node); |
3042 AXObject::trace(visitor); | 3117 AXObject::trace(visitor); |
3043 } | 3118 } |
3044 | 3119 |
3045 } // namespace blink | 3120 } // namespace blin |
OLD | NEW |