| 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...) Expand all 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...) Expand 10 before | Expand all | Expand 10 after 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...) Expand 10 before | Expand all | Expand 10 after 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...) Expand 10 before | Expand all | Expand 10 after 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...) Expand 10 before | Expand all | Expand 10 after 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...) Expand 10 before | Expand all | Expand 10 after 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...) Expand 10 before | Expand all | Expand 10 after 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...) Expand 10 before | Expand all | Expand 10 after 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 |